diff --git a/apps/web/components/Objects/Editor/AI/AIEditorToolkit.tsx b/apps/web/components/Objects/Editor/AI/AIEditorToolkit.tsx
index 46a01045..b4a7824b 100644
--- a/apps/web/components/Objects/Editor/AI/AIEditorToolkit.tsx
+++ b/apps/web/components/Objects/Editor/AI/AIEditorToolkit.tsx
@@ -54,7 +54,7 @@ function AIEditorToolkit(props: AIEditorToolkitProps) {
-
+
@@ -113,42 +113,58 @@ const UserFeedbackModal = (props: AIEditorToolkitProps) => {
if (label === 'Writer') {
let ai_message = '';
let prompt = getPrompt({ label: label, selection: message });
+ await dispatchAIEditor({ type: 'setIsUserInputEnabled', payload: true });
if (prompt) {
+ await dispatchAIEditor({ type: 'setIsUserInputEnabled', payload: false });
+ await dispatchAIEditor({ type: 'setIsWaitingForResponse' });
ai_message = await sendReqWithMessage(prompt);
await fillEditorWithText(ai_message);
+ await dispatchAIEditor({ type: 'setIsNoLongerWaitingForResponse' });
+ await dispatchAIEditor({ type: 'setIsUserInputEnabled', payload: true });
}
} else if (label === 'ContinueWriting') {
+ let ai_message = '';
+ let text_selection = getTipTapEditorSelectedTextGlobal();
+ let prompt = getPrompt({ label: label, selection: text_selection });
+ if (prompt) {
+ await dispatchAIEditor({ type: 'setIsWaitingForResponse' });
+ ai_message = await sendReqWithMessage(prompt);
+ const message_without_original_text = await removeSentences(text_selection, ai_message);
+ await fillEditorWithText(message_without_original_text);
+ await dispatchAIEditor({ type: 'setIsNoLongerWaitingForResponse' });
+ }
+ } else if (label === 'MakeLonger') {
let ai_message = '';
let text_selection = getTipTapEditorSelectedText();
let prompt = getPrompt({ label: label, selection: text_selection });
if (prompt) {
+ await dispatchAIEditor({ type: 'setIsWaitingForResponse' });
ai_message = await sendReqWithMessage(prompt);
- const message_without_original_text = await removeSentences(text_selection, ai_message);
- await fillEditorWithText(message_without_original_text);
+ await replaceSelectedTextWithText(ai_message);
+ await dispatchAIEditor({ type: 'setIsNoLongerWaitingForResponse' });
}
- } 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
+ let ai_message = '';
+ let text_selection = getTipTapEditorSelectedText();
+ let prompt = getPrompt({ label: label, selection: text_selection });
+ if (prompt) {
+ await dispatchAIEditor({ type: 'setIsWaitingForResponse' });
+ ai_message = await sendReqWithMessage(prompt);
+ await replaceSelectedTextWithText(ai_message);
+ await dispatchAIEditor({ type: 'setIsNoLongerWaitingForResponse' });
+ }
}
}
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, '');
@@ -184,6 +200,35 @@ const UserFeedbackModal = (props: AIEditorToolkitProps) => {
}
}
+ async function replaceSelectedTextWithText(text: string) {
+ const words = text.split(' ');
+
+ // Delete the selected text
+ props.editor.chain().focus().deleteSelection().run();
+
+ 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;
@@ -192,7 +237,7 @@ const UserFeedbackModal = (props: AIEditorToolkitProps) => {
} else if (label === 'ContinueWriting') {
return `Continue writing 3 more sentences based on "${selection}"`;
} else if (label === 'MakeLonger') {
- return `Make longer this paragraph "${selection}"`;
+ return `Make longer this text longer : "${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 {
@@ -208,11 +253,12 @@ const UserFeedbackModal = (props: AIEditorToolkitProps) => {
}
" `;
} else if (label === 'Translate') {
- return `Translate ${selection} to selected language`;
+ return `Translate "${selection}" to the ` + aiEditorState.chatInputValue + ` language`;
}
}
- const getTipTapEditorSelectedText = () => {
+ const getTipTapEditorSelectedTextGlobal = () => {
+ // Get the entire node/paragraph that the user is in
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
@@ -221,6 +267,14 @@ const UserFeedbackModal = (props: AIEditorToolkitProps) => {
return paragraph;
}
+ const getTipTapEditorSelectedText = () => {
+ const selection = props.editor.state.selection;
+ const from = selection.from;
+ const to = selection.to;
+ const text = props.editor.state.doc.textBetween(from, to);
+ return text;
+ }
+
return (
{
animate={{ y: 0, opacity: 1, filter: 'blur(0px)' }}
exit={{ y: 50, opacity: 0, filter: 'blur(3px)' }}
transition={{ type: "spring", bounce: 0.35, duration: 1.7, mass: 0.2, velocity: 2 }}
- className='fixed top-0 left-0 w-full h-full z-50 flex justify-center items-center '
+ className='backdrop-blur-md fixed top-0 left-0 w-full h-full z-50 flex justify-center items-center '
style={{ pointerEvents: 'none' }}
>
+ className="backdrop-blur-md z-50 rounded-2xl max-w-screen-2xl my-10 mx-auto w-[500px] h-[200px] fixed bottom-16 left-1/2 transform -translate-x-1/2 shadow-xl ring-1 ring-inset ring-white/10 text-white p-3 flex-col-reverse">
@@ -271,6 +325,21 @@ const AiEditorToolButton = (props: any) => {
await dispatchAIEditor({ type: 'setIsUserInputEnabled', payload: false });
await dispatchAIEditor({ type: 'setIsFeedbackModalOpen' });
}
+ if (label === 'MakeLonger') {
+ await dispatchAIEditor({ type: 'setSelectedTool', payload: label });
+ await dispatchAIEditor({ type: 'setIsUserInputEnabled', payload: false });
+ await dispatchAIEditor({ type: 'setIsFeedbackModalOpen' });
+ }
+ if (label === 'GenerateQuiz') {
+ await dispatchAIEditor({ type: 'setSelectedTool', payload: label });
+ await dispatchAIEditor({ type: 'setIsUserInputEnabled', payload: false });
+ await dispatchAIEditor({ type: 'setIsFeedbackModalOpen' });
+ }
+ if (label === 'Translate') {
+ await dispatchAIEditor({ type: 'setSelectedTool', payload: label });
+ await dispatchAIEditor({ type: 'setIsUserInputEnabled', payload: false });
+ await dispatchAIEditor({ type: 'setIsFeedbackModalOpen' });
+ }
}
return (
@@ -288,18 +357,60 @@ const AiEditorToolButton = (props: any) => {
const AiEditorActionScreen = ({ handleOperation }: { handleOperation: any }) => {
const dispatchAIEditor = useAIEditorDispatch() as any;
const aiEditorState = useAIEditor() as AIEditorStateTypes;
+
+ const handleChange = async (event: React.ChangeEvent
) => {
+ await dispatchAIEditor({ type: 'setChatInputValue', payload: event.currentTarget.value });
+
+ }
+
return (
- {aiEditorState.selectedTool === 'Writer' &&
-
+ {aiEditorState.selectedTool === 'Writer' && !aiEditorState.isWaitingForResponse &&
+
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'>
-
+ {aiEditorState.selectedTool === 'ContinueWriting' && !aiEditorState.isWaitingForResponse &&
+
+
Place your cursor at the end of a sentence to continue writing
+
{
+ handleOperation(aiEditorState.selectedTool, aiEditorState.chatInputValue)
+ }} className='flex cursor-pointer space-x-1.5 p-4 mt-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'>
+
+
+
}
+ {aiEditorState.selectedTool === 'MakeLonger' && !aiEditorState.isWaitingForResponse &&
+
+
Select text to make longer
+
{
+ handleOperation(aiEditorState.selectedTool, aiEditorState.chatInputValue)
+ }} className='flex cursor-pointer space-x-1.5 p-4 mt-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'>
+
+
+
+
}
+ {aiEditorState.selectedTool === 'Translate' && !aiEditorState.isWaitingForResponse &&
+
+
+
Translate selected text to
+
+
+
{
+ handleOperation(aiEditorState.selectedTool, aiEditorState.chatInputValue)
+ }} className='flex cursor-pointer space-x-1.5 p-4 mt-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'>
+
+
+
+
}
+ {aiEditorState.isWaitingForResponse &&
+
+
+
Thinking...
+
}
+
)
}