From 0a8b99a0740996b3c89bfd4f323c8744cd86cf10 Mon Sep 17 00:00:00 2001 From: "M. Palanikannan" <73993394+Palanikannan1437@users.noreply.github.com> Date: Fri, 13 Oct 2023 12:05:49 +0530 Subject: [PATCH] feat: Editor Core Packaging and Restructuring (#2358) * initialized tiptap component with common tailwind config * added common tailwind config to web * abstracted upload and delete functions * removed tiptap pro extension * fixed types * removed old tailwind config and fixed plane package imports * exported tiptap editor with and without ref * updated package name to @plane/editor * finally fixed import errors * added turbo dependency for tiptap * reverted back types and fixed tailwind * migrated all components to use the common package * removed old tiptap dependency * improved dev experience to build the tiptap package before starting dev server * resolved lock life and missing deps * fixed dependency issue with react type resolution * chore: updated pulls build CI for using turbo builds * comment editor basic version added * new structure of editor components * refactored editor to not require workspace slug * added seperation of extensions and props * refactoring to LiteTextEditor and RichTextEditor * fixed global css issue with highlight js * refactoring tiptap to core/lite/rich text editor * read only editor support added * replaced all read-only instances * trimming html at start and end of content added * onSubmit on enterkey captured * removed absolute imports from editor/core package * removed absolute imports from lite-text-editor * removed absolute imports from rich-text-editor * fixed dependencies in editor package * fixed tailwind config for editor * Enter key behaviour added for Comments * fixed modal form issue * added comment editor with fixed menu * added support for range commands * modified turbo config for build pipeline of space and web projects * fixed shift enter behavior for lists * removed extra margin from access specifiers * removed tiptap instance from web * fixed bugs returning empty editor boxes * fixed toggle Underline behvaiour * updated bubble menu to use core package's utilities * added editor/core readme and fixed imports * fixed ts issues with link plugin * added usage of common dependance for slash commands * completed core package's documentation * fixed tsconfig by removing path aliases * Completed readme for rich-text-editor * Added rich text editor documentation * changed readme title of core package --------- Co-authored-by: Henit Chobisa --- .github/workflows/build-test-pull-request.yml | 6 +- .gitignore | 4 + package.json | 9 +- packages/editor/core/Readme.md | 112 ++ packages/editor/core/package.json | 76 + packages/editor/core/postcss.config.js | 9 + packages/editor/core/src/index.ts | 19 + .../editor/core/src/lib/editor-commands.ts | 91 ++ packages/editor/core/src/lib/utils.ts | 45 + packages/editor/core/src/styles/editor.css | 231 +++ packages/editor/core/src/styles/tailwind.css | 3 + .../editor/core/src/types/delete-image.ts | 1 + .../editor/core/src/types/upload-image.ts | 1 + .../src/ui/components/editor-container.tsx | 20 + .../core/src/ui/components/editor-content.tsx | 20 + .../src/ui/extensions/image}/image-resize.tsx | 0 .../core/src/ui/extensions/image/index.tsx | 23 + .../ui/extensions/image/read-only-image.tsx | 17 + .../editor/core/src/ui}/extensions/index.tsx | 70 +- .../core/src/ui/extensions/table/index.ts | 0 .../src/ui}/extensions/table/table-cell.ts | 0 .../src/ui}/extensions/table/table-header.ts | 0 .../editor/core/src/ui/hooks/useEditor.tsx | 69 + .../core/src/ui/hooks/useReadOnlyEditor.tsx | 43 + packages/editor/core/src/ui/index.tsx | 92 ++ .../core/src/ui/menus/menu-items/index.tsx | 109 ++ .../table-menu/InsertBottomTableIcon.tsx | 0 .../menus}/table-menu/InsertLeftTableIcon.tsx | 0 .../table-menu/InsertRightTableIcon.tsx | 0 .../menus}/table-menu/InsertTopTableIcon.tsx | 0 .../core/src/ui/menus}/table-menu/index.tsx | 11 +- .../core/src/ui/menus/table-menu/tooltip.tsx | 77 + .../core/src/ui}/plugins/delete-image.tsx | 10 +- .../core/src/ui}/plugins/upload-image.tsx | 45 +- .../editor/core/src/ui}/props.tsx | 12 +- .../core/src/ui/read-only/extensions.tsx | 92 ++ .../editor/core/src/ui/read-only/props.tsx | 8 + packages/editor/core/tailwind.config.js | 6 + packages/editor/core/tsconfig.json | 12 + packages/editor/core/tsup.config.ts | 11 + packages/editor/lite-text-editor/Readme.md | 97 ++ packages/editor/lite-text-editor/package.json | 63 + .../editor/lite-text-editor/postcss.config.js | 9 + packages/editor/lite-text-editor/src/index.ts | 2 + .../ui/extensions/custom-list-extension.tsx | 9 + .../src/ui/extensions/enter-key-extension.tsx | 16 + .../src/ui/extensions/index.tsx | 7 + .../editor/lite-text-editor/src/ui/index.tsx | 95 ++ .../src/ui/menus/fixed-menu/icon.tsx | 13 + .../src/ui/menus/fixed-menu/index.tsx | 170 +++ .../src/ui/read-only/index.tsx | 54 + .../lite-text-editor/src/ui/tooltip.tsx | 77 + .../lite-text-editor/tailwind.config.js | 6 + .../editor/lite-text-editor/tsconfig.json | 12 + .../editor/lite-text-editor/tsup.config.ts | 11 + packages/editor/rich-text-editor/Readme.md | 99 ++ packages/editor/rich-text-editor/package.json | 61 + .../editor/rich-text-editor/postcss.config.js | 9 + packages/editor/rich-text-editor/src/index.ts | 4 + .../src/styles/github-dark.css | 2 + .../src/ui/extensions/index.tsx | 59 + .../src/ui/extensions/slash-command.tsx | 256 ++-- .../editor/rich-text-editor/src/ui/index.tsx | 80 + .../src/ui/menus}/bubble-menu/index.tsx | 39 +- .../ui/menus}/bubble-menu/link-selector.tsx | 8 +- .../ui/menus}/bubble-menu/node-selector.tsx | 68 +- .../src/ui/read-only/index.tsx | 54 + .../rich-text-editor/tailwind.config.js | 6 + .../editor/rich-text-editor/tsconfig.json | 12 + .../editor/rich-text-editor/tsup.config.ts | 11 + packages/tailwind-config-custom/package.json | 9 +- .../tailwind-config-custom/tailwind.config.js | 19 +- packages/tsconfig/base.json | 4 +- .../{react-library.json => react.json} | 4 +- .../peek-overview/comment/add-comment.tsx | 14 +- .../comment/comment-detail-card.tsx | 27 +- .../issues/peek-overview/issue-details.tsx | 52 +- space/components/tiptap/bubble-menu/index.tsx | 121 -- .../tiptap/bubble-menu/link-selector.tsx | 92 -- .../tiptap/bubble-menu/node-selector.tsx | 130 -- .../bubble-menu/utils/link-validator.tsx | 11 - .../tiptap/extensions/updated-image.tsx | 22 - space/components/tiptap/index.tsx | 110 -- .../tiptap/plugins/delete-image.tsx | 68 - .../tiptap/plugins/upload-image.tsx | 127 -- space/components/tiptap/props.tsx | 69 - space/components/tiptap/table-menu/index.tsx | 143 -- space/components/tiptap/utils.ts | 6 - space/package.json | 29 +- space/services/file.service.ts | 26 +- space/styles/editor.css | 2 +- turbo.json | 58 +- .../core/modals/gpt-assistant-modal.tsx | 31 +- .../core/modals/image-upload-modal.tsx | 10 +- web/components/issues/comment/add-comment.tsx | 72 +- .../issues/comment/comment-card.tsx | 15 +- web/components/issues/description-form.tsx | 71 +- web/components/issues/draft-issue-form.tsx | 45 +- web/components/issues/form.tsx | 57 +- web/components/issues/sub-issues/issue.tsx | 1 + .../issues/sub-issues/issues-list.tsx | 1 + web/components/issues/sub-issues/root.tsx | 4 + .../pages/create-update-block-inline.tsx | 14 +- web/components/pages/single-page-block.tsx | 8 +- .../bubble-menu/utils/link-validator.tsx | 11 - .../tiptap/extensions/image-resize.tsx | 44 - web/components/tiptap/extensions/index.tsx | 149 -- .../tiptap/extensions/table/table-cell.ts | 32 - .../tiptap/extensions/table/table-header.ts | 7 - .../tiptap/extensions/table/table.ts | 9 - .../tiptap/extensions/updated-image.tsx | 22 - web/components/tiptap/index.tsx | 110 -- web/components/tiptap/slash-command/index.tsx | 365 ----- .../table-menu/InsertBottomTableIcon.tsx | 16 - .../tiptap/table-menu/InsertLeftTableIcon.tsx | 15 - .../table-menu/InsertRightTableIcon.tsx | 16 - .../tiptap/table-menu/InsertTopTableIcon.tsx | 15 - web/components/tiptap/utils.ts | 6 - web/components/web-view/add-comment.tsx | 72 +- .../{commend-card.tsx => comment-card.tsx} | 20 +- web/components/web-view/index.ts | 2 +- .../web-view/issue-web-view-form.tsx | 65 +- web/package.json | 31 +- web/pages/[workspaceSlug]/editor.tsx | 9 +- .../[workspaceSlug]/me/profile/activity.tsx | 18 +- web/pages/m/[workspaceSlug]/editor.tsx | 13 +- web/postcss.config.js | 9 +- web/services/file.service.ts | 27 +- web/styles/editor.css | 2 +- web/tailwind.config.js | 6 +- yarn.lock | 1330 +++++++++++------ 131 files changed, 3730 insertions(+), 2926 deletions(-) create mode 100644 packages/editor/core/Readme.md create mode 100644 packages/editor/core/package.json create mode 100644 packages/editor/core/postcss.config.js create mode 100644 packages/editor/core/src/index.ts create mode 100644 packages/editor/core/src/lib/editor-commands.ts create mode 100644 packages/editor/core/src/lib/utils.ts create mode 100644 packages/editor/core/src/styles/editor.css create mode 100644 packages/editor/core/src/styles/tailwind.css create mode 100644 packages/editor/core/src/types/delete-image.ts create mode 100644 packages/editor/core/src/types/upload-image.ts create mode 100644 packages/editor/core/src/ui/components/editor-container.tsx create mode 100644 packages/editor/core/src/ui/components/editor-content.tsx rename {space/components/tiptap/extensions => packages/editor/core/src/ui/extensions/image}/image-resize.tsx (100%) create mode 100644 packages/editor/core/src/ui/extensions/image/index.tsx create mode 100644 packages/editor/core/src/ui/extensions/image/read-only-image.tsx rename {space/components/tiptap => packages/editor/core/src/ui}/extensions/index.tsx (54%) rename space/components/tiptap/extensions/table/table.ts => packages/editor/core/src/ui/extensions/table/index.ts (100%) rename {space/components/tiptap => packages/editor/core/src/ui}/extensions/table/table-cell.ts (100%) rename {space/components/tiptap => packages/editor/core/src/ui}/extensions/table/table-header.ts (100%) create mode 100644 packages/editor/core/src/ui/hooks/useEditor.tsx create mode 100644 packages/editor/core/src/ui/hooks/useReadOnlyEditor.tsx create mode 100644 packages/editor/core/src/ui/index.tsx create mode 100644 packages/editor/core/src/ui/menus/menu-items/index.tsx rename {space/components/tiptap => packages/editor/core/src/ui/menus}/table-menu/InsertBottomTableIcon.tsx (100%) rename {space/components/tiptap => packages/editor/core/src/ui/menus}/table-menu/InsertLeftTableIcon.tsx (100%) rename {space/components/tiptap => packages/editor/core/src/ui/menus}/table-menu/InsertRightTableIcon.tsx (100%) rename {space/components/tiptap => packages/editor/core/src/ui/menus}/table-menu/InsertTopTableIcon.tsx (100%) rename {web/components/tiptap => packages/editor/core/src/ui/menus}/table-menu/index.tsx (92%) create mode 100644 packages/editor/core/src/ui/menus/table-menu/tooltip.tsx rename {web/components/tiptap => packages/editor/core/src/ui}/plugins/delete-image.tsx (84%) rename {web/components/tiptap => packages/editor/core/src/ui}/plugins/upload-image.tsx (78%) rename {web/components/tiptap => packages/editor/core/src/ui}/props.tsx (85%) create mode 100644 packages/editor/core/src/ui/read-only/extensions.tsx create mode 100644 packages/editor/core/src/ui/read-only/props.tsx create mode 100644 packages/editor/core/tailwind.config.js create mode 100644 packages/editor/core/tsconfig.json create mode 100644 packages/editor/core/tsup.config.ts create mode 100644 packages/editor/lite-text-editor/Readme.md create mode 100644 packages/editor/lite-text-editor/package.json create mode 100644 packages/editor/lite-text-editor/postcss.config.js create mode 100644 packages/editor/lite-text-editor/src/index.ts create mode 100644 packages/editor/lite-text-editor/src/ui/extensions/custom-list-extension.tsx create mode 100644 packages/editor/lite-text-editor/src/ui/extensions/enter-key-extension.tsx create mode 100644 packages/editor/lite-text-editor/src/ui/extensions/index.tsx create mode 100644 packages/editor/lite-text-editor/src/ui/index.tsx create mode 100644 packages/editor/lite-text-editor/src/ui/menus/fixed-menu/icon.tsx create mode 100644 packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx create mode 100644 packages/editor/lite-text-editor/src/ui/read-only/index.tsx create mode 100644 packages/editor/lite-text-editor/src/ui/tooltip.tsx create mode 100644 packages/editor/lite-text-editor/tailwind.config.js create mode 100644 packages/editor/lite-text-editor/tsconfig.json create mode 100644 packages/editor/lite-text-editor/tsup.config.ts create mode 100644 packages/editor/rich-text-editor/Readme.md create mode 100644 packages/editor/rich-text-editor/package.json create mode 100644 packages/editor/rich-text-editor/postcss.config.js create mode 100644 packages/editor/rich-text-editor/src/index.ts create mode 100644 packages/editor/rich-text-editor/src/styles/github-dark.css create mode 100644 packages/editor/rich-text-editor/src/ui/extensions/index.tsx rename space/components/tiptap/slash-command/index.tsx => packages/editor/rich-text-editor/src/ui/extensions/slash-command.tsx (51%) create mode 100644 packages/editor/rich-text-editor/src/ui/index.tsx rename {web/components/tiptap => packages/editor/rich-text-editor/src/ui/menus}/bubble-menu/index.tsx (70%) rename {web/components/tiptap => packages/editor/rich-text-editor/src/ui/menus}/bubble-menu/link-selector.tsx (93%) rename {web/components/tiptap => packages/editor/rich-text-editor/src/ui/menus}/bubble-menu/node-selector.tsx (58%) create mode 100644 packages/editor/rich-text-editor/src/ui/read-only/index.tsx create mode 100644 packages/editor/rich-text-editor/tailwind.config.js create mode 100644 packages/editor/rich-text-editor/tsconfig.json create mode 100644 packages/editor/rich-text-editor/tsup.config.ts rename packages/tsconfig/{react-library.json => react.json} (77%) delete mode 100644 space/components/tiptap/bubble-menu/index.tsx delete mode 100644 space/components/tiptap/bubble-menu/link-selector.tsx delete mode 100644 space/components/tiptap/bubble-menu/node-selector.tsx delete mode 100644 space/components/tiptap/bubble-menu/utils/link-validator.tsx delete mode 100644 space/components/tiptap/extensions/updated-image.tsx delete mode 100644 space/components/tiptap/index.tsx delete mode 100644 space/components/tiptap/plugins/delete-image.tsx delete mode 100644 space/components/tiptap/plugins/upload-image.tsx delete mode 100644 space/components/tiptap/props.tsx delete mode 100644 space/components/tiptap/table-menu/index.tsx delete mode 100644 space/components/tiptap/utils.ts delete mode 100644 web/components/tiptap/bubble-menu/utils/link-validator.tsx delete mode 100644 web/components/tiptap/extensions/image-resize.tsx delete mode 100644 web/components/tiptap/extensions/index.tsx delete mode 100644 web/components/tiptap/extensions/table/table-cell.ts delete mode 100644 web/components/tiptap/extensions/table/table-header.ts delete mode 100644 web/components/tiptap/extensions/table/table.ts delete mode 100644 web/components/tiptap/extensions/updated-image.tsx delete mode 100644 web/components/tiptap/index.tsx delete mode 100644 web/components/tiptap/slash-command/index.tsx delete mode 100644 web/components/tiptap/table-menu/InsertBottomTableIcon.tsx delete mode 100644 web/components/tiptap/table-menu/InsertLeftTableIcon.tsx delete mode 100644 web/components/tiptap/table-menu/InsertRightTableIcon.tsx delete mode 100644 web/components/tiptap/table-menu/InsertTopTableIcon.tsx delete mode 100644 web/components/tiptap/utils.ts rename web/components/web-view/{commend-card.tsx => comment-card.tsx} (93%) diff --git a/.github/workflows/build-test-pull-request.yml b/.github/workflows/build-test-pull-request.yml index 6dc7ae1e5..c74975f48 100644 --- a/.github/workflows/build-test-pull-request.yml +++ b/.github/workflows/build-test-pull-request.yml @@ -36,15 +36,13 @@ jobs: - name: Build Plane's Main App if: steps.changed-files.outputs.web_any_changed == 'true' run: | - cd web yarn - yarn build + yarn build --filter=web - name: Build Plane's Deploy App if: steps.changed-files.outputs.deploy_any_changed == 'true' run: | - cd space yarn - yarn build + yarn build --filter=space diff --git a/.gitignore b/.gitignore index c9eb8f652..f0c28f9de 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,7 @@ pnpm-lock.yaml pnpm-workspace.yaml .npmrc + + +## packages +dist diff --git a/package.json b/package.json index 1f2f96414..ba0fd9a61 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,11 @@ "workspaces": [ "web", "space", - "packages/*" + "packages/editor/*", + "packages/eslint-config-custom", + "packages/tailwind-config-custom", + "packages/tsconfig", + "packages/ui" ], "scripts": { "build": "turbo run build", @@ -25,5 +29,8 @@ "tailwindcss": "^3.3.3", "turbo": "latest" }, + "resolutions": { + "@types/react": "18.2.0" + }, "packageManager": "yarn@1.22.19" } diff --git a/packages/editor/core/Readme.md b/packages/editor/core/Readme.md new file mode 100644 index 000000000..56d1a502c --- /dev/null +++ b/packages/editor/core/Readme.md @@ -0,0 +1,112 @@ +# @plane/editor-core + +## Description + +The `@plane/editor-core` package serves as the foundation for our editor system. It provides the base functionality for our other editor packages, but it will not be used directly in any of the projects but only for extending other editors. + +## Utilities + +We provide a wide range of utilities for extending the core itself. + +1. Merging classes and custom styling +2. Adding new extensions +3. Adding custom props +4. Base menu items, and their commands + +This allows for extensive customization and flexibility in the Editors created using our `editor-core` package. + +### Here's a detailed overview of what's exported + +1. useEditor - A hook that you can use to extend the Plane editor. + + | Prop | Type | Description | + | --- | --- | --- | + | `extensions` | `Extension[]` | An array of custom extensions you want to add into the editor to extend it's core features | + | `editorProps` | `EditorProps` | Extend the editor props by passing in a custom props object | + | `uploadFile` | `(file: File) => Promise` | A function that handles file upload. It takes a file as input and handles the process of uploading that file. | + | `deleteFile` | `(assetUrlWithWorkspaceId: string) => Promise` | A function that handles deleting an image. It takes the asset url from your bucket and handles the process of deleting that image. | + | `value` | `html string` | The initial content of the editor. | + | `debouncedUpdatesEnabled` | `boolean` | If set to true, the `onChange` event handler is debounced, meaning it will only be invoked after the specified delay (default 1500ms) once the user has stopped typing. | + | `onChange` | `(json: any, html: string) => void` | This function is invoked whenever the content of the editor changes. It is passed the new content in both JSON and HTML formats. | + | `setIsSubmitting` | `(isSubmitting: "submitting" \| "submitted" \| "saved") => void` | This function is called to update the submission status. | + | `setShouldShowAlert` | `(showAlert: boolean) => void` | This function is used to show or hide an alert in case of content not being "saved". | + | `forwardedRef` | `any` | Pass this in whenever you want to control the editor's state from an external component | + +2. useReadOnlyEditor - A hook that can be used to extend a Read Only instance of the core editor. + + | Prop | Type | Description | + | --- | --- | --- | + | `value` | `string` | The initial content of the editor. | + | `forwardedRef` | `any` | Pass this in whenever you want to control the editor's state from an external component | + | `extensions` | `Extension[]` | An array of custom extensions you want to add into the editor to extend it's core features | + | `editorProps` | `EditorProps` | Extend the editor props by passing in a custom props object | + +3. Items and Commands - H1, H2, H3, task list, quote, code block, etc's methods. + +4. UI Wrappers + +- `EditorContainer` - Wrap your Editor Container with this to apply base classes and styles. +- `EditorContentWrapper` - Use this to get Editor's Content and base menus. + +5. Extending with Custom Styles + +```ts +const customEditorClassNames = getEditorClassNames({ noBorder, borderOnFocus, customClassName }); +``` + +## Core features + +- **Content Trimming**: The Editor’s content is now automatically trimmed of empty line breaks from the start and end before submitting it to the backend. This ensures cleaner, more consistent data. +- **Value Cleaning**: The Editor’s value is cleaned at the editor core level, eliminating the need for additional validation before sending from our app. This results in cleaner code and less potential for errors. +- **Turbo Pipeline**: Added a turbo pipeline for both dev and build tasks for projects depending on the editor package. + +```json + "web#develop": { + "cache": false, + "persistent": true, + "dependsOn": [ + "@plane/lite-text-editor#build", + "@plane/rich-text-editor#build" + ] + }, + "space#develop": { + "cache": false, + "persistent": true, + "dependsOn": [ + "@plane/lite-text-editor#build", + "@plane/rich-text-editor#build" + ] + }, + "web#build": { + "cache": true, + "dependsOn": [ + "@plane/lite-text-editor#build", + "@plane/rich-text-editor#build" + ] + }, + "space#build": { + "cache": true, + "dependsOn": [ + "@plane/lite-text-editor#build", + "@plane/rich-text-editor#build" + ] + }, + +``` + +## Base extensions included + +- BulletList +- OrderedList +- Blockquote +- Code +- Gapcursor +- Link +- Image +- Basic Marks + - Underline + - TextStyle + - Color +- TaskList +- Markdown +- Table diff --git a/packages/editor/core/package.json b/packages/editor/core/package.json new file mode 100644 index 000000000..78bf59db7 --- /dev/null +++ b/packages/editor/core/package.json @@ -0,0 +1,76 @@ +{ + "name": "@plane/editor-core", + "version": "0.0.1", + "description": "Core Editor that powers Plane", + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "files": [ + "dist/**/*" + ], + "exports": { + ".": { + "types": "./dist/index.d.mts", + "import": "./dist/index.mjs", + "module": "./dist/index.mjs" + } + }, + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "check-types": "tsc --noEmit" + }, + "peerDependencies": { + "react": "^18.2.0", + "react-dom": "18.2.0", + "next": "12.3.2", + "next-themes": "^0.2.1" + }, + "dependencies": { + "@blueprintjs/popover2": "^2.0.10", + "@tiptap/core": "^2.1.7", + "@tiptap/extension-color": "^2.1.11", + "@tiptap/extension-image": "^2.1.7", + "@tiptap/extension-link": "^2.1.7", + "@tiptap/extension-table": "^2.1.6", + "@tiptap/extension-table-cell": "^2.1.6", + "@tiptap/extension-table-header": "^2.1.6", + "@tiptap/extension-table-row": "^2.1.6", + "@tiptap/extension-task-item": "^2.1.7", + "@tiptap/extension-task-list": "^2.1.7", + "@tiptap/extension-text-style": "^2.1.11", + "@tiptap/extension-underline": "^2.1.7", + "@tiptap/pm": "^2.1.7", + "@tiptap/react": "^2.1.7", + "@tiptap/starter-kit": "^2.1.10", + "@types/react": "^18.2.5", + "@types/react-dom": "18.0.11", + "@types/node": "18.15.3", + "class-variance-authority": "^0.7.0", + "clsx": "^1.2.1", + "eslint": "8.36.0", + "eslint-config-next": "13.2.4", + "eventsource-parser": "^0.1.0", + "lucide-react": "^0.244.0", + "react-markdown": "^8.0.7", + "tailwind-merge": "^1.14.0", + "tippy.js": "^6.3.7", + "tiptap-markdown": "^0.8.2", + "use-debounce": "^9.0.4" + }, + "devDependencies": { + "eslint": "^7.32.0", + "postcss": "^8.4.29", + "tailwind-config-custom": "*", + "tsconfig": "*", + "tsup": "^7.2.0", + "typescript": "4.9.5" + }, + "keywords": [ + "editor", + "rich-text", + "markdown", + "nextjs", + "react" + ] +} diff --git a/packages/editor/core/postcss.config.js b/packages/editor/core/postcss.config.js new file mode 100644 index 000000000..07aa434b2 --- /dev/null +++ b/packages/editor/core/postcss.config.js @@ -0,0 +1,9 @@ +// If you want to use other PostCSS plugins, see the following: +// https://tailwindcss.com/docs/using-with-preprocessors + +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/editor/core/src/index.ts b/packages/editor/core/src/index.ts new file mode 100644 index 000000000..590b17172 --- /dev/null +++ b/packages/editor/core/src/index.ts @@ -0,0 +1,19 @@ +// styles +// import "./styles/tailwind.css"; +// import "./styles/editor.css"; + +// utils +export * from "./lib/utils"; +export { startImageUpload } from "./ui/plugins/upload-image"; + +// components +export { EditorContainer } from "./ui/components/editor-container"; +export { EditorContentWrapper } from "./ui/components/editor-content"; + +// hooks +export { useEditor } from "./ui/hooks/useEditor"; +export { useReadOnlyEditor } from "./ui/hooks/useReadOnlyEditor"; + +// helper items +export * from "./ui/menus/menu-items"; +export * from "./lib/editor-commands"; diff --git a/packages/editor/core/src/lib/editor-commands.ts b/packages/editor/core/src/lib/editor-commands.ts new file mode 100644 index 000000000..497a63ca6 --- /dev/null +++ b/packages/editor/core/src/lib/editor-commands.ts @@ -0,0 +1,91 @@ +import { Editor, Range } from "@tiptap/core"; +import { UploadImage } from "../types/upload-image"; +import { startImageUpload } from "../ui/plugins/upload-image"; + +export const toggleHeadingOne = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run(); + else editor.chain().focus().toggleHeading({ level: 1 }).run() +}; + +export const toggleHeadingTwo = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).setNode("heading", { level: 2 }).run(); + else editor.chain().focus().toggleHeading({ level: 2 }).run() +}; + +export const toggleHeadingThree = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).setNode("heading", { level: 3 }).run(); + else editor.chain().focus().toggleHeading({ level: 3 }).run() +}; + +export const toggleBold = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).toggleBold().run(); + else editor.chain().focus().toggleBold().run(); +}; + +export const toggleItalic = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).toggleItalic().run(); + else editor.chain().focus().toggleItalic().run(); +}; + +export const toggleUnderline = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).toggleUnderline().run(); + else editor.chain().focus().toggleUnderline().run(); +}; + +export const toggleCode = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).toggleCode().run(); + else editor.chain().focus().toggleCode().run(); +}; +export const toggleOrderedList = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).toggleOrderedList().run(); + else editor.chain().focus().toggleOrderedList().run(); +}; + +export const toggleBulletList = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).toggleBulletList().run(); + else editor.chain().focus().toggleBulletList().run(); +}; + +export const toggleTaskList = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).toggleTaskList().run(); + else editor.chain().focus().toggleTaskList().run() +}; + +export const toggleStrike = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).toggleStrike().run(); + else editor.chain().focus().toggleStrike().run(); +}; + +export const toggleBlockquote = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").toggleBlockquote().run(); + else editor.chain().focus().toggleNode("paragraph", "paragraph").toggleBlockquote().run(); +}; + +export const insertTableCommand = (editor: Editor, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(); + else editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(); +}; + +export const unsetLinkEditor = (editor: Editor) => { + editor.chain().focus().unsetLink().run(); +}; + +export const setLinkEditor = (editor: Editor, url: string) => { + editor.chain().focus().setLink({ href: url }).run(); +}; + +export const insertImageCommand = (editor: Editor, uploadFile: UploadImage, setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void, range?: Range) => { + if (range) editor.chain().focus().deleteRange(range).run(); + const input = document.createElement("input"); + input.type = "file"; + input.accept = "image/*"; + input.onchange = async () => { + if (input.files?.length) { + const file = input.files[0]; + const pos = editor.view.state.selection.from; + startImageUpload(file, editor.view, pos, uploadFile, setIsSubmitting); + } + }; + input.click(); +}; + diff --git a/packages/editor/core/src/lib/utils.ts b/packages/editor/core/src/lib/utils.ts new file mode 100644 index 000000000..484674780 --- /dev/null +++ b/packages/editor/core/src/lib/utils.ts @@ -0,0 +1,45 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; +interface EditorClassNames { + noBorder?: boolean; + borderOnFocus?: boolean; + customClassName?: string; +} + +export const getEditorClassNames = ({ noBorder, borderOnFocus, customClassName }: EditorClassNames) => cn( + 'relative w-full max-w-full sm:rounded-lg mt-2 p-3 relative focus:outline-none rounded-md', + noBorder ? '' : 'border border-custom-border-200', + borderOnFocus ? 'focus:border border-custom-border-300' : 'focus:border-0', + customClassName +); + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +export const findTableAncestor = ( + node: Node | null +): HTMLTableElement | null => { + while (node !== null && node.nodeName !== "TABLE") { + node = node.parentNode; + } + return node as HTMLTableElement; +}; + +export const getTrimmedHTML = (html: string) => { + html = html.replace(/^(

<\/p>)+/, ''); + html = html.replace(/(

<\/p>)+$/, ''); + return html; +} + +export const isValidHttpUrl = (string: string): boolean => { + let url: URL; + + try { + url = new URL(string); + } catch (_) { + return false; + } + + return url.protocol === "http:" || url.protocol === "https:"; +} diff --git a/packages/editor/core/src/styles/editor.css b/packages/editor/core/src/styles/editor.css new file mode 100644 index 000000000..85d881eeb --- /dev/null +++ b/packages/editor/core/src/styles/editor.css @@ -0,0 +1,231 @@ +.ProseMirror p.is-editor-empty:first-child::before { + content: attr(data-placeholder); + float: left; + color: rgb(var(--color-text-400)); + pointer-events: none; + height: 0; +} + +.ProseMirror .is-empty::before { + content: attr(data-placeholder); + float: left; + color: rgb(var(--color-text-400)); + pointer-events: none; + height: 0; +} + +/* Custom image styles */ + +.ProseMirror img { + transition: filter 0.1s ease-in-out; + + &:hover { + cursor: pointer; + filter: brightness(90%); + } + + &.ProseMirror-selectednode { + outline: 3px solid #5abbf7; + filter: brightness(90%); + } +} + +.ProseMirror-gapcursor:after { + border-top: 1px solid rgb(var(--color-text-100)) !important; +} + +/* Custom TODO list checkboxes – shoutout to this awesome tutorial: https://moderncss.dev/pure-css-custom-checkbox-style/ */ + +ul[data-type="taskList"] li > label { + margin-right: 0.2rem; + user-select: none; +} + +@media screen and (max-width: 768px) { + ul[data-type="taskList"] li > label { + margin-right: 0.5rem; + } +} + +ul[data-type="taskList"] li > label input[type="checkbox"] { + -webkit-appearance: none; + appearance: none; + background-color: rgb(var(--color-background-100)); + margin: 0; + cursor: pointer; + width: 1.2rem; + height: 1.2rem; + position: relative; + border: 2px solid rgb(var(--color-text-100)); + margin-right: 0.3rem; + display: grid; + place-content: center; + + &:hover { + background-color: rgb(var(--color-background-80)); + } + + &:active { + background-color: rgb(var(--color-background-90)); + } + + &::before { + content: ""; + width: 0.65em; + height: 0.65em; + transform: scale(0); + transition: 120ms transform ease-in-out; + box-shadow: inset 1em 1em; + transform-origin: center; + clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); + } + + &:checked::before { + transform: scale(1); + } +} + +ul[data-type="taskList"] li[data-checked="true"] > div > p { + color: rgb(var(--color-text-200)); + text-decoration: line-through; + text-decoration-thickness: 2px; +} + +/* Overwrite tippy-box original max-width */ + +.tippy-box { + max-width: 400px !important; +} + +.ProseMirror { + position: relative; + word-wrap: break-word; + white-space: pre-wrap; + -moz-tab-size: 4; + tab-size: 4; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + outline: none; + cursor: text; + line-height: 1.2; + font-family: inherit; + font-size: 14px; + color: inherit; + -moz-box-sizing: border-box; + box-sizing: border-box; + appearance: textfield; + -webkit-appearance: textfield; + -moz-appearance: textfield; +} + +.fadeIn { + opacity: 1; + transition: opacity 0.3s ease-in; +} + +.fadeOut { + opacity: 0; + transition: opacity 0.2s ease-out; +} + +.img-placeholder { + position: relative; + width: 35%; + + &:before { + content: ""; + box-sizing: border-box; + position: absolute; + top: 50%; + left: 45%; + width: 20px; + height: 20px; + border-radius: 50%; + border: 3px solid rgba(var(--color-text-200)); + border-top-color: rgba(var(--color-text-800)); + animation: spinning 0.6s linear infinite; + } +} + +@keyframes spinning { + to { + transform: rotate(360deg); + } +} + +#editor-container { + table { + border-collapse: collapse; + table-layout: fixed; + margin: 0; + border: 1px solid rgb(var(--color-border-200)); + width: 100%; + + td, + th { + min-width: 1em; + border: 1px solid rgb(var(--color-border-200)); + padding: 10px 15px; + vertical-align: top; + box-sizing: border-box; + position: relative; + transition: background-color 0.3s ease; + + > * { + margin-bottom: 0; + } + } + + th { + font-weight: bold; + text-align: left; + background-color: rgb(var(--color-primary-100)); + } + + td:hover { + background-color: rgba(var(--color-primary-300), 0.1); + } + + .selectedCell:after { + z-index: 2; + position: absolute; + content: ""; + left: 0; + right: 0; + top: 0; + bottom: 0; + background-color: rgba(var(--color-primary-300), 0.1); + pointer-events: none; + } + + .column-resize-handle { + position: absolute; + right: -2px; + top: 0; + bottom: -2px; + width: 2px; + background-color: rgb(var(--color-primary-400)); + pointer-events: none; + } + } +} + +.tableWrapper { + overflow-x: auto; +} + +.resize-cursor { + cursor: ew-resize; + cursor: col-resize; +} + +.ProseMirror table * p { + padding: 0px 1px; + margin: 6px 2px; +} + +.ProseMirror table * .is-empty::before { + opacity: 0; +} diff --git a/packages/editor/core/src/styles/tailwind.css b/packages/editor/core/src/styles/tailwind.css new file mode 100644 index 000000000..b5c61c956 --- /dev/null +++ b/packages/editor/core/src/styles/tailwind.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/packages/editor/core/src/types/delete-image.ts b/packages/editor/core/src/types/delete-image.ts new file mode 100644 index 000000000..40bfffe2f --- /dev/null +++ b/packages/editor/core/src/types/delete-image.ts @@ -0,0 +1 @@ +export type DeleteImage = (assetUrlWithWorkspaceId: string) => Promise; diff --git a/packages/editor/core/src/types/upload-image.ts b/packages/editor/core/src/types/upload-image.ts new file mode 100644 index 000000000..3cf1408d2 --- /dev/null +++ b/packages/editor/core/src/types/upload-image.ts @@ -0,0 +1 @@ +export type UploadImage = (file: File) => Promise; diff --git a/packages/editor/core/src/ui/components/editor-container.tsx b/packages/editor/core/src/ui/components/editor-container.tsx new file mode 100644 index 000000000..8de6298b5 --- /dev/null +++ b/packages/editor/core/src/ui/components/editor-container.tsx @@ -0,0 +1,20 @@ +import { Editor } from "@tiptap/react"; +import { ReactNode } from "react"; + +interface EditorContainerProps { + editor: Editor | null; + editorClassNames: string; + children: ReactNode; +} + +export const EditorContainer = ({ editor, editorClassNames, children }: EditorContainerProps) => ( +

{ + editor?.chain().focus().run(); + }} + className={`cursor-text ${editorClassNames}`} + > + {children} +
+); diff --git a/packages/editor/core/src/ui/components/editor-content.tsx b/packages/editor/core/src/ui/components/editor-content.tsx new file mode 100644 index 000000000..972184b08 --- /dev/null +++ b/packages/editor/core/src/ui/components/editor-content.tsx @@ -0,0 +1,20 @@ +import { Editor, EditorContent } from "@tiptap/react"; +import { ReactNode } from "react"; +import { ImageResizer } from "../extensions/image/image-resize"; +import { TableMenu } from "../menus/table-menu"; + +interface EditorContentProps { + editor: Editor | null; + editorContentCustomClassNames: string | undefined; + children?: ReactNode; +} + +export const EditorContentWrapper = ({ editor, editorContentCustomClassNames = '', children }: EditorContentProps) => ( +
+ {/* @ts-ignore */} + + {editor?.isEditable && } + {(editor?.isActive("image") && editor?.isEditable) && } + {children} +
+); diff --git a/space/components/tiptap/extensions/image-resize.tsx b/packages/editor/core/src/ui/extensions/image/image-resize.tsx similarity index 100% rename from space/components/tiptap/extensions/image-resize.tsx rename to packages/editor/core/src/ui/extensions/image/image-resize.tsx diff --git a/packages/editor/core/src/ui/extensions/image/index.tsx b/packages/editor/core/src/ui/extensions/image/index.tsx new file mode 100644 index 000000000..f9345509d --- /dev/null +++ b/packages/editor/core/src/ui/extensions/image/index.tsx @@ -0,0 +1,23 @@ +import Image from "@tiptap/extension-image"; +import TrackImageDeletionPlugin from "../../plugins/delete-image"; +import UploadImagesPlugin from "../../plugins/upload-image"; +import { DeleteImage } from "../../../types/delete-image"; + +const ImageExtension = (deleteImage: DeleteImage) => Image.extend({ + addProseMirrorPlugins() { + return [UploadImagesPlugin(), TrackImageDeletionPlugin(deleteImage)]; + }, + addAttributes() { + return { + ...this.parent?.(), + width: { + default: "35%", + }, + height: { + default: null, + }, + }; + }, +}); + +export default ImageExtension; diff --git a/packages/editor/core/src/ui/extensions/image/read-only-image.tsx b/packages/editor/core/src/ui/extensions/image/read-only-image.tsx new file mode 100644 index 000000000..73a763d04 --- /dev/null +++ b/packages/editor/core/src/ui/extensions/image/read-only-image.tsx @@ -0,0 +1,17 @@ +import Image from "@tiptap/extension-image"; + +const ReadOnlyImageExtension = Image.extend({ + addAttributes() { + return { + ...this.parent?.(), + width: { + default: "35%", + }, + height: { + default: null, + }, + }; + }, +}); + +export default ReadOnlyImageExtension; diff --git a/space/components/tiptap/extensions/index.tsx b/packages/editor/core/src/ui/extensions/index.tsx similarity index 54% rename from space/components/tiptap/extensions/index.tsx rename to packages/editor/core/src/ui/extensions/index.tsx index 8ad4e07b4..62d53e0e1 100644 --- a/space/components/tiptap/extensions/index.tsx +++ b/packages/editor/core/src/ui/extensions/index.tsx @@ -1,35 +1,26 @@ import StarterKit from "@tiptap/starter-kit"; -import HorizontalRule from "@tiptap/extension-horizontal-rule"; import TiptapLink from "@tiptap/extension-link"; -import Placeholder from "@tiptap/extension-placeholder"; import TiptapUnderline from "@tiptap/extension-underline"; import TextStyle from "@tiptap/extension-text-style"; import { Color } from "@tiptap/extension-color"; import TaskItem from "@tiptap/extension-task-item"; import TaskList from "@tiptap/extension-task-list"; import { Markdown } from "tiptap-markdown"; -import Highlight from "@tiptap/extension-highlight"; -import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight"; -import { lowlight } from "lowlight/lib/core"; -import SlashCommand from "../slash-command"; -import { InputRule } from "@tiptap/core"; import Gapcursor from "@tiptap/extension-gapcursor"; -import ts from "highlight.js/lib/languages/typescript"; - -import "highlight.js/styles/github-dark.css"; -import UpdatedImage from "./updated-image"; -import isValidHttpUrl from "../bubble-menu/utils/link-validator"; import { CustomTableCell } from "./table/table-cell"; -import { Table } from "./table/table"; +import { Table } from "./table"; import { TableHeader } from "./table/table-header"; import { TableRow } from "@tiptap/extension-table-row"; -lowlight.registerLanguage("ts", ts); +import ImageExtension from "./image"; -export const TiptapExtensions = ( - workspaceSlug: string, - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void +import { DeleteImage } from "../../types/delete-image"; +import { isValidHttpUrl } from "../../lib/utils"; + + +export const CoreEditorExtensions = ( + deleteFile: DeleteImage, ) => [ StarterKit.configure({ bulletList: { @@ -67,32 +58,6 @@ export const TiptapExtensions = ( }, gapcursor: false, }), - CodeBlockLowlight.configure({ - lowlight, - }), - HorizontalRule.extend({ - addInputRules() { - return [ - new InputRule({ - find: /^(?:---|—-|___\s|\*\*\*\s)$/, - handler: ({ state, range, commands }) => { - commands.splitBlock(); - - const attributes = {}; - const { tr } = state; - const start = range.from; - const end = range.to; - // @ts-ignore - tr.replaceWith(start - 1, end, this.type.create(attributes)); - }, - }), - ]; - }, - }).configure({ - HTMLAttributes: { - class: "mb-6 border-t border-custom-border-300", - }, - }), Gapcursor, TiptapLink.configure({ protocols: ["http", "https"], @@ -102,31 +67,14 @@ export const TiptapExtensions = ( "text-custom-primary-300 underline underline-offset-[3px] hover:text-custom-primary-500 transition-colors cursor-pointer", }, }), - UpdatedImage.configure({ + ImageExtension(deleteFile).configure({ HTMLAttributes: { class: "rounded-lg border border-custom-border-300", }, }), - Placeholder.configure({ - placeholder: ({ node }) => { - if (node.type.name === "heading") { - return `Heading ${node.attrs.level}`; - } - if (node.type.name === "image" || node.type.name === "table") { - return ""; - } - - return "Press '/' for commands..."; - }, - includeChildren: true, - }), - SlashCommand(workspaceSlug, setIsSubmitting), TiptapUnderline, TextStyle, Color, - Highlight.configure({ - multicolor: true, - }), TaskList.configure({ HTMLAttributes: { class: "not-prose pl-2", diff --git a/space/components/tiptap/extensions/table/table.ts b/packages/editor/core/src/ui/extensions/table/index.ts similarity index 100% rename from space/components/tiptap/extensions/table/table.ts rename to packages/editor/core/src/ui/extensions/table/index.ts diff --git a/space/components/tiptap/extensions/table/table-cell.ts b/packages/editor/core/src/ui/extensions/table/table-cell.ts similarity index 100% rename from space/components/tiptap/extensions/table/table-cell.ts rename to packages/editor/core/src/ui/extensions/table/table-cell.ts diff --git a/space/components/tiptap/extensions/table/table-header.ts b/packages/editor/core/src/ui/extensions/table/table-header.ts similarity index 100% rename from space/components/tiptap/extensions/table/table-header.ts rename to packages/editor/core/src/ui/extensions/table/table-header.ts diff --git a/packages/editor/core/src/ui/hooks/useEditor.tsx b/packages/editor/core/src/ui/hooks/useEditor.tsx new file mode 100644 index 000000000..837700915 --- /dev/null +++ b/packages/editor/core/src/ui/hooks/useEditor.tsx @@ -0,0 +1,69 @@ +import { useEditor as useCustomEditor, Editor } from "@tiptap/react"; +import { useImperativeHandle, useRef, MutableRefObject } from "react"; +import { useDebouncedCallback } from "use-debounce"; +import { DeleteImage } from '../../types/delete-image'; +import { CoreEditorProps } from "../props"; +import { CoreEditorExtensions } from "../extensions"; +import { EditorProps } from '@tiptap/pm/view'; +import { getTrimmedHTML } from "../../lib/utils"; +import { UploadImage } from "../../types/upload-image"; + +const DEBOUNCE_DELAY = 1500; + +interface CustomEditorProps { + uploadFile: UploadImage; + setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void; + setShouldShowAlert?: (showAlert: boolean) => void; + value: string; + deleteFile: DeleteImage; + debouncedUpdatesEnabled?: boolean; + onChange?: (json: any, html: string) => void; + extensions?: any; + editorProps?: EditorProps; + forwardedRef?: any; +} + +export const useEditor = ({ uploadFile, deleteFile, editorProps = {}, value, extensions = [], onChange, setIsSubmitting, debouncedUpdatesEnabled, forwardedRef, setShouldShowAlert, }: CustomEditorProps) => { + const editor = useCustomEditor({ + editorProps: { + ...CoreEditorProps(uploadFile, setIsSubmitting), + ...editorProps, + }, + extensions: [...CoreEditorExtensions(deleteFile), ...extensions], + content: (typeof value === "string" && value.trim() !== "") ? value : "

