(edited)
The AI speech should be written quickly and not in this way, so that the site stops for about 5 seconds until the AI finishes its speech in a strange way, as I will attach in the video. This is a very annoying reality. I have been in this problem for a week and I cannot find a solution. I am even starting to lose hope in the project. I will attach all the codes and all the pictures that are posted. What is happening?
[image]

/* eslint-disable @typescript-eslint/no-unused-vars */"use client"
import { Chat } from "@/components/ui/chat";import { AppSidebar } from "../components/app-sidebar"import { Icon } from "@iconify/react";import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator,} from "@/components/ui/breadcrumb"import { Separator } from "@/components/ui/separator"import { Form } from '@heroui/form';import { Badge } from '@heroui/badge'import { Button, Card, CardBody, CardFooter, CardHeader, Divider, Link } from '@heroui/react';import Image from "next/image";import { Input, Textarea } from '@heroui/input'import { Avatar } from '@heroui/avatar';import { Tooltip } from '@heroui/tooltip';import ProseMirrorMarkdownEditor from "@/components/ProseMirrorMarkdownEditor";import { SidebarInset, SidebarProvider, SidebarTrigger, useSidebar } from "@/components/ui/sidebar"import { useChat } from "ai/react"import { useSession } from "next-auth/react";import { Spinner } from "@heroui/spinner"import Cookies from "js-cookie";import { useEffect, useState } from "react";import { Logo, SideBarPhoneIcon, SideBarPhoneIcon2 } from "@/components/icons";import SyntaxHighlighter from "react-syntax-highlighter";import { dark } from "react-syntax-highlighter/dist/esm/styles/prism";import ReactMarkdown from "react-markdown";import remarkGfm from "remark-gfm";import { ButtonGroup, Chip, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger } from "@heroui/react";import { useTheme } from "next-themes";import Header from "@/components/Header";import { toast } from "sonner";import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";
export default function Page() { const { messages, input, handleInputChange, handleSubmit, append, reload, error, isLoading, stop, } = useChat() const session = useSession() const { open, toggleSidebar, isMobile } = useSidebar() const [selectedAi, setSelectedAi] = useState("DeepSeek R1") const { theme, setTheme } = useTheme(); const savedTheme = Cookies.get("theme"); const [copied, setcopy] = useState(false) const [src, setsrc] = useState("/LogoBlack.png") const handleCopy = (input: string) => { if(copied) return; navigator.clipboard.writeText(input) .then(() => { setcopy(true) setTimeout(() => { setcopy(false); }, 5000); }) .catch((err) => { toast.error("Can't copy :(") }); };
return ( <> <AppSidebar />
<SidebarInset> <header className="flex h-16 shrink-0 sticky top-0 z-30 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12 border-b-small bg-white dark:bg-background dark:border-sidebar-border border-gray-200"> <div className="flex items-center gap-2 px-4"> {!isMobile ? ( open ? null : ( <> <Button isIconOnly variant='light' onPress={toggleSidebar} > { isMobile ? <SideBarPhoneIcon2 size={25} /> : <Icon icon="solar:siderbar-outline" width={25} /> } </Button> <Divider orientation="vertical" className="mr-2 h-4" /> </> ) ) : ( <> <Button isIconOnly variant='light' onPress={toggleSidebar} > { isMobile ? <SideBarPhoneIcon2 size={25} /> : <Icon icon="solar:siderbar-outline" width={25} /> } </Button> <Separator orientation="vertical" className="mr-2 h-4" /></> )} <Dropdown> <DropdownTrigger> <Button className="sm:max-w-none max-w-24 text-foreground-400" startContent={<Icon icon="svg-spinners:blocks-scale" />} variant='light' >{selectedAi}</Button> </DropdownTrigger> <DropdownMenu aria-label="Example with disabled actions" selectedKeys={[selectedAi]}> <DropdownItem onSelect={() => setSelectedAi("Gemini")} endContent={<Chip size='sm' radius='lg' color='warning' className="text-[#C9A9E9] bg-[#643b8d]">Pro</Chip>} key="gemini">Gemini</DropdownItem> <DropdownItem onSelect={() => setSelectedAi("DeepSeek R1")} endContent={<Chip size='sm' radius='full' className="text-[#17C946] bg-[#17c94680]" color="success">New</Chip>} key="deepseek">DeepSeek R1</DropdownItem> </DropdownMenu> </Dropdown> </div> </header> {messages.length === 0 ? ( <div id="scroll-auto"> <div className="flex-grow flex flex-col items-center gap-4 justify-center min-h-[656px] pb-[5%]">
<div className="justify-start items-start"> <Logo size={150} /> </div> <div className="text-center"> <h1 className="text-4xl font-bold ">Hi, I'm EgyAI.</h1> <p className="text-xl text-gray-600 mt-4">How can I help you today?</p> </div> <div id="search-bar" className="relative w-full flex flex-col justify-center items-center gap-4" > <Textarea className="max-w-[800px] flex flex-col justify-between w-full rounded-2xl border-medium px-3 py-2 pt-3 gap-1.5 hover:border-gray-400 transition-colors duration-200" onChange={handleInputChange} autoFocus value={input} placeholder="Ask EgyAI..." onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSubmit(); } }} endContent={ <> <Button size="sm" onPress={() => handleSubmit()} variant='light' fullWidth isIconOnly><Icon width={32} icon="solar:link-circle-bold" /></Button> <Tooltip closeDelay={3} content={`${input.length === 0 ? "Empty Message" : ""}`}> <div style={{ cursor: `${input.length === 0 ? 'not-allowed' : ""}` }}> <Button isDisabled={input.length === 0} size="sm" onPress={() => (isLoading ? stop() : handleSubmit())} variant='light' fullWidth isIconOnly><Icon width={32} icon={isLoading ? "solar:stop-circle-bold" : "solar:round-arrow-up-bold"} /></Button> </div> </Tooltip> </> } classNames={{ base: "border-none" }} /> </div> </div> </div> ) : (<> <div className="flex flex-col overflow-auto px-3 w-full mx-auto h-full justify-between"> <div className="w-full md:max-w-4xl mx-auto pt-4 pb-2 px-4"> <div className="flex gap-12"> <div className="flex-grow flex flex-col gap-1 relative items-center max-w-full"> <div id="qa-container"> <div id="message" className="flex gap-12 flex-col min-h-0"> <div className="flex-grow flex flex-col gap-2"> <div className="flex-grow flex flex-col gap-2"> {messages.map(message => ( <div key={message.id} className="w-full"> {message.role === 'user' ? <> <article className="w-full text-token-text-primary focus-visible:outline-2 focus-visible:outline-offset-[-4px]" dir="auto" data-testid="conversation-turn-2" data-scroll-anchor="false" > <h5 className="sr-only">User said:</h5> <div className="m-auto text-base py-[18px] px-6"> <div className="mx-auto flex flex-1 gap-4 text-base md:gap-5 lg:gap-6 md:max-w-3xl lg:max-w-[40rem] xl:max-w-[48rem]"> <div className="group/conversation-turn relative flex w-full min-w-0 flex-col @xs/thread:px-0 @sm/thread:px-1.5 @md/thread:px-4"> <div className="flex-col gap-1 md:gap-3"> <div className="flex max-w-full flex-col flex-grow"> <div data-message-author-role="user" data-message-id={message.id} dir="auto" className="min-h-8 text-message overflow-x-hidden flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&]:mt-5" > <div className="flex w-full flex-col gap-1 empty:hidden items-end rtl:items-start"> <Card className="relative w-fit max-w-[70vw] sm:max-w-[70%] rounded-3xl px-5 py-2.5"> {message.content} </Card> </div> </div> </div> </div> </div> </div> </div> </article> <div className="min-w-[70vw]" id="look-saver cuz i'm noob at frontend"></div> </> :
<article className="w-full text-token-text-primary focus-visible:outline-2 focus-visible:outline-offset-[-4px]" dir="auto" data-testid="conversation-turn-3" data-scroll-anchor="true" > <h6 className="sr-only">EgyAi said:</h6> <div className="m-auto text-base py-[18px] px-6"> <div className="mx-auto flex flex-1 gap-4 text-base md:gap-5 lg:gap-6 md:max-w-3xl lg:max-w-[40rem] xl:max-w-[48rem]"> <div className="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn @xs/thread:px-0 @sm/thread:px-1.5 @md/thread:px-4"> <div className="flex-col gap-1 md:gap-3"> <div className="flex max-w-full flex-col flex-grow"> <div data-message-author-role="assistant" data-message-id={message.id} dir="auto" className="min-h-8 text-message flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&]:mt-5" data-message-model-slug={selectedAi} > <ProseMirrorMarkdownEditor initialMarkdown={message.content } />
</div> </div> <div className="mb-2 flex gap-3 empty:hidden -ml-2"> <div className="items-center justify-start rounded-xl p-1 flex"> <div className="flex items-center"> <Tooltip content="SOON" placement='bottom' closeDelay={3}> <Button disabled isIconOnly fullWidth size="sm" variant='light'><Icon width={23} icon="solar:volume-loud-linear" /></Button> </Tooltip> <Tooltip content="Copy" placement='bottom' closeDelay={3}> <Button onPress={() => handleCopy(message.content)} isIconOnly fullWidth size="sm" variant='light'>{copied ? <Icon width={23} icon="solar:check-read-linear" /> : <Icon width={23} icon="solar:copy-linear" />}</Button> </Tooltip> <div className="flex"> <Tooltip content="Good response" placement='bottom' closeDelay={3}> <Button disabled isIconOnly fullWidth size="sm" variant='light'><Icon width={23} icon="solar:like-linear" /></Button> </Tooltip> <Tooltip content="Bad response" placement='bottom' closeDelay={3}> <Button disabled isIconOnly fullWidth size="sm" variant='light'><Icon width={23} icon="solar:dislike-linear" /></Button> </Tooltip> </div> <span className="hidden" /> <Tooltip content="Reload response" placement='bottom' closeDelay={3}> <Button onPress={() => reload()} isIconOnly fullWidth size="sm" variant='light'><Icon width={23} icon="solar:refresh-linear" /></Button> </Tooltip> </div> </div> </div> <div className="pr-2 lg:pr-0" /> <div className="mt-3 w-full empty:hidden"> <div className="text-center" /> </div> </div> </div> </div> </div> </article>} </div> ))} </div> </div> </div> </div> </div> </div> </div>
</div> <div className="sticky bottom-0 mx-auto md:max-w-4xl flex justify-center z-30 w-full px-4 py-4 bg-background"> <Textarea className="max-w-[800px] flex flex-col justify-between w-full rounded-2xl border-medium px-3 py-2 pt-3 gap-1.5 hover:border-gray-400 transition-colors duration-200" onChange={handleInputChange} autoFocus placeholder="Ask EgyAI..." value={input} onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSubmit(); } }} description="Keep in mind EgyAI can make mistakes,Check important information" endContent={ <> <Button size="sm" onPress={() => handleSubmit()} variant='light' fullWidth isIconOnly><Icon width={32} icon="solar:link-circle-bold" /></Button> <Tooltip closeDelay={3} content={`${(isLoading ? false : input.length === 0) ? "Empty Message" : ""}`}> <div style={{ cursor: `${(isLoading ? false : input.length === 0) ? 'not-allowed' : ""}` }}> <Button isDisabled={(isLoading ? false : input.length === 0)} size="sm" onPress={() => (isLoading ? stop() : handleSubmit())} variant='light' fullWidth isIconOnly><Icon width={32} icon={isLoading ? "solar:stop-circle-bold" : "solar:round-arrow-up-bold"} /></Button> </div> </Tooltip> </> }
classNames={{ description: "text-center", base: "border-none" }} /> </div> </>)} </SidebarInset > </> )}MarkDown
import React, { useEffect, useRef } from "react";import { EditorState } from "prosemirror-state";import { EditorView } from "prosemirror-view";import { schema } from "prosemirror-schema-basic";import { defaultMarkdownParser } from "prosemirror-markdown";import hljs from "highlight.js"; // Import highlight.jsimport "highlight.js/styles/github-dark.css"; // Choose any theme
// Function to apply syntax highlighting to code blocksfunction addCodeHighlighting(view) { const codeBlocks = view.dom.querySelectorAll("pre code"); codeBlocks.forEach((block) => { hljs.highlightElement(block); // Apply syntax highlighting });}
// Create a ProseMirror statefunction createEditorState(markdownContent) { const doc = defaultMarkdownParser.parse(markdownContent); return EditorState.create({ doc });}
// React component for a read-only ProseMirror Markdown viewerfunction ProseMirrorMarkdownViewer({ initialMarkdown }) { const editorRef = useRef(null); const viewRef = useRef(null);
useEffect(() => { // Initialize the editor const state = createEditorState(initialMarkdown); const view = new EditorView(editorRef.current, { state, dispatchTransaction(transaction) { const newState = view.state.apply(transaction); view.updateState(newState); addCodeHighlighting(view); // Highlight after updates }, editable: () => false, // Disable editing });
viewRef.current = view; addCodeHighlighting(view); // Initial highlighting
// Cleanup on unmount return () => { view.destroy(); }; }, [initialMarkdown]);
return <div ref={editorRef} className="prosemirror-container" />;}
export default ProseMirrorMarkdownViewer;{ "name": "egpyt-gpt-chat", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "@ai-sdk/deepseek": "^0.1.8", "@ai-sdk/fal": "^0.0.2", "@ai-sdk/fireworks": "^0.1.8", "@ai-sdk/google": "^1.1.8", "@ai-sdk/groq": "^1.1.7", "@ai-sdk/openai": "^1.1.9", "@codemirror/highlight": "^0.19.8", "@codemirror/lang-python": "^6.1.7", "@codemirror/theme-one-dark": "^6.1.2", "@fal-ai/client": "^1.2.3", "@heroui/react": "^2.6.14", "@iconify/react": "^5.2.0", "@mdx-js/loader": "^3.1.0", "@mdx-js/react": "^3.1.0", "@nanostores/react": "github:ai/react", "@next/env": "^15.1.6", "@next/mdx": "^15.1.6", "@prose-ui/core": "^1.0.4", "@prose-ui/next": "^1.0.4", "@radix-ui/react-avatar": "^1.1.2", "@radix-ui/react-collapsible": "^1.1.2", "@radix-ui/react-dialog": "^1.1.5", "@radix-ui/react-dropdown-menu": "^2.1.5", "@radix-ui/react-separator": "^1.1.1", "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.7", "@react-aria/ssr": "^3.9.7", "@tabler/icons-react": "^3.29.0", "@tiptap/core": "^2.11.5", "@tiptap/react": "^2.11.5", "@tiptap/starter-kit": "^2.11.5", "@types/mdx": "^2.0.13", "@types/react-syntax-highlighter": "^15.5.13", "ai": "^4.1.16", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "codemirror": "^6.0.1", "date-fns": "^4.1.0", "framer-motion": "^11.18.2", "geist": "^1.3.1", "highlight.js": "^11.11.1", "js-cookie": "^3.0.5", "lucide-react": "^0.474.0", "next": "14.2.16", "next-auth": "^4.24.11", "next-themes": "^0.4.4", "react": "^18", "react-dom": "^18", "react-markdown": "^9.0.3", "react-remarkable": "^1.1.3", "react-resizable": "^3.0.5", "react-syntax-highlighter": "^15.6.1", "rehype-highlight": "^7.0.2", "remark-gfm": "^4.0.0", "remarkable-react": "^1.4.3", "remeda": "^2.20.0", "shiki": "^2.3.2", "sonner": "^1.7.4", "tailwind-merge": "^3.0.1", "tailwindcss-animate": "^1.0.7", "tiptap-markdown": "^0.3.4", "usehooks-ts": "^3.1.1" }, "devDependencies": { "@iconify-json/solar": "^1.2.2", "@types/js-cookie": "^3.0.6", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "@types/react-resizable": "^3.0.8", "@types/remarkable": "^2.0.8", "eslint": "^8", "eslint-config-next": "14.2.16", "postcss": "^8", "tailwindcss": "^3.4.1", "typescript": "^5" }}