Spaces:
Running
Running
Commit ·
185ecb0
1
Parent(s): 5d26dc5
feat(ui): complete UI overhaul with smooth animations, glowing elements, and refined citation rendering
Browse files- frontend-next/app/globals.css +3 -2
- frontend-next/app/page.tsx +83 -41
frontend-next/app/globals.css
CHANGED
|
@@ -929,8 +929,9 @@ select {
|
|
| 929 |
|
| 930 |
/* -- UI Redesign -- */
|
| 931 |
.layout-wrapper { display: flex; height: 100vh; width: 100vw; overflow: hidden; }
|
| 932 |
-
.sidebar { width: 260px; background: rgba(5, 7, 10, 0.95); border-right: 1px solid var(--border); display: flex; flex-direction: column; overflow-y: auto; padding: 10px; z-index: 100; flex-shrink: 0; transition: transform 0.3s ease; }
|
| 933 |
-
|
|
|
|
| 934 |
.sidebar-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 90; }
|
| 935 |
.new-chat-btn { display: flex; align-items: center; gap: 10px; width: 100%; padding: 12px; border-radius: 8px; background: rgba(255,255,255,0.05); color: #fff; font-weight: 500; font-size: 0.9rem; margin-bottom: 20px; transition: 0.2s; border: 1px solid rgba(255,255,255,0.1); }
|
| 936 |
.new-chat-btn:hover { background: rgba(255,255,255,0.1); border-color: rgba(255,255,255,0.2); }
|
|
|
|
| 929 |
|
| 930 |
/* -- UI Redesign -- */
|
| 931 |
.layout-wrapper { display: flex; height: 100vh; width: 100vw; overflow: hidden; }
|
| 932 |
+
.sidebar { width: 260px; background: rgba(5, 7, 10, 0.95); border-right: 1px solid var(--border); display: flex; flex-direction: column; overflow-y: auto; padding: 10px; z-index: 100; flex-shrink: 0; transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s ease, padding 0.3s ease, margin 0.3s ease; white-space: nowrap; overflow-x: hidden; }
|
| 933 |
+
.sidebar.collapsed { width: 0 !important; padding: 0 !important; border: none; opacity: 0; margin-left: -1px; }
|
| 934 |
+
@media (max-width: 768px) { .sidebar { position: fixed; height: 100vh; transform: translateX(-100%); width: 260px !important; opacity: 1 !important; padding: 10px !important; } .sidebar.open { transform: translateX(0); } }
|
| 935 |
.sidebar-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 90; }
|
| 936 |
.new-chat-btn { display: flex; align-items: center; gap: 10px; width: 100%; padding: 12px; border-radius: 8px; background: rgba(255,255,255,0.05); color: #fff; font-weight: 500; font-size: 0.9rem; margin-bottom: 20px; transition: 0.2s; border: 1px solid rgba(255,255,255,0.1); }
|
| 937 |
.new-chat-btn:hover { background: rgba(255,255,255,0.1); border-color: rgba(255,255,255,0.2); }
|
frontend-next/app/page.tsx
CHANGED
|
@@ -134,8 +134,8 @@ const MessageRenderer = ({ content, isStreaming }: { content: string, isStreamin
|
|
| 134 |
);
|
| 135 |
}
|
| 136 |
|
| 137 |
-
// Match any Arxiv format number (YYYY.NNNNN) regardless of brackets or commas, and convert to Markdown link
|
| 138 |
-
let processedContent = displayed.replace(/\b(\d{4}\.\d{4,5})\b/g, '[$1](CITATION:$1)');
|
| 139 |
|
| 140 |
// Force $$ block math onto separate lines so remarkMath parses it tightly as centered block math
|
| 141 |
processedContent = processedContent.replace(/\$\$([\s\S]*?)\$\$/g, '\n\n$$\n$1\n$$\n\n');
|
|
@@ -238,8 +238,6 @@ export default function App() {
|
|
| 238 |
const [category, setCategory] = useState("All");
|
| 239 |
const [filterYear, setFilterYear] = useState("All");
|
| 240 |
const [apiStatus, setApiStatus] = useState<"connecting" | "online" | "offline">("connecting");
|
| 241 |
-
|
| 242 |
-
// UI Enhancements
|
| 243 |
const [desktopSidebarCollapsed, setDesktopSidebarCollapsed] = useState(false);
|
| 244 |
const [showScrollDown, setShowScrollDown] = useState(false);
|
| 245 |
const [showInfo, setShowInfo] = useState(false);
|
|
@@ -366,7 +364,8 @@ export default function App() {
|
|
| 366 |
id: aiMessageId,
|
| 367 |
role: "assistant",
|
| 368 |
content: "",
|
| 369 |
-
timestamp: Date.now() + 1
|
|
|
|
| 370 |
};
|
| 371 |
|
| 372 |
setSessions(prev => prev.map(s => {
|
|
@@ -542,8 +541,9 @@ export default function App() {
|
|
| 542 |
</div>
|
| 543 |
|
| 544 |
<div className="sidebar-footer">
|
| 545 |
-
<a href="https://github.com/07subhadip" target="_blank" rel="noopener noreferrer" className="github-link">
|
| 546 |
-
<svg viewBox="0 0 24 24" width="
|
|
|
|
| 547 |
</a>
|
| 548 |
</div>
|
| 549 |
</div>
|
|
@@ -593,14 +593,25 @@ export default function App() {
|
|
| 593 |
|
| 594 |
<AnimatePresence>
|
| 595 |
{showInfo && (
|
| 596 |
-
<div className="info-modal-backdrop" onClick={() => setShowInfo(false)} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.
|
| 597 |
<motion.div
|
| 598 |
-
initial={{ opacity: 0, scale: 0.95, y:
|
| 599 |
animate={{ opacity: 1, scale: 1, y: 0 }}
|
| 600 |
-
exit={{ opacity: 0, scale: 0.95, y:
|
| 601 |
onClick={(e) => e.stopPropagation()}
|
| 602 |
className="cyber-panel info-modal"
|
| 603 |
-
style={{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 604 |
>
|
| 605 |
<button className="modal-close" onClick={() => setShowInfo(false)} style={{ position: 'absolute', top: '16px', right: '16px', background: 'transparent', border: 'none', color: 'var(--text-muted)', cursor: 'pointer' }}>
|
| 606 |
<X size={18} />
|
|
@@ -625,7 +636,8 @@ export default function App() {
|
|
| 625 |
<li style={{ marginBottom: '8px' }}><strong>Frontend Application</strong> Next.js 16 (App Router), React, Framer Motion, Vanilla CSS.</li>
|
| 626 |
<li style={{ marginBottom: '8px' }}><strong>Backend Environment</strong> Python, FastAPI, Uvicorn, Pydantic.</li>
|
| 627 |
<li style={{ marginBottom: '8px' }}><strong>Vector Database Engine</strong> Qdrant (GPU Accelerated Dense Vectors).</li>
|
| 628 |
-
<li style={{ marginBottom: '8px' }}><strong>RAG Processing Pipeline</strong> SentenceTransformers (BGE-base), BM25 Sparse Search, Cross-Encoder Reranking
|
|
|
|
| 629 |
<li><strong>Mathematics Engine</strong> KaTeX & React-Markdown for fully dynamic native LaTeX equations.</li>
|
| 630 |
</ul>
|
| 631 |
<h3><Rocket size={18} style={{ display: 'inline', verticalAlign: 'middle', marginRight: '8px' }} /> Phase 2: In-Progress Architecture</h3>
|
|
@@ -661,8 +673,12 @@ export default function App() {
|
|
| 661 |
) : (
|
| 662 |
<div style={{ width: '100%' }}>
|
| 663 |
{/* Name header for AI */}
|
| 664 |
-
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '12px', fontSize: '0.
|
| 665 |
-
<div className="ai-avatar"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 666 |
</div>
|
| 667 |
|
| 668 |
<>
|
|
@@ -705,39 +721,65 @@ export default function App() {
|
|
| 705 |
</div>
|
| 706 |
|
| 707 |
{/* Bottom Input Area */}
|
| 708 |
-
<div className="bottom-input-bar"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 709 |
<div className="bottom-input-bar-inner">
|
| 710 |
{/* Settings Popup inline */}
|
| 711 |
<AnimatePresence>
|
| 712 |
{settingsOpen && (
|
| 713 |
<motion.div
|
| 714 |
-
initial={{ opacity: 0, y:
|
| 715 |
-
animate={{ opacity: 1, y: 0 }}
|
| 716 |
-
exit={{ opacity: 0, y:
|
| 717 |
-
style={{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 718 |
>
|
| 719 |
-
<
|
| 720 |
-
<
|
| 721 |
-
<
|
| 722 |
-
|
| 723 |
-
|
| 724 |
-
|
| 725 |
-
<
|
| 726 |
-
|
| 727 |
-
|
| 728 |
-
|
| 729 |
-
<
|
| 730 |
-
<
|
| 731 |
-
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
| 736 |
-
|
| 737 |
-
|
| 738 |
-
<
|
| 739 |
-
|
| 740 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 741 |
</motion.div>
|
| 742 |
)}
|
| 743 |
</AnimatePresence>
|
|
|
|
| 134 |
);
|
| 135 |
}
|
| 136 |
|
| 137 |
+
// Match any Arxiv format number (YYYY.NNNNN) regardless of brackets or commas, and convert to Markdown link. We eat the surrounding brackets to preserve design.
|
| 138 |
+
let processedContent = displayed.replace(/\[?\s*\b(\d{4}\.\d{4,5})\b\s*\]?/g, '[$1](CITATION:$1)');
|
| 139 |
|
| 140 |
// Force $$ block math onto separate lines so remarkMath parses it tightly as centered block math
|
| 141 |
processedContent = processedContent.replace(/\$\$([\s\S]*?)\$\$/g, '\n\n$$\n$1\n$$\n\n');
|
|
|
|
| 238 |
const [category, setCategory] = useState("All");
|
| 239 |
const [filterYear, setFilterYear] = useState("All");
|
| 240 |
const [apiStatus, setApiStatus] = useState<"connecting" | "online" | "offline">("connecting");
|
|
|
|
|
|
|
| 241 |
const [desktopSidebarCollapsed, setDesktopSidebarCollapsed] = useState(false);
|
| 242 |
const [showScrollDown, setShowScrollDown] = useState(false);
|
| 243 |
const [showInfo, setShowInfo] = useState(false);
|
|
|
|
| 364 |
id: aiMessageId,
|
| 365 |
role: "assistant",
|
| 366 |
content: "",
|
| 367 |
+
timestamp: Date.now() + 1,
|
| 368 |
+
model_used: "Auto-Detecting..."
|
| 369 |
};
|
| 370 |
|
| 371 |
setSessions(prev => prev.map(s => {
|
|
|
|
| 541 |
</div>
|
| 542 |
|
| 543 |
<div className="sidebar-footer">
|
| 544 |
+
<a href="https://github.com/07subhadip" target="_blank" rel="noopener noreferrer" className="github-link" style={{ display: 'flex', alignItems: 'center', gap: '10px', color: 'var(--accent)', textDecoration: 'none', fontSize: '0.9rem', padding: '12px', borderRadius: '8px', transition: '0.3s', background: 'rgba(0, 240, 255, 0.1)', border: '1px solid rgba(0, 240, 255, 0.3)', boxShadow: '0 0 15px rgba(0, 240, 255, 0.2)' }} onMouseOver={e => e.currentTarget.style.boxShadow = '0 0 25px rgba(0, 240, 255, 0.6)'} onMouseOut={e => e.currentTarget.style.boxShadow = '0 0 15px rgba(0, 240, 255, 0.2)'}>
|
| 545 |
+
<svg viewBox="0 0 24 24" width="18" height="18" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" className="lucide lucide-github"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg>
|
| 546 |
+
<span style={{ fontWeight: 600, letterSpacing: '0.05em' }}>@07subhadip</span>
|
| 547 |
</a>
|
| 548 |
</div>
|
| 549 |
</div>
|
|
|
|
| 593 |
|
| 594 |
<AnimatePresence>
|
| 595 |
{showInfo && (
|
| 596 |
+
<div className="info-modal-backdrop" onClick={() => setShowInfo(false)} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.8)', backdropFilter: 'blur(8px)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
| 597 |
<motion.div
|
| 598 |
+
initial={{ opacity: 0, scale: 0.95, y: 30 }}
|
| 599 |
animate={{ opacity: 1, scale: 1, y: 0 }}
|
| 600 |
+
exit={{ opacity: 0, scale: 0.95, y: 30 }}
|
| 601 |
onClick={(e) => e.stopPropagation()}
|
| 602 |
className="cyber-panel info-modal"
|
| 603 |
+
style={{
|
| 604 |
+
background: 'linear-gradient(135deg, rgba(20, 25, 40, 0.95), rgba(10, 15, 25, 0.98))',
|
| 605 |
+
border: '1px solid rgba(0, 240, 255, 0.3)',
|
| 606 |
+
padding: '40px',
|
| 607 |
+
borderRadius: '24px',
|
| 608 |
+
maxWidth: '650px',
|
| 609 |
+
width: '90%',
|
| 610 |
+
position: 'relative',
|
| 611 |
+
maxHeight: '85vh',
|
| 612 |
+
overflowY: 'auto',
|
| 613 |
+
boxShadow: '0 20px 50px rgba(0, 0, 0, 0.9), 0 0 40px rgba(0, 240, 255, 0.1)'
|
| 614 |
+
}}
|
| 615 |
>
|
| 616 |
<button className="modal-close" onClick={() => setShowInfo(false)} style={{ position: 'absolute', top: '16px', right: '16px', background: 'transparent', border: 'none', color: 'var(--text-muted)', cursor: 'pointer' }}>
|
| 617 |
<X size={18} />
|
|
|
|
| 636 |
<li style={{ marginBottom: '8px' }}><strong>Frontend Application</strong> Next.js 16 (App Router), React, Framer Motion, Vanilla CSS.</li>
|
| 637 |
<li style={{ marginBottom: '8px' }}><strong>Backend Environment</strong> Python, FastAPI, Uvicorn, Pydantic.</li>
|
| 638 |
<li style={{ marginBottom: '8px' }}><strong>Vector Database Engine</strong> Qdrant (GPU Accelerated Dense Vectors).</li>
|
| 639 |
+
<li style={{ marginBottom: '8px' }}><strong>RAG Processing Pipeline</strong> SentenceTransformers (BGE-base-en-v1.5), BM25 Sparse Search, Cross-Encoder Reranking.</li>
|
| 640 |
+
<li style={{ marginBottom: '8px' }}><strong>Multi-Modal LLM Fabric</strong> Dynamic routing between Qwen 2.5 72B (Primary), LLaMA 3.3 70B (Fallback), and Qwen 2.5 Coder 7B (Code).</li>
|
| 641 |
<li><strong>Mathematics Engine</strong> KaTeX & React-Markdown for fully dynamic native LaTeX equations.</li>
|
| 642 |
</ul>
|
| 643 |
<h3><Rocket size={18} style={{ display: 'inline', verticalAlign: 'middle', marginRight: '8px' }} /> Phase 2: In-Progress Architecture</h3>
|
|
|
|
| 673 |
) : (
|
| 674 |
<div style={{ width: '100%' }}>
|
| 675 |
{/* Name header for AI */}
|
| 676 |
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '12px', fontSize: '0.85rem', color: 'var(--accent)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
| 677 |
+
<div className="ai-avatar" style={{ background: 'rgba(0, 240, 255, 0.1)', border: '1px solid rgba(0, 240, 255, 0.3)', padding: '6px', borderRadius: '8px' }}><Brain size={14} /></div>
|
| 678 |
+
ResearchPilot
|
| 679 |
+
<span className="model-badge" style={{ background: 'rgba(255,255,255,0.05)', padding: '2px 8px', borderRadius: '4px', border: '1px solid rgba(255,255,255,0.1)', color: 'var(--text-muted)', fontSize: '0.75rem' }}>
|
| 680 |
+
{msg.model_used || "Auto-Detecting..."}
|
| 681 |
+
</span>
|
| 682 |
</div>
|
| 683 |
|
| 684 |
<>
|
|
|
|
| 721 |
</div>
|
| 722 |
|
| 723 |
{/* Bottom Input Area */}
|
| 724 |
+
<div className="bottom-input-bar" style={{
|
| 725 |
+
left: desktopSidebarCollapsed ? '0' : '260px',
|
| 726 |
+
transition: 'left 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
| 727 |
+
width: 'auto'
|
| 728 |
+
}}>
|
| 729 |
<div className="bottom-input-bar-inner">
|
| 730 |
{/* Settings Popup inline */}
|
| 731 |
<AnimatePresence>
|
| 732 |
{settingsOpen && (
|
| 733 |
<motion.div
|
| 734 |
+
initial={{ opacity: 0, y: 15, scale: 0.95 }}
|
| 735 |
+
animate={{ opacity: 1, y: 0, scale: 1 }}
|
| 736 |
+
exit={{ opacity: 0, y: 15, scale: 0.95 }}
|
| 737 |
+
style={{
|
| 738 |
+
background: 'rgba(15, 20, 30, 0.95)',
|
| 739 |
+
backdropFilter: 'blur(20px)',
|
| 740 |
+
border: '1px solid rgba(0, 240, 255, 0.2)',
|
| 741 |
+
padding: '16px',
|
| 742 |
+
borderRadius: '16px',
|
| 743 |
+
display: 'flex',
|
| 744 |
+
gap: '16px',
|
| 745 |
+
flexWrap: 'wrap',
|
| 746 |
+
marginBottom: '12px',
|
| 747 |
+
boxShadow: '0 10px 40px rgba(0,0,0,0.5)'
|
| 748 |
+
}}
|
| 749 |
>
|
| 750 |
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
| 751 |
+
<label style={{ fontSize: '0.7rem', color: 'var(--text-muted)', fontWeight: 700, marginLeft: '4px' }}>RETRIEVAL DEPTH</label>
|
| 752 |
+
<select style={{ background: 'rgba(0,0,0,0.4)', border: '1px solid rgba(255,255,255,0.1)', color: '#fff', padding: '8px 12px', borderRadius: '8px', cursor: 'pointer', outline: 'none', minWidth: '100px' }} value={topK} onChange={(e) => setTopK(Number(e.target.value))}>
|
| 753 |
+
<option value={3}>Fast (3 Papers)</option>
|
| 754 |
+
<option value={5}>Balanced (5 Papers)</option>
|
| 755 |
+
<option value={10}>Deep (10 Papers)</option>
|
| 756 |
+
</select>
|
| 757 |
+
</div>
|
| 758 |
+
|
| 759 |
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
| 760 |
+
<label style={{ fontSize: '0.7rem', color: 'var(--text-muted)', fontWeight: 700, marginLeft: '4px' }}>RESEARCH DOMAIN</label>
|
| 761 |
+
<select style={{ background: 'rgba(0,0,0,0.4)', border: '1px solid rgba(255,255,255,0.1)', color: '#fff', padding: '8px 12px', borderRadius: '8px', cursor: 'pointer', outline: 'none' }} value={category} onChange={(e) => setCategory(e.target.value)}>
|
| 762 |
+
<option value="All">Global Search</option>
|
| 763 |
+
<option value="cs.LG">cs.LG (Machine Learning)</option>
|
| 764 |
+
<option value="cs.AI">cs.AI (Artificial Intelligence)</option>
|
| 765 |
+
<option value="stat.ML">stat.ML (ML Stats)</option>
|
| 766 |
+
<option value="cs.CL">cs.CL (NLP/Language)</option>
|
| 767 |
+
<option value="cs.CV">cs.CV (Vision)</option>
|
| 768 |
+
<option value="cs.RO">cs.RO (Robotics)</option>
|
| 769 |
+
</select>
|
| 770 |
+
</div>
|
| 771 |
+
|
| 772 |
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
| 773 |
+
<label style={{ fontSize: '0.7rem', color: 'var(--text-muted)', fontWeight: 700, marginLeft: '4px' }}>RECENCY FILTER</label>
|
| 774 |
+
<select style={{ background: 'rgba(0,0,0,0.4)', border: '1px solid rgba(255,255,255,0.1)', color: '#fff', padding: '8px 12px', borderRadius: '8px', cursor: 'pointer', outline: 'none' }} value={filterYear} onChange={(e) => setFilterYear(e.target.value)}>
|
| 775 |
+
<option value="All">Legacy & Modern</option>
|
| 776 |
+
<option value="2024">2024 (Latest)</option>
|
| 777 |
+
<option value="2023">2023+</option>
|
| 778 |
+
<option value="2022">2022+</option>
|
| 779 |
+
<option value="2021">2021+</option>
|
| 780 |
+
<option value="2020">2020+</option>
|
| 781 |
+
</select>
|
| 782 |
+
</div>
|
| 783 |
</motion.div>
|
| 784 |
)}
|
| 785 |
</AnimatePresence>
|