Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
zbeyens committed Jan 1, 2025
1 parent cffe886 commit 8576c2c
Show file tree
Hide file tree
Showing 229 changed files with 2,192 additions and 1,827 deletions.
2 changes: 1 addition & 1 deletion apps/www/public/r/styles/default/ai-menu.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"type": "registry:ui"
},
{
"content": "'use client';\n\nimport { useEffect, useMemo } from 'react';\n\nimport { AIChatPlugin, AIPlugin } from '@udecode/plate-ai/react';\nimport {\n type SlateEditor,\n getAncestorNode,\n getEndPoint,\n getNodeString,\n} from '@udecode/plate-common';\nimport {\n type PlateEditor,\n focusEditor,\n useEditorPlugin,\n} from '@udecode/plate-common/react';\nimport { useIsSelecting } from '@udecode/plate-selection/react';\nimport {\n Album,\n BadgeHelp,\n Check,\n CornerUpLeft,\n FeatherIcon,\n ListEnd,\n ListMinus,\n ListPlus,\n PenLine,\n Wand,\n X,\n} from 'lucide-react';\n\nimport { CommandGroup, CommandItem } from './command';\n\nexport type EditorChatState =\n | 'cursorCommand'\n | 'cursorSuggestion'\n | 'selectionCommand'\n | 'selectionSuggestion';\n\nexport const aiChatItems = {\n accept: {\n icon: <Check />,\n label: 'Accept',\n value: 'accept',\n onSelect: ({ editor }) => {\n editor.getTransforms(AIChatPlugin).aiChat.accept();\n editor.focus(getEndPoint(editor, editor.selection!));\n },\n },\n continueWrite: {\n icon: <PenLine />,\n label: 'Continue writing',\n value: 'continueWrite',\n onSelect: ({ editor }) => {\n const ancestorNode = getAncestorNode(editor);\n\n if (!ancestorNode) return;\n\n const isEmpty = getNodeString(ancestorNode[0]).trim().length === 0;\n\n void editor.getApi(AIChatPlugin).aiChat.submit({\n mode: 'insert',\n prompt: isEmpty\n ? `<Document>\n{editor}\n</Document>\nStart writing a new paragraph AFTER <Document> ONLY ONE SENTENCE`\n : 'Continue writing AFTER <Block> ONLY ONE SENTENCE. DONT REPEAT THE TEXT.',\n });\n },\n },\n discard: {\n icon: <X />,\n label: 'Discard',\n shortcut: 'Escape',\n value: 'discard',\n onSelect: ({ editor }) => {\n editor.getTransforms(AIPlugin).ai.undo();\n editor.getApi(AIChatPlugin).aiChat.hide();\n },\n },\n explain: {\n icon: <BadgeHelp />,\n label: 'Explain',\n value: 'explain',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: {\n default: 'Explain {editor}',\n selecting: 'Explain',\n },\n });\n },\n },\n fixSpelling: {\n icon: <Check />,\n label: 'Fix spelling & grammar',\n value: 'fixSpelling',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Fix spelling and grammar',\n });\n },\n },\n improveWriting: {\n icon: <Wand />,\n label: 'Improve writing',\n value: 'improveWriting',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Improve the writing',\n });\n },\n },\n insertBelow: {\n icon: <ListEnd />,\n label: 'Insert below',\n value: 'insertBelow',\n onSelect: ({ aiEditor, editor }) => {\n void editor.getTransforms(AIChatPlugin).aiChat.insertBelow(aiEditor);\n },\n },\n makeLonger: {\n icon: <ListPlus />,\n label: 'Make longer',\n value: 'makeLonger',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Make longer',\n });\n },\n },\n makeShorter: {\n icon: <ListMinus />,\n label: 'Make shorter',\n value: 'makeShorter',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Make shorter',\n });\n },\n },\n replace: {\n icon: <Check />,\n label: 'Replace selection',\n value: 'replace',\n onSelect: ({ aiEditor, editor }) => {\n void editor.getTransforms(AIChatPlugin).aiChat.replaceSelection(aiEditor);\n },\n },\n simplifyLanguage: {\n icon: <FeatherIcon />,\n label: 'Simplify language',\n value: 'simplifyLanguage',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Simplify the language',\n });\n },\n },\n summarize: {\n icon: <Album />,\n label: 'Add a summary',\n value: 'summarize',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n mode: 'insert',\n prompt: {\n default: 'Summarize {editor}',\n selecting: 'Summarize',\n },\n });\n },\n },\n tryAgain: {\n icon: <CornerUpLeft />,\n label: 'Try again',\n value: 'tryAgain',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.reload();\n },\n },\n} satisfies Record<\n string,\n {\n icon: React.ReactNode;\n label: string;\n value: string;\n component?: React.ComponentType<{ menuState: EditorChatState }>;\n filterItems?: boolean;\n items?: { label: string; value: string }[];\n shortcut?: string;\n onSelect?: ({\n aiEditor,\n editor,\n }: {\n aiEditor: SlateEditor;\n editor: PlateEditor;\n }) => void;\n }\n>;\n\nconst menuStateItems: Record<\n EditorChatState,\n {\n items: (typeof aiChatItems)[keyof typeof aiChatItems][];\n heading?: string;\n }[]\n> = {\n cursorCommand: [\n {\n items: [\n aiChatItems.continueWrite,\n aiChatItems.summarize,\n aiChatItems.explain,\n ],\n },\n ],\n cursorSuggestion: [\n {\n items: [aiChatItems.accept, aiChatItems.discard, aiChatItems.tryAgain],\n },\n ],\n selectionCommand: [\n {\n items: [\n aiChatItems.improveWriting,\n aiChatItems.makeLonger,\n aiChatItems.makeShorter,\n aiChatItems.fixSpelling,\n aiChatItems.simplifyLanguage,\n ],\n },\n ],\n selectionSuggestion: [\n {\n items: [\n aiChatItems.replace,\n aiChatItems.insertBelow,\n aiChatItems.discard,\n aiChatItems.tryAgain,\n ],\n },\n ],\n};\n\nexport const AIMenuItems = ({\n aiEditorRef,\n setValue,\n}: {\n aiEditorRef: React.MutableRefObject<SlateEditor | null>;\n setValue: (value: string) => void;\n}) => {\n const { editor, useOption } = useEditorPlugin(AIChatPlugin);\n const { messages } = useOption('chat');\n const isSelecting = useIsSelecting();\n\n const menuState = useMemo(() => {\n if (messages && messages.length > 0) {\n return isSelecting ? 'selectionSuggestion' : 'cursorSuggestion';\n }\n\n return isSelecting ? 'selectionCommand' : 'cursorCommand';\n }, [isSelecting, messages]);\n\n const menuGroups = useMemo(() => {\n const items = menuStateItems[menuState];\n\n return items;\n }, [menuState]);\n\n useEffect(() => {\n if (menuGroups.length > 0 && menuGroups[0].items.length > 0) {\n setValue(menuGroups[0].items[0].value);\n }\n }, [menuGroups, setValue]);\n\n return (\n <>\n {menuGroups.map((group, index) => (\n <CommandGroup key={index} heading={group.heading}>\n {group.items.map((menuItem) => (\n <CommandItem\n key={menuItem.value}\n className=\"[&_svg]:text-muted-foreground\"\n value={menuItem.value}\n onSelect={() => {\n menuItem.onSelect?.({\n aiEditor: aiEditorRef.current!,\n editor: editor,\n });\n }}\n >\n {menuItem.icon}\n <span>{menuItem.label}</span>\n </CommandItem>\n ))}\n </CommandGroup>\n ))}\n </>\n );\n};\n",
"content": "'use client';\n\nimport { useEffect, useMemo } from 'react';\n\nimport { AIChatPlugin, AIPlugin } from '@udecode/plate-ai/react';\nimport {\n type SlateEditor,\n getAncestorNode,\n getEndPoint,\n getNodeString,\n} from '@udecode/plate-common';\nimport {\n type PlateEditor,\n focusEditor,\n useEditorPlugin,\n} from '@udecode/plate-common/react';\nimport { useIsSelecting } from '@udecode/plate-selection/react';\nimport {\n Album,\n BadgeHelp,\n Check,\n CornerUpLeft,\n FeatherIcon,\n ListEnd,\n ListMinus,\n ListPlus,\n PenLine,\n Wand,\n X,\n} from 'lucide-react';\n\nimport { CommandGroup, CommandItem } from './command';\n\nexport type EditorChatState =\n | 'cursorCommand'\n | 'cursorSuggestion'\n | 'selectionCommand'\n | 'selectionSuggestion';\n\nexport const aiChatItems = {\n accept: {\n icon: <Check />,\n label: 'Accept',\n value: 'accept',\n onSelect: ({ editor }) => {\n editor.getTransforms(AIChatPlugin).aiChat.accept();\n editor.tf.focus(getEndPoint(editor, editor.selection!));\n },\n },\n continueWrite: {\n icon: <PenLine />,\n label: 'Continue writing',\n value: 'continueWrite',\n onSelect: ({ editor }) => {\n const ancestorNode = getAncestorNode(editor);\n\n if (!ancestorNode) return;\n\n const isEmpty = getNodeString(ancestorNode[0]).trim().length === 0;\n\n void editor.getApi(AIChatPlugin).aiChat.submit({\n mode: 'insert',\n prompt: isEmpty\n ? `<Document>\n{editor}\n</Document>\nStart writing a new paragraph AFTER <Document> ONLY ONE SENTENCE`\n : 'Continue writing AFTER <Block> ONLY ONE SENTENCE. DONT REPEAT THE TEXT.',\n });\n },\n },\n discard: {\n icon: <X />,\n label: 'Discard',\n shortcut: 'Escape',\n value: 'discard',\n onSelect: ({ editor }) => {\n editor.getTransforms(AIPlugin).ai.undo();\n editor.getApi(AIChatPlugin).aiChat.hide();\n },\n },\n explain: {\n icon: <BadgeHelp />,\n label: 'Explain',\n value: 'explain',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: {\n default: 'Explain {editor}',\n selecting: 'Explain',\n },\n });\n },\n },\n fixSpelling: {\n icon: <Check />,\n label: 'Fix spelling & grammar',\n value: 'fixSpelling',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Fix spelling and grammar',\n });\n },\n },\n improveWriting: {\n icon: <Wand />,\n label: 'Improve writing',\n value: 'improveWriting',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Improve the writing',\n });\n },\n },\n insertBelow: {\n icon: <ListEnd />,\n label: 'Insert below',\n value: 'insertBelow',\n onSelect: ({ aiEditor, editor }) => {\n void editor.getTransforms(AIChatPlugin).aiChat.insertBelow(aiEditor);\n },\n },\n makeLonger: {\n icon: <ListPlus />,\n label: 'Make longer',\n value: 'makeLonger',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Make longer',\n });\n },\n },\n makeShorter: {\n icon: <ListMinus />,\n label: 'Make shorter',\n value: 'makeShorter',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Make shorter',\n });\n },\n },\n replace: {\n icon: <Check />,\n label: 'Replace selection',\n value: 'replace',\n onSelect: ({ aiEditor, editor }) => {\n void editor.getTransforms(AIChatPlugin).aiChat.replaceSelection(aiEditor);\n },\n },\n simplifyLanguage: {\n icon: <FeatherIcon />,\n label: 'Simplify language',\n value: 'simplifyLanguage',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n prompt: 'Simplify the language',\n });\n },\n },\n summarize: {\n icon: <Album />,\n label: 'Add a summary',\n value: 'summarize',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.submit({\n mode: 'insert',\n prompt: {\n default: 'Summarize {editor}',\n selecting: 'Summarize',\n },\n });\n },\n },\n tryAgain: {\n icon: <CornerUpLeft />,\n label: 'Try again',\n value: 'tryAgain',\n onSelect: ({ editor }) => {\n void editor.getApi(AIChatPlugin).aiChat.reload();\n },\n },\n} satisfies Record<\n string,\n {\n icon: React.ReactNode;\n label: string;\n value: string;\n component?: React.ComponentType<{ menuState: EditorChatState }>;\n filterItems?: boolean;\n items?: { label: string; value: string }[];\n shortcut?: string;\n onSelect?: ({\n aiEditor,\n editor,\n }: {\n aiEditor: SlateEditor;\n editor: PlateEditor;\n }) => void;\n }\n>;\n\nconst menuStateItems: Record<\n EditorChatState,\n {\n items: (typeof aiChatItems)[keyof typeof aiChatItems][];\n heading?: string;\n }[]\n> = {\n cursorCommand: [\n {\n items: [\n aiChatItems.continueWrite,\n aiChatItems.summarize,\n aiChatItems.explain,\n ],\n },\n ],\n cursorSuggestion: [\n {\n items: [aiChatItems.accept, aiChatItems.discard, aiChatItems.tryAgain],\n },\n ],\n selectionCommand: [\n {\n items: [\n aiChatItems.improveWriting,\n aiChatItems.makeLonger,\n aiChatItems.makeShorter,\n aiChatItems.fixSpelling,\n aiChatItems.simplifyLanguage,\n ],\n },\n ],\n selectionSuggestion: [\n {\n items: [\n aiChatItems.replace,\n aiChatItems.insertBelow,\n aiChatItems.discard,\n aiChatItems.tryAgain,\n ],\n },\n ],\n};\n\nexport const AIMenuItems = ({\n aiEditorRef,\n setValue,\n}: {\n aiEditorRef: React.MutableRefObject<SlateEditor | null>;\n setValue: (value: string) => void;\n}) => {\n const { editor, useOption } = useEditorPlugin(AIChatPlugin);\n const { messages } = useOption('chat');\n const isSelecting = useIsSelecting();\n\n const menuState = useMemo(() => {\n if (messages && messages.length > 0) {\n return isSelecting ? 'selectionSuggestion' : 'cursorSuggestion';\n }\n\n return isSelecting ? 'selectionCommand' : 'cursorCommand';\n }, [isSelecting, messages]);\n\n const menuGroups = useMemo(() => {\n const items = menuStateItems[menuState];\n\n return items;\n }, [menuState]);\n\n useEffect(() => {\n if (menuGroups.length > 0 && menuGroups[0].items.length > 0) {\n setValue(menuGroups[0].items[0].value);\n }\n }, [menuGroups, setValue]);\n\n return (\n <>\n {menuGroups.map((group, index) => (\n <CommandGroup key={index} heading={group.heading}>\n {group.items.map((menuItem) => (\n <CommandItem\n key={menuItem.value}\n className=\"[&_svg]:text-muted-foreground\"\n value={menuItem.value}\n onSelect={() => {\n menuItem.onSelect?.({\n aiEditor: aiEditorRef.current!,\n editor: editor,\n });\n }}\n >\n {menuItem.icon}\n <span>{menuItem.label}</span>\n </CommandItem>\n ))}\n </CommandGroup>\n ))}\n </>\n );\n};\n",
"path": "plate-ui/ai-menu-items.tsx",
"target": "components/plate-ui/ai-menu-items.tsx",
"type": "registry:ui"
Expand Down
Loading

0 comments on commit 8576c2c

Please sign in to comment.