diff --git a/frontend/package.json b/frontend/package.json index 8b65e4aa..2fd21795 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,7 +33,7 @@ "@radix-ui/react-scroll-area": "^1.2.0", "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-separator": "^1.1.1", - "@radix-ui/react-slot": "^1.1.1", + "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.6", "@radix-ui/react-visually-hidden": "^1.1.1", diff --git a/frontend/src/app/(main)/page.tsx b/frontend/src/app/(main)/page.tsx index 0be78895..befe02a2 100644 --- a/frontend/src/app/(main)/page.tsx +++ b/frontend/src/app/(main)/page.tsx @@ -12,6 +12,7 @@ import { SignInModal } from '@/components/sign-in-modal'; import { SignUpModal } from '@/components/sign-up-modal'; import { useRouter } from 'next/navigation'; import { logger } from '../log/logger'; +import { AuroraText } from '@/components/magicui/aurora-text'; export default function HomePage() { // States for AuthChoiceModal const [showAuthChoice, setShowAuthChoice] = useState(false); @@ -31,6 +32,8 @@ export default function HomePage() { try { const chatId = await createProjectFromPrompt(message, isPublic, model); + + console.log('Project created with ID:', chatId); promptFormRef.current.clearMessage(); router.push(`/chat?id=${chatId}`); } catch (error) { @@ -39,85 +42,144 @@ export default function HomePage() { }; return ( -
- Sentence to a project in seconds. -
-- Codefox built AI agents crew for you to create your next project -
++); }); }; @@ -77,136 +84,201 @@ export default function ChatList({ if (messages.length === 0) { return (- + -); } return ( -++-
- How can I help you today? +
Welcome to CodeFox
++ Ask me anything about coding, project development, or technical + problems. I'm here to help!
--- {messages.map((message, index) => { - const isUser = isUserMessage(message.role); - const isEditing = message.id === editingMessageId; - - return ( -- - {isUser ? ( --- {isEditing ? ( --- ) : ( - <> -{message.content}
- - > +++ + ); + })} + {loadingSubmit && ( -+- - ); - })} + )} + + {/* Tool result or additional info could be added here */} + {isTool && ( ++ {messages.map((message, index) => { + const isUser = isUserMessage(message.role); + const isEditing = message.id === editingMessageId; + const isTool = !isUser && isToolCall(message.content); + + return ( + + {/* Sender info - always on the left */} + ++ + {/* Message content */} ++ {isUser ? ( + <> + + + {isUser ? user.username || 'You' : 'CodeFox'} + ++ + {user.username?.substring(0, 2).toUpperCase()} + + > + ) : ( + <> ++ + AI + + > + )} ++ {/* Edit buttons for user messages */} + {isUser && !isEditing && onMessageEdit && ( + + )} + + {/* Message bubble */} + {isEditing ? ( +- ) : ( -++ ) : ( ++-+ {isUser ? ( + message.content + ) : isTool ? ( +++ ) : ( +++ {renderMessageContent(message.content)} ++ Tool Execution + + {renderMessageContent(message.content)} ++ )} +- -- - {user.username?.substring(0, 2).toUpperCase()} - --- )} -- - - {renderMessageContent(message.content)} - -- + Tool output ++ )} +-- -- -diff --git a/frontend/src/components/chat/chat-panel.tsx b/frontend/src/components/chat/chat-panel.tsx index 6753516d..f194929c 100644 --- a/frontend/src/components/chat/chat-panel.tsx +++ b/frontend/src/components/chat/chat-panel.tsx @@ -1,4 +1,7 @@ +'use client'; + import React from 'react'; +import { motion } from 'framer-motion'; import ChatBottombar from './chat-bottombar'; import ChatTopbar from './chat-topbar'; import { ChatRequestOptions, Message } from '../../const/MessageType'; @@ -37,22 +40,42 @@ export default function ChatContent({ setMessages, }: ChatProps) { return ( -- - - ++ )}+ ++ ++ + CodeFox ++ AI ++-++ + + +-++ + +-+ + +-{ + const updatedMessages = messages.map((msg) => + msg.id === messageId ? { ...msg, content: newContent } : msg + ); + setMessages(updatedMessages); + }} + /> + - ++ ); } diff --git a/frontend/src/components/chat/chat-topbar.tsx b/frontend/src/components/chat/chat-topbar.tsx index 89f1093c..dbe4a015 100644 --- a/frontend/src/components/chat/chat-topbar.tsx +++ b/frontend/src/components/chat/chat-topbar.tsx @@ -1,100 +1,140 @@ 'use client'; +import React, { useState } from 'react'; import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; -import React from 'react'; -import { CaretSortIcon } from '@radix-ui/react-icons'; import { Button } from '../ui/button'; -import { useModels } from '@/hooks/useModels'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Separator } from '@/components/ui/separator'; +import { HistoryIcon, ArchiveIcon, PlusIcon, FileTextIcon } from 'lucide-react'; import { cn } from '@/lib/utils'; +import { Badge } from '../ui/badge'; -export default function ChatTopbar() { - const [open, setOpen] = React.useState(false); - const { - models, - loading: modelsLoading, - setSelectedModel, - selectedModel, - } = useModels(); +// Mock data for chat history +const mockChatHistory = [ + { + id: 'chat-1', + title: 'still working on this feature', + date: '2 hours ago', + isPinned: true, + }, + { + id: 'chat-2', + title: 'Portfolio website design', + date: 'Yesterday', + isPinned: false, + }, +]; - const handleModelChange = (modelName: string) => { - setSelectedModel(modelName); - if (typeof window !== 'undefined') { - localStorage.setItem('selectedModel', modelName); - } - setOpen(false); - }; +interface ChatTopbarProps { + currentChatId?: string; + onChatSelect?: (chatId: string) => void; + onNewChat?: () => void; +} + +export default function ChatTopbar({ + currentChatId, + onChatSelect, + onNewChat, +}: ChatTopbarProps) { + const [open, setOpen] = useState(false); + + // Get current chat title + const currentChat = currentChatId + ? mockChatHistory.find((chat) => chat.id === currentChatId) + : null; return ( -+ -- - - -- --Select Model
-- Choose a model for your chat -
-- {modelsLoading ? ( - - -- ) : models.length > 0 ? ( ++++ ++ + {currentChat ? currentChat.title : 'New Chat'} +
++ {/* History dropdown */} +); } diff --git a/frontend/src/components/chat/code-engine/tabs/code-tab.tsx b/frontend/src/components/chat/code-engine/tabs/code-tab.tsx index 344a6140..c14e3762 100644 --- a/frontend/src/components/chat/code-engine/tabs/code-tab.tsx +++ b/frontend/src/components/chat/code-engine/tabs/code-tab.tsx @@ -43,14 +43,12 @@ const CodeTab = ({ const extension = filePath.split('.').pop()?.toLowerCase(); switch (extension) { case 'js': + case 'jsx': setType('javascript'); break; case 'ts': - setType('typescript'); - break; - case 'jsx': case 'tsx': - setType('typescriptreact'); + setType('typescript'); break; case 'html': setType('html'); @@ -100,7 +98,7 @@ const CodeTab = ({+ + + ++ ++Recent Chats
++ Your conversation history +
+ - {models.map((model, index) => ( -+ {mockChatHistory.map((chat) => ( +- ) : ( -- {index < models.length - 1 && ( -))}- )} + - -- )} - - - + ++ + ++ + + + {/* New chat button */} + +([]); + const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); const formRef = useRef (null); const { models } = useModels(); const [selectedModel, setSelectedModel] = useState(models[0] || 'gpt-4o'); const { refetchChats } = useChatList(); - const route = useRouter(); // Project status monitoring for the current chat const { isReady, projectId, projectName, error } = @@ -102,37 +98,41 @@ export default function Chat() { return chatId ? ( - -+ ++ + + + - +(null); + const textRef = useRef (null); + const containerRef = useRef (null); + const [fontSize, setFontSize] = React.useState(0); + const [dimensions, setDimensions] = React.useState({ width: 0, height: 0 }); + const [isReady, setIsReady] = React.useState(false); + const [textStyle, setTextStyle] = React.useState< + Partial + >({}); + const maskId = useId(); + + // Updated effect to compute all text styles from parent + useEffect(() => { + if (containerRef.current) { + const computedStyle = window.getComputedStyle(containerRef.current); + + // Extract text-related styles + const relevantStyles = { + fontSize: computedStyle.fontSize, + fontFamily: computedStyle.fontFamily, + fontWeight: computedStyle.fontWeight, + fontStyle: computedStyle.fontStyle, + letterSpacing: computedStyle.letterSpacing, + lineHeight: computedStyle.lineHeight, + textTransform: computedStyle.textTransform, + fontVariant: computedStyle.fontVariant, + fontStretch: computedStyle.fontStretch, + fontFeatureSettings: computedStyle.fontFeatureSettings, + }; + + requestAnimationFrame(() => { + setTextStyle(relevantStyles); + }); + } + }, [className]); + + // Updated effect to compute font size from both inline and class styles + useEffect(() => { + const updateFontSize = () => { + if (containerRef.current) { + const computedStyle = window.getComputedStyle(containerRef.current); + const computedFontSize = parseFloat(computedStyle.fontSize); + + requestAnimationFrame(() => { + setFontSize(computedFontSize); + }); + } + }; + + updateFontSize(); + window.addEventListener('resize', updateFontSize); + + return () => window.removeEventListener('resize', updateFontSize); + }, [className]); + + // Update effect to set ready state after dimensions are computed + useEffect(() => { + const updateDimensions = () => { + if (textRef.current) { + const bbox = textRef.current.getBBox(); + setDimensions({ + width: bbox.width, + height: bbox.height, + }); + setIsReady(true); + } + }; + + updateDimensions(); + window.addEventListener('resize', updateDimensions); + + return () => window.removeEventListener('resize', updateDimensions); + }, [children, fontSize]); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + // Set canvas size + canvas.width = dimensions.width; + canvas.height = dimensions.height; + + let time = 0; + const baseSpeed = 0.008; // Original speed as base unit + + function animate() { + if (!ctx || !canvas) return; + ctx.clearRect(0, 0, canvas.width, canvas.height); + + time += baseSpeed * speed; + + colors.forEach((color, i) => { + const x = + canvas.width * + (0.5 + + Math.cos(time * 0.8 + i * 1.3) * 0.4 + + Math.sin(time * 0.5 + i * 0.7) * 0.2); + const y = + canvas.height * + (0.5 + + Math.sin(time * 0.7 + i * 1.5) * 0.4 + + Math.cos(time * 0.6 + i * 0.8) * 0.2); + + const gradient = ctx.createRadialGradient( + x, + y, + 0, + x, + y, + canvas.width * 0.4 + ); + + gradient.addColorStop(0, `${color}99`); + gradient.addColorStop(0.5, `${color}33`); + gradient.addColorStop(1, '#00000000'); + + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, canvas.width, canvas.height); + }); + + requestAnimationFrame(animate); + } + animate(); + }, [dimensions, colors, speed]); + + return ( + + {/* Hidden text for SEO */} + {children} + + {/* Visual placeholder while canvas loads */} + + + + + ); +} diff --git a/frontend/src/components/root/nav-layout.tsx b/frontend/src/components/root/nav-layout.tsx index 13dc185f..7e0932fc 100644 --- a/frontend/src/components/root/nav-layout.tsx +++ b/frontend/src/components/root/nav-layout.tsx @@ -13,13 +13,7 @@ export default function NavLayout({ children }: NavLayoutProps) { const { isAuthorized } = useAuthContext(); const logoElement = !isAuthorized ? ( - + ) : null; return ( diff --git a/frontend/src/components/root/nav.tsx b/frontend/src/components/root/nav.tsx index 565b0b17..a2062415 100644 --- a/frontend/src/components/root/nav.tsx +++ b/frontend/src/components/root/nav.tsx @@ -7,7 +7,6 @@ import { ReactNode, } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; -import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { Github, Star, SunMoon, Home, Info, DollarSign } from 'lucide-react'; import { useTheme } from 'next-themes'; @@ -185,7 +184,7 @@ const FloatingNavbar = forwardRef ( <> ( className={`flex items-center space-x-3 ${logoContainerClassName}`} variants={itemVariants} > - {logo && ( - {logo}- )} {name} @@ -247,7 +243,7 @@ const FloatingNavbar = forwardRef(