From 78f7cd968c160cb0a730028a6e8f9346d51d20fc Mon Sep 17 00:00:00 2001 From: Sebastien Flory Date: Fri, 9 Aug 2024 17:31:46 +0200 Subject: [PATCH] Upg: improve builder UX when using a template for instructions (#6722) * Upg: improve builder UX when using a template for instructions * fix lint * Add: comment about re-rendering cost --- .../assistant_builder/AssistantBuilder.tsx | 6 ++ .../assistant_builder/InstructionScreen.tsx | 66 ++++++++++++++++--- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/front/components/assistant_builder/AssistantBuilder.tsx b/front/components/assistant_builder/AssistantBuilder.tsx index 3dbd7e1739f6..75ee4ec2990c 100644 --- a/front/components/assistant_builder/AssistantBuilder.tsx +++ b/front/components/assistant_builder/AssistantBuilder.tsx @@ -377,6 +377,10 @@ export default function AssistantBuilder({ screen, ]); + const [doTypewriterEffect, setDoTypewriterEffect] = useState( + Boolean(template !== null && builderState.instructions) + ); + const modalTitle = agentConfigurationId ? `Edit @${builderState.handle}` : "New Assistant"; @@ -454,6 +458,8 @@ export default function AssistantBuilder({ resetAt={instructionsResetAt} isUsingTemplate={template !== null} instructionsError={instructionsError} + doTypewriterEffect={doTypewriterEffect} + setDoTypewriterEffect={setDoTypewriterEffect} /> ); case "actions": diff --git a/front/components/assistant_builder/InstructionScreen.tsx b/front/components/assistant_builder/InstructionScreen.tsx index dfc038b6e87d..44048e34c813 100644 --- a/front/components/assistant_builder/InstructionScreen.tsx +++ b/front/components/assistant_builder/InstructionScreen.tsx @@ -104,6 +104,8 @@ export function InstructionScreen({ resetAt, isUsingTemplate, instructionsError, + doTypewriterEffect, + setDoTypewriterEffect, }: { owner: WorkspaceType; plan: PlanType; @@ -115,6 +117,8 @@ export function InstructionScreen({ resetAt: number | null; isUsingTemplate: boolean; instructionsError: string | null; + doTypewriterEffect: boolean; + setDoTypewriterEffect: (doTypewriterEffect: boolean) => void; }) { const editor = useEditor({ extensions: [ @@ -126,19 +130,65 @@ export function InstructionScreen({ limit: INSTRUCTIONS_MAXIMUM_CHARACTER_COUNT, }), ], - content: tipTapContentFromPlainText(builderState.instructions || ""), + editable: !doTypewriterEffect, + content: tipTapContentFromPlainText( + (!doTypewriterEffect && builderState.instructions) || "" + ), onUpdate: ({ editor }) => { - const json = editor.getJSON(); - const plainText = plainTextFromTipTapContent(json); - setEdited(true); - setBuilderState((state) => ({ - ...state, - instructions: plainText, - })); + if (!doTypewriterEffect) { + const json = editor.getJSON(); + const plainText = plainTextFromTipTapContent(json); + setEdited(true); + setBuilderState((state) => ({ + ...state, + instructions: plainText, + })); + } }, }); const editorService = useInstructionEditorService(editor); + const [letterIndex, setLetterIndex] = useState(0); + + // Beware that using this useEffect will cause a lot of re-rendering until we finished the visual effect + // We must be careful to avoid any heavy rendering in this component + useEffect(() => { + if (doTypewriterEffect && editor && builderState.instructions) { + // Get the text from content and strip HTML tags for simplicity and being able to write letter by letter + const textContent = builderState.instructions.replace(/<[^>]*>?/gm, ""); + const delay = 2; // Typing delay in milliseconds + if (letterIndex < textContent.length) { + const timeoutId = setTimeout(() => { + // Append next character + editor + .chain() + .focus("end") + .insertContent(textContent[letterIndex], { + updateSelection: false, + }) + .run(); + setLetterIndex(letterIndex + 1); + }, delay); + return () => clearTimeout(timeoutId); + } else { + // We reset the content at the end otherwise we lose all carriage returns (i'm not sure why) + editor + .chain() + .setContent(tipTapContentFromPlainText(builderState.instructions)) + .focus("end") + .run(); + editor.setEditable(true); + setDoTypewriterEffect(false); + } + } + }, [ + editor, + letterIndex, + builderState.instructions, + doTypewriterEffect, + setDoTypewriterEffect, + ]); + const currentCharacterCount = editor?.storage.characterCount.characters(); useEffect(() => {