diff --git a/public/next.svg b/public/next.svg deleted file mode 100644 index 5174b28c..00000000 --- a/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index 77053960..00000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/window.svg b/public/window.svg deleted file mode 100644 index b2b2a44f..00000000 --- a/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index a52855ec..dc0cf2bf 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -986,6 +986,33 @@ /* indigo-500 */ } +.markdown-content hr { + border: none; + height: 1px; + background-color: rgba(0, 0, 0, 0.1); + margin: 1.5rem 0; +} + +.dark .markdown-content hr { + background-color: rgba(255, 255, 255, 0.1); +} + +.markdown-content[data-message-mode="sentinel"] hr { + background-color: rgba(99, 102, 241, 0.3); +} + +.dark .markdown-content[data-message-mode="sentinel"] hr { + background-color: rgba(99, 102, 241, 0.3); +} + +.markdown-content[data-message-mode="morpheus"] hr { + background-color: rgba(71, 216, 163, 0.3); +} + +.dark .markdown-content[data-message-mode="morpheus"] hr { + background-color: rgba(71, 216, 163, 0.3); +} + .markdown-content ul, .markdown-content ul:first-child, .markdown-content ul:last-child { @@ -1014,7 +1041,7 @@ background-color: white !important; border-bottom: 1px solid rgba(0, 0, 0, 0.1) !important; } - + .dark .header-transparent header { background-color: black !important; border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 05278150..380cc1c7 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -61,7 +61,7 @@ export default function RootLayout({ `} {children} diff --git a/src/app/page.tsx b/src/app/page.tsx index bc389992..2005cee8 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -13,7 +13,7 @@ import { useDisconnectedDarkMode } from "@/hooks/useDisconnectedDarkMode"; import { useChat } from "@/contexts/chat-context"; export default function HomePage() { - const { isConnected, isConnecting, isReconnecting } = useAccount(); + const { isConnected } = useAccount(); const { handleNavigateToRoot } = useChat(); React.useEffect(() => { @@ -22,16 +22,18 @@ export default function HomePage() { useDisconnectedDarkMode(isConnected); - if (!isConnected && !isConnecting && !isReconnecting) { - return ; - } - return ( <> -
- -
- +
+ + {!isConnected && ( + <> + + + + )} + + {isConnected && } ); } diff --git a/src/app/template.tsx b/src/app/template.tsx index f19be732..7c211c81 100644 --- a/src/app/template.tsx +++ b/src/app/template.tsx @@ -25,7 +25,7 @@ export default function AppTemplate({ }, []); return ( -
+
+
{showInput && ( {/* Dynamic title */} {modeTitle} @@ -108,7 +108,6 @@ export function RootLayout() { /> - {/* Example queries */} void; onSubmit?: (e: FormEvent) => Promise; - isInChatMode?: boolean; showReloadButton?: boolean; className?: string; } @@ -52,7 +51,6 @@ export default function BaseChatInput({ initialMode = "morpheus", onSubmitMessage, onSubmit, - isInChatMode = false, showReloadButton = false, className = "", }: BaseChatInputProps) { @@ -232,7 +230,7 @@ export default function BaseChatInput({ variants={containerVariants} initial="hidden" animate="visible" - className={`relative ${isInChatMode ? "mb-[1px]" : ""} ${className}`} + className={`relative ${className}`} >
{/* Action Buttons - Positioned inside the chat input */} @@ -376,20 +374,19 @@ export default function BaseChatInput({
{/* Input field with glow effect */}
{/* Mode Toggle Buttons - Bottom left */} diff --git a/src/components/chat/chat-input.tsx b/src/components/chat/chat-input.tsx index 08bbd421..30a39197 100644 --- a/src/components/chat/chat-input.tsx +++ b/src/components/chat/chat-input.tsx @@ -81,7 +81,6 @@ export default function ChatInput({ initialMode={initialMode} onSubmitMessage={onSubmitMessage} onSubmit={onSubmit} - isInChatMode={true} showReloadButton={!isPendingResponse} className={className} /> diff --git a/src/components/chat/example-queries.tsx b/src/components/chat/example-queries.tsx index f7a711fa..4d96b34d 100644 --- a/src/components/chat/example-queries.tsx +++ b/src/components/chat/example-queries.tsx @@ -1,14 +1,16 @@ "use client"; import * as React from "react"; -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; -import { AnimatePresence, motion } from "framer-motion"; -import { ArrowRight, BookOpen, Database, Info } from "lucide-react"; +import { motion } from "framer-motion"; +import { ArrowRight, BookOpen, Database, Info, X } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; +import { cn } from "@/lib/utils"; + interface ExampleQueriesProps { onSelect: (query: string) => void; activeMode?: "morpheus" | "sentinel"; @@ -23,12 +25,29 @@ export function ExampleQueries({ const [currentMode, setCurrentMode] = useState<"morpheus" | "sentinel">( activeMode ); + const [activeMenu, setActiveMenu] = useState<"morpheus" | "sentinel" | null>( + null + ); + const containerRef = useRef(null); + const menuRef = useRef(null); - // Sync with parent component's activeMode - React.useEffect(() => { + useEffect(() => { setCurrentMode(activeMode); }, [activeMode]); + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (menuRef.current && !menuRef.current.contains(event.target as Node)) { + setActiveMenu(null); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + const queries = { morpheus: [ "What is the sentiment around $BTC this week?", @@ -48,324 +67,295 @@ export function ExampleQueries({ ], }; - const QueryButton = ({ query }: { query: string }) => { - if (!query) return
; - - const handleClick = () => { - onSelect(query); - }; - - // Determine style based on active mode - const activeStyles = - currentMode === "morpheus" - ? { - buttonBg: "bg-white/30 dark:bg-black/30 backdrop-blur-sm", - buttonHoverBg: - "hover:bg-emerald-50/40 dark:hover:bg-emerald-950/20", - buttonBorder: - "border border-emerald-300/70 dark:border-emerald-500/20", - buttonHoverBorder: - "hover:border-emerald-400/80 dark:hover:border-emerald-400/30", - buttonText: "text-gray-800 dark:text-gray-200", - buttonHoverText: - "hover:text-emerald-800 dark:hover:text-emerald-300", - gradientFrom: "from-emerald-100/40 dark:from-emerald-400/20", - } - : { - buttonBg: "bg-white/30 dark:bg-black/30 backdrop-blur-sm", - buttonHoverBg: "hover:bg-indigo-50/40 dark:hover:bg-indigo-950/20", - buttonBorder: - "border border-indigo-300/70 dark:border-indigo-500/20", - buttonHoverBorder: - "hover:border-indigo-400/80 dark:hover:border-indigo-400/30", - buttonText: "text-gray-800 dark:text-gray-200", - buttonHoverText: "hover:text-indigo-800 dark:hover:text-indigo-300", - gradientFrom: "from-indigo-100/40 dark:from-indigo-400/20", - }; + const QueryItem = ({ query }: { query: string }) => { + if (!query) return null; return ( { + if (activeMenu && onModeSelect && currentMode !== activeMenu) { + onModeSelect(activeMenu); + } + onSelect(query); + setActiveMenu(null); + }} + className={`text-left px-3 py-2 text-sm rounded-md w-full transition-colors cursor-pointer ${ + activeMenu === "sentinel" + ? "hover:bg-indigo-50/30 dark:hover:bg-indigo-900/10" + : "hover:bg-emerald-50/30 dark:hover:bg-emerald-900/10" + }`} + whileHover={{ x: 4 }} + whileTap={{ scale: 0.98 }} > -
- +
+ {query} - +
-
); }; + const handleMenuToggle = (mode: "morpheus" | "sentinel") => { + setActiveMenu(activeMenu === mode ? null : mode); + }; + return ( -
-
-
-

- Example queries -

+
+ {/* Info Button */} + + + + + + + +
+

+ Matrix Terminal Modes +

+

+ Choose how you want to interact with the Matrix +

- - +
{ + if (onModeSelect && currentMode !== "morpheus") { + onModeSelect("morpheus"); + } }} + whileHover={{ scale: 1.01 }} + whileTap={{ scale: 0.99 }} > - - - - -
-

- Matrix Terminal Modes -

-

- Choose how you want to interact with the Matrix -

- -
- +
+
{ - if (onModeSelect && currentMode !== "morpheus") { - onModeSelect("morpheus"); - } - }} - whileHover={{ - scale: 1.01, - }} - whileTap={{ - scale: 0.99, - }} - > -
-
-
- -
- - Morpheus Mode - -
- - Intelligence - -
-
-

- Access real-time market data and analytics -

-
    -
  • Live price data and market analysis
  • -
  • Social sentiment from Twitter and Telegram
  • -
  • Historical Trends and market shifts
  • -
  • Trading concepts and strategic positioning
  • -
-

- Example: "What's the market sentiment for - ETH this week?" -

+ ? "bg-emerald-100 dark:bg-emerald-900/60" + : "bg-gray-100 dark:bg-gray-800" + }`} + > +
- + + Morpheus Mode + +
+ + Intelligence + +
+
+

+ Access real-time market data and analytics +

+
    +
  • Live price data and market analysis
  • +
  • Social sentiment from Twitter and Telegram
  • +
  • Historical Trends and market shifts
  • +
  • Trading concepts and strategic positioning
  • +
+

+ Example: "What's the market sentiment for ETH + this week?" +

+
+
- { + if (onModeSelect && currentMode !== "sentinel") { + onModeSelect("sentinel"); + } + }} + whileHover={{ scale: 1.01 }} + whileTap={{ scale: 0.99 }} + > +
+
+
{ - if (onModeSelect && currentMode !== "sentinel") { - onModeSelect("sentinel"); - } - }} - whileHover={{ - scale: 1.01, - }} - whileTap={{ - scale: 0.99, - }} - > -
-
-
- -
- - Sentinel Mode - -
- - Commands - -
-
-

