diff --git a/index.html b/index.html index 64261e6..f2065c3 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@ - Vite + React + TS + Pennyzo Web App
diff --git a/src/assets/Dark-LineLogo-cropped.svg b/src/assets/Dark-LineLogo-cropped.svg new file mode 100644 index 0000000..b5da21b --- /dev/null +++ b/src/assets/Dark-LineLogo-cropped.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/AgentPanel.tsx b/src/components/AgentPanel.tsx new file mode 100644 index 0000000..86135d4 --- /dev/null +++ b/src/components/AgentPanel.tsx @@ -0,0 +1,153 @@ +import React, { useMemo } from 'react'; + +export type Agent = { + id: string; + name: string; + color: string; // tailwind gradient colors + emoji: string; +}; + +interface AgentPanelProps { + agents: Agent[]; + activeAgentId: string; + onChangeAgent: (id: string) => void; +} + +const AgentPanel: React.FC = ({ agents, activeAgentId, onChangeAgent }) => { + const active = useMemo(() => agents.find(a => a.id === activeAgentId) || agents[0], [agents, activeAgentId]); + + // 8 diverse agents for the 3D carousel + const carouselAgents = useMemo(() => [ + { id: 'hr', name: 'HR Command', emoji: '๐Ÿ‘ฅ', color: 'from-luxury-gold/30 to-brass/30' }, + { id: 'finance', name: 'Finance Sage', emoji: '๐Ÿ’ฐ', color: 'from-bronze/30 to-luxury-gold/30' }, + { id: 'project', name: 'Project Sensei', emoji: '๐Ÿ“Š', color: 'from-brass/30 to-bronze/30' }, + { id: 'marketing', name: 'Marketing Genius', emoji: '๐ŸŽฏ', color: 'from-luxury-gold/30 to-cream/30' }, + { id: 'analytics', name: 'Data Wizard', emoji: '๐Ÿ“ˆ', color: 'from-bronze/30 to-brass/30' }, + { id: 'security', name: 'Security Guard', emoji: '๐Ÿ›ก๏ธ', color: 'from-deep-forest/40 to-luxury-gold/30' }, + { id: 'support', name: 'Support Hero', emoji: '๐ŸŽง', color: 'from-brass/30 to-rich-brown/30' }, + { id: 'research', name: 'Research Bot', emoji: '๐Ÿ”', color: 'from-luxury-gold/30 to-bronze/30' } + ], []); + + return ( +
+
+

Personal Assistant

+
+ {active.name} +
+
+ +
+
+ + {/* Creative Orbital Agent Display */} +
+ {/* Central Hub */} +
+ {/* Outer orbital ring */} +
+ {/* Orbital path indicator */} +
+
+ + {/* Rotating agents on the orbit */} + {carouselAgents.map((agent, index) => { + const angle = (index * 360) / carouselAgents.length; + const isActive = agent.id === activeAgentId; + const radian = (angle * Math.PI) / 180; + const radius = 130; + const x = Math.cos(radian) * radius; + const y = Math.sin(radian) * radius; + + return ( +
+ + + {/* Tooltip positioned outside rotating container */} +
+
+ {agent.name} +
+
+
+ ); + })} +
+ + {/* Central Control Hub */} +
+
+
+
๐Ÿค–
+
HUB
+
+
+ + {/* Pulsing energy rings */} +
+
+
+
+
+ + {/* Floating particles for ambiance */} +
+ {[...Array(6)].map((_, i) => ( +
+ ))} +
+
+
+ ); +}; + +export default AgentPanel; diff --git a/src/components/ChatDock.tsx b/src/components/ChatDock.tsx new file mode 100644 index 0000000..c10c2e3 --- /dev/null +++ b/src/components/ChatDock.tsx @@ -0,0 +1,67 @@ +import React, { useState, useRef, useEffect } from 'react'; + +export type ChatMessage = { + id: string; + role: 'user' | 'assistant' | 'system'; + content: string; +}; + +interface ChatDockProps { + messages: ChatMessage[]; + onSend: (text: string) => void; + placeholder?: string; + className?: string; + leftOffsetClass?: string; // e.g., 'left-20 md:left-64' +} + +const ChatDock: React.FC = ({ messages, onSend, placeholder = 'Ask anythingโ€ฆ', className = '', leftOffsetClass = 'left-0' }) => { + const [text, setText] = useState(''); + const listRef = useRef(null); + + useEffect(() => { + if (listRef.current) listRef.current.scrollTop = listRef.current.scrollHeight; + }, [messages]); + + const handleSend = () => { + const t = text.trim(); + if (!t) return; + onSend(t); + setText(''); + }; + + return ( +
+
+
+ {messages.map((m) => ( +
+
+ {m.content} +
+
+ ))} +
+ +
+