From 986d83621c2b6f4b7e780bce9baf3c7ee7f937de Mon Sep 17 00:00:00 2001 From: Felix Feng Date: Sun, 17 Nov 2024 16:22:01 +0800 Subject: [PATCH 1/3] sync --- .../plate-ui/media-placeholder-element.tsx | 6 +- .../default/plate-ui/media-toolbar-button.tsx | 43 ++- .../default/plate-ui/media-upload-toast.tsx | 2 +- .../plate-playground-template/package.json | 15 +- .../plate-playground-template/pnpm-lock.yaml | 169 +++++++++-- .../editor/plugins/block-selection-plugins.ts | 8 - .../components/editor/plugins/dnd-plugins.tsx | 9 +- .../editor/plugins/media-plugins.tsx | 10 +- .../components/editor/use-create-editor.tsx | 18 +- .../src/components/plate-ui/alert-dialog.tsx | 142 +++++++++ .../components/plate-ui/block-selection.tsx | 2 +- .../plate-ui/media-audio-element.tsx | 44 +++ .../plate-ui/media-file-element.tsx | 62 ++++ .../plate-ui/media-placeholder-element.tsx | 272 ++++++++++++++++++ .../plate-ui/media-toolbar-button.tsx | 209 ++++++++++++-- .../plate-ui/media-upload-toast.tsx | 69 +++++ .../plate-ui/media-video-element.tsx | 129 +++++++++ .../src/components/plate-ui/spinner.tsx | 30 ++ .../src/components/plate-ui/toolbar.tsx | 119 +++++++- .../src/lib/uploadthing/handle-error.ts | 27 ++ .../src/lib/uploadthing/index.ts | 3 + .../src/lib/uploadthing/use-upload-file.ts | 77 +++++ yarn.lock | 10 +- 23 files changed, 1370 insertions(+), 105 deletions(-) create mode 100644 templates/plate-playground-template/src/components/plate-ui/alert-dialog.tsx create mode 100644 templates/plate-playground-template/src/components/plate-ui/media-audio-element.tsx create mode 100644 templates/plate-playground-template/src/components/plate-ui/media-file-element.tsx create mode 100644 templates/plate-playground-template/src/components/plate-ui/media-placeholder-element.tsx create mode 100644 templates/plate-playground-template/src/components/plate-ui/media-upload-toast.tsx create mode 100644 templates/plate-playground-template/src/components/plate-ui/media-video-element.tsx create mode 100644 templates/plate-playground-template/src/components/plate-ui/spinner.tsx create mode 100644 templates/plate-playground-template/src/lib/uploadthing/handle-error.ts create mode 100644 templates/plate-playground-template/src/lib/uploadthing/index.ts create mode 100644 templates/plate-playground-template/src/lib/uploadthing/use-upload-file.ts diff --git a/apps/www/src/registry/default/plate-ui/media-placeholder-element.tsx b/apps/www/src/registry/default/plate-ui/media-placeholder-element.tsx index bf3feedecc..5a3c80c232 100644 --- a/apps/www/src/registry/default/plate-ui/media-placeholder-element.tsx +++ b/apps/www/src/registry/default/plate-ui/media-placeholder-element.tsx @@ -6,6 +6,11 @@ import type { ReactNode } from 'react'; import type { TPlaceholderElement } from '@udecode/plate-media'; import { cn } from '@udecode/cn'; +import { + insertNodes, + removeNodes, + withoutSavingHistory, +} from '@udecode/plate-common'; import { findNodePath, useEditorPlugin, @@ -21,7 +26,6 @@ import { VideoPlugin, updateUploadHistory, } from '@udecode/plate-media/react'; -import { insertNodes, removeNodes, withoutSavingHistory } from '@udecode/slate'; import { AudioLines, FileUp, Film, ImageIcon } from 'lucide-react'; import { useFilePicker } from 'use-file-picker'; diff --git a/apps/www/src/registry/default/plate-ui/media-toolbar-button.tsx b/apps/www/src/registry/default/plate-ui/media-toolbar-button.tsx index 9e3597a3f9..effda61402 100644 --- a/apps/www/src/registry/default/plate-ui/media-toolbar-button.tsx +++ b/apps/www/src/registry/default/plate-ui/media-toolbar-button.tsx @@ -5,15 +5,14 @@ import React, { useCallback, useEffect, useState } from 'react'; import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu'; import { cn } from '@udecode/cn'; -import { useEditorRef } from '@udecode/plate-core/react'; +import { insertNodes } from '@udecode/plate-common'; +import { focusEditor, useEditorRef } from '@udecode/plate-common/react'; import { AudioPlugin, FilePlugin, ImagePlugin, VideoPlugin, } from '@udecode/plate-media/react'; -import { insertNodes } from '@udecode/slate'; -import { focusEditor } from '@udecode/slate-react'; import { AudioLinesIcon, FileUpIcon, @@ -171,26 +170,24 @@ export function MediaToolbarButton({ {currentConfig.title} - -
- - setUrl(e.target.value)} - onKeyDown={(e) => { - if (e.key === 'Enter') embedMedia(); - }} - placeholder="" - type="email" - autoFocus - /> -
+ + + setUrl(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') embedMedia(); + }} + placeholder="" + type="email" + autoFocus + />
diff --git a/apps/www/src/registry/default/plate-ui/media-upload-toast.tsx b/apps/www/src/registry/default/plate-ui/media-upload-toast.tsx index 9ac3d1faea..ce39cb2e79 100644 --- a/apps/www/src/registry/default/plate-ui/media-upload-toast.tsx +++ b/apps/www/src/registry/default/plate-ui/media-upload-toast.tsx @@ -1,6 +1,6 @@ import { useEffect } from 'react'; -import { useEditorRef } from '@udecode/plate-core/react'; +import { useEditorRef } from '@udecode/plate-common/react'; import { PlaceholderPlugin, UploadErrorCode } from '@udecode/plate-media/react'; import { toast } from 'sonner'; diff --git a/templates/plate-playground-template/package.json b/templates/plate-playground-template/package.json index e670fa9ce3..564a6538be 100644 --- a/templates/plate-playground-template/package.json +++ b/templates/plate-playground-template/package.json @@ -20,6 +20,7 @@ "@radix-ui/react-checkbox": "^1.1.2", "@radix-ui/react-context-menu": "^2.2.2", "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-separator": "^1.1.0", @@ -27,7 +28,7 @@ "@radix-ui/react-toolbar": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.3", "@udecode/cn": "^39.0.0", - "@udecode/plate-ai": "40.0.4", + "@udecode/plate-ai": "40.1.0", "@udecode/plate-alignment": "^40.0.0", "@udecode/plate-autoformat": "^40.0.0", "@udecode/plate-basic-elements": "^40.0.2", @@ -43,7 +44,7 @@ "@udecode/plate-cursor": "^40.0.0", "@udecode/plate-date": "^40.0.0", "@udecode/plate-dnd": "40.0.0", - "@udecode/plate-docx": "40.0.2", + "@udecode/plate-docx": "40.2.0", "@udecode/plate-emoji": "^40.0.0", "@udecode/plate-excalidraw": "^40.0.0", "@udecode/plate-floating": "^40.0.0", @@ -58,20 +59,24 @@ "@udecode/plate-layout": "^40.0.0", "@udecode/plate-line-height": "^40.0.0", "@udecode/plate-link": "^40.0.0", - "@udecode/plate-markdown": "^40.0.4", + "@udecode/plate-markdown": "40.0.5", "@udecode/plate-math": "^40.0.0", - "@udecode/plate-media": "40.0.0", + "@udecode/plate-media": "40.2.0", "@udecode/plate-mention": "40.0.0", "@udecode/plate-node-id": "^40.0.0", "@udecode/plate-reset-node": "^40.0.0", "@udecode/plate-resizable": "^40.0.0", "@udecode/plate-select": "^40.0.0", - "@udecode/plate-selection": "^40.0.0", + "@udecode/plate-selection": "40.1.0", "@udecode/plate-slash-command": "^40.0.0", "@udecode/plate-tabbable": "^40.0.0", "@udecode/plate-table": "^40.0.0", "@udecode/plate-toggle": "^40.0.0", "@udecode/plate-trailing-block": "^40.0.0", + "zod": "^3.23.8", + "react-player": "^2.16.0", + "sonner": "^1.5.0", + "use-file-picker": "^2.1.2", "ai": "^3.4.33", "class-variance-authority": "0.7.0", "clsx": "^2.1.1", diff --git a/templates/plate-playground-template/pnpm-lock.yaml b/templates/plate-playground-template/pnpm-lock.yaml index ba311d64a2..2ec56219cb 100644 --- a/templates/plate-playground-template/pnpm-lock.yaml +++ b/templates/plate-playground-template/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@faker-js/faker': specifier: ^9.2.0 version: 9.2.0 + '@radix-ui/react-alert-dialog': + specifier: ^1.1.1 + version: 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-avatar': specifier: ^1.1.1 version: 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -51,8 +54,8 @@ importers: specifier: ^39.0.0 version: 39.0.0(@types/react@18.3.12)(class-variance-authority@0.7.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwind-merge@2.5.4) '@udecode/plate-ai': - specifier: 40.0.4 - version: 40.0.4(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) + specifier: 40.1.0 + version: 40.1.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-alignment': specifier: ^40.0.0 version: 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) @@ -99,8 +102,8 @@ importers: specifier: 40.0.0 version: 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dnd-html5-backend@16.0.1)(react-dnd@16.0.1(@types/node@22.9.0)(@types/react@18.3.12)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-docx': - specifier: 40.0.2 - version: 40.0.2(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) + specifier: 40.2.0 + version: 40.2.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-emoji': specifier: ^40.0.0 version: 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) @@ -144,14 +147,14 @@ importers: specifier: ^40.0.0 version: 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-markdown': - specifier: ^40.0.4 - version: 40.0.4(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) + specifier: 40.0.5 + version: 40.0.5(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-math': specifier: ^40.0.0 version: 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-media': - specifier: 40.0.0 - version: 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) + specifier: 40.2.0 + version: 40.2.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-mention': specifier: 40.0.0 version: 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) @@ -168,8 +171,8 @@ importers: specifier: ^40.0.0 version: 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-selection': - specifier: ^40.0.0 - version: 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) + specifier: 40.1.0 + version: 40.1.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-slash-command': specifier: ^40.0.0 version: 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) @@ -227,6 +230,9 @@ importers: react-lite-youtube-embed: specifier: ^2.4.0 version: 2.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-player: + specifier: ^2.16.0 + version: 2.16.0(react@18.3.1) react-resizable-panels: specifier: ^2.1.6 version: 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -248,6 +254,9 @@ importers: slate-react: specifier: ^0.111.0 version: 0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2) + sonner: + specifier: ^1.5.0 + version: 1.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) tailwind-merge: specifier: 2.5.4 version: 2.5.4 @@ -257,6 +266,12 @@ importers: tailwindcss-animate: specifier: 1.0.7 version: 1.0.7(tailwindcss@3.4.14) + use-file-picker: + specifier: ^2.1.2 + version: 2.1.2(react@18.3.1) + zod: + specifier: ^3.23.8 + version: 3.23.8 devDependencies: '@types/node': specifier: ^22.9.0 @@ -866,6 +881,19 @@ packages: '@radix-ui/primitive@1.1.0': resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} + '@radix-ui/react-alert-dialog@1.1.2': + resolution: {integrity: sha512-eGSlLzPhKO+TErxkiGcCZGuvbVMnLA1MTnyBksGOeGRGkxHiiJUujsjmNTdWTm4iHVSRaUao9/4Ur671auMghQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-arrow@1.1.0': resolution: {integrity: sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==} peerDependencies: @@ -1427,8 +1455,8 @@ packages: react-dom: '>=16.8.0' tailwind-merge: '>=2.2.0' - '@udecode/plate-ai@40.0.4': - resolution: {integrity: sha512-g5kd1tj44g79QmYfZHvCIodTQ3cJ7RjMrkZmJ7BAcq1QQNg4TTNmDS8AaMl0+1DDTIob+a5WQLhl84+BB0ee0g==} + '@udecode/plate-ai@40.1.0': + resolution: {integrity: sha512-sIYeEcUyxYOcHTcISlEWCWRoX6XODEltPGsBPgU8pFKuWNMIt34g06Q9YB2f211RLIVSikzGhKrhB/ARsL82fg==} peerDependencies: '@udecode/plate-common': '>=40.0.0' react: '>=16.8.0' @@ -1630,10 +1658,10 @@ packages: slate-hyperscript: '>=0.66.0' slate-react: '>=0.111.0' - '@udecode/plate-docx@40.0.2': - resolution: {integrity: sha512-s2d/onntJbFQNqyhUrxmBZRm9Cr17u2Gxj03ohHRE067L+jN//XBaGHsTI1KxUAtHfW2s4GemSZZd942xgA8Ag==} + '@udecode/plate-docx@40.2.0': + resolution: {integrity: sha512-bS4b4OjfnMwUvu+EKc89DbQWIMnTx3bXwt+DypWXaf0WqAUYDyj0WAN/KXshRuyBjju29UE5nL1RjLQNO3Jumw==} peerDependencies: - '@udecode/plate-common': '>=40.0.2' + '@udecode/plate-common': '>=40.0.3' react: '>=16.8.0' react-dom: '>=16.8.0' slate: '>=0.103.0' @@ -1822,8 +1850,8 @@ packages: slate-hyperscript: '>=0.66.0' slate-react: '>=0.111.0' - '@udecode/plate-markdown@40.0.4': - resolution: {integrity: sha512-SLg8Dy9kU+04yXrxtgi8JBLSEIirMI3LjNEQkgNjAgttJK4bUFIyzJzB4OIwMJ4dcxOl1MZEvs91kc3ryrvtZQ==} + '@udecode/plate-markdown@40.0.5': + resolution: {integrity: sha512-Slbwurt3ltrH1Vru+RvBl4WbwtAC9BUgLbcIBiYLU8HDfd7Yr2+uJ568a3BeeT+QiRLONYuC5H2qIOZ38FFwlA==} peerDependencies: '@udecode/plate-common': '>=40.0.3' react: '>=16.8.0' @@ -1846,10 +1874,10 @@ packages: slate-hyperscript: '>=0.66.0' slate-react: '>=0.111.0' - '@udecode/plate-media@40.0.0': - resolution: {integrity: sha512-VyubrH9Oj+g3EI34+IUmBBqLEcw7RrSlNwASO563bAxaFydQr56mUVRTmvfL2f4nTqBl99IMNaUqw7z499LPXg==} + '@udecode/plate-media@40.2.0': + resolution: {integrity: sha512-7J+qMHVD2CzZ1xxoKl5MtnSFz81Y/6rfHIO5v8S2iBdUa5EDuEh7xmI44kNdQ1XCSeZVlkFhqiK1w74v2DzUnw==} peerDependencies: - '@udecode/plate-common': '>=40.0.0' + '@udecode/plate-common': '>=40.0.3' react: '>=16.8.0' react-dom: '>=16.8.0' slate: '>=0.103.0' @@ -1930,10 +1958,10 @@ packages: slate-hyperscript: '>=0.66.0' slate-react: '>=0.111.0' - '@udecode/plate-selection@40.0.0': - resolution: {integrity: sha512-ZO9GN3zERY/uGywOaTI8KqVDRF85Gqj97hdAmLVh0HHnJd0HJDEI2v2mu3552/fao+g3q4XXKmYBf630TuCNzg==} + '@udecode/plate-selection@40.1.0': + resolution: {integrity: sha512-W6QfdIB8YckUVFmSYG1IfKiy6RwG52hW7S9/KzJOBIzcYx0uvmbuUk+0BZHcjSr2RawSV7BwgQqh2f+ZCsNP6A==} peerDependencies: - '@udecode/plate-common': '>=40.0.0' + '@udecode/plate-common': '>=40.0.3' react: '>=16.8.0' react-dom: '>=16.8.0' slate: '>=0.103.0' @@ -2408,6 +2436,10 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -2748,6 +2780,10 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} + file-selector@0.2.4: + resolution: {integrity: sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==} + engines: {node: '>= 10'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -3163,6 +3199,9 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + load-script@1.0.0: + resolution: {integrity: sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==} + locate-character@3.0.0: resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} @@ -3233,6 +3272,9 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + mensch@0.3.4: resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} @@ -3634,6 +3676,9 @@ packages: peerDependencies: react: ^18.3.1 + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -3643,6 +3688,11 @@ packages: react: '>=18.2.0' react-dom: '>=18.2.0' + react-player@2.16.0: + resolution: {integrity: sha512-mAIPHfioD7yxO0GNYVFD1303QFtI3lyyQZLY229UEAp/a10cSW+hPcakg0Keq8uWJxT2OiT/4Gt+Lc9bD6bJmQ==} + peerDependencies: + react: '>=16.6.0' + react-remove-scroll-bar@2.3.6: resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==} engines: {node: '>=10'} @@ -3858,6 +3908,12 @@ packages: slick@1.12.2: resolution: {integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==} + sonner@1.7.0: + resolution: {integrity: sha512-W6dH7m5MujEPyug3lpI2l3TC3Pp1+LTgK0Efg+IHDrBbtEjyCmCHHo6yfNBOsf1tFZ6zf+jceWwB38baC8yO9g==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + sort-object-keys@1.1.3: resolution: {integrity: sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==} @@ -4139,6 +4195,12 @@ packages: peerDependencies: react: '>=16.8.0' + use-file-picker@2.1.2: + resolution: {integrity: sha512-ZEIzRi1wXeIXDWr5i55gRBVER8rTkSGskDUY94bciTTAZJHlBnOTRLL/LDYjgz6d+US3yELHnRvtBhLxFGtB0A==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16' + use-isomorphic-layout-effect@1.1.2: resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} peerDependencies: @@ -4702,6 +4764,20 @@ snapshots: '@radix-ui/primitive@1.1.0': {} + '@radix-ui/react-alert-dialog@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-dialog': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.12)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + '@radix-ui/react-arrow@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -5288,11 +5364,11 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@udecode/plate-ai@40.0.4(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2)': + '@udecode/plate-ai@40.1.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2)': dependencies: '@udecode/plate-common': 40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) - '@udecode/plate-markdown': 40.0.4(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) - '@udecode/plate-selection': 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) + '@udecode/plate-markdown': 40.0.5(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) + '@udecode/plate-selection': 40.1.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) lodash: 4.17.21 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -5524,13 +5600,13 @@ snapshots: slate-hyperscript: 0.100.0(slate@0.110.2) slate-react: 0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2) - '@udecode/plate-docx@40.0.2(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2)': + '@udecode/plate-docx@40.2.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2)': dependencies: '@udecode/plate-common': 40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-heading': 40.0.2(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-indent': 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-indent-list': 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) - '@udecode/plate-media': 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) + '@udecode/plate-media': 40.2.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) '@udecode/plate-table': 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -5722,7 +5798,7 @@ snapshots: slate-hyperscript: 0.100.0(slate@0.110.2) slate-react: 0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2) - '@udecode/plate-markdown@40.0.4(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2)': + '@udecode/plate-markdown@40.0.5(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2)': dependencies: '@udecode/plate-common': 40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) lodash: 4.17.21 @@ -5751,7 +5827,7 @@ snapshots: slate-hyperscript: 0.100.0(slate@0.110.2) slate-react: 0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2) - '@udecode/plate-media@40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2)': + '@udecode/plate-media@40.2.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2)': dependencies: '@udecode/plate-common': 40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) js-video-url-parser: 0.5.1 @@ -5832,7 +5908,7 @@ snapshots: slate-hyperscript: 0.100.0(slate@0.110.2) slate-react: 0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2) - '@udecode/plate-selection@40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2)': + '@udecode/plate-selection@40.1.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2)': dependencies: '@udecode/plate-common': 40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) copy-to-clipboard: 3.3.3 @@ -6387,6 +6463,8 @@ snapshots: deep-is@0.1.4: {} + deepmerge@4.3.1: {} + define-data-property@1.1.4: dependencies: es-define-property: 1.0.0 @@ -6882,6 +6960,10 @@ snapshots: dependencies: flat-cache: 3.2.0 + file-selector@0.2.4: + dependencies: + tslib: 2.8.1 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -7291,6 +7373,8 @@ snapshots: lines-and-columns@1.2.4: {} + load-script@1.0.0: {} + locate-character@3.0.0: {} locate-path@6.0.0: @@ -7423,6 +7507,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 + memoize-one@5.2.1: {} + mensch@0.3.4: {} merge2@1.4.1: {} @@ -7896,6 +7982,8 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-fast-compare@3.2.2: {} + react-is@16.13.1: {} react-lite-youtube-embed@2.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -7903,6 +7991,15 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + react-player@2.16.0(react@18.3.1): + dependencies: + deepmerge: 4.3.1 + load-script: 1.0.0 + memoize-one: 5.2.1 + prop-types: 15.8.1 + react: 18.3.1 + react-fast-compare: 3.2.2 + react-remove-scroll-bar@2.3.6(@types/react@18.3.12)(react@18.3.1): dependencies: react: 18.3.1 @@ -8186,6 +8283,11 @@ snapshots: slick@1.12.2: {} + sonner@1.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + sort-object-keys@1.1.3: {} sort-package-json@2.10.1: @@ -8529,6 +8631,11 @@ snapshots: dequal: 2.0.3 react: 18.3.1 + use-file-picker@2.1.2(react@18.3.1): + dependencies: + file-selector: 0.2.4 + react: 18.3.1 + use-isomorphic-layout-effect@1.1.2(@types/react@18.3.12)(react@18.3.1): dependencies: react: 18.3.1 diff --git a/templates/plate-playground-template/src/components/editor/plugins/block-selection-plugins.ts b/templates/plate-playground-template/src/components/editor/plugins/block-selection-plugins.ts index ab35e38f68..35edf1fd42 100644 --- a/templates/plate-playground-template/src/components/editor/plugins/block-selection-plugins.ts +++ b/templates/plate-playground-template/src/components/editor/plugins/block-selection-plugins.ts @@ -9,14 +9,6 @@ export const blockSelectionPlugins = [ excludePlugins: ['table', 'code_line', 'column_group', 'column'], }, options: { - areaOptions: { - behaviour: { - scrolling: { - speedDivider: 1.5, - }, - startThreshold: 4, - }, - }, enableContextMenu: true, }, }), diff --git a/templates/plate-playground-template/src/components/editor/plugins/dnd-plugins.tsx b/templates/plate-playground-template/src/components/editor/plugins/dnd-plugins.tsx index 6b0db41ba1..f5569c0f58 100644 --- a/templates/plate-playground-template/src/components/editor/plugins/dnd-plugins.tsx +++ b/templates/plate-playground-template/src/components/editor/plugins/dnd-plugins.tsx @@ -1,7 +1,7 @@ 'use client'; import { DndPlugin } from '@udecode/plate-dnd'; -import { ImagePlugin } from '@udecode/plate-media/react'; +import { PlaceholderPlugin } from '@udecode/plate-media/react'; import { NodeIdPlugin } from '@udecode/plate-node-id'; export const dndPlugins = [ @@ -11,11 +11,8 @@ export const dndPlugins = [ enableScroller: true, onDropFiles: ({ dragItem, editor, target }) => { editor - .getTransforms(ImagePlugin) - .insert.imageFromFiles(dragItem.files, { - at: target, - nextBlock: false, - }); + .getTransforms(PlaceholderPlugin) + .insert.media(dragItem.files, { at: target, nextBlock: false }); }, }, }), diff --git a/templates/plate-playground-template/src/components/editor/plugins/media-plugins.tsx b/templates/plate-playground-template/src/components/editor/plugins/media-plugins.tsx index 36fbf3b4e6..2bd9e43722 100644 --- a/templates/plate-playground-template/src/components/editor/plugins/media-plugins.tsx +++ b/templates/plate-playground-template/src/components/editor/plugins/media-plugins.tsx @@ -11,9 +11,9 @@ import { } from '@udecode/plate-media/react'; import { ImagePreview } from '@/components/plate-ui/image-preview'; +import { MediaUploadToast } from '@/components/plate-ui/media-upload-toast'; export const mediaPlugins = [ - PlaceholderPlugin, ImagePlugin.extend({ options: { disableUploadInsert: true, @@ -27,4 +27,12 @@ export const mediaPlugins = [ CaptionPlugin.configure({ options: { plugins: [ImagePlugin, MediaEmbedPlugin] }, }), + PlaceholderPlugin.configure({ + options: { + disableEmptyPlaceholder: true, + }, + render: { + afterEditable: () => , + }, + }), ] as const; diff --git a/templates/plate-playground-template/src/components/editor/use-create-editor.tsx b/templates/plate-playground-template/src/components/editor/use-create-editor.tsx index bcfbedccd1..d9c3cf6737 100644 --- a/templates/plate-playground-template/src/components/editor/use-create-editor.tsx +++ b/templates/plate-playground-template/src/components/editor/use-create-editor.tsx @@ -31,7 +31,14 @@ import { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react'; import { KbdPlugin } from '@udecode/plate-kbd/react'; import { ColumnItemPlugin, ColumnPlugin } from '@udecode/plate-layout/react'; import { LinkPlugin } from '@udecode/plate-link/react'; -import { ImagePlugin, MediaEmbedPlugin } from '@udecode/plate-media/react'; +import { + AudioPlugin, + FilePlugin, + ImagePlugin, + MediaEmbedPlugin, + PlaceholderPlugin, + VideoPlugin, +} from '@udecode/plate-media/react'; import { MentionInputPlugin, MentionPlugin, @@ -83,12 +90,18 @@ import { TocElement } from '@/components/plate-ui/toc-element'; import { ToggleElement } from '@/components/plate-ui/toggle-element'; import { withDraggables } from '@/components/plate-ui/with-draggables'; +import { MediaAudioElement } from '../plate-ui/media-audio-element'; +import { MediaFileElement } from '../plate-ui/media-file-element'; +import { MediaPlaceholderElement } from '../plate-ui/media-placeholder-element'; +import { MediaVideoElement } from '../plate-ui/media-video-element'; + export const useCreateEditor = () => { return usePlateEditor({ override: { components: withDraggables( withPlaceholders({ [AIPlugin.key]: AILeaf, + [AudioPlugin.key]: MediaAudioElement, [BlockquotePlugin.key]: BlockquoteElement, [BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }), [CodeBlockPlugin.key]: CodeBlockElement, @@ -101,6 +114,7 @@ export const useCreateEditor = () => { [DatePlugin.key]: DateElement, [EmojiInputPlugin.key]: EmojiInputElement, [ExcalidrawPlugin.key]: ExcalidrawElement, + [FilePlugin.key]: MediaFileElement, [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }), [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }), [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: 'h3' }), @@ -117,6 +131,7 @@ export const useCreateEditor = () => { [MentionInputPlugin.key]: MentionInputElement, [MentionPlugin.key]: MentionElement, [ParagraphPlugin.key]: ParagraphElement, + [PlaceholderPlugin.key]: MediaPlaceholderElement, [SlashInputPlugin.key]: SlashInputElement, [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }), [SubscriptPlugin.key]: withProps(PlateLeaf, { as: 'sub' }), @@ -128,6 +143,7 @@ export const useCreateEditor = () => { [TocPlugin.key]: TocElement, [TogglePlugin.key]: ToggleElement, [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }), + [VideoPlugin.key]: MediaVideoElement, }) ), }, diff --git a/templates/plate-playground-template/src/components/plate-ui/alert-dialog.tsx b/templates/plate-playground-template/src/components/plate-ui/alert-dialog.tsx new file mode 100644 index 0000000000..fd3f90a273 --- /dev/null +++ b/templates/plate-playground-template/src/components/plate-ui/alert-dialog.tsx @@ -0,0 +1,142 @@ +'use client'; + +import * as React from 'react'; + +import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'; +import { cn } from '@udecode/cn'; + +import { buttonVariants } from './button'; + +const AlertDialog = AlertDialogPrimitive.Root; + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger; + +const AlertDialogPortal = AlertDialogPrimitive.Portal; + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)); +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +AlertDialogHeader.displayName = 'AlertDialogHeader'; + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +AlertDialogFooter.displayName = 'AlertDialogFooter'; + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName; + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; + +export { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogOverlay, + AlertDialogPortal, + AlertDialogTitle, + AlertDialogTrigger, +}; diff --git a/templates/plate-playground-template/src/components/plate-ui/block-selection.tsx b/templates/plate-playground-template/src/components/plate-ui/block-selection.tsx index b0562fe761..7c7c00517a 100644 --- a/templates/plate-playground-template/src/components/plate-ui/block-selection.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/block-selection.tsx @@ -5,7 +5,7 @@ import { useBlockSelected } from '@udecode/plate-selection/react'; import { type VariantProps, cva } from 'class-variance-authority'; export const blockSelectionVariants = cva( - 'pointer-events-none absolute inset-0 z-[1] bg-brand/[.13] transition-opacity', + 'bg-brand/[.13] pointer-events-none absolute inset-0 z-[1] transition-opacity', { defaultVariants: { active: true, diff --git a/templates/plate-playground-template/src/components/plate-ui/media-audio-element.tsx b/templates/plate-playground-template/src/components/plate-ui/media-audio-element.tsx new file mode 100644 index 0000000000..fa0b95a48c --- /dev/null +++ b/templates/plate-playground-template/src/components/plate-ui/media-audio-element.tsx @@ -0,0 +1,44 @@ +'use client'; + +import React from 'react'; + +import { cn, withRef } from '@udecode/cn'; +import { withHOC } from '@udecode/plate-common/react'; +import { useMediaState } from '@udecode/plate-media/react'; +import { ResizableProvider } from '@udecode/plate-resizable'; + +import { Caption, CaptionTextarea } from './caption'; +import { PlateElement } from './plate-element'; + +export const MediaAudioElement = withHOC( + ResizableProvider, + withRef( + ({ children, className, nodeProps, ...props }, ref) => { + const { align = 'center', readOnly, unsafeUrl } = useMediaState(); + + return ( + +
+
+ {/* eslint-disable-next-line jsx-a11y/media-has-caption */} +
+ + + + +
+ {children} +
+ ); + } + ) +); diff --git a/templates/plate-playground-template/src/components/plate-ui/media-file-element.tsx b/templates/plate-playground-template/src/components/plate-ui/media-file-element.tsx new file mode 100644 index 0000000000..4e08403e14 --- /dev/null +++ b/templates/plate-playground-template/src/components/plate-ui/media-file-element.tsx @@ -0,0 +1,62 @@ +'use client'; + +import React from 'react'; + +import { cn, withRef } from '@udecode/cn'; +import { withHOC } from '@udecode/plate-common/react'; +import { useMediaState } from '@udecode/plate-media/react'; +import { ResizableProvider } from '@udecode/plate-resizable'; +import { FileUp } from 'lucide-react'; +import { useReadOnly } from 'slate-react'; + +import { Caption, CaptionTextarea } from './caption'; +import { PlateElement } from './plate-element'; + +export const MediaFileElement = withHOC( + ResizableProvider, + withRef( + ({ children, className, nodeProps, ...props }, ref) => { + const readOnly = useReadOnly(); + + const { name, unsafeUrl } = useMediaState(); + + const onDownload = () => { + window.open(unsafeUrl); + }; + + return ( + + {/* eslint-disable-next-line jsx-a11y/interactive-supports-focus */} +
+
+ + +
{name}
+ + {/* TODO: add size */} + {/*
{element.size}
*/} +
+ + + + +
+ {children} +
+ ); + } + ) +); diff --git a/templates/plate-playground-template/src/components/plate-ui/media-placeholder-element.tsx b/templates/plate-playground-template/src/components/plate-ui/media-placeholder-element.tsx new file mode 100644 index 0000000000..52ef86b696 --- /dev/null +++ b/templates/plate-playground-template/src/components/plate-ui/media-placeholder-element.tsx @@ -0,0 +1,272 @@ +'use client'; + +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import type { ReactNode } from 'react'; + +import type { TPlaceholderElement } from '@udecode/plate-media'; + +import { cn } from '@udecode/cn'; +import { + insertNodes, + removeNodes, + withoutSavingHistory, +} from '@udecode/plate-common'; +import { + findNodePath, + useEditorPlugin, + withHOC, + withRef, +} from '@udecode/plate-common/react'; +import { + AudioPlugin, + FilePlugin, + ImagePlugin, + PlaceholderPlugin, + PlaceholderProvider, + VideoPlugin, + updateUploadHistory, +} from '@udecode/plate-media/react'; +import { AudioLines, FileUp, Film, ImageIcon } from 'lucide-react'; +import { useFilePicker } from 'use-file-picker'; + +import { useUploadFile } from '@/lib/uploadthing'; + +import { PlateElement } from './plate-element'; +import { Spinner } from './spinner'; + +const CONTENT: Record< + string, + { + accept: string[]; + content: ReactNode; + icon: ReactNode; + } +> = { + [AudioPlugin.key]: { + accept: ['audio/*'], + content: 'Add an audio file', + icon: , + }, + [FilePlugin.key]: { + accept: ['*'], + content: 'Add a file', + icon: , + }, + [ImagePlugin.key]: { + accept: ['image/*'], + content: 'Add an image', + icon: , + }, + [VideoPlugin.key]: { + accept: ['video/*'], + content: 'Add a video', + icon: , + }, +}; + +export const MediaPlaceholderElement = withHOC( + PlaceholderProvider, + withRef( + ({ children, className, editor, nodeProps, ...props }, ref) => { + const element = props.element as TPlaceholderElement; + + const { api } = useEditorPlugin(PlaceholderPlugin); + + const { isUploading, progress, uploadFile, uploadedFile, uploadingFile } = + useUploadFile(); + + const loading = isUploading && uploadingFile; + + const currentContent = CONTENT[element.mediaType]; + + const isImage = element.mediaType === ImagePlugin.key; + + const imageRef = useRef(null); + + const { openFilePicker } = useFilePicker({ + accept: currentContent.accept, + multiple: true, + onFilesSelected: ({ plainFiles: updatedFiles }) => { + const firstFile = updatedFiles[0]; + const restFiles = updatedFiles.slice(1); + + replaceCurrentPlaceholder(firstFile); + + restFiles.length > 0 && (editor as any).tf.insert.media(restFiles); + }, + }); + + const replaceCurrentPlaceholder = useCallback( + (file: File) => { + void uploadFile(file); + api.placeholder.addUploadingFile(element.id as string, file); + }, + [api.placeholder, element.id, uploadFile] + ); + + useEffect(() => { + if (!uploadedFile) return; + + const path = findNodePath(editor, element); + + withoutSavingHistory(editor, () => { + removeNodes(editor, { at: path }); + + const node = { + children: [{ text: '' }], + initialHeight: imageRef.current?.height, + initialWidth: imageRef.current?.width, + isUpload: true, + name: element.mediaType === FilePlugin.key ? uploadedFile.name : '', + placeholderId: element.id as string, + type: element.mediaType!, + url: uploadedFile.url, + }; + + insertNodes(editor, node, { at: path }); + + updateUploadHistory(editor, node); + }); + + api.placeholder.removeUploadingFile(element.id as string); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [uploadedFile, element.id]); + + // React dev mode will call useEffect twice + const isReplaced = useRef(false); + /** Paste and drop */ + useEffect(() => { + if (isReplaced.current) return; + + isReplaced.current = true; + const currentFiles = api.placeholder.getUploadingFile( + element.id as string + ); + + if (!currentFiles) return; + + replaceCurrentPlaceholder(currentFiles); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isReplaced]); + + return ( + + {(!loading || !isImage) && ( +
!loading && openFilePicker()} + contentEditable={false} + > +
+ {currentContent.icon} +
+
+
+ {loading ? uploadingFile?.name : currentContent.content} +
+ + {loading && !isImage && ( +
+
{formatBytes(uploadingFile?.size ?? 0)}
+
+
+ + {progress ?? 0}% +
+
+ )} +
+
+ )} + + {isImage && loading && ( + + )} + + {children} +
+ ); + } + ) +); + +export function ImageProgress({ + className, + file, + imageRef, + progress = 0, +}: { + file: File; + className?: string; + imageRef?: React.RefObject; + progress?: number; +}) { + const [objectUrl, setObjectUrl] = useState(null); + + useEffect(() => { + const url = URL.createObjectURL(file); + setObjectUrl(url); + + return () => { + URL.revokeObjectURL(url); + }; + }, [file]); + + if (!objectUrl) { + return null; + } + + return ( +
+ {file.name} + {progress < 100 && ( +
+ + + {Math.round(progress)}% + +
+ )} +
+ ); +} + +export function formatBytes( + bytes: number, + opts: { + decimals?: number; + sizeType?: 'accurate' | 'normal'; + } = {} +) { + const { decimals = 0, sizeType = 'normal' } = opts; + + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + const accurateSizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB']; + + if (bytes === 0) return '0 Byte'; + + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + + return `${(bytes / Math.pow(1024, i)).toFixed(decimals)} ${ + sizeType === 'accurate' + ? (accurateSizes[i] ?? 'Bytest') + : (sizes[i] ?? 'Bytes') + }`; +} diff --git a/templates/plate-playground-template/src/components/plate-ui/media-toolbar-button.tsx b/templates/plate-playground-template/src/components/plate-ui/media-toolbar-button.tsx index af5a051daf..4a41f63643 100644 --- a/templates/plate-playground-template/src/components/plate-ui/media-toolbar-button.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/media-toolbar-button.tsx @@ -1,28 +1,203 @@ 'use client'; -import React from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; -import { withRef } from '@udecode/cn'; +import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu'; + +import { cn } from '@udecode/cn'; +import { insertNodes } from '@udecode/plate-common'; +import { focusEditor, useEditorRef } from '@udecode/plate-common/react'; import { - type ImagePlugin, - type MediaEmbedPlugin, - useMediaToolbarButton, + AudioPlugin, + FilePlugin, + ImagePlugin, + VideoPlugin, } from '@udecode/plate-media/react'; -import { ImageIcon } from 'lucide-react'; - -import { ToolbarButton } from './toolbar'; +import { + AudioLinesIcon, + FileUpIcon, + FilmIcon, + ImageIcon, + LinkIcon, +} from 'lucide-react'; +import { useFilePicker } from 'use-file-picker'; -export const MediaToolbarButton = withRef< - typeof ToolbarButton, +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from './alert-dialog'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuTrigger, + useOpenState, +} from './dropdown-menu'; +import { Input } from './input'; +import { + ToolbarSplitButton, + ToolbarSplitButtonPrimary, + ToolbarSplitButtonSecondary, +} from './toolbar'; +const MEDIA_CONFIG: Record< + string, { - nodeType?: typeof ImagePlugin.key | typeof MediaEmbedPlugin.key; + accept: string[]; + icon: React.ReactNode; + title: string; + tooltip: string; } ->(({ nodeType, ...rest }, ref) => { - const { props } = useMediaToolbarButton({ nodeType }); +> = { + [AudioPlugin.key]: { + accept: ['audio/*'], + icon: , + title: 'Insert Audio', + tooltip: 'Audio', + }, + [FilePlugin.key]: { + accept: ['*'], + icon: , + title: 'Insert File', + tooltip: 'File', + }, + [ImagePlugin.key]: { + accept: ['image/*'], + icon: , + title: 'Insert Image', + tooltip: 'Image', + }, + [VideoPlugin.key]: { + accept: ['video/*'], + icon: , + title: 'Insert Video', + tooltip: 'Video', + }, +}; + +export function MediaToolbarButton({ + children, + nodeType, + ...props +}: DropdownMenuProps & { nodeType: string }) { + const currentConfig = MEDIA_CONFIG[nodeType]; + + const editor = useEditorRef(); + const openState = useOpenState(); + + const { openFilePicker } = useFilePicker({ + accept: currentConfig.accept, + multiple: true, + onFilesSelected: ({ plainFiles: updatedFiles }) => { + (editor as any).tf.insert.media(updatedFiles); + }, + }); + + const [dialogOpen, setDialogOpen] = useState(false); + + const [url, setUrl] = useState(''); + + const embedMedia = useCallback(() => { + setDialogOpen(false); + insertNodes(editor, { + children: [{ text: '' }], + name: nodeType === FilePlugin.key ? url.split('/').pop() : undefined, + type: nodeType, + url, + }); + }, [editor, nodeType, url]); + + useEffect(() => { + if (!dialogOpen) { + focusEditor(editor); + setUrl(''); + } + }, [dialogOpen, editor]); return ( - - - + <> + + + openFilePicker()} + onMouseDown={(e) => e.preventDefault()} + tooltip={currentConfig.tooltip} + > + {currentConfig.icon} + + + + + + + + + + openFilePicker()} + hideIcon + > +
+ {currentConfig.icon} + Upload from computer +
+
+ setDialogOpen(true)} + hideIcon + > +
+ + Insert via URL +
+
+
+
+
+ + + + + {currentConfig.title} + +
+ + setUrl(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') embedMedia(); + }} + placeholder="" + type="email" + autoFocus + /> +
+
+
+ + Cancel + Accept + +
+
+ ); -}); +} diff --git a/templates/plate-playground-template/src/components/plate-ui/media-upload-toast.tsx b/templates/plate-playground-template/src/components/plate-ui/media-upload-toast.tsx new file mode 100644 index 0000000000..ce39cb2e79 --- /dev/null +++ b/templates/plate-playground-template/src/components/plate-ui/media-upload-toast.tsx @@ -0,0 +1,69 @@ +import { useEffect } from 'react'; + +import { useEditorRef } from '@udecode/plate-common/react'; +import { PlaceholderPlugin, UploadErrorCode } from '@udecode/plate-media/react'; +import { toast } from 'sonner'; + +export const useUploadErrorToast = () => { + const editor = useEditorRef(); + + const uploadError = editor.useOption(PlaceholderPlugin, 'error'); + + useEffect(() => { + if (!uploadError) return; + + const { code, data } = uploadError; + + switch (code) { + case UploadErrorCode.INVALID_FILE_SIZE: { + toast.error( + `The size of files ${data.files + .map((f) => f.name) + .join(', ')} is invalid` + ); + + break; + } + case UploadErrorCode.INVALID_FILE_TYPE: { + toast.error( + `The type of files ${data.files + .map((f) => f.name) + .join(', ')} is invalid` + ); + + break; + } + case UploadErrorCode.TOO_LARGE: { + toast.error( + `The size of files ${data.files + .map((f) => f.name) + .join(', ')} is too large than ${data.maxFileSize}` + ); + + break; + } + case UploadErrorCode.TOO_LESS_FILES: { + toast.error( + `The mini um number of files is ${data.minFileCount} for ${data.fileType}` + ); + + break; + } + case UploadErrorCode.TOO_MANY_FILES: { + toast.error( + `The maximum number of files is ${data.maxFileCount} ${ + data.fileType ? `for ${data.fileType}` : '' + }` + ); + + break; + } + } + }, [uploadError]); +}; + +export const MediaUploadToast = () => { + useUploadErrorToast(); + + return null; +}; diff --git a/templates/plate-playground-template/src/components/plate-ui/media-video-element.tsx b/templates/plate-playground-template/src/components/plate-ui/media-video-element.tsx new file mode 100644 index 0000000000..6108bacecb --- /dev/null +++ b/templates/plate-playground-template/src/components/plate-ui/media-video-element.tsx @@ -0,0 +1,129 @@ +'use client'; + +import React from 'react'; +import LiteYouTubeEmbed from 'react-lite-youtube-embed'; +import ReactPlayer from 'react-player'; + +import { cn, withRef } from '@udecode/cn'; +import { useEditorMounted, withHOC } from '@udecode/plate-common/react'; +import { useDraggable, useDraggableState } from '@udecode/plate-dnd'; +import { parseTwitterUrl, parseVideoUrl } from '@udecode/plate-media'; +import { useMediaState } from '@udecode/plate-media/react'; +import { ResizableProvider, useResizableStore } from '@udecode/plate-resizable'; + +import { Caption, CaptionTextarea } from './caption'; +import { PlateElement } from './plate-element'; +import { + Resizable, + ResizeHandle, + mediaResizeHandleVariants, +} from './resizable'; + +export const MediaVideoElement = withHOC( + ResizableProvider, + withRef( + ({ children, className, nodeProps, ...props }, ref) => { + const { + align = 'center', + embed, + isUpload, + isYoutube, + readOnly, + unsafeUrl, + } = useMediaState({ + urlParsers: [parseTwitterUrl, parseVideoUrl], + }); + const width = useResizableStore().get.width(); + + const isEditorMounted = useEditorMounted(); + + const isTweet = true; + + const state = useDraggableState({ element: props.element }); + const { isDragging } = state; + const { handleRef } = useDraggable(state); + + return ( + +
+ +
+ + + + + {!isUpload && isYoutube && ( +
+ _iframe]:absolute [&_>_iframe]:left-0 [&_>_iframe]:top-0 [&_>_iframe]:size-full', + '[&_>_.lty-playbtn]:z-[1] [&_>_.lty-playbtn]:h-[46px] [&_>_.lty-playbtn]:w-[70px] [&_>_.lty-playbtn]:rounded-[14%] [&_>_.lty-playbtn]:bg-[#212121] [&_>_.lty-playbtn]:opacity-80 [&_>_.lty-playbtn]:[transition:all_0.2s_cubic-bezier(0,_0,_0.2,_1)]', + '[&:hover_>_.lty-playbtn]:bg-[red] [&:hover_>_.lty-playbtn]:opacity-100', + '[&_>_.lty-playbtn]:before:border-y-[11px] [&_>_.lty-playbtn]:before:border-l-[19px] [&_>_.lty-playbtn]:before:border-r-0 [&_>_.lty-playbtn]:before:border-[transparent_transparent_transparent_#fff] [&_>_.lty-playbtn]:before:content-[""]', + '[&_>_.lty-playbtn]:absolute [&_>_.lty-playbtn]:left-1/2 [&_>_.lty-playbtn]:top-1/2 [&_>_.lty-playbtn]:[transform:translate3d(-50%,-50%,0)]', + '[&_>_.lty-playbtn]:before:absolute [&_>_.lty-playbtn]:before:left-1/2 [&_>_.lty-playbtn]:before:top-1/2 [&_>_.lty-playbtn]:before:[transform:translate3d(-50%,-50%,0)]', + '[&.lyt-activated]:cursor-[unset]', + '[&.lyt-activated]:before:pointer-events-none [&.lyt-activated]:before:opacity-0', + '[&.lyt-activated_>_.lty-playbtn]:pointer-events-none [&.lyt-activated_>_.lty-playbtn]:!opacity-0' + )} + /> +
+ )} + + {/* TODO: Lazy load */} + {isUpload && isEditorMounted && ( +
+ +
+ )} +
+
+ + + + +
+ {children} +
+ ); + } + ) +); diff --git a/templates/plate-playground-template/src/components/plate-ui/spinner.tsx b/templates/plate-playground-template/src/components/plate-ui/spinner.tsx new file mode 100644 index 0000000000..94212c23b9 --- /dev/null +++ b/templates/plate-playground-template/src/components/plate-ui/spinner.tsx @@ -0,0 +1,30 @@ +import React from 'react'; + +import { cn } from '@udecode/cn'; +import { type VariantProps, cva } from 'class-variance-authority'; +import { type LucideProps, Loader2Icon } from 'lucide-react'; + +const spinnerVariants = cva('text-muted-foreground animate-spin', { + defaultVariants: { + size: 'default', + }, + variants: { + size: { + default: 'size-4', + icon: 'size-10', + lg: 'size-6', + sm: 'size-2', + }, + }, +}); + +export const Spinner = ({ + className, + size, + ...props +}: Partial>) => ( + +); diff --git a/templates/plate-playground-template/src/components/plate-ui/toolbar.tsx b/templates/plate-playground-template/src/components/plate-ui/toolbar.tsx index 9720c3624f..576806feb2 100644 --- a/templates/plate-playground-template/src/components/plate-ui/toolbar.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/toolbar.tsx @@ -27,12 +27,12 @@ export const ToolbarLink = withCn( export const ToolbarSeparator = withCn( ToolbarPrimitive.Separator, - 'mx-2 my-1 w-px shrink-0 bg-border' + 'bg-border mx-2 my-1 w-px shrink-0' ); const toolbarButtonVariants = cva( cn( - 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium text-foreground ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg:not([data-icon])]:size-4' + 'text-foreground ring-offset-background focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg:not([data-icon])]:size-4' ), { defaultVariants: { @@ -47,9 +47,34 @@ const toolbarButtonVariants = cva( }, variant: { default: - 'bg-transparent hover:bg-muted hover:text-muted-foreground aria-checked:bg-accent aria-checked:text-accent-foreground', + 'hover:bg-muted hover:text-muted-foreground aria-checked:bg-accent aria-checked:text-accent-foreground bg-transparent', outline: - 'border border-input bg-transparent hover:bg-accent hover:text-accent-foreground', + 'border-input hover:bg-accent hover:text-accent-foreground border bg-transparent', + }, + }, + } +); + +const dropdownArrowVariants = cva( + cn( + 'text-foreground focus-visible:ring-ring inline-flex items-center justify-center rounded-r-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50' + ), + { + defaultVariants: { + size: 'sm', + variant: 'default', + }, + variants: { + size: { + default: 'h-10 w-6', + lg: 'h-11 w-8', + sm: 'h-7 w-4', + }, + variant: { + default: + 'hover:bg-muted hover:text-muted-foreground aria-checked:bg-accent aria-checked:text-accent-foreground bg-transparent', + outline: + 'border-input hover:bg-accent hover:text-accent-foreground border border-l-0 bg-transparent', }, }, } @@ -98,7 +123,7 @@ const ToolbarButton = withTooltip(
@@ -131,6 +156,90 @@ ToolbarButton.displayName = 'ToolbarButton'; export { ToolbarButton }; +export const ToolbarSplitButton = React.forwardRef< + React.ElementRef, + { + pressed?: boolean; + tooltip?: string; + } & Omit< + React.ComponentPropsWithoutRef, + 'asChild' | 'value' + > +>(({ children, pressed, ...props }, ref) => { + return ( + + + + ); +}); + +export const ToolbarSplitButtonPrimary = withTooltip( + React.forwardRef< + React.ElementRef, + { + className?: string; + size?: 'default' | 'lg' | 'sm'; + variant?: 'default' | 'outline'; + } & Omit, 'value'> + >(({ children, className, size, variant, ...props }, ref) => { + return ( + + {children} + + ); + }) +); + +export const ToolbarSplitButtonSecondary = React.forwardRef< + HTMLButtonElement, + { + className?: string; + size?: 'default' | 'lg' | 'sm'; + variant?: 'default' | 'outline'; + } & React.ButtonHTMLAttributes +>(({ className, size, variant, ...props }, ref) => { + return ( + + + + ); +}); + +ToolbarSplitButton.displayName = 'ToolbarButton'; + export const ToolbarToggleItem = withVariants( ToolbarPrimitive.ToggleItem, toolbarButtonVariants, diff --git a/templates/plate-playground-template/src/lib/uploadthing/handle-error.ts b/templates/plate-playground-template/src/lib/uploadthing/handle-error.ts new file mode 100644 index 0000000000..f04db185ad --- /dev/null +++ b/templates/plate-playground-template/src/lib/uploadthing/handle-error.ts @@ -0,0 +1,27 @@ +import { isRedirectError } from 'next/dist/client/components/redirect'; +import { toast } from 'sonner'; +import { z } from 'zod'; + +export function getErrorMessage(err: unknown) { + const unknownError = 'Something went wrong, please try again later.'; + + if (err instanceof z.ZodError) { + const errors = err.issues.map((issue) => { + return issue.message; + }); + + return errors.join('\n'); + } else if (err instanceof Error) { + return err.message; + } else if (isRedirectError(err)) { + throw err; + } else { + return unknownError; + } +} + +export function showErrorToast(err: unknown) { + const errorMessage = getErrorMessage(err); + + return toast.error(errorMessage); +} diff --git a/templates/plate-playground-template/src/lib/uploadthing/index.ts b/templates/plate-playground-template/src/lib/uploadthing/index.ts new file mode 100644 index 0000000000..bf5ee4b5e0 --- /dev/null +++ b/templates/plate-playground-template/src/lib/uploadthing/index.ts @@ -0,0 +1,3 @@ +export * from './handle-error'; + +export * from './use-upload-file'; diff --git a/templates/plate-playground-template/src/lib/uploadthing/use-upload-file.ts b/templates/plate-playground-template/src/lib/uploadthing/use-upload-file.ts new file mode 100644 index 0000000000..1c708ec341 --- /dev/null +++ b/templates/plate-playground-template/src/lib/uploadthing/use-upload-file.ts @@ -0,0 +1,77 @@ +import * as React from 'react'; + +import { toast } from 'sonner'; + +import { getErrorMessage } from './handle-error'; + +export interface UploadedFile { + key: string; + appUrl: string; + name: string; + size: number; + type: string; + url: string; +} + +export function useUploadFile() { + const [uploadedFile, setUploadedFile] = React.useState(); + const [uploadingFile, setUploadingFile] = React.useState(); + const [progress, setProgress] = React.useState(0); + const [isUploading, setIsUploading] = React.useState(false); + + async function uploadThing(file: File) { + setIsUploading(true); + setUploadingFile(file); + + try { + // Mock upload for unauthenticated users + // toast.info('User not logged in. Mocking upload process.'); + const mockUploadedFile = { + key: 'mock-key-0', + appUrl: `https://mock-app-url.com/${file.name}`, + name: file.name, + size: file.size, + type: file.type, + url: URL.createObjectURL(file), + } as UploadedFile; + + // Simulate upload progress + let progress = 0; + + const simulateProgress = async () => { + while (progress < 100) { + await new Promise((resolve) => setTimeout(resolve, 100)); + progress += 2; + setProgress(Math.min(progress, 100)); + } + }; + + await simulateProgress(); + + setUploadedFile(mockUploadedFile); + + return mockUploadedFile; + } catch (error) { + const errorMessage = getErrorMessage(error); + + const message = + errorMessage.length > 0 + ? errorMessage + : 'Something went wrong, please try again later.'; + + toast.error(message); + } finally { + setProgress(0); + setIsUploading(false); + setUploadingFile(undefined); + } + } + + return { + isUploading, + progress, + uploadFile: uploadThing, + uploadedFile, + uploadingFile, + }; +} diff --git a/yarn.lock b/yarn.lock index f99eb82feb..c8e876bf61 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6581,7 +6581,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-docx@npm:40.0.2, @udecode/plate-docx@workspace:^, @udecode/plate-docx@workspace:packages/docx": +"@udecode/plate-docx@npm:40.2.0, @udecode/plate-docx@workspace:^, @udecode/plate-docx@workspace:packages/docx": version: 0.0.0-use.local resolution: "@udecode/plate-docx@workspace:packages/docx" dependencies: @@ -6589,7 +6589,7 @@ __metadata: "@udecode/plate-heading": "npm:40.0.2" "@udecode/plate-indent": "npm:40.0.0" "@udecode/plate-indent-list": "npm:40.0.0" - "@udecode/plate-media": "npm:40.0.0" + "@udecode/plate-media": "npm:40.2.0" "@udecode/plate-table": "npm:40.0.0" validator: "npm:^13.12.0" peerDependencies: @@ -6949,7 +6949,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-media@npm:40.0.0, @udecode/plate-media@workspace:^, @udecode/plate-media@workspace:packages/media": +"@udecode/plate-media@npm:40.2.0, @udecode/plate-media@workspace:^, @udecode/plate-media@workspace:packages/media": version: 0.0.0-use.local resolution: "@udecode/plate-media@workspace:packages/media" dependencies: @@ -7285,7 +7285,7 @@ __metadata: "@udecode/plate-common": "npm:40.0.3" "@udecode/plate-csv": "npm:40.0.0" "@udecode/plate-diff": "npm:40.0.0" - "@udecode/plate-docx": "npm:40.0.2" + "@udecode/plate-docx": "npm:40.2.0" "@udecode/plate-find-replace": "npm:40.0.0" "@udecode/plate-floating": "npm:40.0.0" "@udecode/plate-font": "npm:40.0.0" @@ -7301,7 +7301,7 @@ __metadata: "@udecode/plate-link": "npm:40.0.0" "@udecode/plate-list": "npm:40.0.0" "@udecode/plate-markdown": "npm:40.0.5" - "@udecode/plate-media": "npm:40.0.0" + "@udecode/plate-media": "npm:40.2.0" "@udecode/plate-mention": "npm:40.0.0" "@udecode/plate-node-id": "npm:40.0.0" "@udecode/plate-normalizers": "npm:40.0.0" From 80acd3b256c2885748c61f7282eba68a76ec6eb0 Mon Sep 17 00:00:00 2001 From: Felix Feng Date: Sun, 17 Nov 2024 16:39:23 +0800 Subject: [PATCH 2/3] fix --- .../plate-ui/fixed-toolbar-buttons.tsx | 15 +++++++- .../plate-ui/media-toolbar-button.tsx | 38 +++++++++---------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/templates/plate-playground-template/src/components/plate-ui/fixed-toolbar-buttons.tsx b/templates/plate-playground-template/src/components/plate-ui/fixed-toolbar-buttons.tsx index 19f8f00f93..aaae0beaad 100644 --- a/templates/plate-playground-template/src/components/plate-ui/fixed-toolbar-buttons.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/fixed-toolbar-buttons.tsx @@ -15,7 +15,12 @@ import { FontColorPlugin, } from '@udecode/plate-font/react'; import { ListStyleType } from '@udecode/plate-indent-list'; -import { ImagePlugin } from '@udecode/plate-media/react'; +import { + AudioPlugin, + FilePlugin, + ImagePlugin, + VideoPlugin, +} from '@udecode/plate-media/react'; import { BaselineIcon, BoldIcon, @@ -127,10 +132,16 @@ export function FixedToolbarButtons() { + + + + + + + - diff --git a/templates/plate-playground-template/src/components/plate-ui/media-toolbar-button.tsx b/templates/plate-playground-template/src/components/plate-ui/media-toolbar-button.tsx index 4a41f63643..38cd80214c 100644 --- a/templates/plate-playground-template/src/components/plate-ui/media-toolbar-button.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/media-toolbar-button.tsx @@ -170,26 +170,24 @@ export function MediaToolbarButton({ {currentConfig.title} - -
- - setUrl(e.target.value)} - onKeyDown={(e) => { - if (e.key === 'Enter') embedMedia(); - }} - placeholder="" - type="email" - autoFocus - /> -
+ + + setUrl(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') embedMedia(); + }} + placeholder="" + type="email" + autoFocus + />
From 3eea07fe8b429ee49410c111e7551e88d0ea7b33 Mon Sep 17 00:00:00 2001 From: Felix Feng Date: Sun, 17 Nov 2024 16:55:54 +0800 Subject: [PATCH 3/3] uploadthing --- .../plate-playground-template/.env.example | 2 + templates/plate-playground-template/README.md | 1 + .../plate-playground-template/package.json | 2 + .../plate-playground-template/pnpm-lock.yaml | 124 ++++++++++++++++++ .../src/app/api/uploadthing/core.ts | 26 ++++ .../src/app/api/uploadthing/route.ts | 11 ++ .../src/components/plate-ui/image-element.tsx | 16 ++- .../plate-ui/media-placeholder-element.tsx | 2 +- .../src/lib/uploadthing/index.ts | 2 + .../src/lib/uploadthing/uploadthing.ts | 6 + .../src/lib/uploadthing/use-upload-file.ts | 61 +++++---- 11 files changed, 219 insertions(+), 34 deletions(-) create mode 100644 templates/plate-playground-template/.env.example create mode 100644 templates/plate-playground-template/src/app/api/uploadthing/core.ts create mode 100644 templates/plate-playground-template/src/app/api/uploadthing/route.ts create mode 100644 templates/plate-playground-template/src/lib/uploadthing/uploadthing.ts diff --git a/templates/plate-playground-template/.env.example b/templates/plate-playground-template/.env.example new file mode 100644 index 0000000000..372a447bcc --- /dev/null +++ b/templates/plate-playground-template/.env.example @@ -0,0 +1,2 @@ +OPENAI_API_KEY= +UPLOADTHING_TOKEN= \ No newline at end of file diff --git a/templates/plate-playground-template/README.md b/templates/plate-playground-template/README.md index da3bec43a9..508e72eb4e 100644 --- a/templates/plate-playground-template/README.md +++ b/templates/plate-playground-template/README.md @@ -41,6 +41,7 @@ cp .env.example .env.local Configure `.env.local`: - `OPENAI_API_KEY` – OpenAI API key ([get one here](https://platform.openai.com/account/api-keys)) +- `UPLOADTHING_TOKEN` – UploadThing API key ([get one here](https://uploadthing.com/dashboard)) You can also using your own backend Start the development server: diff --git a/templates/plate-playground-template/package.json b/templates/plate-playground-template/package.json index 564a6538be..f8012fee3b 100644 --- a/templates/plate-playground-template/package.json +++ b/templates/plate-playground-template/package.json @@ -73,6 +73,8 @@ "@udecode/plate-table": "^40.0.0", "@udecode/plate-toggle": "^40.0.0", "@udecode/plate-trailing-block": "^40.0.0", + "@uploadthing/react": "7.1.0", + "uploadthing": "7.2.0", "zod": "^3.23.8", "react-player": "^2.16.0", "sonner": "^1.5.0", diff --git a/templates/plate-playground-template/pnpm-lock.yaml b/templates/plate-playground-template/pnpm-lock.yaml index 2ec56219cb..03e34bca64 100644 --- a/templates/plate-playground-template/pnpm-lock.yaml +++ b/templates/plate-playground-template/pnpm-lock.yaml @@ -188,6 +188,9 @@ importers: '@udecode/plate-trailing-block': specifier: ^40.0.0 version: 40.0.0(@udecode/plate-common@40.0.3(@types/react@18.3.12)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate-history@0.110.3(slate@0.110.2))(slate-hyperscript@0.100.0(slate@0.110.2))(slate-react@0.111.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.111.0(slate@0.110.2))(slate@0.110.2))(slate@0.110.2) + '@uploadthing/react': + specifier: 7.1.0 + version: 7.1.0(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(uploadthing@7.2.0(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.14)) ai: specifier: ^3.4.33 version: 3.4.33(react@18.3.1)(sswr@2.1.0(svelte@5.2.0))(svelte@5.2.0)(vue@3.5.12(typescript@5.6.3))(zod@3.23.8) @@ -266,6 +269,9 @@ importers: tailwindcss-animate: specifier: 1.0.7 version: 1.0.7(tailwindcss@3.4.14) + uploadthing: + specifier: 7.2.0 + version: 7.2.0(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.14) use-file-picker: specifier: ^2.1.2 version: 2.1.2(react@18.3.1) @@ -444,6 +450,11 @@ packages: resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} engines: {node: '>=6.9.0'} + '@effect/platform@0.69.8': + resolution: {integrity: sha512-zhBhg0c1MHMMo+grOc/6wC2/3UETLroruwrYNZ89uDtXl6EOcP5alFP+vW3NToKDA2o0hRh22KNqq4aixA7xXg==} + peerDependencies: + effect: ^3.10.3 + '@emnapi/runtime@1.3.1': resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} @@ -2078,6 +2089,22 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@uploadthing/mime-types@0.3.1': + resolution: {integrity: sha512-CaEadjn33CzPSLRaU8uL8IRv8MpW9xU5Rg/R45T5In8608dzovDDk0uQ9jzmmLYU5hHt+4v2qugcG/jirm/KEA==} + + '@uploadthing/react@7.1.0': + resolution: {integrity: sha512-xySIeTkX0/nYoBA+zC4ze7ickq6TB9LB6793J00iK5ELnfIILjUtR2Uyx17dMyxMkP9lmw4wIjLCwjW4dU1GIw==} + peerDependencies: + next: '*' + react: ^17.0.2 || ^18.0.0 + uploadthing: 7.2.0 + peerDependenciesMeta: + next: + optional: true + + '@uploadthing/shared@7.1.0': + resolution: {integrity: sha512-6cdS2hq9jUJFU/tqRKHs5XsDIwc6HdaVI4ka0vRy+IwjPnQBR0iXHwqyCtNNssCCAq4zQxrZr3iNEDPNqc0dqw==} + '@vue/compiler-core@3.5.12': resolution: {integrity: sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==} @@ -2518,6 +2545,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + effect@3.10.3: + resolution: {integrity: sha512-+Z5bUhzTeqYlfoPsfXMZG1pYadqLBKARD3xwMIoEAESsOhKFOrUsHHNCy2ZZW3/6oa4wokgT01k1zavA4BAQ4w==} + electron-to-chromium@1.5.25: resolution: {integrity: sha512-kMb204zvK3PsSlgvvwzI3wBIcAw15tRkYk+NQdsjdDtcQWTp2RABbMQ9rUBy8KNEOM+/E6ep+XC3AykiWZld4g==} @@ -2756,6 +2786,10 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-check@3.23.1: + resolution: {integrity: sha512-u/MudsoQEgBUZgR5N1v87vEgybeVYus9VnDVaIkxkkGP2jt54naghQ3PCQHJiogS8U/GavZCUPFfx3Xkp+NaHw==} + engines: {node: '>=8.0.0'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2784,10 +2818,17 @@ packages: resolution: {integrity: sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==} engines: {node: '>= 10'} + file-selector@0.6.0: + resolution: {integrity: sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==} + engines: {node: '>= 12'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-my-way-ts@0.1.5: + resolution: {integrity: sha512-4GOTMrpGQVzsCH2ruUn2vmwzV/02zF4q+ybhCIrw/Rkt3L8KWcycdC6aJMctJzwN4fXD4SD5F/4B9Sksh5rE0A==} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -3392,6 +3433,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + multipasta@0.2.5: + resolution: {integrity: sha512-c8eMDb1WwZcE02WVjHoOmUVk7fnKU/RmUcosHACglrWAuPQsEJv+E8430sXj6jNc1jHw0zrS16aCjQh4BcEb4A==} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -3641,6 +3685,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -3925,6 +3972,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + sqids@0.3.0: + resolution: {integrity: sha512-lOQK1ucVg+W6n3FhRwwSeUijxe93b51Bfz5PMRMihVf1iVkl82ePQG7V5vwrhzB11v0NtsR25PSZRGiSomJaJw==} + sswr@2.1.0: resolution: {integrity: sha512-Cqc355SYlTAaUt8iDPaC/4DPPXK925PePLMxyBKuWd5kKc5mwsG3nT9+Mq2tyguL5s7b4Jg+IRMpTRsNTAfpSQ==} peerDependencies: @@ -4159,6 +4209,27 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + uploadthing@7.2.0: + resolution: {integrity: sha512-x7UAumRF/o+zAkHDP8Re7Qzi3pQF44BZkpsDdubjOE5lNcLw5RQD8WzUPwXKR0hsWEZcR4uoB8LNEDIHT7lAHw==} + engines: {node: '>=18.13.0'} + peerDependencies: + express: '*' + fastify: '*' + h3: '*' + next: '*' + tailwindcss: '*' + peerDependenciesMeta: + express: + optional: true + fastify: + optional: true + h3: + optional: true + next: + optional: true + tailwindcss: + optional: true + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -4455,6 +4526,12 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 + '@effect/platform@0.69.8(effect@3.10.3)': + dependencies: + effect: 3.10.3 + find-my-way-ts: 0.1.5 + multipasta: 0.2.5 + '@emnapi/runtime@1.3.1': dependencies: tslib: 2.8.1 @@ -6053,6 +6130,23 @@ snapshots: '@ungap/structured-clone@1.2.0': {} + '@uploadthing/mime-types@0.3.1': {} + + '@uploadthing/react@7.1.0(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(uploadthing@7.2.0(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.14))': + dependencies: + '@uploadthing/shared': 7.1.0 + file-selector: 0.6.0 + react: 18.3.1 + uploadthing: 7.2.0(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.14) + optionalDependencies: + next: 15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + '@uploadthing/shared@7.1.0': + dependencies: + '@uploadthing/mime-types': 0.3.1 + effect: 3.10.3 + sqids: 0.3.0 + '@vue/compiler-core@3.5.12': dependencies: '@babel/parser': 7.26.2 @@ -6542,6 +6636,10 @@ snapshots: eastasianwidth@0.2.0: {} + effect@3.10.3: + dependencies: + fast-check: 3.23.1 + electron-to-chromium@1.5.25: {} emoji-regex@8.0.0: {} @@ -6930,6 +7028,10 @@ snapshots: extend@3.0.2: {} + fast-check@3.23.1: + dependencies: + pure-rand: 6.1.0 + fast-deep-equal@3.1.3: {} fast-glob@3.3.1: @@ -6964,10 +7066,16 @@ snapshots: dependencies: tslib: 2.8.1 + file-selector@0.6.0: + dependencies: + tslib: 2.8.1 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 + find-my-way-ts@0.1.5: {} + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -7725,6 +7833,8 @@ snapshots: ms@2.1.3: {} + multipasta@0.2.5: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -7949,6 +8059,8 @@ snapshots: punycode@2.3.1: {} + pure-rand@6.1.0: {} + queue-microtask@1.2.3: {} raf@3.4.1: @@ -8303,6 +8415,8 @@ snapshots: source-map-js@1.2.1: {} + sqids@0.3.0: {} + sswr@2.1.0(svelte@5.2.0): dependencies: svelte: 5.2.0 @@ -8604,6 +8718,16 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.0 + uploadthing@7.2.0(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.14): + dependencies: + '@effect/platform': 0.69.8(effect@3.10.3) + '@uploadthing/mime-types': 0.3.1 + '@uploadthing/shared': 7.1.0 + effect: 3.10.3 + optionalDependencies: + next: 15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tailwindcss: 3.4.14 + uri-js@4.4.1: dependencies: punycode: 2.3.1 diff --git a/templates/plate-playground-template/src/app/api/uploadthing/core.ts b/templates/plate-playground-template/src/app/api/uploadthing/core.ts new file mode 100644 index 0000000000..3233c7cb10 --- /dev/null +++ b/templates/plate-playground-template/src/app/api/uploadthing/core.ts @@ -0,0 +1,26 @@ +import type { FileRouter } from 'uploadthing/next'; + +import { createUploadthing } from 'uploadthing/next'; + +const f = createUploadthing(); + +// FileRouter for your app, can contain multiple FileRoutes +export const ourFileRouter = { + // Define as many FileRoutes as you like, each with a unique routeSlug + imageUploader: f(['image', 'text', 'blob', 'pdf', 'video', 'audio']) + // Set permissions and file types for this FileRoute + .middleware(async ({ req }) => { + // This code runs on your server before upload + + // Whatever is returned here is accessible in onUploadComplete as `metadata` + return {}; + }) + .onUploadComplete(({ file, metadata }) => { + // This code RUNS ON YOUR SERVER after upload + + // !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback + return { file }; + }), +} satisfies FileRouter; + +export type OurFileRouter = typeof ourFileRouter; diff --git a/templates/plate-playground-template/src/app/api/uploadthing/route.ts b/templates/plate-playground-template/src/app/api/uploadthing/route.ts new file mode 100644 index 0000000000..379d038d96 --- /dev/null +++ b/templates/plate-playground-template/src/app/api/uploadthing/route.ts @@ -0,0 +1,11 @@ +import { createRouteHandler } from 'uploadthing/next'; + +import { ourFileRouter } from './core'; + +// Export routes for Next App Router +export const { GET, POST } = createRouteHandler({ + router: ourFileRouter, + + // Apply an (optional) custom config: + // config: { ... }, +}); diff --git a/templates/plate-playground-template/src/components/plate-ui/image-element.tsx b/templates/plate-playground-template/src/components/plate-ui/image-element.tsx index d5915ce915..34e3f8930d 100644 --- a/templates/plate-playground-template/src/components/plate-ui/image-element.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/image-element.tsx @@ -3,7 +3,8 @@ import React from 'react'; import { cn, withRef } from '@udecode/cn'; -import { withHOC } from '@udecode/plate-common/react'; +import { useEditorRef, withHOC } from '@udecode/plate-common/react'; +import { useDraggable, useDraggableState } from '@udecode/plate-dnd'; import { Image, ImagePlugin, useMediaState } from '@udecode/plate-media/react'; import { ResizableProvider, useResizableStore } from '@udecode/plate-resizable'; @@ -20,10 +21,19 @@ export const ImageElement = withHOC( ResizableProvider, withRef( ({ children, className, nodeProps, ...props }, ref) => { + const editor = useEditorRef(); + const { align = 'center', focused, readOnly, selected } = useMediaState(); const width = useResizableStore().get.width(); + const state = editor.plugins.dnd + ? useDraggableState({ element: props.element }) + : ({} as any); + + const { isDragging } = state; + const { handleRef } = useDraggable(state); + return ( (); diff --git a/templates/plate-playground-template/src/lib/uploadthing/use-upload-file.ts b/templates/plate-playground-template/src/lib/uploadthing/use-upload-file.ts index 1c708ec341..d4d62b6aef 100644 --- a/templates/plate-playground-template/src/lib/uploadthing/use-upload-file.ts +++ b/templates/plate-playground-template/src/lib/uploadthing/use-upload-file.ts @@ -1,19 +1,31 @@ import * as React from 'react'; +import type { OurFileRouter } from '@/app/api/uploadthing/core'; +import type { + ClientUploadedFileData, + UploadFilesOptions, +} from 'uploadthing/types'; + import { toast } from 'sonner'; import { getErrorMessage } from './handle-error'; +import { uploadFiles } from './uploadthing'; + +export interface UploadedFile extends ClientUploadedFileData {} -export interface UploadedFile { - key: string; - appUrl: string; - name: string; - size: number; - type: string; - url: string; +interface UseUploadFileProps + extends Pick< + UploadFilesOptions, + 'headers' | 'onUploadBegin' | 'onUploadProgress' | 'skipPolling' + > { + onUploadComplete?: (file: UploadedFile) => void; + onUploadError?: (error: unknown) => void; } -export function useUploadFile() { +export function useUploadFile( + endpoint: keyof OurFileRouter, + { onUploadComplete, onUploadError, ...props }: UseUploadFileProps = {} +) { const [uploadedFile, setUploadedFile] = React.useState(); const [uploadingFile, setUploadingFile] = React.useState(); const [progress, setProgress] = React.useState(0); @@ -24,33 +36,19 @@ export function useUploadFile() { setUploadingFile(file); try { - // Mock upload for unauthenticated users - // toast.info('User not logged in. Mocking upload process.'); - const mockUploadedFile = { - key: 'mock-key-0', - appUrl: `https://mock-app-url.com/${file.name}`, - name: file.name, - size: file.size, - type: file.type, - url: URL.createObjectURL(file), - } as UploadedFile; - - // Simulate upload progress - let progress = 0; - - const simulateProgress = async () => { - while (progress < 100) { - await new Promise((resolve) => setTimeout(resolve, 100)); - progress += 2; + const res = await uploadFiles(endpoint, { + ...props, + files: [file], + onUploadProgress: ({ progress }) => { setProgress(Math.min(progress, 100)); - } - }; + }, + }); - await simulateProgress(); + setUploadedFile(res[0]); - setUploadedFile(mockUploadedFile); + onUploadComplete?.(res[0]); - return mockUploadedFile; + return uploadedFile; } catch (error) { const errorMessage = getErrorMessage(error); @@ -60,6 +58,7 @@ export function useUploadFile() { : 'Something went wrong, please try again later.'; toast.error(message); + onUploadError?.(error); } finally { setProgress(0); setIsUploading(false);