diff --git a/e2e-tests/helpers/test_helper.ts b/e2e-tests/helpers/test_helper.ts index c15d050..8d35fd8 100644 --- a/e2e-tests/helpers/test_helper.ts +++ b/e2e-tests/helpers/test_helper.ts @@ -713,7 +713,7 @@ export class PageObject { getChatInput() { return this.page.locator( - '[data-lexical-editor="true"][aria-placeholder="Ask Dyad to build..."]', + '[data-lexical-editor="true"][aria-placeholder^="Ask Dyad to build"]', ); } diff --git a/src/components/chat/HomeChatInput.tsx b/src/components/chat/HomeChatInput.tsx index cbd91b0..80eb11b 100644 --- a/src/components/chat/HomeChatInput.tsx +++ b/src/components/chat/HomeChatInput.tsx @@ -13,6 +13,7 @@ import { HomeSubmitOptions } from "@/pages/home"; import { ChatInputControls } from "../ChatInputControls"; import { LexicalChatInput } from "./LexicalChatInput"; import { useChatModeToggle } from "@/hooks/useChatModeToggle"; +import { useTypingPlaceholder } from "@/hooks/useTypingPlaceholder"; export function HomeChatInput({ onSubmit, }: { @@ -26,6 +27,13 @@ export function HomeChatInput({ }); // eslint-disable-line @typescript-eslint/no-unused-vars useChatModeToggle(); + const typingText = useTypingPlaceholder([ + "an ecommerce store...", + "an information page...", + "a landing page...", + ]); + const placeholder = `Ask Dyad to build ${typingText ?? ""}`; + // Use the attachments hook const { attachments, @@ -83,7 +91,7 @@ export function HomeChatInput({ onChange={setInputValue} onSubmit={handleCustomSubmit} onPaste={handlePaste} - placeholder="Ask Dyad to build..." + placeholder={placeholder} disabled={isStreaming} excludeCurrentApp={false} disableSendButton={false} diff --git a/src/hooks/useTypingPlaceholder.ts b/src/hooks/useTypingPlaceholder.ts new file mode 100644 index 0000000..e951972 --- /dev/null +++ b/src/hooks/useTypingPlaceholder.ts @@ -0,0 +1,48 @@ +import { useEffect, useState } from "react"; + +export function useTypingPlaceholder( + phrases: string[], + typingSpeed = 100, + deletingSpeed = 50, + pauseTime = 1500, +) { + const [text, setText] = useState(""); + const [index, setIndex] = useState(0); + const [deleting, setDeleting] = useState(false); + const [charIndex, setCharIndex] = useState(0); + + useEffect(() => { + const current = phrases[index]; + const speed = deleting ? deletingSpeed : typingSpeed; + let pauseTimer: NodeJS.Timeout; + const timer = setTimeout(() => { + if (!deleting && charIndex < current.length) { + setText((prev) => prev + current.charAt(charIndex)); + setCharIndex((prev) => prev + 1); + } else if (deleting && charIndex > 0) { + setText((prev) => prev.slice(0, -1)); + setCharIndex((prev) => prev - 1); + } else if (!deleting && charIndex === current.length) { + pauseTimer = setTimeout(() => setDeleting(true), pauseTime); + } else if (deleting && charIndex === 0) { + setDeleting(false); + setIndex((prev) => (prev + 1) % phrases.length); + } + }, speed); + + return () => { + clearTimeout(timer); + clearTimeout(pauseTimer); + }; + }, [ + phrases, + index, + deleting, + charIndex, + typingSpeed, + deletingSpeed, + pauseTime, + ]); + + return text; +}