", + onUpdate: async ({ editor }) => { + // for instant feedback loop + setIsSubmitting?.("submitting"); + setShouldShowAlert?.(true); + if (debouncedUpdatesEnabled) { + debouncedUpdates({ onChange: onChange, editor }); + } else { + onChange?.(editor.getJSON(), getTrimmedHTML(editor.getHTML())); + } + }, + }); + + const editorRef: MutableRefObject = useRef(null); + editorRef.current = editor; + + useImperativeHandle(forwardedRef, () => ({ + clearEditor: () => { + editorRef.current?.commands.clearContent(); + }, + setEditorValue: (content: string) => { + editorRef.current?.commands.setContent(content); + }, + })); + + const debouncedUpdates = useDebouncedCallback(async ({ onChange, editor }) => { + if (onChange) { + onChange(editor.getJSON(), getTrimmedHTML(editor.getHTML())); + } + }, DEBOUNCE_DELAY); + + if (!editor) { + return null; + } + + return editor; +}; diff --git a/packages/editor/core/src/ui/hooks/useReadOnlyEditor.tsx b/packages/editor/core/src/ui/hooks/useReadOnlyEditor.tsx new file mode 100644 index 000000000..3e32c5044 --- /dev/null +++ b/packages/editor/core/src/ui/hooks/useReadOnlyEditor.tsx @@ -0,0 +1,43 @@ +import { useEditor as useCustomEditor, Editor } from "@tiptap/react"; +import { useImperativeHandle, useRef, MutableRefObject } from "react"; +import { CoreReadOnlyEditorExtensions } from "../../ui/read-only/extensions"; +import { CoreReadOnlyEditorProps } from "../../ui/read-only/props"; +import { EditorProps } from '@tiptap/pm/view'; + +interface CustomReadOnlyEditorProps { + value: string; + forwardedRef?: any; + extensions?: any; + editorProps?: EditorProps; +} + +export const useReadOnlyEditor = ({ value, forwardedRef, extensions = [], editorProps = {} }: CustomReadOnlyEditorProps) => { + const editor = useCustomEditor({ + editable: false, + content: (typeof value === "string" && value.trim() !== "") ? value : "

