From 21bdb2293334ca459f193860695cb62b8d119048 Mon Sep 17 00:00:00 2001 From: zbeyens Date: Tue, 5 Nov 2024 12:48:52 +0100 Subject: [PATCH 1/3] fix --- apps/www/content/docs/autoformat.mdx | 2 +- apps/www/public/r/index.json | 29 +++++++++++++++++ .../r/styles/default/todo-list-element.json | 31 +++++++++++++++++++ apps/www/src/__registry__/index.tsx | 12 +++++++ .../(app)/_components/installation-tab.tsx | 2 +- apps/www/src/app/(app)/editors/page.tsx | 2 +- apps/www/src/app/(app)/page.tsx | 2 +- apps/www/src/components/block-toolbar.tsx | 2 +- .../components/playground-preview-toolbar.tsx | 20 +++++++++--- .../www/src/components/playground-preview.tsx | 1 + apps/www/src/registry/registry-ui.ts | 12 +++++++ 11 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 apps/www/public/r/styles/default/todo-list-element.json diff --git a/apps/www/content/docs/autoformat.mdx b/apps/www/content/docs/autoformat.mdx index 299456a16e..7aacbb6529 100644 --- a/apps/www/content/docs/autoformat.mdx +++ b/apps/www/content/docs/autoformat.mdx @@ -58,7 +58,7 @@ const plugins = [ ### `autoformatRules` - + ### `autoformatBlocks` diff --git a/apps/www/public/r/index.json b/apps/www/public/r/index.json index 3cf23d44a1..85bfc0805c 100644 --- a/apps/www/public/r/index.json +++ b/apps/www/public/r/index.json @@ -890,6 +890,35 @@ "registryDependencies": [], "type": "registry:ui" }, + { + "dependencies": [ + "@udecode/plate-list" + ], + "doc": { + "description": "A checkbox list element with interactive todo items.", + "docs": [ + { + "route": "/docs/list", + "title": "List" + } + ], + "examples": [ + "list-demo" + ] + }, + "files": [ + { + "path": "plate-ui/todo-list-element.tsx", + "type": "registry:ui" + } + ], + "name": "todo-list-element", + "registryDependencies": [ + "checkbox", + "plate-element" + ], + "type": "registry:ui" + }, { "dependencies": [ "@udecode/plate-toggle" diff --git a/apps/www/public/r/styles/default/todo-list-element.json b/apps/www/public/r/styles/default/todo-list-element.json new file mode 100644 index 0000000000..18ad94f952 --- /dev/null +++ b/apps/www/public/r/styles/default/todo-list-element.json @@ -0,0 +1,31 @@ +{ + "dependencies": [ + "@udecode/plate-list" + ], + "doc": { + "description": "A checkbox list element with interactive todo items.", + "docs": [ + { + "route": "/docs/list", + "title": "List" + } + ], + "examples": [ + "list-demo" + ] + }, + "files": [ + { + "content": "'use client';\n\nimport React from 'react';\n\nimport { cn, withRef } from '@udecode/cn';\nimport {\n useTodoListElement,\n useTodoListElementState,\n} from '@udecode/plate-list/react';\n\nimport { Checkbox } from './checkbox';\nimport { PlateElement } from './plate-element';\n\nexport const TodoListElement = withRef(\n ({ children, className, ...props }, ref) => {\n const { element } = props;\n const state = useTodoListElementState({ element });\n const { checkboxProps } = useTodoListElement(state);\n\n return (\n \n \n \n \n \n {children}\n \n \n );\n }\n);\n", + "path": "plate-ui/todo-list-element.tsx", + "target": "components/plate-ui/todo-list-element.tsx", + "type": "registry:ui" + } + ], + "name": "todo-list-element", + "registryDependencies": [ + "checkbox", + "plate-element" + ], + "type": "registry:ui" +} \ No newline at end of file diff --git a/apps/www/src/__registry__/index.tsx b/apps/www/src/__registry__/index.tsx index b178971b6f..917887f4d3 100644 --- a/apps/www/src/__registry__/index.tsx +++ b/apps/www/src/__registry__/index.tsx @@ -365,6 +365,18 @@ export const Index: Record = { subcategory: "", chunks: [] }, + "todo-list-element": { + name: "todo-list-element", + description: "", + type: "registry:ui", + registryDependencies: ["checkbox","plate-element"], + files: ["registry/default/plate-ui/todo-list-element.tsx"], + component: React.lazy(() => import("@/registry/default/plate-ui/todo-list-element.tsx")), + source: "", + category: "", + subcategory: "", + chunks: [] + }, "toggle-element": { name: "toggle-element", description: "", diff --git a/apps/www/src/app/(app)/_components/installation-tab.tsx b/apps/www/src/app/(app)/_components/installation-tab.tsx index 88fde9f14a..40473ae23f 100644 --- a/apps/www/src/app/(app)/_components/installation-tab.tsx +++ b/apps/www/src/app/(app)/_components/installation-tab.tsx @@ -117,7 +117,7 @@ export default function InstallationTab() { const installCommands = useMemo(() => { return { - components: `npx shadcx@latest add ${Array.from( + components: `npx shadcx@latest add plate/${Array.from( components.reduce( (uniqueFilenames, { id, filename, noImport, registry }) => { if (noImport) return uniqueFilenames; diff --git a/apps/www/src/app/(app)/editors/page.tsx b/apps/www/src/app/(app)/editors/page.tsx index ae58c1d3f6..75d5682392 100644 --- a/apps/www/src/app/(app)/editors/page.tsx +++ b/apps/www/src/app/(app)/editors/page.tsx @@ -31,7 +31,7 @@ export default async function BlocksPage() {
-
+
{block.name !== 'potion' && ( <> + +
\n \n \n \n AI Settings\n \n Enter your{' '}\n \n OpenAI API key\n \n {' '}\n to use AI features.\n \n \n
\n
\n setTempKey(e.target.value)}\n placeholder=\"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n data-1p-ignore\n type={showKey ? 'text' : 'password'}\n />\n setShowKey(!showKey)}\n type=\"button\"\n >\n {showKey ? (\n \n ) : (\n \n )}\n \n {showKey ? 'Hide' : 'Show'} API key\n \n \n
\n\n \n \n \n {model.label}\n \n \n \n \n \n \n No model found.\n\n \n \n {models.map((m) => (\n {\n setModel(m);\n setOpenModel(false);\n }}\n >\n \n {m.label}\n \n ))}\n \n \n \n \n \n\n \n
\n

\n Not stored anywhere. Used only for current session requests.\n

\n
\n \n );\n}\n", + "content": "'use client';\n\nimport { type ReactNode, createContext, useContext, useState } from 'react';\n\nimport { faker } from '@faker-js/faker';\nimport { cn } from '@udecode/cn';\nimport { CopilotPlugin } from '@udecode/plate-ai/react';\nimport { useEditorPlugin } from '@udecode/plate-common/react';\nimport { useChat as useBaseChat } from 'ai/react';\nimport {\n ArrowUpRight,\n Check,\n ChevronsUpDown,\n Eye,\n EyeOff,\n Settings,\n} from 'lucide-react';\nimport Link from 'next/link';\n\nimport { Button } from '@/components/plate-ui/button';\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/components/plate-ui/command';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '@/components/plate-ui/dialog';\nimport { Input } from '@/components/plate-ui/input';\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/components/plate-ui/popover';\n\nexport const useChat = () => {\n return useBaseChat({\n id: 'editor',\n api: '/api/ai/command',\n body: {\n apiKey: useOpenAI().apiKey,\n model: useOpenAI().model.value,\n },\n fetch: async (input, init) => {\n const res = await fetch(input, init);\n\n if (!res.ok) {\n // Mock the API response. Remove it when you implement the route /api/ai/command\n await new Promise((resolve) => setTimeout(resolve, 400));\n\n const stream = fakeStreamText();\n\n return new Response(stream, {\n headers: {\n Connection: 'keep-alive',\n 'Content-Type': 'text/plain',\n },\n });\n }\n\n return res;\n },\n });\n};\n\n// Used for testing. Remove it after implementing useChat api.\nconst fakeStreamText = ({\n chunkCount = 10,\n streamProtocol = 'data',\n}: {\n chunkCount?: number;\n streamProtocol?: 'data' | 'text';\n} = {}) => {\n const chunks = Array.from({ length: chunkCount }, () => ({\n delay: faker.number.int({ max: 150, min: 50 }),\n texts: faker.lorem.words({ max: 3, min: 1 }) + ' ',\n }));\n const encoder = new TextEncoder();\n\n return new ReadableStream({\n async start(controller) {\n for (const chunk of chunks) {\n await new Promise((resolve) => setTimeout(resolve, chunk.delay));\n\n if (streamProtocol === 'text') {\n controller.enqueue(encoder.encode(chunk.texts));\n } else {\n controller.enqueue(\n encoder.encode(`0:${JSON.stringify(chunk.texts)}\\n`)\n );\n }\n }\n\n if (streamProtocol === 'data') {\n controller.enqueue(\n `d:{\"finishReason\":\"stop\",\"usage\":{\"promptTokens\":0,\"completionTokens\":${chunks.length}}}\\n`\n );\n }\n\n controller.close();\n },\n });\n};\n\ninterface Model {\n label: string;\n value: string;\n}\n\ninterface OpenAIContextType {\n apiKey: string;\n model: Model;\n setApiKey: (key: string) => void;\n setModel: (model: Model) => void;\n}\n\nexport const models: Model[] = [\n { label: 'gpt-4o-mini', value: 'gpt-4o-mini' },\n { label: 'gpt-4o', value: 'gpt-4o' },\n { label: 'gpt-4-turbo', value: 'gpt-4-turbo' },\n { label: 'gpt-4', value: 'gpt-4' },\n { label: 'gpt-3.5-turbo', value: 'gpt-3.5-turbo' },\n { label: 'gpt-3.5-turbo-instruct', value: 'gpt-3.5-turbo-instruct' },\n];\n\nconst OpenAIContext = createContext(undefined);\n\nexport function OpenAIProvider({ children }: { children: ReactNode }) {\n const [apiKey, setApiKey] = useState('');\n const [model, setModel] = useState(models[0]);\n\n return (\n \n {children}\n \n );\n}\n\nexport function useOpenAI() {\n const context = useContext(OpenAIContext);\n\n return (\n context ??\n ({\n apiKey: '',\n model: models[0],\n setApiKey: () => {},\n setModel: () => {},\n } as OpenAIContextType)\n );\n}\n\nexport function SettingsDialog() {\n const { apiKey, model, setApiKey, setModel } = useOpenAI();\n const [tempKey, setTempKey] = useState(apiKey);\n const [showKey, setShowKey] = useState(false);\n const [open, setOpen] = useState(false);\n const [openModel, setOpenModel] = useState(false);\n\n const { getOptions, setOption } = useEditorPlugin(CopilotPlugin);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setApiKey(tempKey);\n setOpen(false);\n\n const completeOptions = getOptions().completeOptions ?? {};\n\n setOption('completeOptions', {\n ...completeOptions,\n body: {\n ...completeOptions.body,\n apiKey: tempKey,\n model: model.value,\n },\n });\n };\n\n return (\n \n \n \n
\n \n \n Settings\n \n
\n \n
\n \n \n AI Settings\n \n Enter your{' '}\n \n OpenAI API key\n \n {' '}\n to use AI features.\n \n \n
\n
\n setTempKey(e.target.value)}\n placeholder=\"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n data-1p-ignore\n type={showKey ? 'text' : 'password'}\n />\n setShowKey(!showKey)}\n type=\"button\"\n >\n {showKey ? (\n \n ) : (\n \n )}\n \n {showKey ? 'Hide' : 'Show'} API key\n \n \n
\n\n \n \n \n {model.label}\n \n \n \n \n \n \n No model found.\n\n \n \n {models.map((m) => (\n {\n setModel(m);\n setOpenModel(false);\n }}\n >\n \n {m.label}\n \n ))}\n \n \n \n \n \n\n \n
\n

\n Not stored anywhere. Used only for current session requests.\n

\n
\n
\n );\n}\n", "path": "components/editor/use-chat.tsx", "target": "components/editor/use-chat.tsx", "type": "registry:component" diff --git a/apps/www/src/registry/default/components/editor/plugins/copilot-plugins.tsx b/apps/www/src/registry/default/components/editor/plugins/copilot-plugins.tsx index 9cfc27b7f5..5630effe3c 100644 --- a/apps/www/src/registry/default/components/editor/plugins/copilot-plugins.tsx +++ b/apps/www/src/registry/default/components/editor/plugins/copilot-plugins.tsx @@ -28,6 +28,7 @@ export const copilotPlugins = [ - If no context is provided or you can't generate a continuation, return "0" without explanation.`, }, onError: () => { + // Mock the API response. Remove it when you implement the route /api/ai/copilot api.copilot.setBlockSuggestion({ text: stripMarkdown(faker.lorem.sentence()), }); @@ -36,7 +37,6 @@ export const copilotPlugins = [ if (completion === '0') return; api.copilot.setBlockSuggestion({ - //stripMarkdownBlocks in plus GhostText text: stripMarkdown(completion), }); }, diff --git a/apps/www/src/registry/default/components/editor/use-chat.tsx b/apps/www/src/registry/default/components/editor/use-chat.tsx index 45aef100bd..4d019c1f60 100644 --- a/apps/www/src/registry/default/components/editor/use-chat.tsx +++ b/apps/www/src/registry/default/components/editor/use-chat.tsx @@ -50,10 +50,10 @@ export const useChat = () => { model: useOpenAI().model.value, }, fetch: async (input, init) => { - try { - return await fetch(input, init); - } catch (error) { - // Mock the API response. Remove it when you implement the route /api/ai + const res = await fetch(input, init); + + if (!res.ok) { + // Mock the API response. Remove it when you implement the route /api/ai/command await new Promise((resolve) => setTimeout(resolve, 400)); const stream = fakeStreamText(); @@ -65,6 +65,8 @@ export const useChat = () => { }, }); } + + return res; }, }); }; diff --git a/templates/plate-playground-template/pnpm-lock.yaml b/templates/plate-playground-template/pnpm-lock.yaml index 3d27f5d508..4532702c5d 100644 --- a/templates/plate-playground-template/pnpm-lock.yaml +++ b/templates/plate-playground-template/pnpm-lock.yaml @@ -607,8 +607,8 @@ packages: '@excalidraw/excalidraw@0.16.4': resolution: {integrity: sha512-x56YTb5jmHAJ9SP2R81ywU28Y+QlOgjmCYHVMgHKPhh1hwKzimt+Z+iz/Rf2x1JpQOJRYbfeoxiGPQNhnYwGWQ==} peerDependencies: - react: '*' - react-dom: '*' + react: ^17.0.2 || ^18.2.0 + react-dom: ^17.0.2 || ^18.2.0 '@faker-js/faker@9.2.0': resolution: {integrity: sha512-ulqQu4KMr1/sTFIYvqSdegHT8NIkt66tFAkugGnHA+1WAfEn6hMzNR+svjXGFRVLnapxvej67Z/LwchFrnLBUg==} @@ -972,7 +972,7 @@ packages: resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} peerDependencies: '@types/react': '*' - react: '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -1017,8 +1017,8 @@ packages: peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: '*' - 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 @@ -1203,7 +1203,7 @@ packages: resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} peerDependencies: '@types/react': '*' - react: '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -1230,7 +1230,7 @@ packages: resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} peerDependencies: '@types/react': '*' - react: '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true diff --git a/templates/plate-playground-template/src/components/editor/plugins/autoformat-plugin.ts b/templates/plate-playground-template/src/components/editor/plugins/autoformat-plugin.ts index a6fd5ed8d0..54ae5cbec1 100644 --- a/templates/plate-playground-template/src/components/editor/plugins/autoformat-plugin.ts +++ b/templates/plate-playground-template/src/components/editor/plugins/autoformat-plugin.ts @@ -1,3 +1,5 @@ +'use client'; + import type { AutoformatBlockRule, AutoformatRule, diff --git a/templates/plate-playground-template/src/components/editor/plugins/copilot-plugins.tsx b/templates/plate-playground-template/src/components/editor/plugins/copilot-plugins.tsx index 7ea20a55a0..13186ebc1e 100644 --- a/templates/plate-playground-template/src/components/editor/plugins/copilot-plugins.tsx +++ b/templates/plate-playground-template/src/components/editor/plugins/copilot-plugins.tsx @@ -1,3 +1,5 @@ +'use client'; + import type { TElement } from '@udecode/plate-common'; import { faker } from '@faker-js/faker'; diff --git a/templates/plate-playground-template/src/components/editor/use-chat.tsx b/templates/plate-playground-template/src/components/editor/use-chat.tsx index 4a51c451bb..9387373e82 100644 --- a/templates/plate-playground-template/src/components/editor/use-chat.tsx +++ b/templates/plate-playground-template/src/components/editor/use-chat.tsx @@ -145,11 +145,15 @@ export function OpenAIProvider({ children }: { children: ReactNode }) { export function useOpenAI() { const context = useContext(OpenAIContext); - if (context === undefined) { - throw new Error('useOpenAI must be used within an OpenAIProvider'); - } - - return context; + return ( + context ?? + ({ + apiKey: '', + model: models[0], + setApiKey: () => {}, + setModel: () => {}, + } as OpenAIContextType) + ); } export function SettingsDialog() { @@ -189,6 +193,7 @@ export function SettingsDialog() { 'rounded-full shadow-md hover:shadow-lg', 'transition-all duration-300 ease-in-out hover:w-[106px]' )} + data-block-hide >
diff --git a/templates/plate-playground-template/src/components/plate-ui/color-dropdown-menu-items.tsx b/templates/plate-playground-template/src/components/plate-ui/color-dropdown-menu-items.tsx index fb8b107bf1..8714bdd987 100644 --- a/templates/plate-playground-template/src/components/plate-ui/color-dropdown-menu-items.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/color-dropdown-menu-items.tsx @@ -44,9 +44,10 @@ export function ColorDropdownMenuItem({ className={cn( buttonVariants({ isMenu: true, + size: 'icon', variant: 'outline', }), - 'flex size-6 items-center justify-center rounded-full border border-solid border-muted p-0 transition-all hover:scale-125', + 'my-1 flex size-6 items-center justify-center rounded-full border border-solid border-muted p-0 transition-all hover:scale-125', !isBrightColor && 'border-transparent text-white hover:!text-white', className )} @@ -87,7 +88,7 @@ export function ColorDropdownMenuItems({ return (
{ return (
- + - + {color && ( - + Clear 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 48129dd57b..d5915ce915 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 @@ -63,6 +63,9 @@ export const ImageElement = withHOC( { + e.preventDefault(); + }} placeholder="Write a caption..." /> diff --git a/templates/plate-playground-template/src/components/plate-ui/toggle-element.tsx b/templates/plate-playground-template/src/components/plate-ui/toggle-element.tsx index 7619893c8e..42366f3b8c 100644 --- a/templates/plate-playground-template/src/components/plate-ui/toggle-element.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/toggle-element.tsx @@ -6,7 +6,7 @@ import { useToggleButton, useToggleButtonState, } from '@udecode/plate-toggle/react'; -import { ChevronDown, ChevronRight } from 'lucide-react'; +import { ChevronRight } from 'lucide-react'; import { Button } from './button'; import { PlateElement } from './plate-element'; @@ -30,7 +30,12 @@ export const ToggleElement = withRef( contentEditable={false} {...buttonProps} > - {open ? : } + {children} diff --git a/templates/plate-playground-template/src/components/plate-ui/turn-into-dropdown-menu.tsx b/templates/plate-playground-template/src/components/plate-ui/turn-into-dropdown-menu.tsx index 67d0bb7878..510b96d344 100644 --- a/templates/plate-playground-template/src/components/plate-ui/turn-into-dropdown-menu.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/turn-into-dropdown-menu.tsx @@ -29,7 +29,10 @@ import { SquareIcon, } from 'lucide-react'; -import { getBlockType, setBlockType } from '@/components/editor/transforms'; +import { + getBlockType, + setBlockType, +} from '@/components/editor/transforms'; import { DropdownMenu,