-
    -
  • Swap and bridge across DEXs and networks
  • -
  • Open, close, manage positions in realtime
  • -
  • Set SL/TPs, manage risk effectively
  • -
  • Access LP and Vault strategies on Hyperliquid
  • -
-

- Example: "Swap 0.5 ETH to USDC, open a long - position on PEPE" -

+ ? "bg-indigo-100 dark:bg-indigo-900/60" + : "bg-gray-100 dark:bg-gray-800" + }`} + > +
- + + Sentinel Mode + +
+ + Commands +
-
- -
-
-
+
+

+
    +
  • Swap and bridge across DEXs and networks
  • +
  • Open, close, manage positions in realtime
  • +
  • Set SL/TPs, manage risk effectively
  • +
  • Access LP and Vault strategies on Hyperliquid
  • +
+

+ Example: "Swap 0.5 ETH to USDC, open a long position + on PEPE" +

+
+ +
+
+ + - - {currentMode === "morpheus" ? ( - - {queries.morpheus.map((query, idx) => ( - - ))} - - ) : ( - - {queries.sentinel.map((query, idx) => ( - - ))} - - )} - + {/* Morpheus Button */} + handleMenuToggle("morpheus")} + whileHover={{ scale: 1.02 }} + whileTap={{ scale: 0.98 }} + > + + Morpheus + + + {/* Sentinel Button */} + handleMenuToggle("sentinel")} + whileHover={{ scale: 1.02 }} + whileTap={{ scale: 0.98 }} + > + + Sentinel +
+ + {/* Centered menu */} + {activeMenu && ( +
+ +
+

+ {activeMenu === "morpheus" + ? "Morpheus Intelligence" + : "Sentinel Commands"} +

+ +
+
+ {queries[activeMenu].map((query, idx) => ( + + ))} +
+
+
+ )}
); } diff --git a/src/components/chat/messages.tsx b/src/components/chat/messages.tsx index 3478d84c..f800fb74 100644 --- a/src/components/chat/messages.tsx +++ b/src/components/chat/messages.tsx @@ -252,44 +252,46 @@ function MessagesComponent() { const buttonMode = q.mode || defaultMode; return ( - - - + + + {colorizeQuestionModes(q.question)} + + +
+ +
+ + +
); })}
@@ -360,7 +362,7 @@ function MessagesComponent() { } return ( -
+
{mainContent && (
-
- YOU -
total + @@ -594,15 +592,8 @@ function MessagesComponent() {
-
-
-
+
+
{group.messages.map( (message: UIMessage, messageIndex: number) => { @@ -622,15 +613,20 @@ function MessagesComponent() { : "" } > -
+
{message.parts?.map( - (part: Part, partIndex: number) => - renderMessagePart( - part, - partIndex, - oracleMessageKey, // Pass unique seed for IDs - message - ) + (part: Part, partIndex: number) => ( + + {renderMessagePart( + part, + partIndex, + oracleMessageKey, + message + )} + + ) )}
diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index e11b7cb3..4d10b137 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -68,7 +68,7 @@ export function Header() { return (
{/* Header - Fixed */}
diff --git a/src/components/shared/DIsconnectedMessage.tsx b/src/components/shared/DIsconnectedMessage.tsx index a69f4d44..c3a32f57 100644 --- a/src/components/shared/DIsconnectedMessage.tsx +++ b/src/components/shared/DIsconnectedMessage.tsx @@ -3,11 +3,9 @@ import Link from "next/link"; import { ConnectKitButton } from "connectkit"; import { RefreshCw, Shield, Sparkles } from "lucide-react"; -import MatrixCanvas from "./MatrixCanvas"; - export function ChatDisconnectedMessage() { return ( -
+
@@ -45,9 +43,7 @@ export function ChatDisconnectedMessage() { export function DisconnectedMessage() { return ( -
- - +
{[...Array(30)].map((_, i) => (
-
+
diff --git a/src/components/shared/MatrixCanvas.tsx b/src/components/shared/MatrixCanvas.tsx index fcb41aec..8bf6b663 100644 --- a/src/components/shared/MatrixCanvas.tsx +++ b/src/components/shared/MatrixCanvas.tsx @@ -2,65 +2,74 @@ import { useEffect, useRef } from "react"; -interface MatrixCanvasProps { - className?: string; -} - -const MatrixCanvas = ({ className = "" }: MatrixCanvasProps) => { +const MatrixCanvas = () => { const canvasRef = useRef(null); useEffect(() => { + // Enforce dark mode on client-side + document.documentElement.classList.add("dark"); + document.body.classList.add("dark"); + const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; - // Set canvas to full screen + // Making the canvas full screen const resizeCanvas = () => { - canvas.width = window.innerWidth; canvas.height = window.innerHeight; + canvas.width = window.innerWidth; }; - // Initial sizing - resizeCanvas(); - - // Handle resize events + // Handle window resize window.addEventListener("resize", resizeCanvas); + resizeCanvas(); - // Matrix characters - using crypto and blockchain related characters + // Matrix characters const matrix = "0123456789abcdefABCDEF₿ΞΨ♦️₮Ł∞☰⧉Ω◈⬢⌘฿◊{}[]()<>_|:;,.+-*/=~!@#$%^&*0x1x2x3xf9b4b2f1c9d8a7e6c5b4a30x1234567890xdefi0xenable0xswap0xabcdef"; + const matrixChars = matrix.split(""); - // Using the exact same settings as the disconnected state const font_size = 10; - const columns = canvas.width / font_size; + const columns = canvas.width / font_size; // Number of columns for the rain - // Drop positions - exactly as in disconnected state + // An array of drops - one per column const drops: number[] = []; for (let x = 0; x < columns; x++) { drops[x] = Math.floor(Math.random() * canvas.height); } - // Main drawing function - with theme awareness + const isDarkMode = document.documentElement.classList.contains("dark"); + ctx.fillStyle = isDarkMode ? "rgba(0, 0, 0, 1)" : "rgba(255, 255, 255, 1)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // Drawing the characters const draw = () => { - // Get current theme from HTML element + // Black BG for the canvas with opacity to create trail effect const isDarkMode = document.documentElement.classList.contains("dark"); - + // Theme-aware background - dark in dark mode, white in light mode - ctx.fillStyle = isDarkMode - ? "rgba(0, 0, 0, 0.04)" // Dark mode: black with opacity as in original - : "rgba(255, 255, 255, 0.04)"; // Light mode: white with same opacity + if (isDarkMode) { + ctx.fillStyle = "rgba(0, 0, 0, 0.06)"; // Dark mode: black with opacity + } else { + ctx.fillStyle = "rgba(255, 255, 255, 0.06)"; // Light mode: white with opacity + } ctx.fillRect(0, 0, canvas.width, canvas.height); // Theme-aware text color - using RainbowKit color scheme - ctx.fillStyle = isDarkMode - ? "#47D8A3" // Dark mode: same brand green as original - : "#108A61"; // Light mode: darker shade of the brand green + + // Reduced opacity for characters + ctx.globalAlpha = 0.6; + if (isDarkMode) { + ctx.fillStyle = "#47D8A3"; // Dark mode: same brand green as original + } else { + ctx.fillStyle = "#108A61"; // Light mode: darker shade of the brand green + } ctx.font = font_size + "px arial"; - // Looping over drops - exactly as in disconnected state + // Looping over drops for (let i = 0; i < drops.length; i++) { // Random character to print const text = @@ -77,10 +86,9 @@ const MatrixCanvas = ({ className = "" }: MatrixCanvasProps) => { } }; - // Using setInterval with 35ms timing for smooth animation - const interval = setInterval(draw, 42); + const interval = setInterval(draw, 55); - // Cleanup function + // Cleanup return () => { clearInterval(interval); window.removeEventListener("resize", resizeCanvas); @@ -89,12 +97,15 @@ const MatrixCanvas = ({ className = "" }: MatrixCanvasProps) => { return ( <> - {/* Theme-responsive background - ensures proper contrast in both modes */} -
+
); }; diff --git a/src/contexts/chat-context.tsx b/src/contexts/chat-context.tsx index 9e97fd73..e0e71b9f 100644 --- a/src/contexts/chat-context.tsx +++ b/src/contexts/chat-context.tsx @@ -344,7 +344,7 @@ export function ChatProvider({ children }: ChatProviderProps) { // Clear initial message data and submit for AI response using the hook's chatData setInitialMessageData(null); - chatData.handleSubmit(new Event('submit') as any); // Use hook's chatData + chatData.handleSubmit(new Event("submit") as any); // Use hook's chatData } else { // Just load existing chat loadChat(chatId, chatMessages, chatTitle, isReadOnly); @@ -364,7 +364,7 @@ export function ChatProvider({ children }: ChatProviderProps) { initialMessageData, activeMode, address, - chatData // Add hook's chatData to dependencies + chatData, // Add hook's chatData to dependencies ] );