", + editorProps: { + ...CoreReadOnlyEditorProps, + ...editorProps, + }, + extensions: [...CoreReadOnlyEditorExtensions, ...extensions], + }); + + const editorRef: MutableRefObject = useRef(null); + editorRef.current = editor; + + useImperativeHandle(forwardedRef, () => ({ + clearEditor: () => { + editorRef.current?.commands.clearContent(); + }, + setEditorValue: (content: string) => { + editorRef.current?.commands.setContent(content); + }, + })); + + + if (!editor) { + return null; + } + + return editor; +}; diff --git a/packages/editor/core/src/ui/index.tsx b/packages/editor/core/src/ui/index.tsx new file mode 100644 index 000000000..3c64e8ba6 --- /dev/null +++ b/packages/editor/core/src/ui/index.tsx @@ -0,0 +1,92 @@ +"use client" +import * as React from 'react'; +import { Extension } from "@tiptap/react"; +import { UploadImage } from '../types/upload-image'; +import { DeleteImage } from '../types/delete-image'; +import { getEditorClassNames } from '../lib/utils'; +import { EditorProps } from '@tiptap/pm/view'; +import { useEditor } from './hooks/useEditor'; +import { EditorContainer } from '../ui/components/editor-container'; +import { EditorContentWrapper } from '../ui/components/editor-content'; + +interface ICoreEditor { + value: string; + uploadFile: UploadImage; + deleteFile: DeleteImage; + noBorder?: boolean; + borderOnFocus?: boolean; + customClassName?: string; + editorContentCustomClassNames?: string; + onChange?: (json: any, html: string) => void; + setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void; + setShouldShowAlert?: (showAlert: boolean) => void; + editable?: boolean; + forwardedRef?: any; + debouncedUpdatesEnabled?: boolean; + accessValue: string; + onAccessChange: (accessKey: string) => void; + commentAccess: { + icon: string; + key: string; + label: "Private" | "Public"; + }[]; + extensions?: Extension[]; + editorProps?: EditorProps; +} + +interface EditorCoreProps extends ICoreEditor { + forwardedRef?: React.Ref; +} + +interface EditorHandle { + clearEditor: () => void; + setEditorValue: (content: string) => void; +} + +const CoreEditor = ({ + onChange, + debouncedUpdatesEnabled, + editable, + setIsSubmitting, + setShouldShowAlert, + editorContentCustomClassNames, + value, + uploadFile, + deleteFile, + noBorder, + borderOnFocus, + customClassName, + forwardedRef, +}: EditorCoreProps) => { + const editor = useEditor({ + onChange, + debouncedUpdatesEnabled, + editable, + setIsSubmitting, + setShouldShowAlert, + value, + uploadFile, + deleteFile, + forwardedRef, + }); + + const editorClassNames = getEditorClassNames({ noBorder, borderOnFocus, customClassName }); + + if (!editor) return null; + + return ( + +
+ +
+
+ ); +}; + +const CoreEditorWithRef = React.forwardRef((props, ref) => ( + +)); + +CoreEditorWithRef.displayName = "CoreEditorWithRef"; + +export { CoreEditor, CoreEditorWithRef }; diff --git a/packages/editor/core/src/ui/menus/menu-items/index.tsx b/packages/editor/core/src/ui/menus/menu-items/index.tsx new file mode 100644 index 000000000..f31b6601e --- /dev/null +++ b/packages/editor/core/src/ui/menus/menu-items/index.tsx @@ -0,0 +1,109 @@ +import { BoldIcon, Heading1, CheckSquare, Heading2, Heading3, QuoteIcon, ImageIcon, TableIcon, ListIcon, ListOrderedIcon, ItalicIcon, UnderlineIcon, StrikethroughIcon, CodeIcon } from "lucide-react"; +import { Editor } from "@tiptap/react"; +import { UploadImage } from "../../../types/upload-image"; +import { insertImageCommand, insertTableCommand, toggleBlockquote, toggleBold, toggleBulletList, toggleCode, toggleHeadingOne, toggleHeadingThree, toggleHeadingTwo, toggleItalic, toggleOrderedList, toggleStrike, toggleTaskList, toggleUnderline, } from "../../../lib/editor-commands"; + +export interface EditorMenuItem { + name: string; + isActive: () => boolean; + command: () => void; + icon: typeof BoldIcon; +} + +export const HeadingOneItem = (editor: Editor): EditorMenuItem => ({ + name: "H1", + isActive: () => editor.isActive("heading", { level: 1 }), + command: () => toggleHeadingOne(editor), + icon: Heading1, +}) + +export const HeadingTwoItem = (editor: Editor): EditorMenuItem => ({ + name: "H2", + isActive: () => editor.isActive("heading", { level: 2 }), + command: () => toggleHeadingTwo(editor), + icon: Heading2, +}) + +export const HeadingThreeItem = (editor: Editor): EditorMenuItem => ({ + name: "H3", + isActive: () => editor.isActive("heading", { level: 3 }), + command: () => toggleHeadingThree(editor), + icon: Heading3, +}) + +export const BoldItem = (editor: Editor): EditorMenuItem => ({ + name: "bold", + isActive: () => editor?.isActive("bold"), + command: () => toggleBold(editor), + icon: BoldIcon, +}) + +export const ItalicItem = (editor: Editor): EditorMenuItem => ({ + name: "italic", + isActive: () => editor?.isActive("italic"), + command: () => toggleItalic(editor), + icon: ItalicIcon, +}) + +export const UnderLineItem = (editor: Editor): EditorMenuItem => ({ + name: "underline", + isActive: () => editor?.isActive("underline"), + command: () => toggleUnderline(editor), + icon: UnderlineIcon, +}) + +export const StrikeThroughItem = (editor: Editor): EditorMenuItem => ({ + name: "strike", + isActive: () => editor?.isActive("strike"), + command: () => toggleStrike(editor), + icon: StrikethroughIcon, +}) + +export const CodeItem = (editor: Editor): EditorMenuItem => ({ + name: "code", + isActive: () => editor?.isActive("code"), + command: () => toggleCode(editor), + icon: CodeIcon, +}) + +export const BulletListItem = (editor: Editor): EditorMenuItem => ({ + name: "bullet-list", + isActive: () => editor?.isActive("bulletList"), + command: () => toggleBulletList(editor), + icon: ListIcon, +}) + +export const TodoListItem = (editor: Editor): EditorMenuItem => ({ + name: "To-do List", + isActive: () => editor.isActive("taskItem"), + command: () => toggleTaskList(editor), + icon: CheckSquare, +}) + +export const NumberedListItem = (editor: Editor): EditorMenuItem => ({ + name: "ordered-list", + isActive: () => editor?.isActive("orderedList"), + command: () => toggleOrderedList(editor), + icon: ListOrderedIcon +}) + +export const QuoteItem = (editor: Editor): EditorMenuItem => ({ + name: "quote", + isActive: () => editor?.isActive("quote"), + command: () => toggleBlockquote(editor), + icon: QuoteIcon +}) + +export const TableItem = (editor: Editor): EditorMenuItem => ({ + name: "quote", + isActive: () => editor?.isActive("table"), + command: () => insertTableCommand(editor), + icon: TableIcon +}) + +export const ImageItem = (editor: Editor, uploadFile: UploadImage, setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void): EditorMenuItem => ({ + name: "image", + isActive: () => editor?.isActive("image"), + command: () => insertImageCommand(editor, uploadFile, setIsSubmitting), + icon: ImageIcon, +}) diff --git a/space/components/tiptap/table-menu/InsertBottomTableIcon.tsx b/packages/editor/core/src/ui/menus/table-menu/InsertBottomTableIcon.tsx similarity index 100% rename from space/components/tiptap/table-menu/InsertBottomTableIcon.tsx rename to packages/editor/core/src/ui/menus/table-menu/InsertBottomTableIcon.tsx diff --git a/space/components/tiptap/table-menu/InsertLeftTableIcon.tsx b/packages/editor/core/src/ui/menus/table-menu/InsertLeftTableIcon.tsx similarity index 100% rename from space/components/tiptap/table-menu/InsertLeftTableIcon.tsx rename to packages/editor/core/src/ui/menus/table-menu/InsertLeftTableIcon.tsx diff --git a/space/components/tiptap/table-menu/InsertRightTableIcon.tsx b/packages/editor/core/src/ui/menus/table-menu/InsertRightTableIcon.tsx similarity index 100% rename from space/components/tiptap/table-menu/InsertRightTableIcon.tsx rename to packages/editor/core/src/ui/menus/table-menu/InsertRightTableIcon.tsx diff --git a/space/components/tiptap/table-menu/InsertTopTableIcon.tsx b/packages/editor/core/src/ui/menus/table-menu/InsertTopTableIcon.tsx similarity index 100% rename from space/components/tiptap/table-menu/InsertTopTableIcon.tsx rename to packages/editor/core/src/ui/menus/table-menu/InsertTopTableIcon.tsx diff --git a/web/components/tiptap/table-menu/index.tsx b/packages/editor/core/src/ui/menus/table-menu/index.tsx similarity index 92% rename from web/components/tiptap/table-menu/index.tsx rename to packages/editor/core/src/ui/menus/table-menu/index.tsx index daa8f6953..c115196db 100644 --- a/web/components/tiptap/table-menu/index.tsx +++ b/packages/editor/core/src/ui/menus/table-menu/index.tsx @@ -1,11 +1,11 @@ import { useState, useEffect } from "react"; import { Rows, Columns, ToggleRight } from "lucide-react"; -import { cn } from "../utils"; -import { Tooltip } from "components/ui"; import InsertLeftTableIcon from "./InsertLeftTableIcon"; import InsertRightTableIcon from "./InsertRightTableIcon"; import InsertTopTableIcon from "./InsertTopTableIcon"; import InsertBottomTableIcon from "./InsertBottomTableIcon"; +import { cn, findTableAncestor } from "../../../lib/utils"; +import { Tooltip } from "./tooltip"; interface TableMenuItem { command: () => void; @@ -14,12 +14,7 @@ interface TableMenuItem { name: string; } -export const findTableAncestor = (node: Node | null): HTMLTableElement | null => { - while (node !== null && node.nodeName !== "TABLE") { - node = node.parentNode; - } - return node as HTMLTableElement; -}; + export const TableMenu = ({ editor }: { editor: any }) => { const [tableLocation, setTableLocation] = useState({ bottom: 0, left: 0 }); diff --git a/packages/editor/core/src/ui/menus/table-menu/tooltip.tsx b/packages/editor/core/src/ui/menus/table-menu/tooltip.tsx new file mode 100644 index 000000000..f29d8a491 --- /dev/null +++ b/packages/editor/core/src/ui/menus/table-menu/tooltip.tsx @@ -0,0 +1,77 @@ +import * as React from 'react'; + +// next-themes +import { useTheme } from "next-themes"; +// tooltip2 +import { Tooltip2 } from "@blueprintjs/popover2"; + +type Props = { + tooltipHeading?: string; + tooltipContent: string | React.ReactNode; + position?: + | "top" + | "right" + | "bottom" + | "left" + | "auto" + | "auto-end" + | "auto-start" + | "bottom-left" + | "bottom-right" + | "left-bottom" + | "left-top" + | "right-bottom" + | "right-top" + | "top-left" + | "top-right"; + children: JSX.Element; + disabled?: boolean; + className?: string; + openDelay?: number; + closeDelay?: number; +}; + +export const Tooltip: React.FC = ({ + tooltipHeading, + tooltipContent, + position = "top", + children, + disabled = false, + className = "", + openDelay = 200, + closeDelay, +}) => { + const { theme } = useTheme(); + + return ( + + {tooltipHeading && ( +
+ {tooltipHeading} +
+ )} + {tooltipContent} + + } + position={position} + renderTarget={({ isOpen: isTooltipOpen, ref: eleReference, ...tooltipProps }) => + React.cloneElement(children, { ref: eleReference, ...tooltipProps, ...children.props }) + } + /> + ); +}; diff --git a/web/components/tiptap/plugins/delete-image.tsx b/packages/editor/core/src/ui/plugins/delete-image.tsx similarity index 84% rename from web/components/tiptap/plugins/delete-image.tsx rename to packages/editor/core/src/ui/plugins/delete-image.tsx index fdf515ccc..ba21d686d 100644 --- a/web/components/tiptap/plugins/delete-image.tsx +++ b/packages/editor/core/src/ui/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 fileService from "services/file.service"; +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 = (): Plugin => +const TrackImageDeletionPlugin = (deleteImage: DeleteImage): Plugin => new Plugin({ key: deleteKey, appendTransaction: (transactions: readonly Transaction[], oldState: EditorState, newState: EditorState) => { @@ -45,7 +45,7 @@ const TrackImageDeletionPlugin = (): Plugin => removedImages.forEach(async (node) => { const src = node.attrs.src; - await onNodeDeleted(src); + await onNodeDeleted(src, deleteImage); }); }); @@ -55,10 +55,10 @@ const TrackImageDeletionPlugin = (): Plugin => export default TrackImageDeletionPlugin; -async function onNodeDeleted(src: string): Promise { +async function onNodeDeleted(src: string, deleteImage: DeleteImage): Promise { try { const assetUrlWithWorkspaceId = new URL(src).pathname.substring(1); - const resStatus = await fileService.deleteImage(assetUrlWithWorkspaceId); + const resStatus = await deleteImage(assetUrlWithWorkspaceId); if (resStatus === 204) { console.log("Image deleted successfully"); } diff --git a/web/components/tiptap/plugins/upload-image.tsx b/packages/editor/core/src/ui/plugins/upload-image.tsx similarity index 78% rename from web/components/tiptap/plugins/upload-image.tsx rename to packages/editor/core/src/ui/plugins/upload-image.tsx index bc0acdc54..cdd62ae48 100644 --- a/web/components/tiptap/plugins/upload-image.tsx +++ b/packages/editor/core/src/ui/plugins/upload-image.tsx @@ -1,6 +1,6 @@ +import { UploadImage } from "../../types/upload-image"; import { EditorState, Plugin, PluginKey } from "@tiptap/pm/state"; import { Decoration, DecorationSet, EditorView } from "@tiptap/pm/view"; -import fileService from "services/file.service"; const uploadKey = new PluginKey("upload-image"); @@ -57,7 +57,7 @@ export async function startImageUpload( file: File, view: EditorView, pos: number, - workspaceSlug: string, + uploadFile: UploadImage, setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void ) { if (!file.type.includes("image/")) { @@ -82,11 +82,8 @@ export async function startImageUpload( view.dispatch(tr); }; - if (!workspaceSlug) { - return; - } setIsSubmitting?.("submitting"); - const src = await UploadImageHandler(file, workspaceSlug); + const src = await UploadImageHandler(file, uploadFile); const { schema } = view.state; pos = findPlaceholder(view.state, id); @@ -100,28 +97,30 @@ export async function startImageUpload( view.dispatch(transaction); } -const UploadImageHandler = (file: File, workspaceSlug: string): Promise => { - if (!workspaceSlug) { - return Promise.reject("Workspace slug is missing"); - } +const UploadImageHandler = (file: File, + uploadFile: UploadImage +): Promise => { try { - const formData = new FormData(); - formData.append("asset", file); - formData.append("attributes", JSON.stringify({})); - return new Promise(async (resolve, reject) => { - const imageUrl = await fileService - .uploadFile(workspaceSlug, formData) - .then((response) => response.asset); + try { + const imageUrl = await uploadFile(file) - const image = new Image(); - image.src = imageUrl; - image.onload = () => { - resolve(imageUrl); - }; + const image = new Image(); + image.src = imageUrl; + image.onload = () => { + resolve(imageUrl); + }; + } catch (error) { + if (error instanceof Error) { + console.log(error.message); + } + reject(error); + } }); } catch (error) { - console.log(error); + if (error instanceof Error) { + console.log(error.message); + } return Promise.reject(error); } }; diff --git a/web/components/tiptap/props.tsx b/packages/editor/core/src/ui/props.tsx similarity index 85% rename from web/components/tiptap/props.tsx rename to packages/editor/core/src/ui/props.tsx index 8233e3ab4..8f002b76c 100644 --- a/web/components/tiptap/props.tsx +++ b/packages/editor/core/src/ui/props.tsx @@ -1,9 +1,10 @@ import { EditorProps } from "@tiptap/pm/view"; +import { findTableAncestor } from "../lib/utils"; import { startImageUpload } from "./plugins/upload-image"; -import { findTableAncestor } from "./table-menu"; +import { UploadImage } from "../types/upload-image"; -export function TiptapEditorProps( - workspaceSlug: string, +export function CoreEditorProps( + uploadFile: UploadImage, setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void ): EditorProps { return { @@ -35,7 +36,7 @@ export function TiptapEditorProps( event.preventDefault(); const file = event.clipboardData.files[0]; const pos = view.state.selection.from; - startImageUpload(file, view, pos, workspaceSlug, setIsSubmitting); + startImageUpload(file, view, pos, uploadFile, setIsSubmitting); return true; } return false; @@ -57,9 +58,8 @@ export function TiptapEditorProps( left: event.clientX, top: event.clientY, }); - // here we deduct 1 from the pos or else the image will create an extra node if (coordinates) { - startImageUpload(file, view, coordinates.pos - 1, workspaceSlug, setIsSubmitting); + startImageUpload(file, view, coordinates.pos - 1, uploadFile, setIsSubmitting); } return true; } diff --git a/packages/editor/core/src/ui/read-only/extensions.tsx b/packages/editor/core/src/ui/read-only/extensions.tsx new file mode 100644 index 000000000..2246c64f9 --- /dev/null +++ b/packages/editor/core/src/ui/read-only/extensions.tsx @@ -0,0 +1,92 @@ +import StarterKit from "@tiptap/starter-kit"; +import TiptapLink from "@tiptap/extension-link"; +import TiptapUnderline from "@tiptap/extension-underline"; +import TextStyle from "@tiptap/extension-text-style"; +import { Color } from "@tiptap/extension-color"; +import TaskItem from "@tiptap/extension-task-item"; +import TaskList from "@tiptap/extension-task-list"; +import { Markdown } from "tiptap-markdown"; +import Gapcursor from "@tiptap/extension-gapcursor"; + +import { CustomTableCell } from "../extensions/table/table-cell"; +import { Table } from "../extensions/table"; +import { TableHeader } from "../extensions/table/table-header"; +import { TableRow } from "@tiptap/extension-table-row"; + +import ReadOnlyImageExtension from "../extensions/image/read-only-image"; +import { isValidHttpUrl } from "../../lib/utils"; + +export const CoreReadOnlyEditorExtensions = [ + StarterKit.configure({ + bulletList: { + HTMLAttributes: { + class: "list-disc list-outside leading-3 -mt-2", + }, + }, + orderedList: { + HTMLAttributes: { + class: "list-decimal list-outside leading-3 -mt-2", + }, + }, + listItem: { + HTMLAttributes: { + class: "leading-normal -mb-2", + }, + }, + blockquote: { + HTMLAttributes: { + class: "border-l-4 border-custom-border-300", + }, + }, + code: { + HTMLAttributes: { + class: + "rounded-md bg-custom-primary-30 mx-1 px-1 py-1 font-mono font-medium text-custom-text-1000", + spellcheck: "false", + }, + }, + codeBlock: false, + horizontalRule: false, + dropcursor: { + color: "rgba(var(--color-text-100))", + width: 2, + }, + gapcursor: false, + }), + Gapcursor, + TiptapLink.configure({ + protocols: ["http", "https"], + validate: (url) => isValidHttpUrl(url), + HTMLAttributes: { + class: + "text-custom-primary-300 underline underline-offset-[3px] hover:text-custom-primary-500 transition-colors cursor-pointer", + }, + }), + ReadOnlyImageExtension.configure({ + HTMLAttributes: { + class: "rounded-lg border border-custom-border-300", + }, + }), + TiptapUnderline, + TextStyle, + Color, + TaskList.configure({ + HTMLAttributes: { + class: "not-prose pl-2", + }, + }), + TaskItem.configure({ + HTMLAttributes: { + class: "flex items-start my-4", + }, + nested: true, + }), + Markdown.configure({ + html: true, + transformCopiedText: true, + }), + Table, + TableHeader, + CustomTableCell, + TableRow, + ]; diff --git a/packages/editor/core/src/ui/read-only/props.tsx b/packages/editor/core/src/ui/read-only/props.tsx new file mode 100644 index 000000000..25db2b68c --- /dev/null +++ b/packages/editor/core/src/ui/read-only/props.tsx @@ -0,0 +1,8 @@ +import { EditorProps } from "@tiptap/pm/view"; + +export const CoreReadOnlyEditorProps: EditorProps = +{ + attributes: { + class: `prose prose-brand max-w-full prose-headings:font-display font-default focus:outline-none`, + }, +}; diff --git a/packages/editor/core/tailwind.config.js b/packages/editor/core/tailwind.config.js new file mode 100644 index 000000000..f32063158 --- /dev/null +++ b/packages/editor/core/tailwind.config.js @@ -0,0 +1,6 @@ +const sharedConfig = require("tailwind-config-custom/tailwind.config.js"); + +module.exports = { + // prefix ui lib classes to avoid conflicting with the app + ...sharedConfig, +}; diff --git a/packages/editor/core/tsconfig.json b/packages/editor/core/tsconfig.json new file mode 100644 index 000000000..61a60f324 --- /dev/null +++ b/packages/editor/core/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "tsconfig/react.json", + "include": [ + "src/**/*", + "index.d.ts" + ], + "exclude": [ + "dist", + "build", + "node_modules" + ] +} diff --git a/packages/editor/core/tsup.config.ts b/packages/editor/core/tsup.config.ts new file mode 100644 index 000000000..5e89e04af --- /dev/null +++ b/packages/editor/core/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig, Options } from "tsup"; + +export default defineConfig((options: Options) => ({ + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: true, + clean: false, + external: ["react"], + injectStyle: true, + ...options, +})); diff --git a/packages/editor/lite-text-editor/Readme.md b/packages/editor/lite-text-editor/Readme.md new file mode 100644 index 000000000..948e2c34b --- /dev/null +++ b/packages/editor/lite-text-editor/Readme.md @@ -0,0 +1,97 @@ +# @plane/lite-text-editor + +## Description + +The `@plane/lite-text-editor` package extends from the `editor-core` package, inheriting its base functionality while adding its own unique features of Custom control over Enter key, etc. + +## Key Features + +- **Exported Components**: There are two components exported from the Lite text editor (with and without Ref), you can choose to use the `withRef` instance whenever you want to control the Editor’s state via a side effect of some external action from within the application code. + + `LiteTextEditor` & `LiteTextEditorWithRef` + +- **Read Only Editor Instances**: We have added a really light weight *Read Only* Editor instance for the Lite editor types (with and without Ref) + `LiteReadOnlyEditor` &`LiteReadOnlyEditorWithRef` + +## LiteTextEditor + +| Prop | Type | Description | +| --- | --- | --- | +| `uploadFile` | `(file: File) => Promise` | A function that handles file upload. It takes a file as input and handles the process of uploading that file. | +| `deleteFile` | `(assetUrlWithWorkspaceId: string) => Promise` | A function that handles deleting an image. It takes the asset url from your bucket and handles the process of deleting that image. | +| `value` | `html string` | The initial content of the editor. | +| `onEnterKeyPress` | `(e) => void` | The event that happens on Enter key press | +| `debouncedUpdatesEnabled` | `boolean` | If set to true, the `onChange` event handler is debounced, meaning it will only be invoked after the specified delay (default 1500ms) once the user has stopped typing. | +| `onChange` | `(json: any, html: string) => void` | This function is invoked whenever the content of the editor changes. It is passed the new content in both JSON and HTML formats. | +| `setIsSubmitting` | `(isSubmitting: "submitting" \| "submitted" \| "saved") => void` | This function is called to update the submission status. | +| `setShouldShowAlert` | `(showAlert: boolean) => void` | This function is used to show or hide an alert incase of content not being "saved". | +| `noBorder` | `boolean` | If set to true, the editor will not have a border. | +| `borderOnFocus` | `boolean` | If set to true, the editor will show a border when it is focused. | +| `customClassName` | `string` | This is a custom CSS class that can be applied to the editor. | +| `editorContentCustomClassNames` | `string` | This is a custom CSS class that can be applied to the editor content. | + +### Usage + +1. Here is an example of how to use the `RichTextEditor` component + +```tsx + { + onChange(comment_html); + }} + /> +``` + +2. Example of how to use the `LiteTextEditorWithRef` component + +```tsx + const editorRef = useRef(null); + + // can use it to set the editor's value + editorRef.current?.setEditorValue(`${watch("description_html")}`); + + // can use it to clear the editor + editorRef?.current?.clearEditor(); + + return ( + { + onChange(comment_html); + }} + /> +) +``` + +## LiteReadOnlyEditor + +| Prop | Type | Description | +| --- | --- | --- | +| `value` | `html string` | The initial content of the editor. | +| `noBorder` | `boolean` | If set to true, the editor will not have a border. | +| `borderOnFocus` | `boolean` | If set to true, the editor will show a border when it is focused. | +| `customClassName` | `string` | This is a custom CSS class that can be applied to the editor. | +| `editorContentCustomClassNames` | `string` | This is a custom CSS class that can be applied to the editor content. | + +### Usage + +Here is an example of how to use the `RichReadOnlyEditor` component + +```tsx + +``` diff --git a/packages/editor/lite-text-editor/package.json b/packages/editor/lite-text-editor/package.json new file mode 100644 index 000000000..47ef154c6 --- /dev/null +++ b/packages/editor/lite-text-editor/package.json @@ -0,0 +1,63 @@ +{ + "name": "@plane/lite-text-editor", + "version": "0.0.1", + "description": "Package that powers Plane's Comment Editor", + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "files": [ + "dist/**/*" + ], + "exports": { + ".": { + "types": "./dist/index.d.mts", + "import": "./dist/index.mjs", + "module": "./dist/index.mjs" + } + }, + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "check-types": "tsc --noEmit" + }, + "peerDependencies": { + "next": "12.3.2", + "next-themes": "^0.2.1", + "react": "^18.2.0", + "react-dom": "18.2.0" + }, + "dependencies": { + "@plane/editor-core": "*", + "@tiptap/extension-list-item": "^2.1.11", + "@types/node": "18.15.3", + "@types/react": "^18.2.5", + "@types/react-dom": "18.0.11", + "class-variance-authority": "^0.7.0", + "clsx": "^1.2.1", + "eslint": "8.36.0", + "eslint-config-next": "13.2.4", + "eventsource-parser": "^0.1.0", + "lowlight": "^2.9.0", + "lucide-react": "^0.244.0", + "react-markdown": "^8.0.7", + "tailwind-merge": "^1.14.0", + "tippy.js": "^6.3.7", + "tiptap-markdown": "^0.8.2", + "use-debounce": "^9.0.4" + }, + "devDependencies": { + "eslint": "^7.32.0", + "postcss": "^8.4.29", + "tailwind-config-custom": "*", + "tsconfig": "*", + "tsup": "^7.2.0", + "typescript": "4.9.5" + }, + "keywords": [ + "editor", + "rich-text", + "markdown", + "nextjs", + "react" + ] +} diff --git a/packages/editor/lite-text-editor/postcss.config.js b/packages/editor/lite-text-editor/postcss.config.js new file mode 100644 index 000000000..07aa434b2 --- /dev/null +++ b/packages/editor/lite-text-editor/postcss.config.js @@ -0,0 +1,9 @@ +// If you want to use other PostCSS plugins, see the following: +// https://tailwindcss.com/docs/using-with-preprocessors + +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/editor/lite-text-editor/src/index.ts b/packages/editor/lite-text-editor/src/index.ts new file mode 100644 index 000000000..de9323b3c --- /dev/null +++ b/packages/editor/lite-text-editor/src/index.ts @@ -0,0 +1,2 @@ +export { LiteTextEditor, LiteTextEditorWithRef } from "./ui"; +export { LiteReadOnlyEditor, LiteReadOnlyEditorWithRef } from "./ui/read-only"; diff --git a/packages/editor/lite-text-editor/src/ui/extensions/custom-list-extension.tsx b/packages/editor/lite-text-editor/src/ui/extensions/custom-list-extension.tsx new file mode 100644 index 000000000..f0bc70cff --- /dev/null +++ b/packages/editor/lite-text-editor/src/ui/extensions/custom-list-extension.tsx @@ -0,0 +1,9 @@ +import ListItem from '@tiptap/extension-list-item' + +export const CustomListItem = ListItem.extend({ + addKeyboardShortcuts() { + return { + 'Shift-Enter': () => this.editor.chain().focus().splitListItem('listItem').run(), + } + }, +}) diff --git a/packages/editor/lite-text-editor/src/ui/extensions/enter-key-extension.tsx b/packages/editor/lite-text-editor/src/ui/extensions/enter-key-extension.tsx new file mode 100644 index 000000000..04c4a1fbe --- /dev/null +++ b/packages/editor/lite-text-editor/src/ui/extensions/enter-key-extension.tsx @@ -0,0 +1,16 @@ +import { Extension } from '@tiptap/core'; + +export const EnterKeyExtension = (onEnterKeyPress?: () => void) => Extension.create({ + name: 'enterKey', + + addKeyboardShortcuts() { + return { + 'Enter': () => { + if (onEnterKeyPress) { + onEnterKeyPress(); + } + return true; + }, + } + }, +}); diff --git a/packages/editor/lite-text-editor/src/ui/extensions/index.tsx b/packages/editor/lite-text-editor/src/ui/extensions/index.tsx new file mode 100644 index 000000000..ccd04a395 --- /dev/null +++ b/packages/editor/lite-text-editor/src/ui/extensions/index.tsx @@ -0,0 +1,7 @@ +import { CustomListItem } from "./custom-list-extension"; +import { EnterKeyExtension } from "./enter-key-extension"; + +export const LiteTextEditorExtensions = (onEnterKeyPress?: () => void) => [ + CustomListItem, + EnterKeyExtension(onEnterKeyPress), +]; diff --git a/packages/editor/lite-text-editor/src/ui/index.tsx b/packages/editor/lite-text-editor/src/ui/index.tsx new file mode 100644 index 000000000..7a497d9aa --- /dev/null +++ b/packages/editor/lite-text-editor/src/ui/index.tsx @@ -0,0 +1,95 @@ +"use client" +import * as React from 'react'; +import { EditorContainer, EditorContentWrapper, getEditorClassNames, useEditor } from '@plane/editor-core'; +import { FixedMenu } from './menus/fixed-menu'; +import { LiteTextEditorExtensions } from './extensions'; + +export type UploadImage = (file: File) => Promise; +export type DeleteImage = (assetUrlWithWorkspaceId: string) => Promise; + +interface ILiteTextEditor { + value: string; + uploadFile: UploadImage; + deleteFile: DeleteImage; + noBorder?: boolean; + borderOnFocus?: boolean; + customClassName?: string; + editorContentCustomClassNames?: string; + onChange?: (json: any, html: string) => void; + setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void; + setShouldShowAlert?: (showAlert: boolean) => void; + forwardedRef?: any; + debouncedUpdatesEnabled?: boolean; + commentAccessSpecifier?: { + accessValue: string, + onAccessChange: (accessKey: string) => void, + showAccessSpecifier: boolean, + commentAccess: { + icon: string; + key: string; + label: "Private" | "Public"; + }[] + }; + onEnterKeyPress?: (e?: any) => void; +} + +interface LiteTextEditorProps extends ILiteTextEditor { + forwardedRef?: React.Ref; +} + +interface EditorHandle { + clearEditor: () => void; + setEditorValue: (content: string) => void; +} + +const LiteTextEditor = ({ + onChange, + debouncedUpdatesEnabled, + setIsSubmitting, + setShouldShowAlert, + editorContentCustomClassNames, + value, + uploadFile, + deleteFile, + noBorder, + borderOnFocus, + customClassName, + forwardedRef, + commentAccessSpecifier, + onEnterKeyPress +}: LiteTextEditorProps) => { + const editor = useEditor({ + onChange, + debouncedUpdatesEnabled, + setIsSubmitting, + setShouldShowAlert, + value, + uploadFile, + deleteFile, + forwardedRef, + extensions: LiteTextEditorExtensions(onEnterKeyPress), + }); + + const editorClassNames = getEditorClassNames({ noBorder, borderOnFocus, customClassName }); + + if (!editor) return null; + + return ( + +
+ +
+ +
+
+
+ ); +}; + +const LiteTextEditorWithRef = React.forwardRef((props, ref) => ( + +)); + +LiteTextEditorWithRef.displayName = "LiteTextEditorWithRef"; + +export { LiteTextEditor, LiteTextEditorWithRef }; diff --git a/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/icon.tsx b/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/icon.tsx new file mode 100644 index 000000000..c0006b3f2 --- /dev/null +++ b/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/icon.tsx @@ -0,0 +1,13 @@ +import React from "react"; + +type Props = { + iconName: string; + className?: string; +}; + +export const Icon: React.FC = ({ iconName, className = "" }) => ( + + {iconName} + +); + diff --git a/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx b/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx new file mode 100644 index 000000000..72a537e7e --- /dev/null +++ b/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx @@ -0,0 +1,170 @@ +import { Editor } from "@tiptap/react"; +import { BoldIcon } from "lucide-react"; + +import { BoldItem, BulletListItem, cn, CodeItem, ImageItem, ItalicItem, NumberedListItem, QuoteItem, StrikeThroughItem, TableItem, UnderLineItem } from "@plane/editor-core"; +import { Icon } from "./icon"; +import { Tooltip } from "../../tooltip"; +import { UploadImage } from "../.."; + +export interface BubbleMenuItem { + name: string; + isActive: () => boolean; + command: () => void; + icon: typeof BoldIcon; +} + +type EditorBubbleMenuProps = { + editor: Editor; + commentAccessSpecifier?: { + accessValue: string, + onAccessChange: (accessKey: string) => void, + showAccessSpecifier: boolean, + commentAccess: { + icon: string; + key: string; + label: "Private" | "Public"; + }[] | undefined; + } + uploadFile: UploadImage; + setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void; +} + +export const FixedMenu = (props: EditorBubbleMenuProps) => { + const basicMarkItems: BubbleMenuItem[] = [ + BoldItem(props.editor), + ItalicItem(props.editor), + UnderLineItem(props.editor), + StrikeThroughItem(props.editor), + ]; + + const listItems: BubbleMenuItem[] = [ + BulletListItem(props.editor), + NumberedListItem(props.editor), + ]; + + const userActionItems: BubbleMenuItem[] = [ + QuoteItem(props.editor), + CodeItem(props.editor), + ]; + + const complexItems: BubbleMenuItem[] = [ + TableItem(props.editor), + ImageItem(props.editor, props.uploadFile, props.setIsSubmitting), + ]; + + const handleAccessChange = (accessKey: string) => { + props.commentAccessSpecifier?.onAccessChange(accessKey); + }; + + + return ( +
+ {props.commentAccessSpecifier && (
+ {props?.commentAccessSpecifier.commentAccess?.map((access) => ( + + + + ))} +
)} +
+ {basicMarkItems.map((item, index) => ( + + ))} +
+
+ {listItems.map((item, index) => ( + + ))} +
+
+ {userActionItems.map((item, index) => ( + + ))} +
+
+ {complexItems.map((item, index) => ( + + ))} +
+
+ ); +}; diff --git a/packages/editor/lite-text-editor/src/ui/read-only/index.tsx b/packages/editor/lite-text-editor/src/ui/read-only/index.tsx new file mode 100644 index 000000000..3990cb734 --- /dev/null +++ b/packages/editor/lite-text-editor/src/ui/read-only/index.tsx @@ -0,0 +1,54 @@ +"use client" +import { EditorContainer, EditorContentWrapper, getEditorClassNames, useReadOnlyEditor } from '@plane/editor-core'; +import * as React from 'react'; + +interface ICoreReadOnlyEditor { + value: string; + editorContentCustomClassNames?: string; + noBorder?: boolean; + borderOnFocus?: boolean; + customClassName?: string; +} + +interface EditorCoreProps extends ICoreReadOnlyEditor { + forwardedRef?: React.Ref; +} + +interface EditorHandle { + clearEditor: () => void; + setEditorValue: (content: string) => void; +} + +const LiteReadOnlyEditor = ({ + editorContentCustomClassNames, + noBorder, + borderOnFocus, + customClassName, + value, + forwardedRef, +}: EditorCoreProps) => { + const editor = useReadOnlyEditor({ + value, + forwardedRef, + }); + + const editorClassNames = getEditorClassNames({ noBorder, borderOnFocus, customClassName }); + + if (!editor) return null; + + return ( + +
+ +
+
+ ); +}; + +const LiteReadOnlyEditorWithRef = React.forwardRef((props, ref) => ( + +)); + +LiteReadOnlyEditorWithRef.displayName = "LiteReadOnlyEditorWithRef"; + +export { LiteReadOnlyEditor , LiteReadOnlyEditorWithRef }; diff --git a/packages/editor/lite-text-editor/src/ui/tooltip.tsx b/packages/editor/lite-text-editor/src/ui/tooltip.tsx new file mode 100644 index 000000000..f29d8a491 --- /dev/null +++ b/packages/editor/lite-text-editor/src/ui/tooltip.tsx @@ -0,0 +1,77 @@ +import * as React from 'react'; + +// next-themes +import { useTheme } from "next-themes"; +// tooltip2 +import { Tooltip2 } from "@blueprintjs/popover2"; + +type Props = { + tooltipHeading?: string; + tooltipContent: string | React.ReactNode; + position?: + | "top" + | "right" + | "bottom" + | "left" + | "auto" + | "auto-end" + | "auto-start" + | "bottom-left" + | "bottom-right" + | "left-bottom" + | "left-top" + | "right-bottom" + | "right-top" + | "top-left" + | "top-right"; + children: JSX.Element; + disabled?: boolean; + className?: string; + openDelay?: number; + closeDelay?: number; +}; + +export const Tooltip: React.FC = ({ + tooltipHeading, + tooltipContent, + position = "top", + children, + disabled = false, + className = "", + openDelay = 200, + closeDelay, +}) => { + const { theme } = useTheme(); + + return ( + + {tooltipHeading && ( +
+ {tooltipHeading} +
+ )} + {tooltipContent} + + } + position={position} + renderTarget={({ isOpen: isTooltipOpen, ref: eleReference, ...tooltipProps }) => + React.cloneElement(children, { ref: eleReference, ...tooltipProps, ...children.props }) + } + /> + ); +}; diff --git a/packages/editor/lite-text-editor/tailwind.config.js b/packages/editor/lite-text-editor/tailwind.config.js new file mode 100644 index 000000000..f32063158 --- /dev/null +++ b/packages/editor/lite-text-editor/tailwind.config.js @@ -0,0 +1,6 @@ +const sharedConfig = require("tailwind-config-custom/tailwind.config.js"); + +module.exports = { + // prefix ui lib classes to avoid conflicting with the app + ...sharedConfig, +}; diff --git a/packages/editor/lite-text-editor/tsconfig.json b/packages/editor/lite-text-editor/tsconfig.json new file mode 100644 index 000000000..61a60f324 --- /dev/null +++ b/packages/editor/lite-text-editor/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "tsconfig/react.json", + "include": [ + "src/**/*", + "index.d.ts" + ], + "exclude": [ + "dist", + "build", + "node_modules" + ] +} diff --git a/packages/editor/lite-text-editor/tsup.config.ts b/packages/editor/lite-text-editor/tsup.config.ts new file mode 100644 index 000000000..5e89e04af --- /dev/null +++ b/packages/editor/lite-text-editor/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig, Options } from "tsup"; + +export default defineConfig((options: Options) => ({ + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: true, + clean: false, + external: ["react"], + injectStyle: true, + ...options, +})); diff --git a/packages/editor/rich-text-editor/Readme.md b/packages/editor/rich-text-editor/Readme.md new file mode 100644 index 000000000..c8414f62d --- /dev/null +++ b/packages/editor/rich-text-editor/Readme.md @@ -0,0 +1,99 @@ +# @plane/rich-text-editor + +## Description + +The `@plane/rich-text-editor` package extends from the `editor-core` package, inheriting its base functionality while adding its own unique features of Slash Commands and many more. + +## Key Features + +- **Exported Components**: There are two components exported from the Rich text editor (with and without Ref), you can choose to use the `withRef` instance whenever you want to control the Editor’s state via a side effect of some external action from within the application code. + + `RichTextEditor` & `RichTextEditorWithRef` + +- **Read Only Editor Instances**: We have added a really light weight *Read Only* Editor instance for the Rich editor types (with and without Ref) + `RichReadOnlyEditor` &`RichReadOnlyEditorWithRef` + +## RichTextEditor + +| Prop | Type | Description | +| --- | --- | --- | +| `uploadFile` | `(file: File) => Promise` | A function that handles file upload. It takes a file as input and handles the process of uploading that file. | +| `deleteFile` | `(assetUrlWithWorkspaceId: string) => Promise` | A function that handles deleting an image. It takes the asset url from your bucket and handles the process of deleting that image. | +| `value` | `html string` | The initial content of the editor. | +| `debouncedUpdatesEnabled` | `boolean` | If set to true, the `onChange` event handler is debounced, meaning it will only be invoked after the specified delay (default 1500ms) once the user has stopped typing. | +| `onChange` | `(json: any, html: string) => void` | This function is invoked whenever the content of the editor changes. It is passed the new content in both JSON and HTML formats. | +| `setIsSubmitting` | `(isSubmitting: "submitting" \| "submitted" \| "saved") => void` | This function is called to update the submission status. | +| `setShouldShowAlert` | `(showAlert: boolean) => void` | This function is used to show or hide an alert incase of content not being "saved". | +| `noBorder` | `boolean` | If set to true, the editor will not have a border. | +| `borderOnFocus` | `boolean` | If set to true, the editor will show a border when it is focused. | +| `customClassName` | `string` | This is a custom CSS class that can be applied to the editor. | +| `editorContentCustomClassNames` | `string` | This is a custom CSS class that can be applied to the editor content. | + +### Usage + +1. Here is an example of how to use the `RichTextEditor` component + +```tsx + { + setShowAlert(true); + setIsSubmitting("submitting"); + onChange(description_html); + // custom stuff you want to do + }} +/> +``` + +2. Example of how to use the `RichTextEditorWithRef` component + +```tsx + const editorRef = useRef(null); + + // can use it to set the editor's value + editorRef.current?.setEditorValue(`${watch("description_html")}`); + + // can use it to clear the editor + editorRef?.current?.clearEditor(); + + return ( { + onChange(description_html); + // custom stuff you want to do + } } />) +``` + +## RichReadOnlyEditor + +| Prop | Type | Description | +| --- | --- | --- | +| `value` | `html string` | The initial content of the editor. | +| `noBorder` | `boolean` | If set to true, the editor will not have a border. | +| `borderOnFocus` | `boolean` | If set to true, the editor will show a border when it is focused. | +| `customClassName` | `string` | This is a custom CSS class that can be applied to the editor. | +| `editorContentCustomClassNames` | `string` | This is a custom CSS class that can be applied to the editor content. | + +### Usage + +Here is an example of how to use the `RichReadOnlyEditor` component + +```tsx + +``` diff --git a/packages/editor/rich-text-editor/package.json b/packages/editor/rich-text-editor/package.json new file mode 100644 index 000000000..7bdd0a58b --- /dev/null +++ b/packages/editor/rich-text-editor/package.json @@ -0,0 +1,61 @@ +{ + "name": "@plane/rich-text-editor", + "version": "0.0.1", + "description": "Rich Text Editor that powers Plane", + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "files": [ + "dist/**/*" + ], + "exports": { + ".": { + "types": "./dist/index.d.mts", + "import": "./dist/index.mjs", + "module": "./dist/index.mjs" + } + }, + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "check-types": "tsc --noEmit" + }, + "peerDependencies": { + "next": "12.3.2", + "next-themes": "^0.2.1", + "react": "^18.2.0", + "react-dom": "18.2.0", + "@tiptap/core": "^2.1.11" + }, + "dependencies": { + "@plane/editor-core": "*", + "@tiptap/extension-code-block-lowlight": "^2.1.11", + "@tiptap/extension-horizontal-rule": "^2.1.11", + "@tiptap/extension-placeholder": "^2.1.11", + "class-variance-authority": "^0.7.0", + "@tiptap/suggestion": "^2.1.7", + "clsx": "^1.2.1", + "highlight.js": "^11.8.0", + "lowlight": "^3.0.0", + "lucide-react": "^0.244.0" + }, + "devDependencies": { + "@types/node": "18.15.3", + "@types/react": "^18.2.5", + "@types/react-dom": "18.0.11", + "eslint": "^7.32.0", + "postcss": "^8.4.29", + "react": "^18.2.0", + "tailwind-config-custom": "*", + "tsconfig": "*", + "tsup": "^7.2.0", + "typescript": "4.9.5" + }, + "keywords": [ + "editor", + "rich-text", + "markdown", + "nextjs", + "react" + ] +} diff --git a/packages/editor/rich-text-editor/postcss.config.js b/packages/editor/rich-text-editor/postcss.config.js new file mode 100644 index 000000000..07aa434b2 --- /dev/null +++ b/packages/editor/rich-text-editor/postcss.config.js @@ -0,0 +1,9 @@ +// If you want to use other PostCSS plugins, see the following: +// https://tailwindcss.com/docs/using-with-preprocessors + +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/editor/rich-text-editor/src/index.ts b/packages/editor/rich-text-editor/src/index.ts new file mode 100644 index 000000000..36d0a95f9 --- /dev/null +++ b/packages/editor/rich-text-editor/src/index.ts @@ -0,0 +1,4 @@ +import "./styles/github-dark.css"; + +export { RichTextEditor, RichTextEditorWithRef } from "./ui"; +export { RichReadOnlyEditor, RichReadOnlyEditorWithRef } from "./ui/read-only"; diff --git a/packages/editor/rich-text-editor/src/styles/github-dark.css b/packages/editor/rich-text-editor/src/styles/github-dark.css new file mode 100644 index 000000000..20a7f4e66 --- /dev/null +++ b/packages/editor/rich-text-editor/src/styles/github-dark.css @@ -0,0 +1,2 @@ +pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px} +.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c} diff --git a/packages/editor/rich-text-editor/src/ui/extensions/index.tsx b/packages/editor/rich-text-editor/src/ui/extensions/index.tsx new file mode 100644 index 000000000..f0f3bed34 --- /dev/null +++ b/packages/editor/rich-text-editor/src/ui/extensions/index.tsx @@ -0,0 +1,59 @@ +import HorizontalRule from "@tiptap/extension-horizontal-rule"; +import Placeholder from "@tiptap/extension-placeholder"; +import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight"; +import { common, createLowlight } from 'lowlight' +import { InputRule } from "@tiptap/core"; + +import ts from "highlight.js/lib/languages/typescript"; + +import SlashCommand from "./slash-command"; +import { UploadImage } from "../"; + +const lowlight = createLowlight(common) +lowlight.register("ts", ts); + +export const RichTextEditorExtensions = ( + uploadFile: UploadImage, + setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void +) => [ + HorizontalRule.extend({ + addInputRules() { + return [ + new InputRule({ + find: /^(?:---|—-|___\s|\*\*\*\s)$/, + handler: ({ state, range, commands }) => { + commands.splitBlock(); + + const attributes = {}; + const { tr } = state; + const start = range.from; + const end = range.to; + // @ts-ignore + tr.replaceWith(start - 1, end, this.type.create(attributes)); + }, + }), + ]; + }, + }).configure({ + HTMLAttributes: { + class: "mb-6 border-t border-custom-border-300", + }, + }), + SlashCommand(uploadFile, setIsSubmitting), + CodeBlockLowlight.configure({ + lowlight, + }), + Placeholder.configure({ + placeholder: ({ node }) => { + if (node.type.name === "heading") { + return `Heading ${node.attrs.level}`; + } + if (node.type.name === "image" || node.type.name === "table") { + return ""; + } + + return "Press '/' for commands..."; + }, + includeChildren: true, + }), + ]; diff --git a/space/components/tiptap/slash-command/index.tsx b/packages/editor/rich-text-editor/src/ui/extensions/slash-command.tsx similarity index 51% rename from space/components/tiptap/slash-command/index.tsx rename to packages/editor/rich-text-editor/src/ui/extensions/slash-command.tsx index 46bf5ea5a..e00585dd8 100644 --- a/space/components/tiptap/slash-command/index.tsx +++ b/packages/editor/rich-text-editor/src/ui/extensions/slash-command.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback, ReactNode, useRef, useLayoutEffect } from "react"; +import { useState, useEffect, useCallback, ReactNode, useRef, useLayoutEffect } from "react"; import { Editor, Range, Extension } from "@tiptap/core"; import Suggestion from "@tiptap/suggestion"; import { ReactRenderer } from "@tiptap/react"; @@ -17,8 +17,8 @@ import { ImageIcon, Table, } from "lucide-react"; -import { startImageUpload } from "../plugins/upload-image"; -import { cn } from "../utils"; +import { UploadImage } from "../"; +import { cn, insertTableCommand, toggleBlockquote, toggleBulletList, toggleOrderedList, toggleTaskList, insertImageCommand, toggleHeadingOne, toggleHeadingTwo, toggleHeadingThree } from "@plane/editor-core"; interface CommandItemProps { title: string; @@ -58,151 +58,128 @@ const Command = Extension.create({ const getSuggestionItems = ( - workspaceSlug: string, + uploadFile: UploadImage, setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void ) => - ({ query }: { query: string }) => - [ - { - title: "Text", - description: "Just start typing with plain text.", - searchTerms: ["p", "paragraph"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").run(); + ({ query }: { query: string }) => + [ + { + title: "Text", + description: "Just start typing with plain text.", + searchTerms: ["p", "paragraph"], + icon: , + command: ({ editor, range }: CommandProps) => { + editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").run(); + }, }, - }, - { - title: "Heading 1", - description: "Big section heading.", - searchTerms: ["title", "big", "large"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run(); + { + title: "Heading 1", + description: "Big section heading.", + searchTerms: ["title", "big", "large"], + icon: , + command: ({ editor, range }: CommandProps) => { + toggleHeadingOne(editor, range); + }, }, - }, - { - title: "Heading 2", - description: "Medium section heading.", - searchTerms: ["subtitle", "medium"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).setNode("heading", { level: 2 }).run(); + { + title: "Heading 2", + description: "Medium section heading.", + searchTerms: ["subtitle", "medium"], + icon: , + command: ({ editor, range }: CommandProps) => { + toggleHeadingTwo(editor, range); + }, }, - }, - { - title: "Heading 3", - description: "Small section heading.", - searchTerms: ["subtitle", "small"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).setNode("heading", { level: 3 }).run(); + { + title: "Heading 3", + description: "Small section heading.", + searchTerms: ["subtitle", "small"], + icon: , + command: ({ editor, range }: CommandProps) => { + toggleHeadingThree(editor, range); + }, }, - }, - { - title: "To-do List", - description: "Track tasks with a to-do list.", - searchTerms: ["todo", "task", "list", "check", "checkbox"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).toggleTaskList().run(); + { + title: "To-do List", + description: "Track tasks with a to-do list.", + searchTerms: ["todo", "task", "list", "check", "checkbox"], + icon: , + command: ({ editor, range }: CommandProps) => { + toggleTaskList(editor, range) + }, }, - }, - { - title: "Bullet List", - description: "Create a simple bullet list.", - searchTerms: ["unordered", "point"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).toggleBulletList().run(); + { + title: "Bullet List", + description: "Create a simple bullet list.", + searchTerms: ["unordered", "point"], + icon: , + command: ({ editor, range }: CommandProps) => { + toggleBulletList(editor, range); + }, }, - }, - { - title: "Divider", - description: "Visually divide blocks", - searchTerms: ["line", "divider", "horizontal", "rule", "separate"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).setHorizontalRule().run(); + { + title: "Divider", + description: "Visually divide blocks", + searchTerms: ["line", "divider", "horizontal", "rule", "separate"], + icon: , + command: ({ editor, range }: CommandProps) => { + editor.chain().focus().deleteRange(range).setHorizontalRule().run(); + }, }, - }, - { - title: "Table", - description: "Create a Table", - searchTerms: ["table", "cell", "db", "data", "tabular"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor - .chain() - .focus() - .deleteRange(range) - .insertTable({ rows: 3, cols: 3, withHeaderRow: true }) - .run(); + { + title: "Table", + description: "Create a Table", + searchTerms: ["table", "cell", "db", "data", "tabular"], + icon:
, + command: ({ editor, range }: CommandProps) => { + insertTableCommand(editor, range); + }, }, - }, - { - title: "Numbered List", - description: "Create a list with numbering.", - searchTerms: ["ordered"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).toggleOrderedList().run(); + { + title: "Numbered List", + description: "Create a list with numbering.", + searchTerms: ["ordered"], + icon: , + command: ({ editor, range }: CommandProps) => { + toggleOrderedList(editor, range) + }, }, - }, - { - title: "Quote", - description: "Capture a quote.", - searchTerms: ["blockquote"], - icon: , - command: ({ editor, range }: CommandProps) => - editor - .chain() - .focus() - .deleteRange(range) - .toggleNode("paragraph", "paragraph") - .toggleBlockquote() - .run(), - }, - { - title: "Code", - description: "Capture a code snippet.", - searchTerms: ["codeblock"], - icon: , - command: ({ editor, range }: CommandProps) => - editor.chain().focus().deleteRange(range).toggleCodeBlock().run(), - }, - { - title: "Image", - description: "Upload an image from your computer.", - searchTerms: ["photo", "picture", "media"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).run(); - // upload image - const input = document.createElement("input"); - input.type = "file"; - input.accept = "image/*"; - input.onchange = async () => { - if (input.files?.length) { - const file = input.files[0]; - const pos = editor.view.state.selection.from; - startImageUpload(file, editor.view, pos, workspaceSlug, setIsSubmitting); - } - }; - input.click(); + { + title: "Quote", + description: "Capture a quote.", + searchTerms: ["blockquote"], + icon: , + command: ({ editor, range }: CommandProps) => + toggleBlockquote(editor, range) }, - }, - ].filter((item) => { - if (typeof query === "string" && query.length > 0) { - const search = query.toLowerCase(); - return ( - item.title.toLowerCase().includes(search) || - item.description.toLowerCase().includes(search) || - (item.searchTerms && item.searchTerms.some((term: string) => term.includes(search))) - ); - } - return true; - }); + { + title: "Code", + description: "Capture a code snippet.", + searchTerms: ["codeblock"], + icon: , + command: ({ editor, range }: CommandProps) => + editor.chain().focus().deleteRange(range).toggleCodeBlock().run(), + }, + { + title: "Image", + description: "Upload an image from your computer.", + searchTerms: ["photo", "picture", "media"], + icon: , + command: ({ editor, range }: CommandProps) => { + insertImageCommand(editor, uploadFile, setIsSubmitting, range); + }, + }, + ].filter((item) => { + if (typeof query === "string" && query.length > 0) { + const search = query.toLowerCase(); + return ( + item.title.toLowerCase().includes(search) || + item.description.toLowerCase().includes(search) || + (item.searchTerms && item.searchTerms.some((term: string) => term.includes(search))) + ); + } + return true; + }); export const updateScrollView = (container: HTMLElement, item: HTMLElement) => { const containerHeight = container.offsetHeight; @@ -312,13 +289,14 @@ const renderItems = () => { onStart: (props: { editor: Editor; clientRect: DOMRect }) => { component = new ReactRenderer(CommandList, { props, + // @ts-ignore editor: props.editor, }); // @ts-ignore popup = tippy("body", { getReferenceClientRect: props.clientRect, - appendTo: () => document.querySelector("#tiptap-container"), + appendTo: () => document.querySelector("#editor-container"), content: component.element, showOnCreate: true, interactive: true, @@ -352,12 +330,12 @@ const renderItems = () => { }; export const SlashCommand = ( - workspaceSlug: string, + uploadFile: UploadImage, setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void ) => Command.configure({ suggestion: { - items: getSuggestionItems(workspaceSlug, setIsSubmitting), + items: getSuggestionItems(uploadFile, setIsSubmitting), render: renderItems, }, }); diff --git a/packages/editor/rich-text-editor/src/ui/index.tsx b/packages/editor/rich-text-editor/src/ui/index.tsx new file mode 100644 index 000000000..ce14962c8 --- /dev/null +++ b/packages/editor/rich-text-editor/src/ui/index.tsx @@ -0,0 +1,80 @@ +"use client" +import * as React from 'react'; +import { EditorContainer, EditorContentWrapper, getEditorClassNames, useEditor } from '@plane/editor-core'; +import { EditorBubbleMenu } from './menus/bubble-menu'; +import { RichTextEditorExtensions } from './extensions'; + +export type UploadImage = (file: File) => Promise; +export type DeleteImage = (assetUrlWithWorkspaceId: string) => Promise; + +interface IRichTextEditor { + value: string; + uploadFile: UploadImage; + deleteFile: DeleteImage; + noBorder?: boolean; + borderOnFocus?: boolean; + customClassName?: string; + editorContentCustomClassNames?: string; + onChange?: (json: any, html: string) => void; + setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void; + setShouldShowAlert?: (showAlert: boolean) => void; + forwardedRef?: any; + debouncedUpdatesEnabled?: boolean; +} + +interface RichTextEditorProps extends IRichTextEditor { + forwardedRef?: React.Ref; +} + +interface EditorHandle { + clearEditor: () => void; + setEditorValue: (content: string) => void; +} + +const RichTextEditor = ({ + onChange, + debouncedUpdatesEnabled, + setIsSubmitting, + setShouldShowAlert, + editorContentCustomClassNames, + value, + uploadFile, + deleteFile, + noBorder, + borderOnFocus, + customClassName, + forwardedRef, +}: RichTextEditorProps) => { + const editor = useEditor({ + onChange, + debouncedUpdatesEnabled, + setIsSubmitting, + setShouldShowAlert, + value, + uploadFile, + deleteFile, + forwardedRef, + extensions: RichTextEditorExtensions(uploadFile, setIsSubmitting) + }); + + const editorClassNames = getEditorClassNames({ noBorder, borderOnFocus, customClassName }); + + if (!editor) return null; + + return ( + + {editor && } +
+ +
+
+ ); +}; + +const RichTextEditorWithRef = React.forwardRef((props, ref) => ( + +)); + +RichTextEditorWithRef.displayName = "RichTextEditorWithRef"; + +export { RichTextEditor, RichTextEditorWithRef}; diff --git a/web/components/tiptap/bubble-menu/index.tsx b/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx similarity index 70% rename from web/components/tiptap/bubble-menu/index.tsx rename to packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx index 217317ea1..14fd73a83 100644 --- a/web/components/tiptap/bubble-menu/index.tsx +++ b/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx @@ -1,10 +1,10 @@ import { BubbleMenu, BubbleMenuProps } from "@tiptap/react"; import { FC, useState } from "react"; -import { BoldIcon, ItalicIcon, UnderlineIcon, StrikethroughIcon, CodeIcon } from "lucide-react"; +import { BoldIcon } from "lucide-react"; import { NodeSelector } from "./node-selector"; import { LinkSelector } from "./link-selector"; -import { cn } from "../utils"; +import { BoldItem, cn, CodeItem, ItalicItem, StrikeThroughItem, UnderLineItem } from "@plane/editor-core"; export interface BubbleMenuItem { name: string; @@ -17,36 +17,11 @@ type EditorBubbleMenuProps = Omit; export const EditorBubbleMenu: FC = (props: any) => { const items: BubbleMenuItem[] = [ - { - name: "bold", - isActive: () => props.editor?.isActive("bold"), - command: () => props.editor?.chain().focus().toggleBold().run(), - icon: BoldIcon, - }, - { - name: "italic", - isActive: () => props.editor?.isActive("italic"), - command: () => props.editor?.chain().focus().toggleItalic().run(), - icon: ItalicIcon, - }, - { - name: "underline", - isActive: () => props.editor?.isActive("underline"), - command: () => props.editor?.chain().focus().toggleUnderline().run(), - icon: UnderlineIcon, - }, - { - name: "strike", - isActive: () => props.editor?.isActive("strike"), - command: () => props.editor?.chain().focus().toggleStrike().run(), - icon: StrikethroughIcon, - }, - { - name: "code", - isActive: () => props.editor?.isActive("code"), - command: () => props.editor?.chain().focus().toggleCode().run(), - icon: CodeIcon, - }, + BoldItem(props.editor), + ItalicItem(props.editor), + UnderLineItem(props.editor), + StrikeThroughItem(props.editor), + CodeItem(props.editor), ]; const bubbleMenuProps: EditorBubbleMenuProps = { diff --git a/web/components/tiptap/bubble-menu/link-selector.tsx b/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/link-selector.tsx similarity index 93% rename from web/components/tiptap/bubble-menu/link-selector.tsx rename to packages/editor/rich-text-editor/src/ui/menus/bubble-menu/link-selector.tsx index 559521db6..7dddc9d98 100644 --- a/web/components/tiptap/bubble-menu/link-selector.tsx +++ b/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/link-selector.tsx @@ -1,8 +1,8 @@ import { Editor } from "@tiptap/core"; import { Check, Trash } from "lucide-react"; import { Dispatch, FC, SetStateAction, useCallback, useEffect, useRef } from "react"; -import { cn } from "../utils"; -import isValidHttpUrl from "./utils/link-validator"; +import { cn, isValidHttpUrl, setLinkEditor, unsetLinkEditor, } from "@plane/editor-core"; + interface LinkSelectorProps { editor: Editor; isOpen: boolean; @@ -16,7 +16,7 @@ export const LinkSelector: FC = ({ editor, isOpen, setIsOpen const input = inputRef.current; const url = input?.value; if (url && isValidHttpUrl(url)) { - editor.chain().focus().setLink({ href: url }).run(); + setLinkEditor(editor, url); setIsOpen(false); } }, [editor, inputRef, setIsOpen]); @@ -68,7 +68,7 @@ export const LinkSelector: FC = ({ editor, isOpen, setIsOpen type="button" className="flex items-center rounded-sm p-1 text-red-600 transition-all hover:bg-red-100 dark:hover:bg-red-800" onClick={() => { - editor.chain().focus().unsetLink().run(); + unsetLinkEditor(editor); setIsOpen(false); }} > diff --git a/web/components/tiptap/bubble-menu/node-selector.tsx b/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/node-selector.tsx similarity index 58% rename from web/components/tiptap/bubble-menu/node-selector.tsx rename to packages/editor/rich-text-editor/src/ui/menus/bubble-menu/node-selector.tsx index 34d40ec06..b8b7ffc58 100644 --- a/web/components/tiptap/bubble-menu/node-selector.tsx +++ b/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/node-selector.tsx @@ -1,20 +1,13 @@ -import { Editor } from "@tiptap/core"; +import { BulletListItem, cn, CodeItem, HeadingOneItem, HeadingThreeItem, HeadingTwoItem, NumberedListItem, QuoteItem, TodoListItem } from "@plane/editor-core"; +import { Editor } from "@tiptap/react"; import { Check, ChevronDown, - Heading1, - Heading2, - Heading3, - TextQuote, - ListOrdered, TextIcon, - Code, - CheckSquare, } from "lucide-react"; import { Dispatch, FC, SetStateAction } from "react"; import { BubbleMenuItem } from "."; -import { cn } from "../utils"; interface NodeSelectorProps { editor: Editor; @@ -33,55 +26,14 @@ export const NodeSelector: FC = ({ editor, isOpen, setIsOpen !editor.isActive("bulletList") && !editor.isActive("orderedList"), }, - { - name: "H1", - icon: Heading1, - command: () => editor.chain().focus().toggleHeading({ level: 1 }).run(), - isActive: () => editor.isActive("heading", { level: 1 }), - }, - { - name: "H2", - icon: Heading2, - command: () => editor.chain().focus().toggleHeading({ level: 2 }).run(), - isActive: () => editor.isActive("heading", { level: 2 }), - }, - { - name: "H3", - icon: Heading3, - command: () => editor.chain().focus().toggleHeading({ level: 3 }).run(), - isActive: () => editor.isActive("heading", { level: 3 }), - }, - { - name: "To-do List", - icon: CheckSquare, - command: () => editor.chain().focus().toggleTaskList().run(), - isActive: () => editor.isActive("taskItem"), - }, - { - name: "Bullet List", - icon: ListOrdered, - command: () => editor.chain().focus().toggleBulletList().run(), - isActive: () => editor.isActive("bulletList"), - }, - { - name: "Numbered List", - icon: ListOrdered, - command: () => editor.chain().focus().toggleOrderedList().run(), - isActive: () => editor.isActive("orderedList"), - }, - { - name: "Quote", - icon: TextQuote, - command: () => - editor.chain().focus().toggleNode("paragraph", "paragraph").toggleBlockquote().run(), - isActive: () => editor.isActive("blockquote"), - }, - { - name: "Code", - icon: Code, - command: () => editor.chain().focus().toggleCodeBlock().run(), - isActive: () => editor.isActive("codeBlock"), - }, + HeadingOneItem(editor), + HeadingTwoItem(editor), + HeadingThreeItem(editor), + TodoListItem(editor), + BulletListItem(editor), + NumberedListItem(editor), + QuoteItem(editor), + CodeItem(editor), ]; const activeItem = items.filter((item) => item.isActive()).pop() ?? { diff --git a/packages/editor/rich-text-editor/src/ui/read-only/index.tsx b/packages/editor/rich-text-editor/src/ui/read-only/index.tsx new file mode 100644 index 000000000..dc058cf89 --- /dev/null +++ b/packages/editor/rich-text-editor/src/ui/read-only/index.tsx @@ -0,0 +1,54 @@ +"use client" +import { EditorContainer, EditorContentWrapper, getEditorClassNames, useReadOnlyEditor } from '@plane/editor-core'; +import * as React from 'react'; + +interface IRichTextReadOnlyEditor { + value: string; + editorContentCustomClassNames?: string; + noBorder?: boolean; + borderOnFocus?: boolean; + customClassName?: string; +} + +interface RichTextReadOnlyEditorProps extends IRichTextReadOnlyEditor { + forwardedRef?: React.Ref; +} + +interface EditorHandle { + clearEditor: () => void; + setEditorValue: (content: string) => void; +} + +const RichReadOnlyEditor = ({ + editorContentCustomClassNames, + noBorder, + borderOnFocus, + customClassName, + value, + forwardedRef, +}: RichTextReadOnlyEditorProps) => { + const editor = useReadOnlyEditor({ + value, + forwardedRef, + }); + + const editorClassNames = getEditorClassNames({ noBorder, borderOnFocus, customClassName }); + + if (!editor) return null; + + return ( + +
+ +
+
+ ); +}; + +const RichReadOnlyEditorWithRef = React.forwardRef((props, ref) => ( + +)); + +RichReadOnlyEditorWithRef.displayName = "RichReadOnlyEditorWithRef"; + +export { RichReadOnlyEditor , RichReadOnlyEditorWithRef }; diff --git a/packages/editor/rich-text-editor/tailwind.config.js b/packages/editor/rich-text-editor/tailwind.config.js new file mode 100644 index 000000000..f32063158 --- /dev/null +++ b/packages/editor/rich-text-editor/tailwind.config.js @@ -0,0 +1,6 @@ +const sharedConfig = require("tailwind-config-custom/tailwind.config.js"); + +module.exports = { + // prefix ui lib classes to avoid conflicting with the app + ...sharedConfig, +}; diff --git a/packages/editor/rich-text-editor/tsconfig.json b/packages/editor/rich-text-editor/tsconfig.json new file mode 100644 index 000000000..61a60f324 --- /dev/null +++ b/packages/editor/rich-text-editor/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "tsconfig/react.json", + "include": [ + "src/**/*", + "index.d.ts" + ], + "exclude": [ + "dist", + "build", + "node_modules" + ] +} diff --git a/packages/editor/rich-text-editor/tsup.config.ts b/packages/editor/rich-text-editor/tsup.config.ts new file mode 100644 index 000000000..5e89e04af --- /dev/null +++ b/packages/editor/rich-text-editor/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig, Options } from "tsup"; + +export default defineConfig((options: Options) => ({ + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: true, + clean: false, + external: ["react"], + injectStyle: true, + ...options, +})); diff --git a/packages/tailwind-config-custom/package.json b/packages/tailwind-config-custom/package.json index 6edaa0ec4..1336379b7 100644 --- a/packages/tailwind-config-custom/package.json +++ b/packages/tailwind-config-custom/package.json @@ -4,7 +4,12 @@ "description": "common tailwind configuration across monorepo", "main": "index.js", "devDependencies": { - "@tailwindcss/typography": "^0.5.10", - "tailwindcss-animate": "^1.0.7" + "@tailwindcss/typography": "^0.5.9", + "autoprefixer": "^10.4.14", + "postcss": "^8.4.21", + "prettier": "^2.8.8", + "prettier-plugin-tailwindcss": "^0.3.0", + "tailwindcss": "^3.2.7", + "tailwindcss-animate": "^1.0.6" } } diff --git a/packages/tailwind-config-custom/tailwind.config.js b/packages/tailwind-config-custom/tailwind.config.js index 061168c4f..dd2f9291b 100644 --- a/packages/tailwind-config-custom/tailwind.config.js +++ b/packages/tailwind-config-custom/tailwind.config.js @@ -1,14 +1,19 @@ const convertToRGB = (variableName) => `rgba(var(${variableName}))`; +/** @type {import('tailwindcss').Config} */ module.exports = { darkMode: "class", - content: [ - "./components/**/*.tsx", - "./constants/**/*.{js,ts,jsx,tsx}", - "./layouts/**/*.tsx", - "./pages/**/*.tsx", - "./ui/**/*.tsx", - ], + content: { + relative: true, + files: [ + "./components/**/*.tsx", + "./constants/**/*.{js,ts,jsx,tsx}", + "./layouts/**/*.tsx", + "./pages/**/*.tsx", + "./ui/**/*.tsx", + "../packages/editor/**/*.{js,ts,jsx,tsx}" + ] + }, theme: { extend: { boxShadow: { diff --git a/packages/tsconfig/base.json b/packages/tsconfig/base.json index d72a9f3a2..2825abe07 100644 --- a/packages/tsconfig/base.json +++ b/packages/tsconfig/base.json @@ -16,5 +16,7 @@ "skipLibCheck": true, "strict": true }, - "exclude": ["node_modules"] + "exclude": [ + "node_modules" + ] } diff --git a/packages/tsconfig/react-library.json b/packages/tsconfig/react.json similarity index 77% rename from packages/tsconfig/react-library.json rename to packages/tsconfig/react.json index bdd954367..36b62be38 100644 --- a/packages/tsconfig/react-library.json +++ b/packages/tsconfig/react.json @@ -3,8 +3,8 @@ "display": "React Library", "extends": "./base.json", "compilerOptions": { - "jsx": "react", - "lib": ["ES2015"], + "jsx": "react-jsx", + "lib": ["ES2015", "DOM"], "module": "ESNext", "target": "es6" } diff --git a/space/components/issues/peek-overview/comment/add-comment.tsx b/space/components/issues/peek-overview/comment/add-comment.tsx index 3ea4308d7..c7e7a468e 100644 --- a/space/components/issues/peek-overview/comment/add-comment.tsx +++ b/space/components/issues/peek-overview/comment/add-comment.tsx @@ -11,7 +11,9 @@ import { SecondaryButton } from "components/ui"; // types import { Comment } from "types/issue"; // components -import { TipTapEditor } from "components/tiptap"; +import { LiteTextEditorWithRef } from "@plane/lite-text-editor"; +// service +import fileService from "services/file.service"; const defaultValues: Partial = { comment_html: "", @@ -69,8 +71,14 @@ export const AddComment: React.FC = observer((props) => { name="comment_html" control={control} render={({ field: { value, onChange } }) => ( - { + userStore.requiredLogin(() => { + handleSubmit(onSubmit)(e); + }); + }} + uploadFile={fileService.getUploadFileFunction(workspace_slug as string)} + deleteFile={fileService.deleteImage} ref={editorRef} value={ !value || value === "" || (typeof value === "object" && Object.keys(value).length === 0) diff --git a/space/components/issues/peek-overview/comment/comment-detail-card.tsx b/space/components/issues/peek-overview/comment/comment-detail-card.tsx index 0b4d2d5b0..f6b4d9bf3 100644 --- a/space/components/issues/peek-overview/comment/comment-detail-card.tsx +++ b/space/components/issues/peek-overview/comment/comment-detail-card.tsx @@ -9,7 +9,8 @@ import { Menu, Transition } from "@headlessui/react"; // lib import { useMobxStore } from "lib/mobx/store-provider"; // components -import { TipTapEditor } from "components/tiptap"; +import { LiteReadOnlyEditorWithRef, LiteTextEditorWithRef } from "@plane/lite-text-editor"; + import { CommentReactions } from "components/issues/peek-overview"; // icons import { ChatBubbleLeftEllipsisIcon, CheckIcon, XMarkIcon, EllipsisVerticalIcon } from "@heroicons/react/24/outline"; @@ -17,6 +18,8 @@ import { ChatBubbleLeftEllipsisIcon, CheckIcon, XMarkIcon, EllipsisVerticalIcon import { timeAgo } from "helpers/date-time.helper"; // types import { Comment } from "types/issue"; +import fileService from "services/file.service"; +// services type Props = { workspaceSlug: string; @@ -100,8 +103,10 @@ export const CommentCard: React.FC = observer((props) => { control={control} name="comment_html" render={({ field: { onChange, value } }) => ( - = observer((props) => {
- @@ -147,7 +150,7 @@ export const CommentCard: React.FC = observer((props) => { {}} + onClick={() => { }} className="relative grid place-items-center rounded p-1 text-custom-text-200 hover:text-custom-text-100 outline-none cursor-pointer hover:bg-custom-background-80" > @@ -171,9 +174,8 @@ export const CommentCard: React.FC = observer((props) => { onClick={() => { setIsEditing(true); }} - className={`w-full select-none truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80 ${ - active ? "bg-custom-background-80" : "" - }`} + className={`w-full select-none truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80 ${active ? "bg-custom-background-80" : "" + }`} > Edit @@ -186,9 +188,8 @@ export const CommentCard: React.FC = observer((props) => { diff --git a/space/components/issues/peek-overview/issue-details.tsx b/space/components/issues/peek-overview/issue-details.tsx index 0b329568c..24dd65651 100644 --- a/space/components/issues/peek-overview/issue-details.tsx +++ b/space/components/issues/peek-overview/issue-details.tsx @@ -1,6 +1,5 @@ import { IssueReactions } from "components/issues/peek-overview"; -import { TipTapEditor } from "components/tiptap"; -import { useRouter } from "next/router"; +import { RichReadOnlyEditor } from "@plane/rich-text-editor"; // types import { IIssue } from "types/issue"; @@ -8,33 +7,22 @@ type Props = { issueDetails: IIssue; }; -export const PeekOverviewIssueDetails: React.FC = ({ issueDetails }) => { - const router = useRouter(); - const { workspace_slug } = router.query; - - return ( -
-
- {issueDetails.project_detail.identifier}-{issueDetails.sequence_id} -
-

{issueDetails.name}

- {issueDetails.description_html !== "" && issueDetails.description_html !== "

" && ( -

" - : issueDetails.description_html - } - customClassName="p-3 min-h-[50px] shadow-sm" - debouncedUpdatesEnabled={false} - editable={false} - /> - )} - -
- ); -}; +export const PeekOverviewIssueDetails: React.FC = ({ issueDetails }) => ( +
+
+ {issueDetails.project_detail.identifier}-{issueDetails.sequence_id} +
+

{issueDetails.name}

+ {issueDetails.description_html !== "" && issueDetails.description_html !== "

" && ( +

" + : issueDetails.description_html} + customClassName="p-3 min-h-[50px] shadow-sm" /> + )} + +
+); diff --git a/space/components/tiptap/bubble-menu/index.tsx b/space/components/tiptap/bubble-menu/index.tsx deleted file mode 100644 index 217317ea1..000000000 --- a/space/components/tiptap/bubble-menu/index.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { BubbleMenu, BubbleMenuProps } from "@tiptap/react"; -import { FC, useState } from "react"; -import { BoldIcon, ItalicIcon, UnderlineIcon, StrikethroughIcon, CodeIcon } from "lucide-react"; - -import { NodeSelector } from "./node-selector"; -import { LinkSelector } from "./link-selector"; -import { cn } from "../utils"; - -export interface BubbleMenuItem { - name: string; - isActive: () => boolean; - command: () => void; - icon: typeof BoldIcon; -} - -type EditorBubbleMenuProps = Omit; - -export const EditorBubbleMenu: FC = (props: any) => { - const items: BubbleMenuItem[] = [ - { - name: "bold", - isActive: () => props.editor?.isActive("bold"), - command: () => props.editor?.chain().focus().toggleBold().run(), - icon: BoldIcon, - }, - { - name: "italic", - isActive: () => props.editor?.isActive("italic"), - command: () => props.editor?.chain().focus().toggleItalic().run(), - icon: ItalicIcon, - }, - { - name: "underline", - isActive: () => props.editor?.isActive("underline"), - command: () => props.editor?.chain().focus().toggleUnderline().run(), - icon: UnderlineIcon, - }, - { - name: "strike", - isActive: () => props.editor?.isActive("strike"), - command: () => props.editor?.chain().focus().toggleStrike().run(), - icon: StrikethroughIcon, - }, - { - name: "code", - isActive: () => props.editor?.isActive("code"), - command: () => props.editor?.chain().focus().toggleCode().run(), - icon: CodeIcon, - }, - ]; - - const bubbleMenuProps: EditorBubbleMenuProps = { - ...props, - shouldShow: ({ editor }) => { - if (!editor.isEditable) { - return false; - } - if (editor.isActive("image")) { - return false; - } - return editor.view.state.selection.content().size > 0; - }, - tippyOptions: { - moveTransition: "transform 0.15s ease-out", - onHidden: () => { - setIsNodeSelectorOpen(false); - setIsLinkSelectorOpen(false); - }, - }, - }; - - const [isNodeSelectorOpen, setIsNodeSelectorOpen] = useState(false); - const [isLinkSelectorOpen, setIsLinkSelectorOpen] = useState(false); - - return ( - - {!props.editor.isActive("table") && ( - { - setIsNodeSelectorOpen(!isNodeSelectorOpen); - setIsLinkSelectorOpen(false); - }} - /> - )} - { - setIsLinkSelectorOpen(!isLinkSelectorOpen); - setIsNodeSelectorOpen(false); - }} - /> -
- {items.map((item, index) => ( - - ))} -
-
- ); -}; diff --git a/space/components/tiptap/bubble-menu/link-selector.tsx b/space/components/tiptap/bubble-menu/link-selector.tsx deleted file mode 100644 index 559521db6..000000000 --- a/space/components/tiptap/bubble-menu/link-selector.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { Editor } from "@tiptap/core"; -import { Check, Trash } from "lucide-react"; -import { Dispatch, FC, SetStateAction, useCallback, useEffect, useRef } from "react"; -import { cn } from "../utils"; -import isValidHttpUrl from "./utils/link-validator"; -interface LinkSelectorProps { - editor: Editor; - isOpen: boolean; - setIsOpen: Dispatch>; -} - -export const LinkSelector: FC = ({ editor, isOpen, setIsOpen }) => { - const inputRef = useRef(null); - - const onLinkSubmit = useCallback(() => { - const input = inputRef.current; - const url = input?.value; - if (url && isValidHttpUrl(url)) { - editor.chain().focus().setLink({ href: url }).run(); - setIsOpen(false); - } - }, [editor, inputRef, setIsOpen]); - - useEffect(() => { - inputRef.current && inputRef.current?.focus(); - }); - - return ( -
- - {isOpen && ( -
{ - if (e.key === "Enter") { - e.preventDefault(); - onLinkSubmit(); - } - }} - > - - {editor.getAttributes("link").href ? ( - - ) : ( - - )} -
- )} -
- ); -}; diff --git a/space/components/tiptap/bubble-menu/node-selector.tsx b/space/components/tiptap/bubble-menu/node-selector.tsx deleted file mode 100644 index 34d40ec06..000000000 --- a/space/components/tiptap/bubble-menu/node-selector.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { Editor } from "@tiptap/core"; -import { - Check, - ChevronDown, - Heading1, - Heading2, - Heading3, - TextQuote, - ListOrdered, - TextIcon, - Code, - CheckSquare, -} from "lucide-react"; -import { Dispatch, FC, SetStateAction } from "react"; - -import { BubbleMenuItem } from "."; -import { cn } from "../utils"; - -interface NodeSelectorProps { - editor: Editor; - isOpen: boolean; - setIsOpen: Dispatch>; -} - -export const NodeSelector: FC = ({ editor, isOpen, setIsOpen }) => { - const items: BubbleMenuItem[] = [ - { - name: "Text", - icon: TextIcon, - command: () => editor.chain().focus().toggleNode("paragraph", "paragraph").run(), - isActive: () => - editor.isActive("paragraph") && - !editor.isActive("bulletList") && - !editor.isActive("orderedList"), - }, - { - name: "H1", - icon: Heading1, - command: () => editor.chain().focus().toggleHeading({ level: 1 }).run(), - isActive: () => editor.isActive("heading", { level: 1 }), - }, - { - name: "H2", - icon: Heading2, - command: () => editor.chain().focus().toggleHeading({ level: 2 }).run(), - isActive: () => editor.isActive("heading", { level: 2 }), - }, - { - name: "H3", - icon: Heading3, - command: () => editor.chain().focus().toggleHeading({ level: 3 }).run(), - isActive: () => editor.isActive("heading", { level: 3 }), - }, - { - name: "To-do List", - icon: CheckSquare, - command: () => editor.chain().focus().toggleTaskList().run(), - isActive: () => editor.isActive("taskItem"), - }, - { - name: "Bullet List", - icon: ListOrdered, - command: () => editor.chain().focus().toggleBulletList().run(), - isActive: () => editor.isActive("bulletList"), - }, - { - name: "Numbered List", - icon: ListOrdered, - command: () => editor.chain().focus().toggleOrderedList().run(), - isActive: () => editor.isActive("orderedList"), - }, - { - name: "Quote", - icon: TextQuote, - command: () => - editor.chain().focus().toggleNode("paragraph", "paragraph").toggleBlockquote().run(), - isActive: () => editor.isActive("blockquote"), - }, - { - name: "Code", - icon: Code, - command: () => editor.chain().focus().toggleCodeBlock().run(), - isActive: () => editor.isActive("codeBlock"), - }, - ]; - - const activeItem = items.filter((item) => item.isActive()).pop() ?? { - name: "Multiple", - }; - - return ( -
- - - {isOpen && ( -
- {items.map((item, index) => ( - - ))} -
- )} -
- ); -}; diff --git a/space/components/tiptap/bubble-menu/utils/link-validator.tsx b/space/components/tiptap/bubble-menu/utils/link-validator.tsx deleted file mode 100644 index 9af366c02..000000000 --- a/space/components/tiptap/bubble-menu/utils/link-validator.tsx +++ /dev/null @@ -1,11 +0,0 @@ -export default function isValidHttpUrl(string: string): boolean { - let url; - - try { - url = new URL(string); - } catch (_) { - return false; - } - - return url.protocol === "http:" || url.protocol === "https:"; -} diff --git a/space/components/tiptap/extensions/updated-image.tsx b/space/components/tiptap/extensions/updated-image.tsx deleted file mode 100644 index b62050953..000000000 --- a/space/components/tiptap/extensions/updated-image.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import Image from "@tiptap/extension-image"; -import TrackImageDeletionPlugin from "../plugins/delete-image"; -import UploadImagesPlugin from "../plugins/upload-image"; - -const UpdatedImage = Image.extend({ - addProseMirrorPlugins() { - return [UploadImagesPlugin(), TrackImageDeletionPlugin()]; - }, - addAttributes() { - return { - ...this.parent?.(), - width: { - default: "35%", - }, - height: { - default: null, - }, - }; - }, -}); - -export default UpdatedImage; diff --git a/space/components/tiptap/index.tsx b/space/components/tiptap/index.tsx deleted file mode 100644 index 84f691c35..000000000 --- a/space/components/tiptap/index.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { useImperativeHandle, useRef, forwardRef, useEffect } from "react"; -import { useEditor, EditorContent, Editor } from "@tiptap/react"; -import { useDebouncedCallback } from "use-debounce"; -// components -import { EditorBubbleMenu } from "./bubble-menu"; -import { TiptapExtensions } from "./extensions"; -import { TiptapEditorProps } from "./props"; -import { ImageResizer } from "./extensions/image-resize"; -import { TableMenu } from "./table-menu"; - -export interface ITipTapRichTextEditor { - value: string; - noBorder?: boolean; - borderOnFocus?: boolean; - customClassName?: string; - editorContentCustomClassNames?: string; - onChange?: (json: any, html: string) => void; - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void; - setShouldShowAlert?: (showAlert: boolean) => void; - workspaceSlug: string; - editable?: boolean; - forwardedRef?: any; - debouncedUpdatesEnabled?: boolean; -} - -const Tiptap = (props: ITipTapRichTextEditor) => { - const { - onChange, - debouncedUpdatesEnabled, - forwardedRef, - editable, - setIsSubmitting, - setShouldShowAlert, - editorContentCustomClassNames, - value, - noBorder, - workspaceSlug, - borderOnFocus, - customClassName, - } = props; - - const editor = useEditor({ - editable: editable ?? true, - editorProps: TiptapEditorProps(workspaceSlug, setIsSubmitting), - extensions: TiptapExtensions(workspaceSlug, setIsSubmitting), - content: value, - onUpdate: async ({ editor }) => { - // for instant feedback loop - setIsSubmitting?.("submitting"); - setShouldShowAlert?.(true); - if (debouncedUpdatesEnabled) { - debouncedUpdates({ onChange, editor }); - } else { - onChange?.(editor.getJSON(), editor.getHTML()); - } - }, - }); - - const editorRef: React.MutableRefObject = useRef(null); - - useImperativeHandle(forwardedRef, () => ({ - clearEditor: () => { - editorRef.current?.commands.clearContent(); - }, - setEditorValue: (content: string) => { - editorRef.current?.commands.setContent(content); - }, - })); - - const debouncedUpdates = useDebouncedCallback(async ({ onChange, editor }) => { - setTimeout(async () => { - if (onChange) { - onChange(editor.getJSON(), editor.getHTML()); - } - }, 500); - }, 1000); - - const editorClassNames = `relative w-full max-w-full sm:rounded-lg mt-2 p-3 relative focus:outline-none rounded-md - ${noBorder ? "" : "border border-custom-border-200"} ${ - borderOnFocus ? "focus:border border-custom-border-300" : "focus:border-0" - } ${customClassName}`; - - if (!editor) return null; - editorRef.current = editor; - - return ( -
{ - editor?.chain().focus().run(); - }} - className={`tiptap-editor-container cursor-text ${editorClassNames}`} - > - {editor && } -
- - - {editor?.isActive("image") && } -
-
- ); -}; - -const TipTapEditor = forwardRef((props, ref) => ( - -)); - -TipTapEditor.displayName = "TipTapEditor"; - -export { TipTapEditor }; diff --git a/space/components/tiptap/plugins/delete-image.tsx b/space/components/tiptap/plugins/delete-image.tsx deleted file mode 100644 index fdf515ccc..000000000 --- a/space/components/tiptap/plugins/delete-image.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { EditorState, Plugin, PluginKey, Transaction } from "@tiptap/pm/state"; -import { Node as ProseMirrorNode } from "@tiptap/pm/model"; -import fileService from "services/file.service"; - -const deleteKey = new PluginKey("delete-image"); -const IMAGE_NODE_TYPE = "image"; - -interface ImageNode extends ProseMirrorNode { - attrs: { - src: string; - id: string; - }; -} - -const TrackImageDeletionPlugin = (): Plugin => - new Plugin({ - key: deleteKey, - appendTransaction: (transactions: readonly Transaction[], oldState: EditorState, newState: EditorState) => { - const newImageSources = new Set(); - newState.doc.descendants((node) => { - if (node.type.name === IMAGE_NODE_TYPE) { - newImageSources.add(node.attrs.src); - } - }); - - transactions.forEach((transaction) => { - if (!transaction.docChanged) return; - - const removedImages: ImageNode[] = []; - - oldState.doc.descendants((oldNode, oldPos) => { - if (oldNode.type.name !== IMAGE_NODE_TYPE) return; - if (oldPos < 0 || oldPos > newState.doc.content.size) return; - if (!newState.doc.resolve(oldPos).parent) return; - - const newNode = newState.doc.nodeAt(oldPos); - - // Check if the node has been deleted or replaced - if (!newNode || newNode.type.name !== IMAGE_NODE_TYPE) { - if (!newImageSources.has(oldNode.attrs.src)) { - removedImages.push(oldNode as ImageNode); - } - } - }); - - removedImages.forEach(async (node) => { - const src = node.attrs.src; - await onNodeDeleted(src); - }); - }); - - return null; - }, - }); - -export default TrackImageDeletionPlugin; - -async function onNodeDeleted(src: string): Promise { - try { - const assetUrlWithWorkspaceId = new URL(src).pathname.substring(1); - const resStatus = await fileService.deleteImage(assetUrlWithWorkspaceId); - if (resStatus === 204) { - console.log("Image deleted successfully"); - } - } catch (error) { - console.error("Error deleting image: ", error); - } -} diff --git a/space/components/tiptap/plugins/upload-image.tsx b/space/components/tiptap/plugins/upload-image.tsx deleted file mode 100644 index bc0acdc54..000000000 --- a/space/components/tiptap/plugins/upload-image.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { EditorState, Plugin, PluginKey } from "@tiptap/pm/state"; -import { Decoration, DecorationSet, EditorView } from "@tiptap/pm/view"; -import fileService from "services/file.service"; - -const uploadKey = new PluginKey("upload-image"); - -const UploadImagesPlugin = () => - new Plugin({ - key: uploadKey, - state: { - init() { - return DecorationSet.empty; - }, - apply(tr, set) { - set = set.map(tr.mapping, tr.doc); - // See if the transaction adds or removes any placeholders - const action = tr.getMeta(uploadKey); - if (action && action.add) { - const { id, pos, src } = action.add; - - const placeholder = document.createElement("div"); - placeholder.setAttribute("class", "img-placeholder"); - const image = document.createElement("img"); - image.setAttribute("class", "opacity-10 rounded-lg border border-custom-border-300"); - image.src = src; - placeholder.appendChild(image); - const deco = Decoration.widget(pos + 1, placeholder, { - id, - }); - set = set.add(tr.doc, [deco]); - } else if (action && action.remove) { - set = set.remove(set.find(undefined, undefined, (spec) => spec.id == action.remove.id)); - } - return set; - }, - }, - props: { - decorations(state) { - return this.getState(state); - }, - }, - }); - -export default UploadImagesPlugin; - -function findPlaceholder(state: EditorState, id: {}) { - const decos = uploadKey.getState(state); - const found = decos.find( - undefined, - undefined, - (spec: { id: number | undefined }) => spec.id == id - ); - return found.length ? found[0].from : null; -} - -export async function startImageUpload( - file: File, - view: EditorView, - pos: number, - workspaceSlug: string, - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void -) { - if (!file.type.includes("image/")) { - return; - } - - const id = {}; - - const tr = view.state.tr; - if (!tr.selection.empty) tr.deleteSelection(); - - const reader = new FileReader(); - reader.readAsDataURL(file); - reader.onload = () => { - tr.setMeta(uploadKey, { - add: { - id, - pos, - src: reader.result, - }, - }); - view.dispatch(tr); - }; - - if (!workspaceSlug) { - return; - } - setIsSubmitting?.("submitting"); - const src = await UploadImageHandler(file, workspaceSlug); - const { schema } = view.state; - pos = findPlaceholder(view.state, id); - - if (pos == null) return; - const imageSrc = typeof src === "object" ? reader.result : src; - - const node = schema.nodes.image.create({ src: imageSrc }); - const transaction = view.state.tr - .replaceWith(pos, pos, node) - .setMeta(uploadKey, { remove: { id } }); - view.dispatch(transaction); -} - -const UploadImageHandler = (file: File, workspaceSlug: string): Promise => { - if (!workspaceSlug) { - return Promise.reject("Workspace slug is missing"); - } - try { - const formData = new FormData(); - formData.append("asset", file); - formData.append("attributes", JSON.stringify({})); - - return new Promise(async (resolve, reject) => { - const imageUrl = await fileService - .uploadFile(workspaceSlug, formData) - .then((response) => response.asset); - - const image = new Image(); - image.src = imageUrl; - image.onload = () => { - resolve(imageUrl); - }; - }); - } catch (error) { - console.log(error); - return Promise.reject(error); - } -}; diff --git a/space/components/tiptap/props.tsx b/space/components/tiptap/props.tsx deleted file mode 100644 index 8233e3ab4..000000000 --- a/space/components/tiptap/props.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { EditorProps } from "@tiptap/pm/view"; -import { startImageUpload } from "./plugins/upload-image"; -import { findTableAncestor } from "./table-menu"; - -export function TiptapEditorProps( - workspaceSlug: string, - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void -): EditorProps { - return { - attributes: { - class: `prose prose-brand max-w-full prose-headings:font-display font-default focus:outline-none`, - }, - handleDOMEvents: { - keydown: (_view, event) => { - // prevent default event listeners from firing when slash command is active - if (["ArrowUp", "ArrowDown", "Enter"].includes(event.key)) { - const slashCommand = document.querySelector("#slash-command"); - if (slashCommand) { - return true; - } - } - }, - }, - handlePaste: (view, event) => { - if (typeof window !== "undefined") { - const selection: any = window?.getSelection(); - if (selection.rangeCount !== 0) { - const range = selection.getRangeAt(0); - if (findTableAncestor(range.startContainer)) { - return; - } - } - } - if (event.clipboardData && event.clipboardData.files && event.clipboardData.files[0]) { - event.preventDefault(); - const file = event.clipboardData.files[0]; - const pos = view.state.selection.from; - startImageUpload(file, view, pos, workspaceSlug, setIsSubmitting); - return true; - } - return false; - }, - handleDrop: (view, event, _slice, moved) => { - if (typeof window !== "undefined") { - const selection: any = window?.getSelection(); - if (selection.rangeCount !== 0) { - const range = selection.getRangeAt(0); - if (findTableAncestor(range.startContainer)) { - return; - } - } - } - if (!moved && event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files[0]) { - event.preventDefault(); - const file = event.dataTransfer.files[0]; - const coordinates = view.posAtCoords({ - left: event.clientX, - top: event.clientY, - }); - // here we deduct 1 from the pos or else the image will create an extra node - if (coordinates) { - startImageUpload(file, view, coordinates.pos - 1, workspaceSlug, setIsSubmitting); - } - return true; - } - return false; - }, - }; -} diff --git a/space/components/tiptap/table-menu/index.tsx b/space/components/tiptap/table-menu/index.tsx deleted file mode 100644 index 94f9c0f8d..000000000 --- a/space/components/tiptap/table-menu/index.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { useState, useEffect } from "react"; -import { Rows, Columns, ToggleRight } from "lucide-react"; -import { cn } from "../utils"; -import { Tooltip } from "components/ui"; -import InsertLeftTableIcon from "./InsertLeftTableIcon"; -import InsertRightTableIcon from "./InsertRightTableIcon"; -import InsertTopTableIcon from "./InsertTopTableIcon"; -import InsertBottomTableIcon from "./InsertBottomTableIcon"; - -interface TableMenuItem { - command: () => void; - icon: any; - key: string; - name: string; -} - -export const findTableAncestor = (node: Node | null): HTMLTableElement | null => { - while (node !== null && node.nodeName !== "TABLE") { - node = node.parentNode; - } - return node as HTMLTableElement; -}; - -export const TableMenu = ({ editor }: { editor: any }) => { - const [tableLocation, setTableLocation] = useState({ bottom: 0, left: 0 }); - const isOpen = editor?.isActive("table"); - - const items: TableMenuItem[] = [ - { - command: () => editor.chain().focus().addColumnBefore().run(), - icon: InsertLeftTableIcon, - key: "insert-column-left", - name: "Insert 1 column left", - }, - { - command: () => editor.chain().focus().addColumnAfter().run(), - icon: InsertRightTableIcon, - key: "insert-column-right", - name: "Insert 1 column right", - }, - { - command: () => editor.chain().focus().addRowBefore().run(), - icon: InsertTopTableIcon, - key: "insert-row-above", - name: "Insert 1 row above", - }, - { - command: () => editor.chain().focus().addRowAfter().run(), - icon: InsertBottomTableIcon, - key: "insert-row-below", - name: "Insert 1 row below", - }, - { - command: () => editor.chain().focus().deleteColumn().run(), - icon: Columns, - key: "delete-column", - name: "Delete column", - }, - { - command: () => editor.chain().focus().deleteRow().run(), - icon: Rows, - key: "delete-row", - name: "Delete row", - }, - { - command: () => editor.chain().focus().toggleHeaderRow().run(), - icon: ToggleRight, - key: "toggle-header-row", - name: "Toggle header row", - }, - ]; - - useEffect(() => { - if (!window) return; - - const handleWindowClick = () => { - const selection: any = window?.getSelection(); - - if (selection.rangeCount !== 0) { - const range = selection.getRangeAt(0); - const tableNode = findTableAncestor(range.startContainer); - - let parent = tableNode?.parentElement; - - if (tableNode) { - const tableRect = tableNode.getBoundingClientRect(); - const tableCenter = tableRect.left + tableRect.width / 2; - const menuWidth = 45; - const menuLeft = tableCenter - menuWidth / 2; - const tableBottom = tableRect.bottom; - - setTableLocation({ bottom: tableBottom, left: menuLeft }); - - while (parent) { - if (!parent.classList.contains("disable-scroll")) - parent.classList.add("disable-scroll"); - parent = parent.parentElement; - } - } else { - const scrollDisabledContainers = document.querySelectorAll(".disable-scroll"); - - scrollDisabledContainers.forEach((container) => { - container.classList.remove("disable-scroll"); - }); - } - } - }; - - window.addEventListener("click", handleWindowClick); - - return () => { - window.removeEventListener("click", handleWindowClick); - }; - }, [tableLocation, editor]); - - return ( -
- {items.map((item, index) => ( - - - - ))} -
- ); -}; diff --git a/space/components/tiptap/utils.ts b/space/components/tiptap/utils.ts deleted file mode 100644 index a5ef19350..000000000 --- a/space/components/tiptap/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { clsx, type ClassValue } from "clsx"; -import { twMerge } from "tailwind-merge"; - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} diff --git a/space/package.json b/space/package.json index b539fbb65..86248e2ac 100644 --- a/space/package.json +++ b/space/package.json @@ -3,7 +3,8 @@ "version": "0.13.2", "private": true, "scripts": { - "dev": "next dev -p 4000", + "dev": "turbo run develop", + "develop": "next dev -p 4000", "build": "next build", "start": "next start -p 4000", "lint": "next lint" @@ -17,26 +18,6 @@ "@heroicons/react": "^2.0.12", "@mui/icons-material": "^5.14.1", "@mui/material": "^5.14.1", - "@tiptap/extension-code-block-lowlight": "^2.0.4", - "@tiptap/extension-color": "^2.0.4", - "@tiptap/extension-gapcursor": "^2.1.7", - "@tiptap/extension-highlight": "^2.0.4", - "@tiptap/extension-horizontal-rule": "^2.0.4", - "@tiptap/extension-image": "^2.0.4", - "@tiptap/extension-link": "^2.0.4", - "@tiptap/extension-placeholder": "^2.0.4", - "@tiptap/extension-table": "^2.1.6", - "@tiptap/extension-table-cell": "^2.1.6", - "@tiptap/extension-table-header": "^2.1.6", - "@tiptap/extension-table-row": "^2.1.6", - "@tiptap/extension-task-item": "^2.0.4", - "@tiptap/extension-task-list": "^2.0.4", - "@tiptap/extension-text-style": "^2.0.4", - "@tiptap/extension-underline": "^2.0.4", - "@tiptap/pm": "^2.0.4", - "@tiptap/react": "^2.0.4", - "@tiptap/starter-kit": "^2.0.4", - "@tiptap/suggestion": "^2.0.4", "axios": "^1.3.4", "clsx": "^2.0.0", "js-cookie": "^3.0.1", @@ -51,13 +32,11 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.38.0", - "react-moveable": "^0.54.1", "swr": "^2.2.2", "tailwind-merge": "^1.14.0", - "tiptap-markdown": "^0.8.2", "typescript": "4.9.5", - "use-debounce": "^9.0.4", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "@plane/rich-text-editor": "*" }, "devDependencies": { "@types/js-cookie": "^3.0.3", diff --git a/space/services/file.service.ts b/space/services/file.service.ts index 6df6423f4..1ba4cd4d2 100644 --- a/space/services/file.service.ts +++ b/space/services/file.service.ts @@ -25,19 +25,37 @@ interface UnSplashImageUrls { small_s3: string; } -class FileServices extends APIService { +class FileService extends APIService { constructor() { super(API_BASE_URL); + this.uploadFile = this.uploadFile.bind(this); + this.deleteImage = this.deleteImage.bind(this); } async uploadFile(workspaceSlug: string, file: FormData): Promise { - return this.mediaUpload(`/api/workspaces/${workspaceSlug}/file-assets/`, file) + return this.post(`/api/workspaces/${workspaceSlug}/file-assets/`, file, { + headers: { + ...this.getHeaders(), + "Content-Type": "multipart/form-data", + }, + }) .then((response) => response?.data) .catch((error) => { throw error?.response?.data; }); } + getUploadFileFunction(workspaceSlug: string): (file: File) => Promise { + return async (file: File) => { + const formData = new FormData(); + formData.append("asset", file); + formData.append("attributes", JSON.stringify({})); + + const data = await this.uploadFile(workspaceSlug, formData); + return data.asset; + }; + } + async deleteImage(assetUrlWithWorkspaceId: string): Promise { return this.delete(`/api/workspaces/file-assets/${assetUrlWithWorkspaceId}/`) .then((response) => response?.status) @@ -76,6 +94,6 @@ class FileServices extends APIService { } } -const fileServices = new FileServices(); +const fileService = new FileService(); -export default fileServices; +export default fileService; diff --git a/space/styles/editor.css b/space/styles/editor.css index 9da250dd1..85d881eeb 100644 --- a/space/styles/editor.css +++ b/space/styles/editor.css @@ -155,7 +155,7 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { } } -#tiptap-container { +#editor-container { table { border-collapse: collapse; table-layout: fixed; diff --git a/turbo.json b/turbo.json index e40a56ab7..4b64d6231 100644 --- a/turbo.json +++ b/turbo.json @@ -28,11 +28,60 @@ ], "pipeline": { "build": { - "dependsOn": ["^build"], - "outputs": [".next/**", "dist/**"] + "dependsOn": [ + "^build" + ], + "outputs": [ + ".next/**", + "dist/**" + ] + }, + "web#develop": { + "cache": false, + "persistent": true, + "dependsOn": [ + "@plane/lite-text-editor#build", + "@plane/rich-text-editor#build" + ] + }, + "space#develop": { + "cache": false, + "persistent": true, + "dependsOn": [ + "@plane/lite-text-editor#build", + "@plane/rich-text-editor#build" + ] + }, + "web#build": { + "cache": true, + "dependsOn": [ + "@plane/lite-text-editor#build", + "@plane/rich-text-editor#build" + ] + }, + "space#build": { + "cache": true, + "dependsOn": [ + "@plane/lite-text-editor#build", + "@plane/rich-text-editor#build" + ] + }, + "@plane/lite-text-editor#build": { + "cache": true, + "dependsOn": [ + "@plane/editor-core#build" + ] + }, + "@plane/rich-text-editor#build": { + "cache": true, + "dependsOn": [ + "@plane/editor-core#build" + ] }, "test": { - "dependsOn": ["^build"], + "dependsOn": [ + "^build" + ], "outputs": [] }, "lint": { @@ -41,6 +90,9 @@ "dev": { "cache": false }, + "develop": { + "cache": false + }, "start": { "cache": false }, diff --git a/web/components/core/modals/gpt-assistant-modal.tsx b/web/components/core/modals/gpt-assistant-modal.tsx index 81c9b7d71..65495805a 100644 --- a/web/components/core/modals/gpt-assistant-modal.tsx +++ b/web/components/core/modals/gpt-assistant-modal.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, forwardRef, useRef } from "react"; +import React, { useEffect, useState, useRef } from "react"; import { useRouter } from "next/router"; // react-hook-form import { useForm } from "react-hook-form"; @@ -10,7 +10,8 @@ import useToast from "hooks/use-toast"; import useUserAuth from "hooks/use-user-auth"; // ui import { Input, PrimaryButton, SecondaryButton } from "components/ui"; -import { TipTapEditor } from "components/tiptap"; +// components +import { RichReadOnlyEditor, RichReadOnlyEditorWithRef } from "@plane/rich-text-editor"; // types import { IIssue, IPageBlock } from "types"; @@ -133,20 +134,17 @@ export const GptAssistantModal: React.FC = ({ return (
{((content && content !== "") || (htmlContent && htmlContent !== "

")) && (
Content: - ${content}

`} customClassName="-m-3" noBorder borderOnFocus={false} - editable={false} ref={editorRef} />
@@ -154,13 +152,11 @@ export const GptAssistantModal: React.FC = ({ {response !== "" && (
Response: - ${response}

`} customClassName="-mx-3 -my-3" noBorder borderOnFocus={false} - editable={false} />
)} @@ -174,11 +170,10 @@ export const GptAssistantModal: React.FC = ({ type="text" name="task" register={register} - placeholder={`${ - content && content !== "" - ? "Tell AI what action to perform on this content..." - : "Ask AI anything..." - }`} + placeholder={`${content && content !== "" + ? "Tell AI what action to perform on this content..." + : "Ask AI anything..." + }`} autoComplete="off" />
@@ -214,8 +209,8 @@ export const GptAssistantModal: React.FC = ({ {isSubmitting ? "Generating response..." : response === "" - ? "Generate response" - : "Generate again"} + ? "Generate response" + : "Generate again"}
diff --git a/web/components/core/modals/image-upload-modal.tsx b/web/components/core/modals/image-upload-modal.tsx index df4f21e12..e1795ed04 100644 --- a/web/components/core/modals/image-upload-modal.tsx +++ b/web/components/core/modals/image-upload-modal.tsx @@ -7,7 +7,7 @@ import { useDropzone } from "react-dropzone"; // headless ui import { Transition, Dialog } from "@headlessui/react"; // services -import fileServices from "services/file.service"; +import fileService from "services/file.service"; // hooks import useWorkspaceDetails from "hooks/use-workspace-details"; // ui @@ -64,7 +64,7 @@ export const ImageUploadModal: React.FC = ({ formData.append("attributes", JSON.stringify({})); if (userImage) { - fileServices + fileService .uploadUserFile(formData) .then((res) => { const imageUrl = res.asset; @@ -73,13 +73,13 @@ export const ImageUploadModal: React.FC = ({ setIsImageUploading(false); setImage(null); - if (value) fileServices.deleteUserFile(value); + if (value) fileService.deleteUserFile(value); }) .catch((err) => { console.error(err); }); } else - fileServices + fileService .uploadFile(workspaceSlug as string, formData) .then((res) => { const imageUrl = res.asset; @@ -87,7 +87,7 @@ export const ImageUploadModal: React.FC = ({ setIsImageUploading(false); setImage(null); - if (value && workspaceDetails) fileServices.deleteFile(workspaceDetails.id, value); + if (value && workspaceDetails) fileService.deleteFile(workspaceDetails.id, value); }) .catch((err) => { console.error(err); diff --git a/web/components/issues/comment/add-comment.tsx b/web/components/issues/comment/add-comment.tsx index 33d7f2289..b5d91adee 100644 --- a/web/components/issues/comment/add-comment.tsx +++ b/web/components/issues/comment/add-comment.tsx @@ -3,11 +3,13 @@ import { useRouter } from "next/router"; // react-hook-form import { useForm, Controller } from "react-hook-form"; // components -import { TipTapEditor } from "components/tiptap"; +import { LiteTextEditorWithRef } from "@plane/lite-text-editor"; // ui -import { Icon, SecondaryButton, Tooltip } from "components/ui"; +import { SecondaryButton } from "components/ui"; // types import type { IIssueComment } from "types"; +// services +import fileService from "services/file.service"; const defaultValues: Partial = { access: "INTERNAL", @@ -20,7 +22,12 @@ type Props = { showAccessSpecifier?: boolean; }; -const commentAccess = [ +type commentAccessType = { + icon: string; + key: string; + label: "Private" | "Public"; +} +const commentAccess: commentAccessType[] = [ { icon: "lock", key: "INTERNAL", @@ -64,49 +71,26 @@ export const AddComment: React.FC = ({
- {showAccessSpecifier && ( -
- ( -
- {commentAccess.map((access) => ( - - - - ))} -
- )} - /> -
- )} ( -

" : value} - customClassName="p-3 min-h-[100px] shadow-sm" - debouncedUpdatesEnabled={false} - onChange={(comment_json: Object, comment_html: string) => onChange(comment_html)} + render={({ field: { onChange: onAccessChange, value: accessValue } }) => ( + ( +

" : commentValue} + customClassName="p-3 min-h-[100px] shadow-sm" + debouncedUpdatesEnabled={false} + onChange={(comment_json: Object, comment_html: string) => onCommentChange(comment_html)} + commentAccessSpecifier={{ accessValue, onAccessChange, showAccessSpecifier, commentAccess }} + /> + )} /> )} /> diff --git a/web/components/issues/comment/comment-card.tsx b/web/components/issues/comment/comment-card.tsx index 3d3e43b39..7e169a2d5 100644 --- a/web/components/issues/comment/comment-card.tsx +++ b/web/components/issues/comment/comment-card.tsx @@ -9,11 +9,13 @@ import useUser from "hooks/use-user"; // ui import { CustomMenu, Icon } from "components/ui"; import { CommentReaction } from "components/issues"; -import { TipTapEditor } from "components/tiptap"; +import { LiteTextEditorWithRef, LiteReadOnlyEditorWithRef } from "@plane/lite-text-editor"; // helpers import { timeAgo } from "helpers/date-time.helper"; // types import type { IIssueComment } from "types"; +import fileService from "services/file.service"; +// services type Props = { comment: IIssueComment; @@ -107,11 +109,12 @@ export const CommentCard: React.FC = ({
- = ({ />
)} - diff --git a/web/components/issues/description-form.tsx b/web/components/issues/description-form.tsx index 54ea0a976..8596d5fe8 100644 --- a/web/components/issues/description-form.tsx +++ b/web/components/issues/description-form.tsx @@ -7,9 +7,10 @@ import useReloadConfirmations from "hooks/use-reload-confirmation"; import { useDebouncedCallback } from "use-debounce"; // components import { TextArea } from "components/ui"; -import { TipTapEditor } from "components/tiptap"; // types import { IIssue } from "types"; +import { RichTextEditor } from "@plane/rich-text-editor"; +import fileService from "services/file.service"; export interface IssueDescriptionFormValues { name: string; @@ -84,10 +85,8 @@ export const IssueDescriptionForm: FC = ({ }, [issue, reset]); const debouncedTitleSave = useDebouncedCallback(async () => { - setTimeout(async () => { - handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted")); - }, 500); - }, 1000); + handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted")); + }, 1500); return (
@@ -99,7 +98,7 @@ export const IssueDescriptionForm: FC = ({ placeholder="Enter issue name" register={register} onFocus={() => setCharacterLimit(true)} - onChange={(e) => { + onChange={() => { setCharacterLimit(false); setIsSubmitting("submitting"); debouncedTitleSave(); @@ -115,9 +114,8 @@ export const IssueDescriptionForm: FC = ({ {characterLimit && isAllowed && (
255 ? "text-red-500" : "" - }`} + className={`${watch("name").length === 0 || watch("name").length > 255 ? "text-red-500" : "" + }`} > {watch("name").length} @@ -130,43 +128,28 @@ export const IssueDescriptionForm: FC = ({ { - if (!value) return <>; - - return ( -

" - : value - } - workspaceSlug={workspaceSlug} - debouncedUpdatesEnabled={true} - setShouldShowAlert={setShowAlert} - setIsSubmitting={setIsSubmitting} - customClassName={ - isAllowed ? "min-h-[150px] shadow-sm" : "!p-0 !pt-2 text-custom-text-200" - } - noBorder={!isAllowed} - onChange={(description: Object, description_html: string) => { - setShowAlert(true); - setIsSubmitting("submitting"); - onChange(description_html); - handleSubmit(handleDescriptionFormSubmit)().finally(() => - setIsSubmitting("submitted") - ); - }} - editable={isAllowed} - /> - ); - }} + render={({ field: { value, onChange } }) => ( + { + setShowAlert(true); + setIsSubmitting("submitting"); + onChange(description_html); + handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted") + ); + } } /> + )} />
{isSubmitting === "submitting" ? "Saving..." : "Saved"}
diff --git a/web/components/issues/draft-issue-form.tsx b/web/components/issues/draft-issue-form.tsx index 4a653dd86..aa89a9f5d 100644 --- a/web/components/issues/draft-issue-form.tsx +++ b/web/components/issues/draft-issue-form.tsx @@ -25,11 +25,14 @@ import { CreateStateModal } from "components/states"; import { CreateLabelModal } from "components/labels"; // ui import { CustomMenu, Input, PrimaryButton, SecondaryButton, ToggleSwitch } from "components/ui"; -import { TipTapEditor } from "components/tiptap"; +// components +import { RichTextEditorWithRef } from "@plane/rich-text-editor"; // icons import { SparklesIcon, XMarkIcon } from "@heroicons/react/24/outline"; // types import type { ICurrentUserResponse, IIssue, ISearchIssueResponse } from "types"; +import fileService from "services/file.service"; +// services const defaultValues: Partial = { project: "", @@ -411,29 +414,23 @@ export const DraftIssueForm: FC = (props) => { { - if (!value && !watch("description_html")) return <>; - - return ( - { - onChange(description_html); - setValue("description", description); - }} - /> - ); - }} + render={({ field: { value, onChange } }) => ( + { + onChange(description_html); + setValue("description", description); + } } /> + )} /> = { project: "", @@ -350,9 +354,8 @@ export const IssueForm: FC = (props) => { {issueName && issueName !== "" && (
= (props) => { ? "Updating Issue..." : "Update Issue" : isSubmitting - ? "Adding Issue..." - : "Add Issue"} + ? "Adding Issue..." + : "Add Issue"}
diff --git a/web/components/issues/sub-issues/issue.tsx b/web/components/issues/sub-issues/issue.tsx index a53d18bb9..4bb8ea234 100644 --- a/web/components/issues/sub-issues/issue.tsx +++ b/web/components/issues/sub-issues/issue.tsx @@ -19,6 +19,7 @@ import { SubIssuesRootList } from "./issues-list"; import { IssueProperty } from "./properties"; // ui import { Tooltip, CustomMenu } from "components/ui"; + // types import { ICurrentUserResponse, IIssue } from "types"; import { ISubIssuesRootLoaders, ISubIssuesRootLoadersHandler } from "./root"; diff --git a/web/components/issues/sub-issues/issues-list.tsx b/web/components/issues/sub-issues/issues-list.tsx index 9fc77992e..c79e64e91 100644 --- a/web/components/issues/sub-issues/issues-list.tsx +++ b/web/components/issues/sub-issues/issues-list.tsx @@ -5,6 +5,7 @@ import useSWR from "swr"; import { SubIssues } from "./issue"; // types import { ICurrentUserResponse, IIssue } from "types"; + import { ISubIssuesRootLoaders, ISubIssuesRootLoadersHandler } from "./root"; // services import issuesService from "services/issues.service"; diff --git a/web/components/issues/sub-issues/root.tsx b/web/components/issues/sub-issues/root.tsx index a2c6c81a4..881898a3f 100644 --- a/web/components/issues/sub-issues/root.tsx +++ b/web/components/issues/sub-issues/root.tsx @@ -14,7 +14,9 @@ import { ProgressBar } from "./progressbar"; import { CustomMenu } from "components/ui"; // hooks import { useProjectMyMembership } from "contexts/project-member.context"; + import useToast from "hooks/use-toast"; + // helpers import { copyTextToClipboard } from "helpers/string.helper"; // types @@ -48,6 +50,7 @@ export const SubIssuesRoot: React.FC = ({ parentIssue, user }) = }; const { memberRole } = useProjectMyMembership(); + const { setToastAlert } = useToast(); const { data: issues, isLoading } = useSWR( @@ -119,6 +122,7 @@ export const SubIssuesRoot: React.FC = ({ parentIssue, user }) = const payload = { sub_issue_ids: data.map((i) => i.id), }; + await issuesService.addSubIssues(workspaceSlug, projectId, issueId, payload).finally(() => { if (issueId) mutate(SUB_ISSUES(issueId)); }); diff --git a/web/components/pages/create-update-block-inline.tsx b/web/components/pages/create-update-block-inline.tsx index cee9fe8e3..49d6fbeed 100644 --- a/web/components/pages/create-update-block-inline.tsx +++ b/web/components/pages/create-update-block-inline.tsx @@ -11,12 +11,14 @@ import aiService from "services/ai.service"; import useToast from "hooks/use-toast"; // components import { GptAssistantModal } from "components/core"; -import { TipTapEditor } from "components/tiptap"; +import { RichTextEditorWithRef } from "@plane/rich-text-editor"; import { PrimaryButton, SecondaryButton, TextArea } from "components/ui"; // types import { ICurrentUserResponse, IPageBlock } from "types"; // fetch-keys import { PAGE_BLOCKS_LIST } from "constants/fetch-keys"; +// services +import fileService from "services/file.service"; type Props = { handleClose: () => void; @@ -279,8 +281,9 @@ export const CreateUpdateBlockInline: React.FC = ({ render={({ field: { value, onChange } }) => { if (!data) return ( -

"} debouncedUpdatesEnabled={false} @@ -299,8 +302,9 @@ export const CreateUpdateBlockInline: React.FC = ({ ); return ( - 0 diff --git a/web/components/pages/single-page-block.tsx b/web/components/pages/single-page-block.tsx index 18fba83bc..e37168d49 100644 --- a/web/components/pages/single-page-block.tsx +++ b/web/components/pages/single-page-block.tsx @@ -19,7 +19,7 @@ import useOutsideClickDetector from "hooks/use-outside-click-detector"; // components import { GptAssistantModal } from "components/core"; import { CreateUpdateBlockInline } from "components/pages"; -import { TipTapEditor } from "components/tiptap"; +import { RichTextEditor } from "@plane/rich-text-editor"; // ui import { CustomMenu, TextArea } from "components/ui"; // icons @@ -39,6 +39,7 @@ import { copyTextToClipboard } from "helpers/string.helper"; import { ICurrentUserResponse, IIssue, IPageBlock, IProject } from "types"; // fetch-keys import { PAGE_BLOCKS_LIST } from "constants/fetch-keys"; +import fileService from "services/file.service"; type Props = { block: IPageBlock; @@ -451,8 +452,9 @@ export const SinglePageBlock: React.FC = ({ {showBlockDetails ? block.description_html.length > 7 && ( - { - const updateMediaSize = () => { - const imageInfo = document.querySelector(".ProseMirror-selectednode") as HTMLImageElement; - if (imageInfo) { - const selection = editor.state.selection; - editor.commands.setImage({ - src: imageInfo.src, - width: Number(imageInfo.style.width.replace("px", "")), - height: Number(imageInfo.style.height.replace("px", "")), - } as any); - editor.commands.setNodeSelection(selection.from); - } - }; - - return ( - <> - { - delta[0] && (target!.style.width = `${width}px`); - delta[1] && (target!.style.height = `${height}px`); - }} - onResizeEnd={() => { - updateMediaSize(); - }} - scalable={true} - renderDirections={["w", "e"]} - onScale={({ target, transform }: any) => { - target!.style.transform = transform; - }} - /> - - ); -}; diff --git a/web/components/tiptap/extensions/index.tsx b/web/components/tiptap/extensions/index.tsx deleted file mode 100644 index 8ad4e07b4..000000000 --- a/web/components/tiptap/extensions/index.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import StarterKit from "@tiptap/starter-kit"; -import HorizontalRule from "@tiptap/extension-horizontal-rule"; -import TiptapLink from "@tiptap/extension-link"; -import Placeholder from "@tiptap/extension-placeholder"; -import TiptapUnderline from "@tiptap/extension-underline"; -import TextStyle from "@tiptap/extension-text-style"; -import { Color } from "@tiptap/extension-color"; -import TaskItem from "@tiptap/extension-task-item"; -import TaskList from "@tiptap/extension-task-list"; -import { Markdown } from "tiptap-markdown"; -import Highlight from "@tiptap/extension-highlight"; -import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight"; -import { lowlight } from "lowlight/lib/core"; -import SlashCommand from "../slash-command"; -import { InputRule } from "@tiptap/core"; -import Gapcursor from "@tiptap/extension-gapcursor"; - -import ts from "highlight.js/lib/languages/typescript"; - -import "highlight.js/styles/github-dark.css"; -import UpdatedImage from "./updated-image"; -import isValidHttpUrl from "../bubble-menu/utils/link-validator"; -import { CustomTableCell } from "./table/table-cell"; -import { Table } from "./table/table"; -import { TableHeader } from "./table/table-header"; -import { TableRow } from "@tiptap/extension-table-row"; - -lowlight.registerLanguage("ts", ts); - -export const TiptapExtensions = ( - workspaceSlug: string, - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void -) => [ - StarterKit.configure({ - bulletList: { - HTMLAttributes: { - class: "list-disc list-outside leading-3 -mt-2", - }, - }, - orderedList: { - HTMLAttributes: { - class: "list-decimal list-outside leading-3 -mt-2", - }, - }, - listItem: { - HTMLAttributes: { - class: "leading-normal -mb-2", - }, - }, - blockquote: { - HTMLAttributes: { - class: "border-l-4 border-custom-border-300", - }, - }, - code: { - HTMLAttributes: { - class: - "rounded-md bg-custom-primary-30 mx-1 px-1 py-1 font-mono font-medium text-custom-text-1000", - spellcheck: "false", - }, - }, - codeBlock: false, - horizontalRule: false, - dropcursor: { - color: "rgba(var(--color-text-100))", - width: 2, - }, - gapcursor: false, - }), - CodeBlockLowlight.configure({ - lowlight, - }), - HorizontalRule.extend({ - addInputRules() { - return [ - new InputRule({ - find: /^(?:---|—-|___\s|\*\*\*\s)$/, - handler: ({ state, range, commands }) => { - commands.splitBlock(); - - const attributes = {}; - const { tr } = state; - const start = range.from; - const end = range.to; - // @ts-ignore - tr.replaceWith(start - 1, end, this.type.create(attributes)); - }, - }), - ]; - }, - }).configure({ - HTMLAttributes: { - class: "mb-6 border-t border-custom-border-300", - }, - }), - Gapcursor, - TiptapLink.configure({ - protocols: ["http", "https"], - validate: (url) => isValidHttpUrl(url), - HTMLAttributes: { - class: - "text-custom-primary-300 underline underline-offset-[3px] hover:text-custom-primary-500 transition-colors cursor-pointer", - }, - }), - UpdatedImage.configure({ - HTMLAttributes: { - class: "rounded-lg border border-custom-border-300", - }, - }), - Placeholder.configure({ - placeholder: ({ node }) => { - if (node.type.name === "heading") { - return `Heading ${node.attrs.level}`; - } - if (node.type.name === "image" || node.type.name === "table") { - return ""; - } - - return "Press '/' for commands..."; - }, - includeChildren: true, - }), - SlashCommand(workspaceSlug, setIsSubmitting), - TiptapUnderline, - TextStyle, - Color, - Highlight.configure({ - multicolor: true, - }), - TaskList.configure({ - HTMLAttributes: { - class: "not-prose pl-2", - }, - }), - TaskItem.configure({ - HTMLAttributes: { - class: "flex items-start my-4", - }, - nested: true, - }), - Markdown.configure({ - html: true, - transformCopiedText: true, - }), - Table, - TableHeader, - CustomTableCell, - TableRow, - ]; diff --git a/web/components/tiptap/extensions/table/table-cell.ts b/web/components/tiptap/extensions/table/table-cell.ts deleted file mode 100644 index 643cb8c64..000000000 --- a/web/components/tiptap/extensions/table/table-cell.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { TableCell } from "@tiptap/extension-table-cell"; - -export const CustomTableCell = TableCell.extend({ - addAttributes() { - return { - ...this.parent?.(), - isHeader: { - default: false, - parseHTML: (element) => { - isHeader: element.tagName === "TD"; - }, - renderHTML: (attributes) => { - tag: attributes.isHeader ? "th" : "td"; - }, - }, - }; - }, - renderHTML({ HTMLAttributes }) { - if (HTMLAttributes.isHeader) { - return [ - "th", - { - ...HTMLAttributes, - class: `relative ${HTMLAttributes.class}`, - }, - ["span", { class: "absolute top-0 right-0" }], - 0, - ]; - } - return ["td", HTMLAttributes, 0]; - }, -}); diff --git a/web/components/tiptap/extensions/table/table-header.ts b/web/components/tiptap/extensions/table/table-header.ts deleted file mode 100644 index f23aa93ef..000000000 --- a/web/components/tiptap/extensions/table/table-header.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TableHeader as BaseTableHeader } from "@tiptap/extension-table-header"; - -const TableHeader = BaseTableHeader.extend({ - content: "paragraph", -}); - -export { TableHeader }; diff --git a/web/components/tiptap/extensions/table/table.ts b/web/components/tiptap/extensions/table/table.ts deleted file mode 100644 index 9b727bb51..000000000 --- a/web/components/tiptap/extensions/table/table.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Table as BaseTable } from "@tiptap/extension-table"; - -const Table = BaseTable.configure({ - resizable: true, - cellMinWidth: 100, - allowTableNodeSelection: true, -}); - -export { Table }; diff --git a/web/components/tiptap/extensions/updated-image.tsx b/web/components/tiptap/extensions/updated-image.tsx deleted file mode 100644 index b62050953..000000000 --- a/web/components/tiptap/extensions/updated-image.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import Image from "@tiptap/extension-image"; -import TrackImageDeletionPlugin from "../plugins/delete-image"; -import UploadImagesPlugin from "../plugins/upload-image"; - -const UpdatedImage = Image.extend({ - addProseMirrorPlugins() { - return [UploadImagesPlugin(), TrackImageDeletionPlugin()]; - }, - addAttributes() { - return { - ...this.parent?.(), - width: { - default: "35%", - }, - height: { - default: null, - }, - }; - }, -}); - -export default UpdatedImage; diff --git a/web/components/tiptap/index.tsx b/web/components/tiptap/index.tsx deleted file mode 100644 index 44076234e..000000000 --- a/web/components/tiptap/index.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { useImperativeHandle, useRef, forwardRef, useEffect } from "react"; -import { useEditor, EditorContent, Editor } from "@tiptap/react"; -import { useDebouncedCallback } from "use-debounce"; -// components -import { EditorBubbleMenu } from "./bubble-menu"; -import { TiptapExtensions } from "./extensions"; -import { TiptapEditorProps } from "./props"; -import { ImageResizer } from "./extensions/image-resize"; -import { TableMenu } from "./table-menu"; - -export interface ITipTapRichTextEditor { - value: string; - noBorder?: boolean; - borderOnFocus?: boolean; - customClassName?: string; - editorContentCustomClassNames?: string; - onChange?: (json: any, html: string) => void; - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void; - setShouldShowAlert?: (showAlert: boolean) => void; - workspaceSlug: string; - editable?: boolean; - forwardedRef?: any; - debouncedUpdatesEnabled?: boolean; -} - -const Tiptap = (props: ITipTapRichTextEditor) => { - const { - onChange, - debouncedUpdatesEnabled, - forwardedRef, - editable, - setIsSubmitting, - setShouldShowAlert, - editorContentCustomClassNames, - value, - noBorder, - workspaceSlug, - borderOnFocus, - customClassName, - } = props; - - const editor = useEditor({ - editable: editable ?? true, - editorProps: TiptapEditorProps(workspaceSlug, setIsSubmitting), - extensions: TiptapExtensions(workspaceSlug, setIsSubmitting), - content: value, - onUpdate: async ({ editor }) => { - // for instant feedback loop - setIsSubmitting?.("submitting"); - setShouldShowAlert?.(true); - if (debouncedUpdatesEnabled) { - debouncedUpdates({ onChange, editor }); - } else { - onChange?.(editor.getJSON(), editor.getHTML()); - } - }, - }); - - const editorRef: React.MutableRefObject = useRef(null); - - useImperativeHandle(forwardedRef, () => ({ - clearEditor: () => { - editorRef.current?.commands.clearContent(); - }, - setEditorValue: (content: string) => { - editorRef.current?.commands.setContent(content); - }, - })); - - const debouncedUpdates = useDebouncedCallback(async ({ onChange, editor }) => { - setTimeout(async () => { - if (onChange) { - onChange(editor.getJSON(), editor.getHTML()); - } - }, 500); - }, 1000); - - const editorClassNames = `relative w-full max-w-full sm:rounded-lg mt-2 p-3 relative focus:outline-none rounded-md - ${noBorder ? "" : "border border-custom-border-200"} ${ - borderOnFocus ? "focus:border border-custom-border-300" : "focus:border-0" - } ${customClassName}`; - - if (!editor) return null; - editorRef.current = editor; - - return ( -
{ - editor?.chain().focus().run(); - }} - className={`tiptap-editor-container relative cursor-text ${editorClassNames}`} - > - {editor && } -
- - - {editor?.isActive("image") && } -
-
- ); -}; - -const TipTapEditor = forwardRef((props, ref) => ( - -)); - -TipTapEditor.displayName = "TipTapEditor"; - -export { TipTapEditor }; diff --git a/web/components/tiptap/slash-command/index.tsx b/web/components/tiptap/slash-command/index.tsx deleted file mode 100644 index 46bf5ea5a..000000000 --- a/web/components/tiptap/slash-command/index.tsx +++ /dev/null @@ -1,365 +0,0 @@ -import React, { useState, useEffect, useCallback, ReactNode, useRef, useLayoutEffect } from "react"; -import { Editor, Range, Extension } from "@tiptap/core"; -import Suggestion from "@tiptap/suggestion"; -import { ReactRenderer } from "@tiptap/react"; -import tippy from "tippy.js"; -import { - Heading1, - Heading2, - Heading3, - List, - ListOrdered, - Text, - TextQuote, - Code, - MinusSquare, - CheckSquare, - ImageIcon, - Table, -} from "lucide-react"; -import { startImageUpload } from "../plugins/upload-image"; -import { cn } from "../utils"; - -interface CommandItemProps { - title: string; - description: string; - icon: ReactNode; -} - -interface CommandProps { - editor: Editor; - range: Range; -} - -const Command = Extension.create({ - name: "slash-command", - addOptions() { - return { - suggestion: { - char: "/", - command: ({ editor, range, props }: { editor: Editor; range: Range; props: any }) => { - props.command({ editor, range }); - }, - }, - }; - }, - addProseMirrorPlugins() { - return [ - Suggestion({ - editor: this.editor, - allow({ editor }) { - return !editor.isActive("table"); - }, - ...this.options.suggestion, - }), - ]; - }, -}); - -const getSuggestionItems = - ( - workspaceSlug: string, - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void - ) => - ({ query }: { query: string }) => - [ - { - title: "Text", - description: "Just start typing with plain text.", - searchTerms: ["p", "paragraph"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").run(); - }, - }, - { - title: "Heading 1", - description: "Big section heading.", - searchTerms: ["title", "big", "large"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run(); - }, - }, - { - title: "Heading 2", - description: "Medium section heading.", - searchTerms: ["subtitle", "medium"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).setNode("heading", { level: 2 }).run(); - }, - }, - { - title: "Heading 3", - description: "Small section heading.", - searchTerms: ["subtitle", "small"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).setNode("heading", { level: 3 }).run(); - }, - }, - { - title: "To-do List", - description: "Track tasks with a to-do list.", - searchTerms: ["todo", "task", "list", "check", "checkbox"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).toggleTaskList().run(); - }, - }, - { - title: "Bullet List", - description: "Create a simple bullet list.", - searchTerms: ["unordered", "point"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).toggleBulletList().run(); - }, - }, - { - title: "Divider", - description: "Visually divide blocks", - searchTerms: ["line", "divider", "horizontal", "rule", "separate"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).setHorizontalRule().run(); - }, - }, - { - title: "Table", - description: "Create a Table", - searchTerms: ["table", "cell", "db", "data", "tabular"], - icon:
, - command: ({ editor, range }: CommandProps) => { - editor - .chain() - .focus() - .deleteRange(range) - .insertTable({ rows: 3, cols: 3, withHeaderRow: true }) - .run(); - }, - }, - { - title: "Numbered List", - description: "Create a list with numbering.", - searchTerms: ["ordered"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).toggleOrderedList().run(); - }, - }, - { - title: "Quote", - description: "Capture a quote.", - searchTerms: ["blockquote"], - icon: , - command: ({ editor, range }: CommandProps) => - editor - .chain() - .focus() - .deleteRange(range) - .toggleNode("paragraph", "paragraph") - .toggleBlockquote() - .run(), - }, - { - title: "Code", - description: "Capture a code snippet.", - searchTerms: ["codeblock"], - icon: , - command: ({ editor, range }: CommandProps) => - editor.chain().focus().deleteRange(range).toggleCodeBlock().run(), - }, - { - title: "Image", - description: "Upload an image from your computer.", - searchTerms: ["photo", "picture", "media"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).run(); - // upload image - const input = document.createElement("input"); - input.type = "file"; - input.accept = "image/*"; - input.onchange = async () => { - if (input.files?.length) { - const file = input.files[0]; - const pos = editor.view.state.selection.from; - startImageUpload(file, editor.view, pos, workspaceSlug, setIsSubmitting); - } - }; - input.click(); - }, - }, - ].filter((item) => { - if (typeof query === "string" && query.length > 0) { - const search = query.toLowerCase(); - return ( - item.title.toLowerCase().includes(search) || - item.description.toLowerCase().includes(search) || - (item.searchTerms && item.searchTerms.some((term: string) => term.includes(search))) - ); - } - return true; - }); - -export const updateScrollView = (container: HTMLElement, item: HTMLElement) => { - const containerHeight = container.offsetHeight; - const itemHeight = item ? item.offsetHeight : 0; - - const top = item.offsetTop; - const bottom = top + itemHeight; - - if (top < container.scrollTop) { - container.scrollTop -= container.scrollTop - top + 5; - } else if (bottom > containerHeight + container.scrollTop) { - container.scrollTop += bottom - containerHeight - container.scrollTop + 5; - } -}; - -const CommandList = ({ - items, - command, -}: { - items: CommandItemProps[]; - command: any; - editor: any; - range: any; -}) => { - const [selectedIndex, setSelectedIndex] = useState(0); - - const selectItem = useCallback( - (index: number) => { - const item = items[index]; - if (item) { - command(item); - } - }, - [command, items] - ); - - useEffect(() => { - const navigationKeys = ["ArrowUp", "ArrowDown", "Enter"]; - const onKeyDown = (e: KeyboardEvent) => { - if (navigationKeys.includes(e.key)) { - e.preventDefault(); - if (e.key === "ArrowUp") { - setSelectedIndex((selectedIndex + items.length - 1) % items.length); - return true; - } - if (e.key === "ArrowDown") { - setSelectedIndex((selectedIndex + 1) % items.length); - return true; - } - if (e.key === "Enter") { - selectItem(selectedIndex); - return true; - } - return false; - } - }; - document.addEventListener("keydown", onKeyDown); - return () => { - document.removeEventListener("keydown", onKeyDown); - }; - }, [items, selectedIndex, setSelectedIndex, selectItem]); - - useEffect(() => { - setSelectedIndex(0); - }, [items]); - - const commandListContainer = useRef(null); - - useLayoutEffect(() => { - const container = commandListContainer?.current; - - const item = container?.children[selectedIndex] as HTMLElement; - - if (item && container) updateScrollView(container, item); - }, [selectedIndex]); - - return items.length > 0 ? ( -
- {items.map((item: CommandItemProps, index: number) => ( - - ))} -
- ) : null; -}; - -const renderItems = () => { - let component: ReactRenderer | null = null; - let popup: any | null = null; - - return { - onStart: (props: { editor: Editor; clientRect: DOMRect }) => { - component = new ReactRenderer(CommandList, { - props, - editor: props.editor, - }); - - // @ts-ignore - popup = tippy("body", { - getReferenceClientRect: props.clientRect, - appendTo: () => document.querySelector("#tiptap-container"), - content: component.element, - showOnCreate: true, - interactive: true, - trigger: "manual", - placement: "bottom-start", - }); - }, - onUpdate: (props: { editor: Editor; clientRect: DOMRect }) => { - component?.updateProps(props); - - popup && - popup[0].setProps({ - getReferenceClientRect: props.clientRect, - }); - }, - onKeyDown: (props: { event: KeyboardEvent }) => { - if (props.event.key === "Escape") { - popup?.[0].hide(); - - return true; - } - - // @ts-ignore - return component?.ref?.onKeyDown(props); - }, - onExit: () => { - popup?.[0].destroy(); - component?.destroy(); - }, - }; -}; - -export const SlashCommand = ( - workspaceSlug: string, - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void -) => - Command.configure({ - suggestion: { - items: getSuggestionItems(workspaceSlug, setIsSubmitting), - render: renderItems, - }, - }); - -export default SlashCommand; diff --git a/web/components/tiptap/table-menu/InsertBottomTableIcon.tsx b/web/components/tiptap/table-menu/InsertBottomTableIcon.tsx deleted file mode 100644 index 0e42ba648..000000000 --- a/web/components/tiptap/table-menu/InsertBottomTableIcon.tsx +++ /dev/null @@ -1,16 +0,0 @@ -const InsertBottomTableIcon = (props: any) => ( - - - -); - -export default InsertBottomTableIcon; diff --git a/web/components/tiptap/table-menu/InsertLeftTableIcon.tsx b/web/components/tiptap/table-menu/InsertLeftTableIcon.tsx deleted file mode 100644 index 1fd75fe87..000000000 --- a/web/components/tiptap/table-menu/InsertLeftTableIcon.tsx +++ /dev/null @@ -1,15 +0,0 @@ -const InsertLeftTableIcon = (props: any) => ( - - - -); -export default InsertLeftTableIcon; diff --git a/web/components/tiptap/table-menu/InsertRightTableIcon.tsx b/web/components/tiptap/table-menu/InsertRightTableIcon.tsx deleted file mode 100644 index 1a6570969..000000000 --- a/web/components/tiptap/table-menu/InsertRightTableIcon.tsx +++ /dev/null @@ -1,16 +0,0 @@ -const InsertRightTableIcon = (props: any) => ( - - - -); - -export default InsertRightTableIcon; diff --git a/web/components/tiptap/table-menu/InsertTopTableIcon.tsx b/web/components/tiptap/table-menu/InsertTopTableIcon.tsx deleted file mode 100644 index 8f04f4f61..000000000 --- a/web/components/tiptap/table-menu/InsertTopTableIcon.tsx +++ /dev/null @@ -1,15 +0,0 @@ -const InsertTopTableIcon = (props: any) => ( - - - -); -export default InsertTopTableIcon; diff --git a/web/components/tiptap/utils.ts b/web/components/tiptap/utils.ts deleted file mode 100644 index a5ef19350..000000000 --- a/web/components/tiptap/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { clsx, type ClassValue } from "clsx"; -import { twMerge } from "tailwind-merge"; - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} diff --git a/web/components/web-view/add-comment.tsx b/web/components/web-view/add-comment.tsx index b4f49d7be..fe7e3eb04 100644 --- a/web/components/web-view/add-comment.tsx +++ b/web/components/web-view/add-comment.tsx @@ -10,16 +10,16 @@ import { useForm, Controller } from "react-hook-form"; import useProjectDetails from "hooks/use-project-details"; // components -import { TipTapEditor } from "components/tiptap"; - +import { LiteTextEditorWithRef } from "@plane/lite-text-editor"; // icons import { Send } from "lucide-react"; // ui -import { Icon, SecondaryButton, Tooltip, PrimaryButton } from "components/ui"; +import { PrimaryButton } from "components/ui"; // types import type { IIssueComment } from "types"; +import fileService from "services/file.service"; const defaultValues: Partial = { access: "INTERNAL", @@ -31,7 +31,13 @@ type Props = { onSubmit: (data: IIssueComment) => Promise; }; -const commentAccess = [ +type commentAccessType = { + icon: string; + key: string; + label: "Private" | "Public"; +} + +const commentAccess: commentAccessType[] = [ { icon: "lock", key: "INTERNAL", @@ -52,7 +58,7 @@ export const AddComment: React.FC = ({ disabled = false, onSubmit }) => { const { projectDetails } = useProjectDetails(); - const showAccessSpecifier = projectDetails?.is_deployed; + const showAccessSpecifier = projectDetails?.is_deployed || false; const { control, @@ -73,47 +79,25 @@ export const AddComment: React.FC = ({ disabled = false, onSubmit }) => { return (
- {showAccessSpecifier && ( -
- ( -
- {commentAccess.map((access) => ( - - - - ))} -
- )} - /> -
- )} ( -

" : value} - customClassName="p-3 min-h-[100px] shadow-sm" - debouncedUpdatesEnabled={false} - onChange={(comment_json: Object, comment_html: string) => onChange(comment_html)} + render={({ field: { onChange: onAccessChange, value: accessValue } }) => ( + ( +

" : commentValue} + customClassName="p-3 min-h-[100px] shadow-sm" + debouncedUpdatesEnabled={false} + onChange={(comment_json: Object, comment_html: string) => onCommentChange(comment_html)} + commentAccessSpecifier={{ accessValue, onAccessChange, showAccessSpecifier, commentAccess }} + /> + )} /> )} /> diff --git a/web/components/web-view/commend-card.tsx b/web/components/web-view/comment-card.tsx similarity index 93% rename from web/components/web-view/commend-card.tsx rename to web/components/web-view/comment-card.tsx index d214ea90a..9f0d40a6b 100644 --- a/web/components/web-view/commend-card.tsx +++ b/web/components/web-view/comment-card.tsx @@ -9,11 +9,14 @@ import useUser from "hooks/use-user"; // ui import { CustomMenu, Icon } from "components/ui"; import { CommentReaction } from "components/issues"; -import { TipTapEditor } from "components/tiptap"; +import { LiteTextEditorWithRef, LiteReadOnlyEditorWithRef } from "@plane/lite-text-editor"; + // helpers import { timeAgo } from "helpers/date-time.helper"; // types import type { IIssueComment } from "types"; +// service +import fileService from "services/file.service"; type Props = { comment: IIssueComment; @@ -109,13 +112,12 @@ export const CommentCard: React.FC = (props) => {

- +
- = (props) => { />
)} - = (props) => { { - if (!value) return <>; - - return ( -

" - : value - } - workspaceSlug={workspaceSlug!.toString()} - debouncedUpdatesEnabled={true} - setShouldShowAlert={setShowAlert} - setIsSubmitting={setIsSubmitting} - customClassName={ - isAllowed ? "min-h-[150px] shadow-sm" : "!p-0 !pt-2 text-custom-text-200" - } - noBorder={!isAllowed} - onChange={(description: Object, description_html: string) => { - if (!isAllowed) return; - setShowAlert(true); - setIsSubmitting("submitting"); - onChange(description_html); - handleSubmit(handleDescriptionFormSubmit)().finally(() => - setIsSubmitting("submitted") - ); - }} - editable={isAllowed} - /> - ); - }} + render={({ field: { value, onChange } }) => ( +

" + : value + } + debouncedUpdatesEnabled={true} + setShouldShowAlert={setShowAlert} + setIsSubmitting={setIsSubmitting} + customClassName={ + isAllowed ? "min-h-[150px] shadow-sm" : "!p-0 !pt-2 text-custom-text-200" + } + noBorder={!isAllowed} + onChange={(description: Object, description_html: string) => { + setShowAlert(true); + setIsSubmitting("submitting"); + onChange(description_html); + handleSubmit(handleDescriptionFormSubmit)().finally(() => + setIsSubmitting("submitted") + ); + }} + /> + )} />
{ const [user, setUser] = useState(); @@ -134,7 +135,9 @@ const Editor: NextPage = () => { name="description_html" control={control} render={({ field: { value, onChange } }) => ( - { ? watch("description_html") : value } - editable={editable === "true"} noBorder={true} - workspaceSlug={cookies.MOBILE_slug ?? ""} debouncedUpdatesEnabled={true} setShouldShowAlert={setShowAlert} setIsSubmitting={setIsSubmitting} diff --git a/web/pages/[workspaceSlug]/me/profile/activity.tsx b/web/pages/[workspaceSlug]/me/profile/activity.tsx index d8588390e..9305577cf 100644 --- a/web/pages/[workspaceSlug]/me/profile/activity.tsx +++ b/web/pages/[workspaceSlug]/me/profile/activity.tsx @@ -9,7 +9,7 @@ import userService from "services/user.service"; import { WorkspaceAuthorizationLayout } from "layouts/auth-layout"; // components import { ActivityIcon, ActivityMessage } from "components/core"; -import { TipTapEditor } from "components/tiptap"; +import { RichReadOnlyEditor } from "@plane/rich-text-editor"; // icons import { ArrowTopRightOnSquareIcon, ChatBubbleLeftEllipsisIcon } from "@heroicons/react/24/outline"; // ui @@ -96,8 +96,7 @@ const ProfileActivity = () => {

- { customClassName="text-xs border border-custom-border-200 bg-custom-background-100" noBorder borderOnFocus={false} - editable={false} />
@@ -117,11 +115,11 @@ const ProfileActivity = () => { const message = activityItem.verb === "created" && - activityItem.field !== "cycles" && - activityItem.field !== "modules" && - activityItem.field !== "attachment" && - activityItem.field !== "link" && - activityItem.field !== "estimate" ? ( + activityItem.field !== "cycles" && + activityItem.field !== "modules" && + activityItem.field !== "attachment" && + activityItem.field !== "link" && + activityItem.field !== "estimate" ? ( created{" "} {
{activityItem.field === "archived_at" && - activityItem.new_value !== "restore" ? ( + activityItem.new_value !== "restore" ? ( Plane ) : activityItem.actor_detail.is_bot ? ( diff --git a/web/pages/m/[workspaceSlug]/editor.tsx b/web/pages/m/[workspaceSlug]/editor.tsx index d19b281ea..55cbe156f 100644 --- a/web/pages/m/[workspaceSlug]/editor.tsx +++ b/web/pages/m/[workspaceSlug]/editor.tsx @@ -14,9 +14,10 @@ import { Controller, useForm } from "react-hook-form"; import WebViewLayout from "layouts/web-view-layout"; // components -import { TipTapEditor } from "components/tiptap"; -import { PrimaryButton } from "components/ui"; -import { Spinner } from "components/web-view"; +import { RichTextEditor } from "@plane/rich-text-editor"; +import { PrimaryButton, Spinner } from "components/ui"; +import fileService from "services/file.service"; +// services const Editor: NextPage = () => { const [isLoading, setIsLoading] = useState(false); @@ -53,7 +54,9 @@ const Editor: NextPage = () => { name="data_html" control={control} render={({ field: { value, onChange } }) => ( - { ? watch("data_html") : value } - editable={isEditable} noBorder={true} - workspaceSlug={workspaceSlug?.toString() ?? ""} customClassName="h-full shadow-sm overflow-auto" editorContentCustomClassNames="pb-9" onChange={(description: Object, description_html: string) => { diff --git a/web/postcss.config.js b/web/postcss.config.js index 129aa7f59..6887c8262 100644 --- a/web/postcss.config.js +++ b/web/postcss.config.js @@ -1 +1,8 @@ -module.exports = require("tailwind-config-custom/postcss.config"); +module.exports = { + plugins: { + "postcss-import": {}, + "tailwindcss/nesting": {}, + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/web/services/file.service.ts b/web/services/file.service.ts index 15cdc1786..2b7bdb651 100644 --- a/web/services/file.service.ts +++ b/web/services/file.service.ts @@ -26,19 +26,37 @@ interface UnSplashImageUrls { small_s3: string; } -class FileServices extends APIService { +class FileService extends APIService { constructor() { super(API_BASE_URL); + this.uploadFile = this.uploadFile.bind(this); + this.deleteImage = this.deleteImage.bind(this); } async uploadFile(workspaceSlug: string, file: FormData): Promise { - return this.mediaUpload(`/api/workspaces/${workspaceSlug}/file-assets/`, file) + return this.post(`/api/workspaces/${workspaceSlug}/file-assets/`, file, { + headers: { + ...this.getHeaders(), + "Content-Type": "multipart/form-data", + }, + }) .then((response) => response?.data) .catch((error) => { throw error?.response?.data; }); } + getUploadFileFunction(workspaceSlug: string): (file: File) => Promise { + return async (file: File) => { + const formData = new FormData(); + formData.append("asset", file); + formData.append("attributes", JSON.stringify({})); + + const data = await this.uploadFile(workspaceSlug, formData); + return data.asset; + }; + } + async deleteImage(assetUrlWithWorkspaceId: string): Promise { return this.delete(`/api/workspaces/file-assets/${assetUrlWithWorkspaceId}/`) .then((response) => response?.status) @@ -57,6 +75,7 @@ class FileServices extends APIService { throw error?.response?.data; }); } + async uploadUserFile(file: FormData): Promise { return this.mediaUpload(`/api/users/file-assets/`, file) .then((response) => response?.data) @@ -97,4 +116,6 @@ class FileServices extends APIService { } } -export default new FileServices(); +const fileService = new FileService(); + +export default fileService; diff --git a/web/styles/editor.css b/web/styles/editor.css index 9da250dd1..85d881eeb 100644 --- a/web/styles/editor.css +++ b/web/styles/editor.css @@ -155,7 +155,7 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { } } -#tiptap-container { +#editor-container { table { border-collapse: collapse; table-layout: fixed; diff --git a/web/tailwind.config.js b/web/tailwind.config.js index 1e1e59826..05bc93bdc 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -1 +1,5 @@ -module.exports = require("tailwind-config-custom/tailwind.config"); +const sharedConfig = require("tailwind-config-custom/tailwind.config.js"); + +module.exports = { + presets: [sharedConfig], +}; diff --git a/yarn.lock b/yarn.lock index 4b7d3f07c..a39b30639 100644 --- a/yarn.lock +++ b/yarn.lock @@ -44,26 +44,26 @@ "@babel/highlight" "^7.22.13" chalk "^2.4.2" -"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" - integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== +"@babel/compat-data@^7.22.20", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.20.tgz#8df6e96661209623f1975d66c35ffca66f3306d0" + integrity sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw== "@babel/core@^7.11.1": - version "7.22.17" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.17.tgz#2f9b0b395985967203514b24ee50f9fd0639c866" - integrity sha512-2EENLmhpwplDux5PSsZnSbnSkB3tZ6QTksgO25xwEL7pIDcNOMhF5v/s6RzwjMZzZzw9Ofc30gHv5ChCC8pifQ== + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.20.tgz#e3d0eed84c049e2a2ae0a64d27b6a37edec385b7" + integrity sha512-Y6jd1ahLubuYweD/zJH+vvOY141v4f9igNQAQ+MBgq9JlHS2iTsZKn1aMsb3vGccZsXI16VzTBw52Xx0DWmtnA== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.22.13" "@babel/generator" "^7.22.15" "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-module-transforms" "^7.22.17" + "@babel/helper-module-transforms" "^7.22.20" "@babel/helpers" "^7.22.15" "@babel/parser" "^7.22.16" "@babel/template" "^7.22.15" - "@babel/traverse" "^7.22.17" - "@babel/types" "^7.22.17" + "@babel/traverse" "^7.22.20" + "@babel/types" "^7.22.19" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -140,10 +140,10 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" -"@babel/helper-environment-visitor@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" - integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== +"@babel/helper-environment-visitor@^7.22.20", "@babel/helper-environment-visitor@^7.22.5": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== "@babel/helper-function-name@^7.22.5": version "7.22.5" @@ -160,7 +160,7 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.22.5": +"@babel/helper-member-expression-to-functions@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.15.tgz#b95a144896f6d491ca7863576f820f3628818621" integrity sha512-qLNsZbgrNh0fDQBCPocSL8guki1hcPvltGDv/NxvUoABwFq7GkKSu1nRXeJkVZc+wJvne2E0RKQz+2SQrz6eAA== @@ -174,16 +174,16 @@ dependencies: "@babel/types" "^7.22.15" -"@babel/helper-module-transforms@^7.22.15", "@babel/helper-module-transforms@^7.22.17", "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": - version "7.22.17" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.17.tgz#7edf129097a51ccc12443adbc6320e90eab76693" - integrity sha512-XouDDhQESrLHTpnBtCKExJdyY4gJCdrvH2Pyv8r8kovX2U8G0dRUOT45T9XlbLtuu9CLXP15eusnkprhoPV5iQ== +"@babel/helper-module-transforms@^7.22.15", "@babel/helper-module-transforms@^7.22.20", "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.20.tgz#da9edc14794babbe7386df438f3768067132f59e" + integrity sha512-dLT7JVWIUUxKOs1UnJUBR3S70YK+pKX6AbJgB2vMIvEkZkrfJDbYDJesnPshtKV4LhDOR3Oc5YULeDizRek+5A== dependencies: - "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-module-imports" "^7.22.15" "@babel/helper-simple-access" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.15" + "@babel/helper-validator-identifier" "^7.22.20" "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" @@ -198,21 +198,21 @@ integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== "@babel/helper-remap-async-to-generator@^7.22.5", "@babel/helper-remap-async-to-generator@^7.22.9": - version "7.22.17" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.17.tgz#dabaa50622b3b4670bd6546fc8db23eb12d89da0" - integrity sha512-bxH77R5gjH3Nkde6/LuncQoLaP16THYPscurp1S8z7S9ZgezCyV3G8Hc+TZiCmY8pz4fp8CvKSgtJMW0FkLAxA== + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" + integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-wrap-function" "^7.22.17" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-wrap-function" "^7.22.20" "@babel/helper-replace-supers@^7.22.5", "@babel/helper-replace-supers@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz#cbdc27d6d8d18cd22c81ae4293765a5d9afd0779" - integrity sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg== + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" + integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== dependencies: - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-member-expression-to-functions" "^7.22.15" "@babel/helper-optimise-call-expression" "^7.22.5" "@babel/helper-simple-access@^7.22.5": @@ -241,24 +241,24 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== -"@babel/helper-validator-identifier@^7.22.15", "@babel/helper-validator-identifier@^7.22.5": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz#601fa28e4cc06786c18912dca138cec73b882044" - integrity sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ== +"@babel/helper-validator-identifier@^7.22.19", "@babel/helper-validator-identifier@^7.22.20", "@babel/helper-validator-identifier@^7.22.5": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== "@babel/helper-validator-option@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== -"@babel/helper-wrap-function@^7.22.17": - version "7.22.17" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.17.tgz#222ac3ff9cc8f9b617cc1e5db75c0b538e722801" - integrity sha512-nAhoheCMlrqU41tAojw9GpVEKDlTS8r3lzFmF0lP52LwblCPbuFSO7nGIZoIcoU5NIm1ABrna0cJExE4Ay6l2Q== +"@babel/helper-wrap-function@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz#15352b0b9bfb10fc9c76f79f6342c00e3411a569" + integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw== dependencies: "@babel/helper-function-name" "^7.22.5" "@babel/template" "^7.22.15" - "@babel/types" "^7.22.17" + "@babel/types" "^7.22.19" "@babel/helpers@^7.22.15": version "7.22.15" @@ -270,11 +270,11 @@ "@babel/types" "^7.22.15" "@babel/highlight@^7.10.4", "@babel/highlight@^7.22.13": - version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.13.tgz#9cda839e5d3be9ca9e8c26b6dd69e7548f0cbf16" - integrity sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ== + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== dependencies: - "@babel/helper-validator-identifier" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" chalk "^2.4.2" js-tokens "^4.0.0" @@ -820,11 +820,11 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/preset-env@^7.11.0": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.15.tgz#142716f8e00bc030dae5b2ac6a46fbd8b3e18ff8" - integrity sha512-tZFHr54GBkHk6hQuVA8w4Fmq+MSPsfvMG0vPnOYyTnJpyfMqybL8/MbNCPRT9zc2KBO2pe4tq15g6Uno4Jpoag== + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.20.tgz#de9e9b57e1127ce0a2f580831717f7fb677ceedb" + integrity sha512-11MY04gGC4kSzlPHRfvVkNAZhUxOvm7DCJ37hPDnUENwe06npjIRAfInEMTGSb4LZK5ZgDFkv5hw0lGebHeTyg== dependencies: - "@babel/compat-data" "^7.22.9" + "@babel/compat-data" "^7.22.20" "@babel/helper-compilation-targets" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-validator-option" "^7.22.15" @@ -898,7 +898,7 @@ "@babel/plugin-transform-unicode-regex" "^7.22.5" "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" "@babel/preset-modules" "0.1.6-no-external-plugins" - "@babel/types" "^7.22.15" + "@babel/types" "^7.22.19" babel-plugin-polyfill-corejs2 "^0.4.5" babel-plugin-polyfill-corejs3 "^0.8.3" babel-plugin-polyfill-regenerator "^0.5.2" @@ -935,29 +935,29 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" -"@babel/traverse@^7.22.15", "@babel/traverse@^7.22.17": - version "7.22.17" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.17.tgz#b23c203ab3707e3be816043081b4a994fcacec44" - integrity sha512-xK4Uwm0JnAMvxYZxOVecss85WxTEIbTa7bnGyf/+EgCL5Zt3U7htUpEOWv9detPlamGKuRzCqw74xVglDWpPdg== +"@babel/traverse@^7.22.15", "@babel/traverse@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.20.tgz#db572d9cb5c79e02d83e5618b82f6991c07584c9" + integrity sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw== dependencies: "@babel/code-frame" "^7.22.13" "@babel/generator" "^7.22.15" - "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-function-name" "^7.22.5" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" "@babel/parser" "^7.22.16" - "@babel/types" "^7.22.17" + "@babel/types" "^7.22.19" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.22.15", "@babel/types@^7.22.17", "@babel/types@^7.22.5", "@babel/types@^7.4.4": - version "7.22.17" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.17.tgz#f753352c4610ffddf9c8bc6823f9ff03e2303eee" - integrity sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg== +"@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.4.4": + version "7.22.19" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.19.tgz#7425343253556916e440e662bb221a93ddb75684" + integrity sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg== dependencies: "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.15" + "@babel/helper-validator-identifier" "^7.22.19" to-fast-properties "^2.0.0" "@blueprintjs/colors@^4.2.1": @@ -967,6 +967,13 @@ dependencies: tslib "~2.5.0" +"@blueprintjs/colors@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@blueprintjs/colors/-/colors-5.0.2.tgz#c1308fbf156b6ebc3e22e88eaad47dc274c2a4b4" + integrity sha512-icP/d5sheRT8ReRy6jf6WunvLmDQWXFjFU97/xKsqF5SMOWIYC92I0b/705dmc+z5lAXntkU67pCMRuNWSZ9lQ== + dependencies: + tslib "~2.5.0" + "@blueprintjs/core@^4.16.3", "@blueprintjs/core@^4.20.2": version "4.20.2" resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-4.20.2.tgz#ae1bbaf13bd1bf887b506760c478cc940f6d6e20" @@ -984,6 +991,20 @@ react-transition-group "^4.4.5" tslib "~2.5.0" +"@blueprintjs/core@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-5.3.0.tgz#5e4d00797c684f6e417e3d1707ac2141f67f70a0" + integrity sha512-Pzd/ptszeX/Vt5rMa7AFSRlxw8sMs2xhCs4Xje2tTsQUaElbmH1oJbzDyAjSs50na6ncKEmPIvkhXz5ggB1rrA== + dependencies: + "@blueprintjs/colors" "^5.0.2" + "@blueprintjs/icons" "^5.1.6" + "@popperjs/core" "^2.11.7" + classnames "^2.3.1" + normalize.css "^8.0.1" + react-popper "^2.3.0" + react-transition-group "^4.4.5" + tslib "~2.5.0" + "@blueprintjs/icons@^4.16.0": version "4.16.0" resolved "https://registry.yarnpkg.com/@blueprintjs/icons/-/icons-4.16.0.tgz#47f9e8abe64d84fc18721080b8f191d8aac075d8" @@ -993,6 +1014,15 @@ classnames "^2.3.1" tslib "~2.5.0" +"@blueprintjs/icons@^5.1.6": + version "5.1.6" + resolved "https://registry.yarnpkg.com/@blueprintjs/icons/-/icons-5.1.6.tgz#3882cd3a01a1f83dbe584851955a7459d0a78704" + integrity sha512-W87oUP082sZ+dh5oxbgARCHGDyELVKXC+ffxkMYeK3M3hDn0UBSCUZgWZMLltf0qGfFRXlkY+Vn3G08at6xXSw== + dependencies: + change-case "^4.1.2" + classnames "^2.3.1" + tslib "~2.5.0" + "@blueprintjs/popover2@^1.13.3": version "1.14.11" resolved "https://registry.yarnpkg.com/@blueprintjs/popover2/-/popover2-1.14.11.tgz#0698fdeaf6710460cef0b71bed592ca37f40d1f9" @@ -1006,6 +1036,15 @@ react-popper "^2.3.0" tslib "~2.5.0" +"@blueprintjs/popover2@^2.0.10": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@blueprintjs/popover2/-/popover2-2.0.10.tgz#916718688a7d2a9dfc6a5af0a806a8ecef8273f6" + integrity sha512-NE6lgzu6MXfI4lruTw0sXkQ0i7H4RyEWJ0nCEAW1FGdy2KJOkJ7U5RNtBKjT/F4ChaKmoHVL94CaCwqQtB7yOQ== + dependencies: + "@blueprintjs/core" "^5.3.0" + classnames "^2.3.1" + tslib "~2.5.0" + "@cfcs/core@^0.0.6": version "0.0.6" resolved "https://registry.yarnpkg.com/@cfcs/core/-/core-0.0.6.tgz#9f8499dcd2ad29fd96d8fa72055411cd4a249121" @@ -1147,6 +1186,116 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== +"@esbuild/android-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" + integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== + +"@esbuild/android-arm@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" + integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== + +"@esbuild/android-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" + integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== + +"@esbuild/darwin-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" + integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== + +"@esbuild/darwin-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" + integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== + +"@esbuild/freebsd-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" + integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== + +"@esbuild/freebsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" + integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== + +"@esbuild/linux-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" + integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== + +"@esbuild/linux-arm@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" + integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== + +"@esbuild/linux-ia32@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" + integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== + +"@esbuild/linux-loong64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" + integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== + +"@esbuild/linux-mips64el@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" + integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== + +"@esbuild/linux-ppc64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" + integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== + +"@esbuild/linux-riscv64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" + integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== + +"@esbuild/linux-s390x@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" + integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== + +"@esbuild/linux-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338" + integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== + +"@esbuild/netbsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" + integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== + +"@esbuild/openbsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" + integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== + +"@esbuild/sunos-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" + integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== + +"@esbuild/win32-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" + integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== + +"@esbuild/win32-ia32@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" + integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== + +"@esbuild/win32-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" + integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -1189,7 +1338,7 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/eslintrc@^2.1.2": +"@eslint/eslintrc@^2.0.1", "@eslint/eslintrc@^2.1.2": version "2.1.2" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== @@ -1204,25 +1353,30 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/js@8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe" + integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== + "@eslint/js@8.49.0": version "8.49.0" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.49.0.tgz#86f79756004a97fa4df866835093f1df3d03c333" integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w== -"@floating-ui/core@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.4.1.tgz#0d633f4b76052668afb932492ac452f7ebe97f17" - integrity sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ== +"@floating-ui/core@^1.4.2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.0.tgz#5c05c60d5ae2d05101c3021c1a2a350ddc027f8c" + integrity sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg== dependencies: - "@floating-ui/utils" "^0.1.1" + "@floating-ui/utils" "^0.1.3" "@floating-ui/dom@^1.5.1": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.2.tgz#6812e89d1d4d4ea32f10d15c3b81feb7f9836d89" - integrity sha512-6ArmenS6qJEWmwzczWyhvrXRdI/rI78poBcW0h/456+onlabit+2G+QxHx5xTOX60NBJQXjsCLFbW2CmsXpUog== + version "1.5.3" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.3.tgz#54e50efcb432c06c23cd33de2b575102005436fa" + integrity sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA== dependencies: - "@floating-ui/core" "^1.4.1" - "@floating-ui/utils" "^0.1.1" + "@floating-ui/core" "^1.4.2" + "@floating-ui/utils" "^0.1.3" "@floating-ui/react-dom@^2.0.2": version "2.0.2" @@ -1231,10 +1385,10 @@ dependencies: "@floating-ui/dom" "^1.5.1" -"@floating-ui/utils@^0.1.1": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.2.tgz#b7e9309ccce5a0a40ac482cb894f120dba2b357f" - integrity sha512-ou3elfqG/hZsbmF4bxeJhPHIf3G2pm0ujc39hYEZrfVqt7Vk/Zji6CXc3W0pmYM8BW1g40U+akTl9DKZhFhInQ== +"@floating-ui/utils@^0.1.3": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.4.tgz#19654d1026cc410975d46445180e70a5089b3e7d" + integrity sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA== "@headlessui/react@^1.7.13", "@headlessui/react@^1.7.3": version "1.7.17" @@ -1347,23 +1501,23 @@ resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA== -"@mui/base@5.0.0-beta.15": - version "5.0.0-beta.15" - resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.15.tgz#76bebd377cc3b7fdc80924759a4100e5319ed0f9" - integrity sha512-Xtom3YSdi0iwYPtyVRFUEGoRwi6IHWixPwifDKaK+4PkEPtUWMU5YOIJfTsmC59ri+dFvA3oBNSiTPUGGrklZw== +"@mui/base@5.0.0-beta.16": + version "5.0.0-beta.16" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.16.tgz#5869b8cc83ea5da0083bb11790bda007c2384564" + integrity sha512-OYxhC81c9bO0wobGcM8rrY5bRwpCXAI21BL0P2wz/2vTv4ek7ALz9+U5M8wgdmtRNUhmCmAB4L2WRwFRf5Cd8Q== dependencies: "@babel/runtime" "^7.22.15" "@floating-ui/react-dom" "^2.0.2" "@mui/types" "^7.2.4" - "@mui/utils" "^5.14.9" + "@mui/utils" "^5.14.10" "@popperjs/core" "^2.11.8" clsx "^2.0.0" prop-types "^15.8.1" -"@mui/core-downloads-tracker@^5.14.9": - version "5.14.9" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.9.tgz#97a4e2decce1583983b4a0cded8bcb2be1b1cb31" - integrity sha512-JAU/R5hM3l2zP1Q4KnioDRhq5V3vZ4mmjEZ+TwARDb2xFhg3p59McacQuzkSu0sUHJnH9aJos36+hU5sPQBcFQ== +"@mui/core-downloads-tracker@^5.14.10": + version "5.14.10" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.10.tgz#32a8581be98344bbda5ed31fc7b41788bd2e3bc5" + integrity sha512-kPHu/NhZq1k+vSZR5wq3AyUfD4bnfWAeuKpps0+8PS7ZHQ2Lyv1cXJh+PlFdCIOa0PK98rk3JPwMzS8BMhdHwQ== "@mui/icons-material@^5.14.1": version "5.14.9" @@ -1373,16 +1527,16 @@ "@babel/runtime" "^7.22.15" "@mui/material@^5.14.1": - version "5.14.9" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.14.9.tgz#d536505a3728441cfe8003443f143ae87457767b" - integrity sha512-pbBy5kc5iUGXPxgbb+t+yEPvLK5nE3bPUb8WbAafJ8iZ40ZGui0xC4xiiIyzbVexzsLmyN7MaSo4LkxLmPKqUQ== + version "5.14.10" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.14.10.tgz#b8c6ba17c25c0df54053cb0f1bb35083bc91dace" + integrity sha512-ejFMppnO+lzBXpzju+N4SSz0Mhmi5sihXUGcr5FxpgB6bfUP0Lpe32O0Sw/3s8xlmLEvG1fqVT0rRyAVMlCA+A== dependencies: "@babel/runtime" "^7.22.15" - "@mui/base" "5.0.0-beta.15" - "@mui/core-downloads-tracker" "^5.14.9" - "@mui/system" "^5.14.9" + "@mui/base" "5.0.0-beta.16" + "@mui/core-downloads-tracker" "^5.14.10" + "@mui/system" "^5.14.10" "@mui/types" "^7.2.4" - "@mui/utils" "^5.14.9" + "@mui/utils" "^5.14.10" "@types/react-transition-group" "^4.4.6" clsx "^2.0.0" csstype "^3.1.2" @@ -1390,36 +1544,35 @@ react-is "^18.2.0" react-transition-group "^4.4.5" -"@mui/private-theming@^5.14.9": - version "5.14.9" - resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.14.9.tgz#085041c44cc28c25f3431a293339922ec3d9b5f8" - integrity sha512-0PzoUFqFXTXiNchhR7K4b7kZunasPOjx6Qf7AagCmfZDNASHedA0x6evHVhnST918x/AHY9xykYNKfB0Z4xMBg== +"@mui/private-theming@^5.14.10": + version "5.14.10" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.14.10.tgz#42b176b27435931aff40d50833413d10150ac007" + integrity sha512-f67xOj3H06wWDT9xBg7hVL/HSKNF+HG1Kx0Pm23skkbEqD2Ef2Lif64e5nPdmWVv+7cISCYtSuE2aeuzrZe78w== dependencies: "@babel/runtime" "^7.22.15" - "@mui/utils" "^5.14.9" + "@mui/utils" "^5.14.10" prop-types "^15.8.1" -"@mui/styled-engine@^5.14.9": - version "5.14.9" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.14.9.tgz#bc2121db1399bb84ea5390b40beac742b6be7023" - integrity sha512-LEQxLrW9oWvea33pge08+oyNeTz704jb6Nhe26xEJKojXWd34Rr327Zzx3dmo70AcS4h0b99vQjEpUzm6ASqUw== +"@mui/styled-engine@^5.14.10": + version "5.14.10" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.14.10.tgz#2ec443031e48425cd6fda63be498cfa262c1d3a0" + integrity sha512-EJckxmQHrsBvDbFu1trJkvjNw/1R7jfNarnqPSnL+jEQawCkQIqVELWLrlOa611TFtxSJGkdUfCFXeJC203HVg== dependencies: "@babel/runtime" "^7.22.15" "@emotion/cache" "^11.11.0" csstype "^3.1.2" prop-types "^15.8.1" - react "^18.2.0" -"@mui/system@^5.14.9": - version "5.14.9" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.14.9.tgz#90a20473a85622ddabf5a2409de1980fad98f38d" - integrity sha512-Z00Wj590QXk5+SIxmxayBo7SWrao+y433LKGChneJxO4QcT/caSCeEWtyeoLs1Q8ys0zOzl2kkKee6n8TaKzhQ== +"@mui/system@^5.14.10": + version "5.14.10" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.14.10.tgz#b125f8370c1c92af04f1839c40e034d4edc4ad29" + integrity sha512-QQmtTG/R4gjmLiL5ECQ7kRxLKDm8aKKD7seGZfbINtRVJDyFhKChA1a+K2bfqIAaBo1EMDv+6FWNT1Q5cRKjFA== dependencies: "@babel/runtime" "^7.22.15" - "@mui/private-theming" "^5.14.9" - "@mui/styled-engine" "^5.14.9" + "@mui/private-theming" "^5.14.10" + "@mui/styled-engine" "^5.14.10" "@mui/types" "^7.2.4" - "@mui/utils" "^5.14.9" + "@mui/utils" "^5.14.10" clsx "^2.0.0" csstype "^3.1.2" prop-types "^15.8.1" @@ -1429,12 +1582,13 @@ resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.4.tgz#b6fade19323b754c5c6de679a38f068fd50b9328" integrity sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA== -"@mui/utils@^5.14.9": - version "5.14.9" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.14.9.tgz#eeefef88dbee687ac90e8972c63f0d41f19348a3" - integrity sha512-9ysB5e+RwS7ofn0n3nwAg1/3c81vBTmSvauD3EuK9LmqMzhmF//BFDaC44U4yITvB/0m1kWyDqg924Ll3VHCcg== +"@mui/utils@^5.14.10": + version "5.14.10" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.14.10.tgz#4b0a2a26f1ee12323010daa9d7aecf3384acfc3c" + integrity sha512-Rn+vYQX7FxkcW0riDX/clNUwKuOJFH45HiULxwmpgnzQoQr3A0lb+QYwaZ+FAkZrR7qLoHKmLQlcItu6LT0y/Q== dependencies: "@babel/runtime" "^7.22.15" + "@types/prop-types" "^15.7.5" prop-types "^15.8.1" react-is "^18.2.0" @@ -1464,6 +1618,13 @@ dependencies: glob "7.1.7" +"@next/eslint-plugin-next@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.2.4.tgz#3e124cd10ce24dab5d3448ce04104b4f1f4c6ca7" + integrity sha512-ck1lI+7r1mMJpqLNa3LJ5pxCfOB1lfJncKmRJeJxcJqcngaFwylreLP7da6Rrjr6u2gVRTfmnkSkjc80IiQCwQ== + dependencies: + glob "7.1.7" + "@next/swc-android-arm-eabi@12.3.2": version "12.3.2" resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.2.tgz#806e3be9741bc14aafdfad0f0c4c6a8de5b77ee1" @@ -1995,9 +2156,9 @@ picomatch "^2.3.1" "@rushstack/eslint-patch@^1.1.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.3.3.tgz#16ab6c727d8c2020a5b6e4a176a243ecd88d8d69" - integrity sha512-0xd7qez0AQ+MbHatZTlI1gu5vkG8r7MYRUJAHPAHJBmGLs16zpkrpAVLvjQKQOqaXPDUBwOiJzNc00znHSCVBw== + version "1.4.0" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.4.0.tgz#77e948b9760bd22736a5d26e335a690f76fda37b" + integrity sha512-cEjvTPU32OM9lUFegJagO0mRnIn+rbqrG89vV8/xLnLFX0DoR0r1oy5IlTga71Q7uT3Qus7qm7wgeiMT/+Irlg== "@scena/dragscroll@^1.4.0": version "1.4.0" @@ -2164,7 +2325,7 @@ dependencies: tslib "^2.4.0" -"@tailwindcss/typography@^0.5.10": +"@tailwindcss/typography@^0.5.9": version "0.5.10" resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.10.tgz#2abde4c6d5c797ab49cf47610830a301de4c1e0a" integrity sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw== @@ -2174,191 +2335,196 @@ lodash.merge "^4.6.2" postcss-selector-parser "6.0.10" -"@tiptap/core@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.1.8.tgz#4555dc7d86580dee790d4aded1ce7fb79319da70" - integrity sha512-QTGgqki7hkonLJ93gWqCUkD6cCAQ3rEX9gbMLwzfnegIZ+/BKLQYKYCozsEMZnMPXgdRrKuyRBOL+RH+IolMeA== +"@tiptap/core@^2.1.10", "@tiptap/core@^2.1.7": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.1.10.tgz#6d8f3c777f1700dcc6c903b1185576754175e366" + integrity sha512-yhUKsac6nlqbPQfwQnp+4Jb110EqmzocXKoZacLwzHpM7JVsr2+LXMDu9kahtrvHNJErJljhnQvDHRsrrYeJkQ== -"@tiptap/extension-blockquote@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.1.8.tgz#eb3f70d03807b2d51645cf5450a1e84ccb53633b" - integrity sha512-NhTE90ZDb/BbtkgeNjwLYPYMryAfCXCM+Zpk8AMsVODZ+bDy+lsqpnDw7uRxUK3guLMnqKgSe2eTaXqx7AKE+A== +"@tiptap/extension-blockquote@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.1.10.tgz#dc475bef70dd460fc730a14b3b4cc18f37cd1b2d" + integrity sha512-lpBF/a+qgv4Bdf7HYisTkMFdFdGfn2SqspsydvG8UI7N9B/PfnCCrtoMaC3bqTaT6u8ZVxyM3Y3vnq2AxXJvBw== -"@tiptap/extension-bold@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.1.8.tgz#2047345c814cad672d150b303436928d46aecbc1" - integrity sha512-rDdmir78a0JTiV+vrycGh3yS1ZzRF1bRvBt4jr7Rne0LOl03kc7Wm936ommiL3McWUpZZV37ZpCm5JfE8rQb+w== +"@tiptap/extension-bold@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.1.10.tgz#fb71c2575087d3d2a9c6d214b3c1587da931cc61" + integrity sha512-I43WCwc7pyz5vtKGj24Rjv7HN0EK5S4PlADQPBuhC1qQvfCTFvjrBB6ZmsekUMGmllW0qMOFVLSjtffpckqshA== -"@tiptap/extension-bubble-menu@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.1.8.tgz#26d5f2ebc198553c5339d9ea6d06fcb02b6a938f" - integrity sha512-Na9Maz20jS+3UrHtAGLkfFt3uu+HD9SSK3+3WyNeylkWciJa/qkZKqwhptHrjpin0IHSF2JNche+ZA+hSmnm2Q== +"@tiptap/extension-bubble-menu@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.1.10.tgz#551e93219b98f097331b30865123d36e95c37404" + integrity sha512-XxgJajXkfAj/fChXkIwKBs7/3pd7OxV1uGc6Opx1qW/nSRYx/rr97654Sx/sg6auwIlbpRoqTmyqjbykGX1/yA== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-bullet-list@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.1.8.tgz#d9d4ee46d1adb2b70d3b8c19540ebcdd8a1eaaec" - integrity sha512-VWj3XZMwJQVb7e4ZM0N+o6o+905lyMMS4C35yw/sxN5CDw4TJpQMSPSAmBVNtK469XUdlGOxeLc/+Q00aU+S8A== +"@tiptap/extension-bullet-list@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.1.10.tgz#e7d7fb578502da6c6208a4daa3e2fe4249ae6280" + integrity sha512-e6aFr29OSOmXsjFZB2zt3p8aeCWOx0C9Ayrpdf4QBUCOUJtt6FQPxxiYc+XZcdrYbLGLznA7QJlulCK9SGv2Fw== -"@tiptap/extension-code-block-lowlight@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.1.8.tgz#7577ade0e00be3d28629e2f8e6c5e7b9b4155946" - integrity sha512-qiUIh8JRfvd2rhDKFjHCxBp+nRy3HedovQoVFX9YEnBbg6so+I1nLE2Eck4t3KhBVfVRBrxBKZPLVb83zQ0s4w== +"@tiptap/extension-code-block-lowlight@^2.1.11": + version "2.1.11" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.1.11.tgz#6eec38c3b8662fae81ec2f117a2d18564f1fbb1a" + integrity sha512-k3olDvsRYO32JR9hyNa6VLqUdhwcpLwvR4Z6tJ66jHag5rsfP/7JZxJhrX9A1AF/jRCILdTiq9DTKybHieFjsw== -"@tiptap/extension-code-block@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.1.8.tgz#d2dccf64a583bfb12e2ebd04b3724b7e9430549d" - integrity sha512-EjegLBBz8ATvIuJlqosGrcOsKNu8YveI8rogGfUmnXWMNcPSSqBDoWK2EpLTUzGccPWRxo7yBsr5wItikfPPYA== +"@tiptap/extension-code-block@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.1.10.tgz#a125a12f716728b271a130178c6fc60237ed46f5" + integrity sha512-M+s89V9mP3tOoS6p/X2Dzw/Z7Fcg9EF0ZXlsMNifdlpwJlhAIYxI7vjPBmkMAFXTDB5eMZblXyNQaZ7v6V2Yeg== -"@tiptap/extension-code@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.1.8.tgz#c3dccd1a12972cab8d0c98f75a3960bab64905bd" - integrity sha512-dQL8aUYzSEkES5P4sBYZ6SiCMnFK1cUKKGruaRV1TJyFu/ClZ8Y+BKS2GCCMcyH0tKjqsibYsNFBWz9/Q5gjEg== +"@tiptap/extension-code@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.1.10.tgz#704798f90a32d6166ce96dc65ef4a541f424f895" + integrity sha512-1yy/kR0FAeMkDdAt1LW/FH6vlyZLqLZqY6BM+wBCiGrr+XeA5FTXih9iT/4gbTRuIzG0EPqx18nvroG7hUsWBg== -"@tiptap/extension-color@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-color/-/extension-color-2.1.8.tgz#7692e82c0a50e63d6bb474a6e109852c5d657ea0" - integrity sha512-eAfJhUbqqNFTdgWraLcg6O1d7YWj6Ivga0YVfhnBpWmZiS8JXAmgOHq9b7sRrDOMdEp6D2njDBEIWpltHNmi1A== +"@tiptap/extension-color@^2.1.11": + version "2.1.11" + resolved "https://registry.yarnpkg.com/@tiptap/extension-color/-/extension-color-2.1.11.tgz#be6989bb2d6630fc8f8e3c81add6fdfe1b77c790" + integrity sha512-xfSfZRnNd40YtFfrXvzpGa2OZsRAZapq0Ce09q7bCEpudhiD7yIIVOjOjggagllOFnafKTwKkFaDLIA0K0eIwg== -"@tiptap/extension-document@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.1.8.tgz#fa4dce27fd5d25b54f7e7e9a93db69606a624b96" - integrity sha512-mLPZqd5QUv3FKo+5zOaf7dGqZPci7Myr92U1Y6Vw0V+hCRC9Emm3I/xssQYGsWXmXQuyNJ5WRlpXgag3Ae+CkA== +"@tiptap/extension-document@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.1.10.tgz#6d2ab2301c86139d711fa460a311aa2c8bb343f8" + integrity sha512-jNlNGQIGg471DvzhADaEoRINa3LNghowrBbKK9d5wGVnbKRykNEPwjCf8zNl+m5NBmCZl3lsdznlwBk5zyh5Bg== -"@tiptap/extension-dropcursor@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.1.8.tgz#f7128aebe9e2bb05cd6508d782deaf436ae3c46e" - integrity sha512-KilbUHApYya2Q6brq5qW+B+pPkb6lvgnjRfuFuv6doM/v+lfEdozUE1Ma8C19UXtzl7BmPDut9HRMDL17Pqwyg== +"@tiptap/extension-dropcursor@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.1.10.tgz#490c9aa82656592c9820c55214381fb9bfea92f2" + integrity sha512-GhsWsCq6wLb8HJ32BeAm7ndv4lPyu1F7FFwmnARzEF5q54FV20kWSv2zC+Dv0dTvynXR3quXybdUM92xeNDovw== -"@tiptap/extension-floating-menu@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.1.8.tgz#49a7f83c3a0769c044d4e83352aae0f86d63f7c5" - integrity sha512-lc8bjHGqWSgXKmoU2HAlBFWzu7wnFKb5Vg0R3PECBrOZ9hXkmNA0mHxrvHglwjLtfe7XOfZf4FLySG/5S+BdeQ== +"@tiptap/extension-floating-menu@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.1.10.tgz#82914a02e04e019d8b5da5158b32ffb29d4cce80" + integrity sha512-uChrDrY3usnF9wSegqq+YGaqd229p9gmaB5xyOyMERDs972hKj4Ul95rXzBBiMKAWUMw9eM09i7+ijTzz4KDUw== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-gapcursor@^2.1.7", "@tiptap/extension-gapcursor@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.1.8.tgz#bc7e745fdc8990a8e370c4c728ab8733ba0910c4" - integrity sha512-0EQgV/kF2dg2dOpw0fTbwwNaubwS8QNhEPPbnXQP8xqZpupuia+DKKgC+ttzbE9XhS4Sv1fGib52Sr7MMIduhA== +"@tiptap/extension-gapcursor@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.1.10.tgz#712853ce82642108e50a37014d585ff72af6758d" + integrity sha512-WSBT9X7dzg0HyMoMP/Yyxl28QwIJO90YzobI9z5mav86BQv7C5wU0fQSpbpAbsN3s7lxKhPwNrXkwkpnXT4ZCA== -"@tiptap/extension-hard-break@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.1.8.tgz#b898c3c9c7f96726307bd51b24f557731e25d12e" - integrity sha512-K86FTizvZu7779Gz2XigW1IxAjZXduyZ7w0ipwe+5QBa/Lh6Vfl9wa8TgV1lFAkC2VATsAa3aa36llMIDBgeew== +"@tiptap/extension-hard-break@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.1.10.tgz#e885e83d936b45891bf4dc40c713d042f84eb8c4" + integrity sha512-sYrzpPoV5jQri+duGb50nDTs+hOBQDxXTKlJuZNFfZMwgx6epwxb8xICcGAUJFShuuW8UAWCNcB4jG9tMqgvyw== -"@tiptap/extension-heading@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.1.8.tgz#d34db2bdfca559567ecb6f741fa4eb5d4d54a897" - integrity sha512-6PHWzhGPC/QjfswlflU1Cy2UYZiyzwa639bWW7Dl4BHZgK+e09lbc7RwzPrrex6+jA10K4nlww19xsI590ogBw== +"@tiptap/extension-heading@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.1.10.tgz#1b32726551466c29987861181966e5675417b28c" + integrity sha512-1OgmrRPMcY52WI7I4799xd4eIsEX/bI813B8mZvNYXLzZI75pLW1hmz1mUvBYyMwlcek74zVTGYgPy11o+2JEg== -"@tiptap/extension-highlight@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-highlight/-/extension-highlight-2.1.8.tgz#b2d2d995344e06dd36cd8a395a72113b87981bd7" - integrity sha512-OCXtFWCbwsgOHq7IP4Qr02EfjwYeRRcuL1ipv0LojGtMcvnkw7OLhQZ8oocrqi4/6QCOtPLSGlcqrQ6pmN7jww== +"@tiptap/extension-history@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.1.10.tgz#efa60d657a76818361a3af14769660672d4bc227" + integrity sha512-tApuN8MIJMzc0dxvkYJPt3t5cea9NuZBGNiuVedJwMMUF6hbFpMZAt20GW2qwjBaZ76rQwbLp1s3KnImFsPe5A== -"@tiptap/extension-history@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.1.8.tgz#68a65b51effc1d612e3862928c3ccede3ce83592" - integrity sha512-Cyq4YsmosfgHGlaf2wiiU8VaLweUMG8LHuhZ5A2RAoriy3G09Bqgn6eqLmho8KoU1VgvffXTVBaYKxz9gVgu3w== +"@tiptap/extension-horizontal-rule@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.1.10.tgz#cfdb67530be100054fc8511942d4ec3534acf828" + integrity sha512-91lGpK2d6WMPhrMDPBURS8z8pEg1CUBYy7GmBenKvvgh+JzVhG+U6MtykfWNfm2R4iRXOl1xLbyUOCiOSUXodQ== -"@tiptap/extension-horizontal-rule@^2.0.4", "@tiptap/extension-horizontal-rule@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.1.8.tgz#db23468176fd5359240feb8601fd3fcf747d5e6d" - integrity sha512-qUNz8p/p3gth0ueYFkmMdVRcRVmtCwQGJsHWwbx23XrF/a7AJ0FSdiW0sk8YD6Dbw+i1cB3cnRyO+qq9XuWdqw== +"@tiptap/extension-horizontal-rule@^2.1.11": + version "2.1.11" + resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.1.11.tgz#e423a2b41123ef7f8d778a1cd026e6606e7be28b" + integrity sha512-uvHPa2YCKnDhtSBSZB3lk5U4H3wRKP0DNvVx4Y2F7MdQianVzcyOd1pZYO9BQs+lUB1aZots6doE69Zqz3mU2Q== -"@tiptap/extension-image@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.1.8.tgz#99d78ad1d8c6f513f945beae7de352759f30189f" - integrity sha512-o+vUIYLvYcJHftIMoIukzZZ+fTTfC/gXXvQIYz51p3f1qeYXszD11FbtkaJCgXYj8BcGCO7QuzcCdQg+wyROZw== +"@tiptap/extension-image@^2.1.7": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.1.10.tgz#6c597ad02285f1f3508fd4aa21e30213657cbd7c" + integrity sha512-d7+d4J2TJ99+phFbVTpsFhi208jAgcrfbdwUDkkwjdF+PQhax5pounSt/8eZPWdyCXj+EWYjCjx0znwsD6+SCA== -"@tiptap/extension-italic@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.1.8.tgz#b559c8d6b387e292e047985acd0def48a80a7aa0" - integrity sha512-cR6kSoMraA/dCdwmus8A09WAwpxiZiGG+B0OqsludGF+MdZLilhoGyXDbTeO3aKoKccfqxZGk1YKK13C/gRM1Q== +"@tiptap/extension-italic@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.1.10.tgz#7183119c8c61beb2ac635ca3c2066624530b4a56" + integrity sha512-ebw5m+rWx6K5UoBVXSkz3fpvDJh/wScfYmwl6pkbjc2jNbZiln2LSiLHYc2eIYJ2aTsVxcw/n0Azfk5Lb19InA== -"@tiptap/extension-link@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.1.8.tgz#644229c309ef9a91db329126df23cba083ec3c61" - integrity sha512-f3yPNbbo3rNuusEX+Xh/oKUWkq/P1yyVip6ZmtUJVrrG4PFeq/w+f1vEVnlC+uZk3qoC4o8J1DTAOrlrZehx/g== +"@tiptap/extension-link@^2.1.7": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.1.10.tgz#c2a33fdf33dd2d97f29381ae2163c10318dc371f" + integrity sha512-dXxPTWzJzpbDRAewM4P8jN/n9h8uUH83lOLwweuODYCqHRdjQL/uGkQworFFrgqmRHs+9JjHZ4DETILZVawJ+Q== dependencies: linkifyjs "^4.1.0" -"@tiptap/extension-list-item@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.1.8.tgz#b5dc1e04bfb96ca10a0821821ade5014fa188dbb" - integrity sha512-fiYVRhHvcXMcVuuiXBx/0AFWwGoKzs9784VSuVUeSSzSuH6vOchM1kZCH+v6acs7vltFKNDrluyEiwGIz1b8qA== +"@tiptap/extension-list-item@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.1.10.tgz#0615e4fb68161e6457e6041e195f454bfd537d44" + integrity sha512-rRRyB14vOcSjTMAh8Y+50TRC/jO469CelGwFjOLrK1ZSEag5wmLDaqpWOOb52BFYnvCHuIm1HqZtdL5bTI/J1w== -"@tiptap/extension-ordered-list@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.1.8.tgz#f489ac85ccd93ad811318bed6af7906c035ba313" - integrity sha512-qTVSWTlSjFNRwPNmWmfe9TsW9XL3LQCNJsfaBxtVZfhDN9rhoIZ6rPTBO7f2TTiPK1+uyLTvK+znWYvU9RtD5A== +"@tiptap/extension-list-item@^2.1.11": + version "2.1.11" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.1.11.tgz#466e4d1dcad153b4e52678b08d2bf073339c2dc9" + integrity sha512-YhwHaPGhffsFsg/zjCu1G24//j/BTRDRZbZXmMwp77m1yEqPULcWyoWrI+gUzetQxJRD/ruAucqjLtoLLfICmQ== -"@tiptap/extension-paragraph@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.1.8.tgz#f337c3f84cbfddd1ea16860e934f2049c46211ce" - integrity sha512-ZuwvwKaG5GeoYRgeh96PToLk2TjxsLiZKnLN6rkUCsW6aLoseK7/8/7vm3dP2N9dAUN35ESw0/pRk2Q/VK1/+g== +"@tiptap/extension-ordered-list@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.1.10.tgz#ef5d5ba68baf86e9b66c1b2c1cec458aa111ad44" + integrity sha512-jouo3RHUMxU4dPzZcfZdUzmsLVp1KHrLIAD2YAxBuqArACrBNfJpIhtkTKuGLlaFhKqGr+EmNdNQnK8JOBhLtQ== -"@tiptap/extension-placeholder@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.1.8.tgz#5ead71aca842c17cdc9cff6fff8809a1f327119d" - integrity sha512-4yhyvvqsXTzXtJs+39cgvsld4df3ppbajCoxkzHYntKoonm3DtgFTSh+lbdEVCQgDmIfIt1o6DKY1n8NAJRQUQ== +"@tiptap/extension-paragraph@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.1.10.tgz#ee1238d2d6e9460b2a929b05a5fd43cfb58a6017" + integrity sha512-kzuHbrxcxpWkha5P+JFzCKT54pNqb4IBKMU5qT9YGhZSdNTtU63ncdCHM+Ad1ukLuvXAv95zh1IQC5j+Z1Qk4A== -"@tiptap/extension-strike@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.1.8.tgz#ba6966a9afb9493d8bd30d4c617ffe6966b90379" - integrity sha512-JGPiGudEZAKTiOirua9gtDG+HILHEx4CGODW5PDBMA1xYDfyo7ZJk5xgfJWZ1SOo7YviF26HSY4KKV9ThINq2Q== +"@tiptap/extension-placeholder@^2.1.11": + version "2.1.11" + resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.1.11.tgz#ba115f714dd48d5bbc65df277b74f357ff3b100e" + integrity sha512-laHYRFxJWj6m72Yf1v6Q5nF2nvwWpQlKUj6Yu/yluOOoVE92HpLqCAvA8RamqLtPiw5VxR3v3oCY0WNeQRvyIg== + +"@tiptap/extension-strike@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.1.10.tgz#ec311395d16af15345b63d2dac2d459b9ad5fa9e" + integrity sha512-KW63lZLPFIir5AIeh2I7UK6Tx1O3jetD7JIPUzEqp1I1BfJlHGHVQxV8VXAmJl0hTOzjQBsHW42PmBxSC97NUg== "@tiptap/extension-table-cell@^2.1.6": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-table-cell/-/extension-table-cell-2.1.8.tgz#2e28bb008de979943f5efbf5f48f58d26728e6ad" - integrity sha512-cUI3vMfRZ6Utmjsu+aAF8BsH3r3YzWaLJWW5SuH8784K6ImclCwGTyi/HJqsRDHM7ujvtjjc+vmFtSD/eqF15Q== + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-table-cell/-/extension-table-cell-2.1.10.tgz#e594b55622435c43a95edf6f2adfaca402f5cbed" + integrity sha512-NQOTKjPOTJrkI7VaR9wFF3UKB9N2THD8zJZJDcECKQxLR740udF6/6jWm1uwkTwdkBekVKHBMQvKKK9W1bOBiw== "@tiptap/extension-table-header@^2.1.6": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-table-header/-/extension-table-header-2.1.8.tgz#ae4a081f2dc7754078fbc7ba11043da8726d5f4a" - integrity sha512-oUYaQaAowbVLYyeYmAwqoe0ZHZS1XP6qV4EyOig/mOElASwBB6xAfydMncRKTxsT9Zq6z/CC6qnH0xlld/KAfQ== + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-table-header/-/extension-table-header-2.1.10.tgz#6250676a26946e5b7186198a06990ea70f578a87" + integrity sha512-NSC0Y10kXDvPGiJckJY/QU8VA7HHU0tI20Dj7/r1oD9itBWSnWP0zAOXzHVlQt9GpThhFNo2nu3fAaVQNfKoTg== "@tiptap/extension-table-row@^2.1.6": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-table-row/-/extension-table-row-2.1.8.tgz#d1113e7362f675faaef88cc1fb32c29d37855a09" - integrity sha512-rjMCZjaemC3x4T3fUExi8J8ffo1I29u9e8rSHRIna2ObTRq4PeI48uVTET5EREBD5/CbfX6zHFgkZfUZeR0g+A== + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-table-row/-/extension-table-row-2.1.10.tgz#e7a1ca8342b623a400848b437c82d57680e551e3" + integrity sha512-yMOnAaXE7vK7MwULuVUO8v6AYZu6wxTfHAWQe/FqPeMf9tG0HL6+gyt1audremw0xBFMGPx6v4t8vlqPXW9p2g== "@tiptap/extension-table@^2.1.6": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.1.8.tgz#1c777498caf7b649be26cba8eecbd3ebd623e673" - integrity sha512-n+89XGTYmZgKFrvZrqgCG2SbRbIi8xX61KLptLD2DF/w4y0bR4Cr2pJBep/MMEZh3N2CIDQ3mS7eIfASJHk5hQ== + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.1.10.tgz#5654426366b547631c647ffc5dacf040e65307e1" + integrity sha512-fsf0c6qA+R6NzbFx+tm1l5POZsgadHjREsedvq5q1i8rCq1Gt1AK+lR7WQsaXlSeIRsWtg4RT0eUjAYNCmKkug== -"@tiptap/extension-task-item@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.1.8.tgz#2036360be6702ab753cbc77b60ab24fb33ff20a6" - integrity sha512-PoY2PDiYEQC44qDQLubzDuhZ3f6OL7sui89960M1HUQR2URnPvToOBaa5veNY8VyACdAolm+LwTpseBKKkcpmw== +"@tiptap/extension-task-item@^2.1.7": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.1.10.tgz#8eb0d3e8b1234fa44205dd91619f3f1937ca3254" + integrity sha512-jiH37e8c41T/UKWXzznOg325huAkAiFtjNkvfQfS23a7UDfIM90IJ+VjvFt/5EEgJ2mozBweQan4yIzlC6uWaQ== -"@tiptap/extension-task-list@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.1.8.tgz#993c415d85d414039baf7379df7c3b19b1d342d9" - integrity sha512-PmEPJHTOgy0AveE6YoxY6w09+bh5OqkrMI/sluY88291cnSPPEf9sFWmBHOrONNj54Ti6ua37arudUY5mqxOCA== +"@tiptap/extension-task-list@^2.1.7": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.1.10.tgz#faf2d520c5f5b7b5084a0804b8e65f69fea361be" + integrity sha512-1nLI81lQ/HYrcxVRSv3EyG8kcWygtaQcOZ9p6PeQjwN+z5D5PoQVHK4+8zOO1Lpz4BDR3mc4nolA7q/Li0ilOw== -"@tiptap/extension-text-style@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text-style/-/extension-text-style-2.1.8.tgz#42e9fa179f76d4e88f73f2c66aee3b06162e659b" - integrity sha512-xnx/Pq5ttt2/gOQPmqVQIBz/jo3MErtYdYk22fUaOyu1xT36X4BDJYsrLyWhcs3aWR/tv1/XylbNOFvhrDOHoQ== +"@tiptap/extension-text-style@^2.1.11": + version "2.1.11" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text-style/-/extension-text-style-2.1.11.tgz#b2fbea4f52b68f339b4941103ea2e03d1ac996a3" + integrity sha512-+JDWmcSUyFKzMDm/1xqlk7e0qPJ1nQ/UKIRuDeRtqgbxTyEw4fNlkV2k7GHCoELXqxUoplzweLID+kM1Vk2OaA== -"@tiptap/extension-text@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.1.8.tgz#7f537d0c490feab8b800644e2ad24b6478c67044" - integrity sha512-ha7oTtUdcJdTVLr8CrxbNMucbAmOBCi83MLxdKZclVf1VpdIVpE3NTojfH2mnZCVMvtPhj4PILQp2hGO95SFig== +"@tiptap/extension-text@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.1.10.tgz#db297fb5d2ee50ef7a14650539e3d335f772f755" + integrity sha512-ubU/WQwNB0MVKyMAHr8ka3Nu3jCR03HARGKUwNRzppZYtRXWyXHNlAaJdplNb1NMGb8hd0ElBJmwFlVqmh8haQ== -"@tiptap/extension-underline@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.1.8.tgz#a2f0904805f57e118c2f0b165929abe41b5c1fbf" - integrity sha512-vsmdyR8z40xNPZzTSNGLcCMaIf8Tgm9OzsZb1qWILe+PYuv/mIM1LogBbfouEzVpG5sPoxwFTDgxnC+M3Ohgzg== +"@tiptap/extension-underline@^2.1.7": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.1.10.tgz#d7a3ac39c2363da94651a960abe3a16bb24de398" + integrity sha512-f+rJKviGNqORGv4/1pTLZuVTb9VsKMZMLucL8423M6s8TdrH//sBB8QeU92JSnO9PjAGwxWjS1f23/KtufxP8g== -"@tiptap/pm@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.1.8.tgz#9108af0365fd653d64d620d45016e7079961bebc" - integrity sha512-H3NGAu5xdH1PpXa6OQlvecaWJIZR/9tVkc1mdpLanvG7mW85DuY+5fC36Xnv9SPMVcO3zWXS6Ii4os6HbdP6bQ== +"@tiptap/pm@^2.1.7": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.1.10.tgz#84d5ae574568dca00ee62698559523d77e980620" + integrity sha512-Y+AqizKnjQpx4pSaA6m/cCD5QHQRPtALhO4ZO4YFZV1idYmsJA3/S5lgJI3ZL5eAHKHcGk6Vv3/8Y+eej5YIPw== dependencies: prosemirror-changeset "^2.2.0" prosemirror-collab "^1.3.0" @@ -2379,43 +2545,43 @@ prosemirror-transform "^1.7.0" prosemirror-view "^1.28.2" -"@tiptap/react@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.1.8.tgz#50a834a9b76b36eebb33e71dcb765aa9cb80f276" - integrity sha512-yTjlin4tOfYNwBdpX4+2CmNxybq2Ms50rX0RIRLABbnCTqhBIKko/eBLFq7DCot/Dwdw6c5Y098/fayKywfJWg== +"@tiptap/react@^2.1.7": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.1.10.tgz#51cd96462e61f6fffa0ca4eb359d8d7d15ebf422" + integrity sha512-kzCWzbV2dnD5NmHjN8GiS+k0GOmoEhKnMuMzuuU6FjtOALhJzPTrIXITzWDpU3jL+r/4eeXYhAt64Wp7PVwscg== dependencies: - "@tiptap/extension-bubble-menu" "^2.1.8" - "@tiptap/extension-floating-menu" "^2.1.8" + "@tiptap/extension-bubble-menu" "^2.1.10" + "@tiptap/extension-floating-menu" "^2.1.10" -"@tiptap/starter-kit@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.1.8.tgz#d33f04478cd7b4956cb312335bcbed109269b651" - integrity sha512-LfCQgENw501XyTbCEcmiKt1d7XQi+6nTrQQfI16cCwc7lqp+LREz9EOFidkjTtrKuUHwlTaZzS7C76Cfc87mXA== +"@tiptap/starter-kit@^2.1.10": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.1.10.tgz#5f19c199c79d90ef5e3b8990ca3aa76ce625d68c" + integrity sha512-h5mH1qv7SDFXWZPbOWC8zpGZ62EnDizRNtM45Gani0HYWJXcbPFpgN1qJmESP/jP+v+0hxtnVEkgfpiy3LRm6A== dependencies: - "@tiptap/core" "^2.1.8" - "@tiptap/extension-blockquote" "^2.1.8" - "@tiptap/extension-bold" "^2.1.8" - "@tiptap/extension-bullet-list" "^2.1.8" - "@tiptap/extension-code" "^2.1.8" - "@tiptap/extension-code-block" "^2.1.8" - "@tiptap/extension-document" "^2.1.8" - "@tiptap/extension-dropcursor" "^2.1.8" - "@tiptap/extension-gapcursor" "^2.1.8" - "@tiptap/extension-hard-break" "^2.1.8" - "@tiptap/extension-heading" "^2.1.8" - "@tiptap/extension-history" "^2.1.8" - "@tiptap/extension-horizontal-rule" "^2.1.8" - "@tiptap/extension-italic" "^2.1.8" - "@tiptap/extension-list-item" "^2.1.8" - "@tiptap/extension-ordered-list" "^2.1.8" - "@tiptap/extension-paragraph" "^2.1.8" - "@tiptap/extension-strike" "^2.1.8" - "@tiptap/extension-text" "^2.1.8" + "@tiptap/core" "^2.1.10" + "@tiptap/extension-blockquote" "^2.1.10" + "@tiptap/extension-bold" "^2.1.10" + "@tiptap/extension-bullet-list" "^2.1.10" + "@tiptap/extension-code" "^2.1.10" + "@tiptap/extension-code-block" "^2.1.10" + "@tiptap/extension-document" "^2.1.10" + "@tiptap/extension-dropcursor" "^2.1.10" + "@tiptap/extension-gapcursor" "^2.1.10" + "@tiptap/extension-hard-break" "^2.1.10" + "@tiptap/extension-heading" "^2.1.10" + "@tiptap/extension-history" "^2.1.10" + "@tiptap/extension-horizontal-rule" "^2.1.10" + "@tiptap/extension-italic" "^2.1.10" + "@tiptap/extension-list-item" "^2.1.10" + "@tiptap/extension-ordered-list" "^2.1.10" + "@tiptap/extension-paragraph" "^2.1.10" + "@tiptap/extension-strike" "^2.1.10" + "@tiptap/extension-text" "^2.1.10" -"@tiptap/suggestion@^2.0.4": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.1.8.tgz#ffd61c13aa11c4b51e50a9c83f0813f71ab0881b" - integrity sha512-3QypKFCeZSRrjgSz0n0JE5SimisolaxDZn45GGtkXuJWmKGCmsJw9UsXeH3S9ZuP3pvPImL0P9uAHlhRReRw1w== +"@tiptap/suggestion@^2.1.7": + version "2.1.10" + resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.1.10.tgz#fe6dd160ea93f8135c0831ae4accc7a708bac019" + integrity sha512-k9WTTWT81UkHaxZksjp+wE31E85QL0jyLd0ZEKAs+btW148Pon1KwBeLnODNHILcdQaRPxRvb28a47cRHEKTiw== "@types/debug@^4.0.0": version "4.1.8" @@ -2448,29 +2614,36 @@ "@types/node" "*" "@types/hast@^2.0.0": - version "2.3.5" - resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.5.tgz#08caac88b44d0fdd04dc17a19142355f43bd8a7a" - integrity sha512-SvQi0L/lNpThgPoleH53cdjB3y9zpLlVjRbqB3rH8hx1jiRSBGAhyjV3H+URFjNVRqt2EdYNrbZE5IsGlNfpRg== + version "2.3.6" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.6.tgz#bb8b05602112a26d22868acb70c4b20984ec7086" + integrity sha512-47rJE80oqPmFdVDCD7IheXBrVdwuBgsYwoczFvKmwfo2Mzsnt+V9OONsYauFmICb6lQPpCuXYJWejBNs4pDJRg== dependencies: "@types/unist" "^2" +"@types/hast@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.1.tgz#e1705ec9258ac4885659c2d50bac06b4fcd16466" + integrity sha512-hs/iBJx2aydugBQx5ETV3ZgeSS0oIreQrFJ4bjBl0XvM4wAmDjFEALY7p0rTSLt2eL+ibjRAAs9dTPiCLtmbqQ== + dependencies: + "@types/unist" "*" + "@types/hoist-non-react-statics@^3.3.0": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" - integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + version "3.3.2" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#dc1e9ded53375d37603c479cc12c693b0878aa2a" + integrity sha512-YIQtIg4PKr7ZyqNPZObpxfHsHEmuB8dXCxd6qVcGuQVDK2bpsF7bYNnBJ4Nn7giuACZg+WewExgrtAJ3XnA4Xw== dependencies: "@types/react" "*" hoist-non-react-statics "^3.3.0" "@types/js-cookie@^3.0.2", "@types/js-cookie@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.3.tgz#d6bfbbdd0c187354ca555213d1962f6d0691ff4e" - integrity sha512-Xe7IImK09HP1sv2M/aI+48a20VX+TdRJucfq4vfRVy6nWN8PYPOEnlMRSgxJAgYQIXJVL8dZ4/ilAM7dWNaOww== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.4.tgz#23475b6d3b03acc84192e7c24da88eb38c1039ef" + integrity sha512-vMMnFF+H5KYqdd/myCzq6wLDlPpteJK+jGFgBus3Da7lw+YsDmx2C8feGTzY2M3Fo823yON+HC2CL240j4OV+w== "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": - version "7.0.12" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" - integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== + version "7.0.13" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85" + integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ== "@types/json5@^0.0.29": version "0.0.29" @@ -2525,9 +2698,9 @@ integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== "@types/node@*": - version "20.6.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.0.tgz#9d7daa855d33d4efec8aea88cd66db1c2f0ebe16" - integrity sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg== + version "20.6.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.3.tgz#5b763b321cd3b80f6b8dde7a37e1a77ff9358dd9" + integrity sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA== "@types/node@18.0.6": version "18.0.6" @@ -2539,6 +2712,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.1.tgz#90dad8476f1e42797c49d6f8b69aaf9f876fc69f" integrity sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ== +"@types/node@18.15.3": + version "18.15.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.3.tgz#f0b991c32cfc6a4e7f3399d6cb4b8cf9a0315014" + integrity sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw== + "@types/nprogress@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@types/nprogress/-/nprogress-0.2.0.tgz#86c593682d4199212a0509cc3c4d562bbbd6e45f" @@ -2559,10 +2737,10 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/prop-types@*", "@types/prop-types@^15.0.0": - version "15.7.5" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" - integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== +"@types/prop-types@*", "@types/prop-types@^15.0.0", "@types/prop-types@^15.7.5": + version "15.7.6" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.6.tgz#bbf819813d6be21011b8f5801058498bec555572" + integrity sha512-RK/kBbYOQQHLYj9Z95eh7S6t7gq4Ojt/NT8HTk8bWVhA5DaF+5SMnxHKkP4gPNN3wAZkKP+VjAf0ebtYzf+fxg== "@types/react-beautiful-dnd@^13.1.2": version "13.1.4" @@ -2627,28 +2805,10 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.0.17": - version "18.2.21" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.21.tgz#774c37fd01b522d0b91aed04811b58e4e0514ed9" - integrity sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@18.0.15": - version "18.0.15" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.15.tgz#d355644c26832dc27f3e6cbf0c4f4603fc4ab7fe" - integrity sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@18.0.28": - version "18.0.28" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065" - integrity sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew== +"@types/react@*", "@types/react@18.0.15", "@types/react@18.0.28", "@types/react@18.2.0", "@types/react@^18.0.17", "@types/react@^18.2.5": + version "18.2.0" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.0.tgz#15cda145354accfc09a18d2f2305f9fc099ada21" + integrity sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2688,6 +2848,11 @@ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.4.tgz#2b38784cd16957d3782e8e2b31c03bc1d13b4d65" integrity sha512-IDaobHimLQhjwsQ/NMwRVfa/yL7L/wriQPMhw1ZJall0KX6E1oxk29XMDeilW5qTIg5aoiqf5Udy8U/51aNoQQ== +"@types/unist@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.0.tgz#988ae8af1e5239e89f9fbb1ade4c935f4eeedf9a" + integrity sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w== + "@types/unist@^2", "@types/unist@^2.0.0": version "2.0.8" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.8.tgz#bb197b9639aa1a04cf464a617fe800cccd92ad5c" @@ -2699,9 +2864,9 @@ integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== "@types/uuid@^9.0.1": - version "9.0.3" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.3.tgz#6cdd939b4316b4f81625de9f06028d848c4a1533" - integrity sha512-taHQQH/3ZyI3zP8M/puluDEIEvtQHVYcC6y3N8ijFtAd28+Ey/G4sg1u2gB01S8MwybLOKAp9/yCMu/uR5l3Ug== + version "9.0.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.4.tgz#e884a59338da907bda8d2ed03e01c5c49d036f1c" + integrity sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA== "@typescript-eslint/eslint-plugin@^5.48.2", "@typescript-eslint/eslint-plugin@^5.51.0": version "5.62.0" @@ -2980,7 +3145,7 @@ array.prototype.tosorted@^1.1.1: es-shim-unscopables "^1.0.0" get-intrinsic "^1.2.1" -arraybuffer.prototype.slice@^1.0.1: +arraybuffer.prototype.slice@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== @@ -3030,6 +3195,18 @@ attr-accept@^2.2.2: resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b" integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== +autoprefixer@^10.4.14: + version "10.4.16" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.16.tgz#fad1411024d8670880bdece3970aa72e3572feb8" + integrity sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ== + dependencies: + browserslist "^4.21.10" + caniuse-lite "^1.0.30001538" + fraction.js "^4.3.6" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + autoprefixer@^10.4.15: version "10.4.15" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.15.tgz#a1230f4aeb3636b89120b34a1f513e2f6834d530" @@ -3200,6 +3377,18 @@ builtin-modules@^3.1.0: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== +bundle-require@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bundle-require/-/bundle-require-4.0.1.tgz#2cc1ad76428043d15e0e7f30990ee3d5404aa2e3" + integrity sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ== + dependencies: + load-tsconfig "^0.2.3" + +cac@^6.7.12: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -3227,9 +3416,14 @@ camelcase-css@^2.0.1: integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001517, caniuse-lite@^1.0.30001520: - version "1.0.30001534" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001534.tgz#f24a9b2a6d39630bac5c132b5dff89b39a12e7dd" - integrity sha512-vlPVrhsCS7XaSh2VvWluIQEzVhefrUQcEsQWSS5A5V+dM07uv1qHeQzAOTGIMy9i3e9bH15+muvI/UHojVgS/Q== + version "1.0.30001538" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001538.tgz#9dbc6b9af1ff06b5eb12350c2012b3af56744f3f" + integrity sha512-HWJnhnID+0YMtGlzcp3T9drmBJUVDchPJ08tpUGFLs9CYlwWPH2uLgpHn8fND5pCgXVtnGS3H4QR9XLMHVNkHw== + +caniuse-lite@^1.0.30001538: + version "1.0.30001541" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001541.tgz#b1aef0fadd87fb72db4dcb55d220eae17b81cdb1" + integrity sha512-bLOsqxDgTqUBkzxbNlSBt8annkDpQB9NdzdTbO2ooJ+eC/IQcvDspDc058g84ejCelF7vHUx57KIOjEecOHXaw== capital-case@^1.0.4: version "1.0.4" @@ -3293,7 +3487,7 @@ character-entities@^2.0.0: resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== -chokidar@^3.5.3: +chokidar@^3.5.1, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -3313,6 +3507,13 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== +class-variance-authority@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522" + integrity sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A== + dependencies: + clsx "2.0.0" + classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" @@ -3330,11 +3531,16 @@ client-only@^0.0.1: resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== -clsx@^2.0.0: +clsx@2.0.0, clsx@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b" integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q== +clsx@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + cmdk@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/cmdk/-/cmdk-0.2.0.tgz#53c52d56d8776c8bb8ced1055b5054100c388f7c" @@ -3472,7 +3678,7 @@ crelt@^1.0.0: resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.6.tgz#7cc898ea74e190fb6ef9dae57f8f81cf7302df72" integrity sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g== -cross-spawn@^7.0.2: +cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -3621,7 +3827,7 @@ date-fns@^2.0.1, date-fns@^2.30.0: dependencies: "@babel/runtime" "^7.21.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -3732,6 +3938,13 @@ detect-node-es@^1.1.0: resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== +devlop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + didyoumean@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" @@ -3807,9 +4020,9 @@ ejs@^3.1.6: jake "^10.8.5" electron-to-chromium@^1.4.477: - version "1.4.520" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.520.tgz#c19c25a10d87bd88a9aae2b76cae9235a50c2994" - integrity sha512-Frfus2VpYADsrh1lB3v/ft/WVFlVzOIm+Q0p7U7VqHI6qr7NWHYKe+Wif3W50n7JAFoBsWVsoU0+qDks6WQ60g== + version "1.4.525" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.525.tgz#614284f33901fbecd3e90176c0d60590cd939700" + integrity sha512-GIZ620hDK4YmIqAWkscG4W6RwY6gOx1y5J6f4JUQwctiJrqH2oxZYU4mXHi35oV32tr630UcepBzSBGJ/WYcZA== emoji-regex@^8.0.0: version "8.0.0" @@ -3862,17 +4075,17 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" es-abstract@^1.22.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" - integrity sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw== + version "1.22.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.2.tgz#90f7282d91d0ad577f505e423e52d4c1d93c1b8a" + integrity sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA== dependencies: array-buffer-byte-length "^1.0.0" - arraybuffer.prototype.slice "^1.0.1" + arraybuffer.prototype.slice "^1.0.2" available-typed-arrays "^1.0.5" call-bind "^1.0.2" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" - function.prototype.name "^1.1.5" + function.prototype.name "^1.1.6" get-intrinsic "^1.2.1" get-symbol-description "^1.0.0" globalthis "^1.0.3" @@ -3888,23 +4101,23 @@ es-abstract@^1.22.1: is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" - is-typed-array "^1.1.10" + is-typed-array "^1.1.12" is-weakref "^1.0.2" object-inspect "^1.12.3" object-keys "^1.1.1" object.assign "^4.1.4" - regexp.prototype.flags "^1.5.0" - safe-array-concat "^1.0.0" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.7" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" typed-array-buffer "^1.0.0" typed-array-byte-length "^1.0.0" typed-array-byte-offset "^1.0.0" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" - which-typed-array "^1.1.10" + which-typed-array "^1.1.11" es-iterator-helpers@^1.0.12: version "1.0.15" @@ -3951,6 +4164,34 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +esbuild@^0.18.2: + version "0.18.20" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6" + integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA== + optionalDependencies: + "@esbuild/android-arm" "0.18.20" + "@esbuild/android-arm64" "0.18.20" + "@esbuild/android-x64" "0.18.20" + "@esbuild/darwin-arm64" "0.18.20" + "@esbuild/darwin-x64" "0.18.20" + "@esbuild/freebsd-arm64" "0.18.20" + "@esbuild/freebsd-x64" "0.18.20" + "@esbuild/linux-arm" "0.18.20" + "@esbuild/linux-arm64" "0.18.20" + "@esbuild/linux-ia32" "0.18.20" + "@esbuild/linux-loong64" "0.18.20" + "@esbuild/linux-mips64el" "0.18.20" + "@esbuild/linux-ppc64" "0.18.20" + "@esbuild/linux-riscv64" "0.18.20" + "@esbuild/linux-s390x" "0.18.20" + "@esbuild/linux-x64" "0.18.20" + "@esbuild/netbsd-x64" "0.18.20" + "@esbuild/openbsd-x64" "0.18.20" + "@esbuild/sunos-x64" "0.18.20" + "@esbuild/win32-arm64" "0.18.20" + "@esbuild/win32-ia32" "0.18.20" + "@esbuild/win32-x64" "0.18.20" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -4011,17 +4252,32 @@ eslint-config-next@13.2.1: eslint-plugin-react "^7.31.7" eslint-plugin-react-hooks "^4.5.0" +eslint-config-next@13.2.4: + version "13.2.4" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.2.4.tgz#8aa4d42da3a575a814634ba9c88c8d25266c5fdd" + integrity sha512-lunIBhsoeqw6/Lfkd6zPt25w1bn0znLA/JCL+au1HoEpSb4/PpsOYsYtgV/q+YPsoKIOzFyU5xnb04iZnXjUvg== + dependencies: + "@next/eslint-plugin-next" "13.2.4" + "@rushstack/eslint-patch" "^1.1.3" + "@typescript-eslint/parser" "^5.42.0" + eslint-import-resolver-node "^0.3.6" + eslint-import-resolver-typescript "^3.5.2" + eslint-plugin-import "^2.26.0" + eslint-plugin-jsx-a11y "^6.5.1" + eslint-plugin-react "^7.31.7" + eslint-plugin-react-hooks "^4.5.0" + eslint-config-prettier@^8.3.0: version "8.10.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== eslint-config-turbo@latest: - version "1.10.13" - resolved "https://registry.yarnpkg.com/eslint-config-turbo/-/eslint-config-turbo-1.10.13.tgz#3743def3c76e27c3969c16222137f5bd913a10e2" - integrity sha512-Ffa0SxkRCPMtfUX/HDanEqsWoLwZTQTAXO9W4IsOtycb2MzJDrVcLmoFW5sMwCrg7gjqbrC4ZJoD+1SPPzIVqg== + version "1.10.14" + resolved "https://registry.yarnpkg.com/eslint-config-turbo/-/eslint-config-turbo-1.10.14.tgz#5e0c5ef1942783a2f7ffb631f4f322906a52663d" + integrity sha512-ZeB+IcuFXy1OICkLuAplVa0euoYbhK+bMEQd0nH9+Lns18lgZRm33mVz/iSoH9VdUzl/1ZmFmoK+RpZc+8R80A== dependencies: - eslint-plugin-turbo "1.10.13" + eslint-plugin-turbo "1.10.14" eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.7: version "0.3.9" @@ -4155,10 +4411,10 @@ eslint-plugin-react@^7.29.4, eslint-plugin-react@^7.31.7: semver "^6.3.1" string.prototype.matchall "^4.0.8" -eslint-plugin-turbo@1.10.13: - version "1.10.13" - resolved "https://registry.yarnpkg.com/eslint-plugin-turbo/-/eslint-plugin-turbo-1.10.13.tgz#d2c14c7e733ee1462c94d79dce29d75f42ced275" - integrity sha512-el4AAmn0zXmvHEyp1h0IQMfse10Vy8g5Vbg4IU3+vD9CSj5sDbX07iFVt8sCKg7og9Q5FAa9mXzlCf7t4vYgzg== +eslint-plugin-turbo@1.10.14: + version "1.10.14" + resolved "https://registry.yarnpkg.com/eslint-plugin-turbo/-/eslint-plugin-turbo-1.10.14.tgz#b9b7ffc2faef52cb158088e00f9ef6fe39966bd9" + integrity sha512-sBdBDnYr9AjT1g4lR3PBkZDonTrMnR4TvuGv5W0OiF7z9az1rI68yj2UHJZvjkwwcGu5mazWA1AfB0oaagpmfg== dependencies: dotenv "16.0.3" @@ -4252,6 +4508,52 @@ eslint@8.34.0: strip-json-comments "^3.1.0" text-table "^0.2.0" +eslint@8.36.0: + version "8.36.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf" + integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.0.1" + "@eslint/js" "8.36.0" + "@humanwhocodes/config-array" "^0.11.8" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-visitor-keys "^3.3.0" + espree "^9.5.0" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + eslint@^7.23.0, eslint@^7.32.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" @@ -4350,7 +4652,7 @@ espree@^7.3.0, espree@^7.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" -espree@^9.4.0, espree@^9.6.0, espree@^9.6.1: +espree@^9.4.0, espree@^9.5.0, espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== @@ -4403,6 +4705,26 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +eventsource-parser@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-0.1.0.tgz#4a6b84751ca8e704040e6f7f50e7d77344fa1b7c" + integrity sha512-M9QjFtEIkwytUarnx113HGmgtk52LSn3jNAtnWKi3V+b9rqSfQeVdLsaD5AG/O4IrGQwmAAHBIsqbmURPTd2rA== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + expand-template@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" @@ -4534,14 +4856,14 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== follow-redirects@^1.15.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + version "1.15.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" + integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== for-each@^0.3.3: version "0.3.3" @@ -4564,7 +4886,7 @@ format@^0.2.0: resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" integrity sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww== -fraction.js@^4.2.0: +fraction.js@^4.2.0, fraction.js@^4.3.6: version "4.3.6" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.6.tgz#e9e3acec6c9a28cf7bc36cbe35eea4ceb2c5c92d" integrity sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg== @@ -4604,7 +4926,7 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function.prototype.name@^1.1.5: +function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== @@ -4657,6 +4979,11 @@ get-own-enumerable-property-symbols@^3.0.0: resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -4757,7 +5084,7 @@ globalthis@^1.0.3: dependencies: define-properties "^1.1.3" -globby@^11.0.4, globby@^11.1.0: +globby@^11.0.3, globby@^11.0.4, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -4886,6 +5213,11 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + idb@^7.0.1: version "7.1.1" resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b" @@ -5205,7 +5537,7 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.10, is-typed-array@^1.1.9: +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: version "1.1.12" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== @@ -5291,6 +5623,11 @@ jiti@^1.18.2: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.20.0.tgz#2d823b5852ee8963585c8dd8b7992ffc1ae83b42" integrity sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA== +joycon@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== + js-cookie@^3.0.1: version "3.0.5" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" @@ -5478,6 +5815,11 @@ linkifyjs@^4.1.0: resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.1.1.tgz#73d427e3bbaaf4ca8e71c589ad4ffda11a9a5fde" integrity sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA== +load-tsconfig@^0.2.3: + version "0.2.5" + resolved "https://registry.yarnpkg.com/load-tsconfig/-/load-tsconfig-0.2.5.tgz#453b8cd8961bfb912dea77eb6c168fe8cca3d3a1" + integrity sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg== + loader-utils@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" @@ -5571,6 +5913,15 @@ lowlight@^2.9.0: fault "^2.0.0" highlight.js "~11.8.0" +lowlight@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-3.0.0.tgz#8772e6514f1c14cd576b5a7a22668f5aa2ddd10b" + integrity sha512-kedX6yxvgak8P4LGh3vKRDQuMbVcnP+qRuDJlve2w+mNJAbEhEQPjYCp9QJnpVL5F2aAAVjeIzzrbQZUKHiDJw== + dependencies: + "@types/hast" "^3.0.0" + devlop "^1.0.0" + highlight.js "~11.8.0" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -5590,6 +5941,11 @@ lru_map@^0.3.3: resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== +lucide-react@^0.244.0: + version "0.244.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.244.0.tgz#9626f44881830280012dad23afda7ddbcffff24b" + integrity sha512-PeDVbx5PlIRrVvdxiuSxPfBo7sK5qrL3LbvvRoGVNiHYRAkBm/48lKqoioxcmp0bgsyJs9lMw7CdtGFvnMJbVg== + lucide-react@^0.263.1: version "0.263.1" resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.263.1.tgz#a456ee0d171aa373929bd3ee20d6f9fb4429c301" @@ -5929,6 +6285,11 @@ mime-types@^2.1.12, mime-types@^2.1.27: dependencies: mime-db "1.52.0" +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + mimic-response@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" @@ -6119,6 +6480,13 @@ normalize.css@^8.0.1: resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + nprogress@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" @@ -6228,6 +6596,13 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + optionator@^0.9.1, optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -6346,7 +6721,7 @@ path-is-inside@^1.0.2: resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -6471,10 +6846,10 @@ postcss@8.4.14: picocolors "^1.0.0" source-map-js "^1.0.2" -postcss@^8.4.23, postcss@^8.4.29: - version "8.4.29" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.29.tgz#33bc121cf3b3688d4ddef50be869b2a54185a1dd" - integrity sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw== +postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.29: + version "8.4.30" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.30.tgz#0e0648d551a606ef2192a26da4cabafcc09c1aa7" + integrity sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" @@ -6503,12 +6878,17 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prettier-plugin-tailwindcss@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.3.0.tgz#8299b307c7f6467f52732265579ed9375be6c818" + integrity sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA== + prettier-plugin-tailwindcss@^0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.4.tgz#ebfacbcb90e2ca1df671ffe4083e99f81d72040d" integrity sha512-QZzzB1bID6qPsKHTeA9qPo1APmmxfFrA5DD3LQ+vbTmAnY40eJI7t9Q1ocqel2EKMWNPLJqdTDWZj1hKYgqSgg== -prettier@^2.8.7: +prettier@^2.8.7, prettier@^2.8.8: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== @@ -6628,10 +7008,10 @@ prosemirror-menu@^1.2.1: prosemirror-history "^1.0.0" prosemirror-state "^1.0.0" -prosemirror-model@1.18.1, 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: - version "1.18.1" - resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.18.1.tgz#1d5d6b6de7b983ee67a479dc607165fdef3935bd" - integrity sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw== +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: + 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== dependencies: orderedmap "^2.0.0" @@ -6781,9 +7161,9 @@ react-css-styled@^1.1.9: framework-utils "^1.1.0" react-datepicker@^4.8.0: - version "4.17.0" - resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.17.0.tgz#1b3eb5d1d60709e991e4c46b049e97911993193c" - integrity sha512-z50H44XbnkYlns7gVHzHK4jWAzLfvQehh5Lvindb09J97yVJKIbsmHs98D0f77tdZc3dSYM7oAqsFY55dBeOGQ== + version "4.18.0" + resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.18.0.tgz#d66301acc47833d31fa6f46f98781b084106da0e" + integrity sha512-0MYt3HmLbHVk1sw4v+RCbLAVg5TA3jWP7RyjZbo53PC+SEi+pjdgc92lB53ai/ENZaTOhbXmgni9GzvMrorMAw== dependencies: "@popperjs/core" "^2.11.8" classnames "^2.2.6" @@ -6861,9 +7241,9 @@ react-markdown@^8.0.7: vfile "^5.0.0" react-moveable@^0.54.1: - version "0.54.1" - resolved "https://registry.yarnpkg.com/react-moveable/-/react-moveable-0.54.1.tgz#3c69748c444184700e6999501b0da953c934205e" - integrity sha512-Kj2ifw9nk3LZvu7ezhst8Z5WBPRr+yVv9oROwrBirFlHmwGHHZXUGk5Gaezu+JGqqNRsQJncVMW5Uf68KSSOvg== + version "0.54.2" + resolved "https://registry.yarnpkg.com/react-moveable/-/react-moveable-0.54.2.tgz#87ce9af3499dc1c8218bce7e174b10264c1bbecf" + integrity sha512-NGaVLbn0i9pb3+BWSKGWFqI/Mgm4+WMeWHxXXQ4Qi1tHxWCXrUrbGvpxEpt69G/hR7dez+/m68ex+fabjnvcUg== dependencies: "@daybrush/utils" "^1.13.0" "@egjs/agent" "^2.2.1" @@ -7019,9 +7399,9 @@ reflect.getprototypeof@^1.0.4: which-builtin-type "^1.1.3" regenerate-unicode-properties@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" - integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== + version "10.1.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480" + integrity sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q== dependencies: regenerate "^1.4.2" @@ -7042,7 +7422,7 @@ regenerator-transform@^0.15.2: dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.5.0: +regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== @@ -7104,15 +7484,20 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve-pkg-maps@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== resolve@^1.1.7, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.22.0, resolve@^1.22.2, resolve@^1.22.4: - version "1.22.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" - integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + version "1.22.6" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362" + integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== dependencies: is-core-module "^2.13.0" path-parse "^1.0.7" @@ -7170,6 +7555,13 @@ rollup@^2.43.1: optionalDependencies: fsevents "~2.3.2" +rollup@^3.2.5: + version "3.29.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.2.tgz#cbc76cd5b03b9f9e93be991d23a1dff9c6d5b740" + integrity sha512-CJouHoZ27v6siztc21eEQGo0kIcE5D1gVPA571ez0mMYb25LGYGKnVNXpEj5MGlepmDWGXNjDB5q7uNiPHC11A== + optionalDependencies: + fsevents "~2.3.2" + rope-sequence@^1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.4.tgz#df85711aaecd32f1e756f76e43a415171235d425" @@ -7189,7 +7581,7 @@ sade@^1.7.3: dependencies: mri "^1.1.0" -safe-array-concat@^1.0.0, safe-array-concat@^1.0.1: +safe-array-concat@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== @@ -7299,9 +7691,9 @@ set-function-name@^2.0.0, set-function-name@^2.0.1: has-property-descriptors "^1.0.0" sharp@^0.32.1: - version "0.32.5" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.32.5.tgz#9ddc78ead6446094f51e50355a2d4ec6e7220cd4" - integrity sha512-0dap3iysgDkNaPOaOL4X/0akdu0ma62GcdC2NBQ+93eqpePdDdr2/LM0sFdDSMmN7yS+odyZtPsb7tx/cYBKnQ== + version "0.32.6" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.32.6.tgz#6ad30c0b7cd910df65d5f355f774aa4fce45732a" + integrity sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w== dependencies: color "^4.2.3" detect-libc "^2.0.2" @@ -7333,6 +7725,11 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -7376,11 +7773,6 @@ snake-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" -sonner@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/sonner/-/sonner-0.6.2.tgz#d87420e80d8b25b6d2bd6aabcc28465f03962bdc" - integrity sha512-bh4FWhYoNN481ZIW94W4e0kSLBTMGislYg2YXvDS1px1AJJz4erQe9jHV8s5pS1VMVDgfh3CslNSFLaU6Ldrnw== - 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" @@ -7399,6 +7791,13 @@ source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" +source-map@0.8.0-beta.0, source-map@^0.8.0-beta.0: + version "0.8.0-beta.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" + integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== + dependencies: + whatwg-url "^7.0.0" + source-map@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -7409,13 +7808,6 @@ source-map@^0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.8.0-beta.0: - version "0.8.0-beta.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" - integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== - dependencies: - whatwg-url "^7.0.0" - sourcemap-codec@^1.4.8: version "1.4.8" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" @@ -7470,7 +7862,7 @@ string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.7, string.proto set-function-name "^2.0.0" side-channel "^1.0.4" -string.prototype.trim@^1.2.7: +string.prototype.trim@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== @@ -7479,7 +7871,7 @@ string.prototype.trim@^1.2.7: define-properties "^1.2.0" es-abstract "^1.22.1" -string.prototype.trimend@^1.0.6: +string.prototype.trimend@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== @@ -7488,7 +7880,7 @@ string.prototype.trimend@^1.0.6: define-properties "^1.2.0" es-abstract "^1.22.1" -string.prototype.trimstart@^1.0.6: +string.prototype.trimstart@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== @@ -7530,6 +7922,11 @@ strip-comments@^2.0.1: resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-2.0.1.tgz#4ad11c3fbcac177a67a40ac224ca339ca1c1ba9b" integrity sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw== +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -7557,7 +7954,7 @@ stylis@4.2.0: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== -sucrase@^3.32.0: +sucrase@^3.20.3, sucrase@^3.32.0: version "3.34.0" resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.34.0.tgz#1e0e2d8fcf07f8b9c3569067d92fbd8690fb576f" integrity sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw== @@ -7620,12 +8017,12 @@ tailwind-merge@^1.14.0: resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-1.14.0.tgz#e677f55d864edc6794562c63f5001f45093cdb8b" integrity sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ== -tailwindcss-animate@^1.0.7: +tailwindcss-animate@^1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4" integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA== -tailwindcss@^3.3.3: +tailwindcss@^3.2.7, tailwindcss@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.3.3.tgz#90da807393a2859189e48e9e7000e6880a736daf" integrity sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w== @@ -7813,6 +8210,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + trim-lines@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" @@ -7853,6 +8255,26 @@ tslib@~2.5.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== +tsup@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/tsup/-/tsup-7.2.0.tgz#bb24c0d5e436477900c712e42adc67200607303c" + integrity sha512-vDHlczXbgUvY3rWvqFEbSqmC1L7woozbzngMqTtL2PGBODTtWlRwGDDawhvWzr5c1QjKe4OAKqJGfE1xeXUvtQ== + dependencies: + bundle-require "^4.0.0" + cac "^6.7.12" + chokidar "^3.5.1" + debug "^4.3.1" + esbuild "^0.18.2" + execa "^5.0.0" + globby "^11.0.3" + joycon "^3.0.1" + postcss-load-config "^4.0.1" + resolve-from "^5.0.0" + rollup "^3.2.5" + source-map "0.8.0-beta.0" + sucrase "^3.20.3" + tree-kill "^1.2.2" + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -7867,47 +8289,47 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -turbo-darwin-64@1.10.13: - version "1.10.13" - resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.10.13.tgz#7abc33838bf74c7e7ba2955cb6acc40342d6964c" - integrity sha512-vmngGfa2dlYvX7UFVncsNDMuT4X2KPyPJ2Jj+xvf5nvQnZR/3IeDEGleGVuMi/hRzdinoxwXqgk9flEmAYp0Xw== +turbo-darwin-64@1.10.14: + version "1.10.14" + resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.10.14.tgz#9e8061bc0d706a69bc61d88fbfa7690fa2a897fc" + integrity sha512-I8RtFk1b9UILAExPdG/XRgGQz95nmXPE7OiGb6ytjtNIR5/UZBS/xVX/7HYpCdmfriKdVwBKhalCoV4oDvAGEg== -turbo-darwin-arm64@1.10.13: - version "1.10.13" - resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.10.13.tgz#89f2d14a28789e5eaf276a17fecd4116d61fd16d" - integrity sha512-eMoJC+k7gIS4i2qL6rKmrIQGP6Wr9nN4odzzgHFngLTMimok2cGLK3qbJs5O5F/XAtEeRAmuxeRnzQwTl/iuAw== +turbo-darwin-arm64@1.10.14: + version "1.10.14" + resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.10.14.tgz#2589aeb814257601c979dd40ff8a6cace6c9e4e5" + integrity sha512-KAdUWryJi/XX7OD0alOuOa0aJ5TLyd4DNIYkHPHYcM6/d7YAovYvxRNwmx9iv6Vx6IkzTnLeTiUB8zy69QkG9Q== -turbo-linux-64@1.10.13: - version "1.10.13" - resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.10.13.tgz#803e2b351984ec81034dc4b6935cdfb0a7d7d572" - integrity sha512-0CyYmnKTs6kcx7+JRH3nPEqCnzWduM0hj8GP/aodhaIkLNSAGAa+RiYZz6C7IXN+xUVh5rrWTnU2f1SkIy7Gdg== +turbo-linux-64@1.10.14: + version "1.10.14" + resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.10.14.tgz#c01d26f62f6c47701026dea083054918fe8caf47" + integrity sha512-BOBzoREC2u4Vgpap/WDxM6wETVqVMRcM8OZw4hWzqCj2bqbQ6L0wxs1LCLWVrghQf93JBQtIGAdFFLyCSBXjWQ== -turbo-linux-arm64@1.10.13: - version "1.10.13" - resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.10.13.tgz#92439a927ec98801be1be266016736be5730e40c" - integrity sha512-0iBKviSGQQlh2OjZgBsGjkPXoxvRIxrrLLbLObwJo3sOjIH0loGmVIimGS5E323soMfi/o+sidjk2wU1kFfD7Q== +turbo-linux-arm64@1.10.14: + version "1.10.14" + resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.10.14.tgz#7cbcb091995a09134342a57e0672501115b46df2" + integrity sha512-D8T6XxoTdN5D4V5qE2VZG+/lbZX/89BkAEHzXcsSUTRjrwfMepT3d2z8aT6hxv4yu8EDdooZq/2Bn/vjMI32xw== -turbo-windows-64@1.10.13: - version "1.10.13" - resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.10.13.tgz#870815cdcb20f2480296c208778f9127be61eec6" - integrity sha512-S5XySRfW2AmnTeY1IT+Jdr6Goq7mxWganVFfrmqU+qqq3Om/nr0GkcUX+KTIo9mPrN0D3p5QViBRzulwB5iuUQ== +turbo-windows-64@1.10.14: + version "1.10.14" + resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.10.14.tgz#935d2d1da7fb1f9a941fb3aa122fd9a70b61c416" + integrity sha512-zKNS3c1w4i6432N0cexZ20r/aIhV62g69opUn82FLVs/zk3Ie0GVkSB6h0rqIvMalCp7enIR87LkPSDGz9K4UA== -turbo-windows-arm64@1.10.13: - version "1.10.13" - resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.10.13.tgz#2da38b71b1716732541dfc5bd7475dc0903921f8" - integrity sha512-nKol6+CyiExJIuoIc3exUQPIBjP9nIq5SkMJgJuxsot2hkgGrafAg/izVDRDrRduQcXj2s8LdtxJHvvnbI8hEQ== +turbo-windows-arm64@1.10.14: + version "1.10.14" + resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.10.14.tgz#cb3a55d30a75aa76258991e929e92f207cac872f" + integrity sha512-rkBwrTPTxNSOUF7of8eVvvM+BkfkhA2OvpHM94if8tVsU+khrjglilp8MTVPHlyS9byfemPAmFN90oRIPB05BA== turbo@latest: - version "1.10.13" - resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.10.13.tgz#8050bcd09f8a20d5a626f41e86cd8760e49a0df6" - integrity sha512-vOF5IPytgQPIsgGtT0n2uGZizR2N3kKuPIn4b5p5DdeLoI0BV7uNiydT7eSzdkPRpdXNnO8UwS658VaI4+YSzQ== + version "1.10.14" + resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.10.14.tgz#31f3bb3017218191e0de64728da199678a50aa7d" + integrity sha512-hr9wDNYcsee+vLkCDIm8qTtwhJ6+UAMJc3nIY6+PNgUTtXcQgHxCq8BGoL7gbABvNWv76CNbK5qL4Lp9G3ZYRA== optionalDependencies: - turbo-darwin-64 "1.10.13" - turbo-darwin-arm64 "1.10.13" - turbo-linux-64 "1.10.13" - turbo-linux-arm64 "1.10.13" - turbo-windows-64 "1.10.13" - turbo-windows-arm64 "1.10.13" + turbo-darwin-64 "1.10.14" + turbo-darwin-arm64 "1.10.14" + turbo-linux-64 "1.10.14" + turbo-linux-arm64 "1.10.14" + turbo-windows-64 "1.10.14" + turbo-windows-arm64 "1.10.14" type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -8303,7 +8725,7 @@ which-collection@^1.0.1: is-weakmap "^2.0.1" is-weakset "^2.0.1" -which-typed-array@^1.1.10, which-typed-array@^1.1.11, which-typed-array@^1.1.9: +which-typed-array@^1.1.11, which-typed-array@^1.1.9: version "1.1.11" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==