import React from 'react' import learnhouseAI_icon from "public/learnhouse_ai_simple.png"; import { motion, AnimatePresence } from 'framer-motion'; import Image from 'next/image'; import { BetweenHorizontalStart, FastForward, Feather, FileStack, HelpCircle, Languages, MessageCircle, MoreVertical, Pen, X } from 'lucide-react'; import { Editor } from '@tiptap/react'; import { AIChatBotStateTypes, useAIChatBot, useAIChatBotDispatch } from '@components/Contexts/AI/AIChatBotContext'; import { AIEditorStateTypes, useAIEditor, useAIEditorDispatch } from '@components/Contexts/AI/AIEditorContext'; import { sendActivityAIChatMessage, startActivityAIChatSession } from '@services/ai/ai'; type AIEditorToolkitProps = { editor: Editor, activity: any } type AIPromptsLabels = { label: 'Writer' | 'ContinueWriting' | 'MakeLonger' | 'GenerateQuiz' | 'Translate', selection: string } function AIEditorToolkit(props: AIEditorToolkitProps) { const dispatchAIEditor = useAIEditorDispatch() as any; const aiEditorState = useAIEditor() as AIEditorStateTypes; return ( {aiEditorState.isModalOpen && <> {aiEditorState.isFeedbackModalOpen && }
AI Editor
Promise.all([dispatchAIEditor({ type: 'setIsModalClose' }), dispatchAIEditor({ type: 'setIsFeedbackModalClose' })])} size={20} className='text-white/50 hover:cursor-pointer bg-white/10 p-1 rounded-full items-center' />
}
) } const UserFeedbackModal = (props: AIEditorToolkitProps) => { const dispatchAIEditor = useAIEditorDispatch() as any; const aiEditorState = useAIEditor() as AIEditorStateTypes; const handleChange = async (event: React.ChangeEvent) => { await dispatchAIEditor({ type: 'setChatInputValue', payload: event.currentTarget.value }); } const sendReqWithMessage = async (message: string) => { if (aiEditorState.aichat_uuid) { await dispatchAIEditor({ type: 'addMessage', payload: { sender: 'user', message: message, type: 'user' } }); await dispatchAIEditor({ type: 'setIsWaitingForResponse' }); const response = await sendActivityAIChatMessage(message, aiEditorState.aichat_uuid, props.activity.activity_uuid) await dispatchAIEditor({ type: 'setIsNoLongerWaitingForResponse' }); await dispatchAIEditor({ type: 'setChatInputValue', payload: '' }); await dispatchAIEditor({ type: 'addMessage', payload: { sender: 'ai', message: response.message, type: 'ai' } }); return response.message; } else { await dispatchAIEditor({ type: 'addMessage', payload: { sender: 'user', message: message, type: 'user' } }); await dispatchAIEditor({ type: 'setIsWaitingForResponse' }); const response = await startActivityAIChatSession(message, props.activity.activity_uuid) await dispatchAIEditor({ type: 'setAichat_uuid', payload: response.aichat_uuid }); await dispatchAIEditor({ type: 'setIsNoLongerWaitingForResponse' }); await dispatchAIEditor({ type: 'setChatInputValue', payload: '' }); await dispatchAIEditor({ type: 'addMessage', payload: { sender: 'ai', message: response.message, type: 'ai' } }); return response.message; } } const handleKeyPress = async (event: React.KeyboardEvent) => { if (event.key === 'Enter') { await handleOperation(aiEditorState.selectedTool, aiEditorState.chatInputValue); } } const handleOperation = async (label: 'Writer' | 'ContinueWriting' | 'MakeLonger' | 'GenerateQuiz' | 'Translate', message: string) => { // Set selected tool await dispatchAIEditor({ type: 'setSelectedTool', payload: label }); // Check what operation that was if (label === 'Writer') { let ai_message = ''; let prompt = getPrompt({ label: label, selection: message }); if (prompt) { ai_message = await sendReqWithMessage(prompt); await fillEditorWithText(ai_message); } } else if (label === 'ContinueWriting') { let ai_message = ''; let text_selection = getTipTapEditorSelectedText(); let prompt = getPrompt({ label: label, selection: text_selection }); if (prompt) { ai_message = await sendReqWithMessage(prompt); const message_without_original_text = await removeSentences(text_selection, ai_message); await fillEditorWithText(message_without_original_text); } } else if (label === 'MakeLonger') { // Send message to AI // Wait for response // Add response to editor // Close modal } else if (label === 'GenerateQuiz') { // Send message to AI // Wait for response // Add response to editor // Close modal } else if (label === 'Translate') { // Send message to AI // Wait for response // Add response to editor // Close modal } } const removeSentences = async (textToRemove: string, originalText: string) => { const phrase = textToRemove.toLowerCase(); const original = originalText.toLowerCase(); if (original.includes(phrase)) { const regex = new RegExp(phrase, 'g'); const newText = original.replace(regex, ''); return newText; } else { return originalText; } } async function fillEditorWithText(text: string) { const words = text.split(' '); for (let i = 0; i < words.length; i++) { const textNode = { type: 'text', text: words[i], }; props.editor.chain().focus().insertContent(textNode).run(); // Add a space after each word except the last one if (i < words.length - 1) { const spaceNode = { type: 'text', text: ' ', }; props.editor.chain().focus().insertContent(spaceNode).run(); } // Wait for 0.3 seconds before adding the next word await new Promise(resolve => setTimeout(resolve, 120)); } } const getPrompt = (args: AIPromptsLabels) => { const { label, selection } = args; if (label === 'Writer') { return `Write 3 sentences about ${selection}`; } else if (label === 'ContinueWriting') { return `Continue writing 3 more sentences based on "${selection}"`; } else if (label === 'MakeLonger') { return `Make longer this paragraph "${selection}"`; } else if (label === 'GenerateQuiz') { return `Generate a quiz about "${selection}", only return an array of objects, every object should respect the following interface: interface Answer { answer_id: string; answer: string; correct: boolean; } interface Question { question_id: string; question: string; type: "multiple_choice" answers: Answer[]; } " `; } else if (label === 'Translate') { return `Translate ${selection} to selected language`; } } const getTipTapEditorSelectedText = () => { const pos = props.editor.state.selection.$from.pos; // get the cursor position const resolvedPos = props.editor.state.doc.resolve(pos); // resolve the position in the document const start = resolvedPos.before(1); // get the start position of the node const end = resolvedPos.after(1); // get the end position of the node const paragraph = props.editor.state.doc.textBetween(start, end, '\n', '\n'); // get the text of the node return paragraph; } return (
{aiEditorState.isUserInputEnabled &&
}
) } const AiEditorToolButton = (props: any) => { const dispatchAIEditor = useAIEditorDispatch() as any; const aiEditorState = useAIEditor() as AIEditorStateTypes; const handleToolButtonClick = async (label: 'Writer' | 'ContinueWriting' | 'MakeLonger' | 'GenerateQuiz' | 'Translate') => { if (label === 'Writer') { await dispatchAIEditor({ type: 'setSelectedTool', payload: label }); await dispatchAIEditor({ type: 'setIsUserInputEnabled', payload: true }); await dispatchAIEditor({ type: 'setIsFeedbackModalOpen' }); } if (label === 'ContinueWriting') { await dispatchAIEditor({ type: 'setSelectedTool', payload: label }); await dispatchAIEditor({ type: 'setIsUserInputEnabled', payload: false }); await dispatchAIEditor({ type: 'setIsFeedbackModalOpen' }); } } return ( ) } const AiEditorActionScreen = ({ handleOperation }: { handleOperation: any }) => { const dispatchAIEditor = useAIEditorDispatch() as any; const aiEditorState = useAIEditor() as AIEditorStateTypes; return (
{aiEditorState.selectedTool === 'Writer' &&
Write about...
} {aiEditorState.selectedTool === 'ContinueWriting' &&
{ handleOperation(aiEditorState.selectedTool, aiEditorState.chatInputValue) }} className='flex cursor-pointer space-x-1.5 p-4 items-center bg-white/10 rounded-md outline outline-1 outline-neutral-200/20 text-2xl font-semibold text-white/70 hover:bg-white/20 hover:outline-neutral-200/40 delay-75 ease-linear transition-all'>
}
) } export default AIEditorToolkit