diff --git a/web/src/app/app/page.tsx b/web/src/app/app/page.tsx new file mode 100644 index 0000000..51536fc --- /dev/null +++ b/web/src/app/app/page.tsx @@ -0,0 +1,394 @@ +'use client'; + +import { useState, useRef, useEffect } from 'react'; +import Link from 'next/link'; + +interface Message { + id: string; + role: 'user' | 'assistant'; + content: string; + timestamp: Date; +} + +interface Conversation { + id: string; + title: string; + messages: Message[]; + createdAt: Date; +} + +const suggestedPrompts = [ + { icon: 'search', text: 'Search for transits in TIC 307210830' }, + { icon: 'clock', text: 'Is TOI-700 d in the habitable zone?' }, + { icon: 'chart', text: 'Analyze light curve for Kepler-11' }, + { icon: 'doc', text: 'Generate a report for my candidate planet' }, +]; + +const quickActions = [ + { label: 'TIC lookup', prompt: 'TIC lookup for ' }, + { label: 'Upload light curve', action: 'upload' }, + { label: 'Run detection', prompt: 'Run detection on ' }, + { label: 'Check HZ', prompt: 'Check habitable zone for ' }, +]; + +export default function ChatPage() { + const [sidebarOpen, setSidebarOpen] = useState(true); + const [messages, setMessages] = useState([]); + const [conversations, setConversations] = useState([]); + const [inputValue, setInputValue] = useState(''); + const [isTyping, setIsTyping] = useState(false); + const [showUploadModal, setShowUploadModal] = useState(false); + const messagesEndRef = useRef(null); + const inputRef = useRef(null); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }; + + useEffect(() => { + scrollToBottom(); + }, [messages]); + + const handleInputChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value); + e.target.style.height = 'auto'; + e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px'; + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + sendMessage(); + } + }; + + const generateResponse = (query: string): string => { + const q = query.toLowerCase(); + if (q.includes('tic') || q.includes('search') || q.includes('transit')) { + return "I'll search for transit signals in that target. The TinyML model is analyzing the light curve data from TESS...\n\nAnalysis complete! Found potential transit signal with:\n- Period: 3.42 days\n- Depth: 0.012%\n- SNR: 8.7\n\nThis appears to be a promising exoplanet candidate. Would you like me to run a detailed vetting analysis?"; + } + if (q.includes('habitable') || q.includes('toi-700')) { + return "TOI-700 d is indeed located in the habitable zone of its host star!\n\nKey facts:\n- Orbital period: 37.4 days\n- Equilibrium temperature: ~268K (-5C)\n- Size: 1.19 Earth radii\n- Receives ~86% of Earth's solar flux\n\nThis makes it one of the most Earth-like planets discovered by TESS."; + } + if (q.includes('kepler') || q.includes('light curve') || q.includes('analyze')) { + return "Analyzing Kepler-11 light curve data...\n\nThis is a fascinating multi-planet system! I detected 6 transiting planets:\n- Kepler-11b: 1.97 Re, 10.3d period\n- Kepler-11c: 3.15 Re, 13.0d period\n- Kepler-11d: 3.43 Re, 22.7d period\n- Kepler-11e: 4.52 Re, 32.0d period\n- Kepler-11f: 2.61 Re, 46.7d period\n- Kepler-11g: 3.66 Re, 118.4d period\n\nAll six planets orbit closer to their star than Venus does to the Sun!"; + } + if (q.includes('report')) { + return "I can generate a comprehensive report for your candidate. Please provide:\n\n1. Target ID (TIC or KIC number)\n2. Detected period\n3. Transit depth\n\nOr upload your light curve data and I'll extract these parameters automatically."; + } + return "I'm Larun, your exoplanet discovery assistant. I can help you:\n\n- Search for transit signals in TESS/Kepler data\n- Analyze light curves for periodic signals\n- Calculate habitable zone boundaries\n- Generate discovery reports\n\nWhat would you like to explore?"; + }; + + const sendMessage = () => { + if (!inputValue.trim()) return; + + const userMessage: Message = { + id: Date.now().toString(), + role: 'user', + content: inputValue.trim(), + timestamp: new Date(), + }; + + setMessages(prev => [...prev, userMessage]); + setInputValue(''); + if (inputRef.current) { + inputRef.current.style.height = 'auto'; + } + setIsTyping(true); + + // Simulate AI response + setTimeout(() => { + const response = generateResponse(userMessage.content); + const assistantMessage: Message = { + id: (Date.now() + 1).toString(), + role: 'assistant', + content: response, + timestamp: new Date(), + }; + setMessages(prev => [...prev, assistantMessage]); + setIsTyping(false); + }, 1000 + Math.random() * 1000); + }; + + const useSuggestedPrompt = (prompt: string) => { + setInputValue(prompt); + inputRef.current?.focus(); + }; + + const startNewChat = () => { + setMessages([]); + setInputValue(''); + setSidebarOpen(false); + }; + + const formatMessage = (content: string) => { + return content + .replace(/`([^`]+)`/g, '$1') + .replace(/\n/g, '
'); + }; + + return ( +
+ {/* Sidebar */} + + + {/* Sidebar Overlay (mobile) */} + {sidebarOpen && ( +
setSidebarOpen(false)} /> + )} + + {/* Main Chat Area */} +
+ {/* Header */} +
+
+ + + Larun. + +
+
+ + Sign In + + + + + + Upgrade + + + Docs + +
+
+ + {/* Messages Container */} +
+
+ {messages.length === 0 ? ( + /* Welcome Screen */ +
+

+ Larun. +

+

What would you like to discover today?

+ +
+ {suggestedPrompts.map((prompt, index) => ( + + ))} +
+
+ ) : ( + /* Messages */ +
+ {messages.map((message) => ( +
+
+ {message.role === 'assistant' ? ( + + + + ) : ( + U + )} +
+
+
+ ))} + + {/* Typing Indicator */} + {isTyping && ( +
+
+ + + +
+
+
+
+
+
+
+ )} + +
+
+ )} +
+
+ + {/* Input Area */} +
+
+
+