From 2dabaa3b7ab9afe282d6ac902ce398f2662612c4 Mon Sep 17 00:00:00 2001 From: Yash Vardhan Date: Mon, 10 Aug 2026 01:32:12 +0530 Subject: [PATCH 1/3] chore: improve UI and UX for better perfomance metrics --- frontend/app/[topic]/TopicLayout.tsx | 285 +++++++++++++++- frontend/app/page.tsx | 468 ++++++++++++++++++--------- frontend/package-lock.json | 10 + frontend/package.json | 1 + 4 files changed, 604 insertions(+), 160 deletions(-) diff --git a/frontend/app/[topic]/TopicLayout.tsx b/frontend/app/[topic]/TopicLayout.tsx index 389c5a3..b260e6c 100644 --- a/frontend/app/[topic]/TopicLayout.tsx +++ b/frontend/app/[topic]/TopicLayout.tsx @@ -2,15 +2,20 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { useTheme } from "../../src/context/ThemeContext"; import { useRouter } from "next/navigation"; +import { FaGithub} from "react-icons/fa"; import { ArrowLeft, + ArrowUp, BookOpenText, Braces, ChevronDown, + ChevronRight, Code2, + Menu, Search, TerminalSquare, Workflow, + X, } from "lucide-react"; export default function TopicLayout({ @@ -38,17 +43,48 @@ export default function TopicLayout({ Array<{ id: string; title: string; depth: number }> >([]); + // Mobile navigation state + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + const [isMobileTocOpen, setIsMobileTocOpen] = useState(false); + + // Scroll to Top state + const [showScrollTop, setShowScrollTop] = useState(false); + + // UX Enhancements: OS detection for the shortcut hint const [isMounted, setIsMounted] = useState(false); const [modifierKey, setModifierKey] = useState("⌘"); useEffect(() => { setIsMounted(true); + // Detect if the user is on a Mac or Apple device if (typeof window !== "undefined") { const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform); setModifierKey(isMac ? "⌘" : "Ctrl "); } }, []); + // Prevent background scrolling when mobile menu is open + useEffect(() => { + if (isMobileMenuOpen) { + document.body.style.overflow = "hidden"; + } else { + document.body.style.overflow = ""; + } + return () => { + document.body.style.overflow = ""; + }; + }, [isMobileMenuOpen]); + + // Scroll to Top listener + useEffect(() => { + const handleScroll = () => { + setShowScrollTop(window.scrollY > 400); + }; + + window.addEventListener("scroll", handleScroll, { passive: true }); + return () => window.removeEventListener("scroll", handleScroll); + }, []); + useEffect(() => { const article = document.querySelector("article"); const headingNodes = article @@ -101,6 +137,7 @@ export default function TopicLayout({ return [...sectionMatches, ...topicMatches].slice(0, 12); }, [activeHeadings, hasSearchQuery, normalizedQuery, topics]); + // Decoupled outlineGroups from search state const outlineGroups = useMemo(() => { const groups: Array<{ id: string; @@ -203,7 +240,7 @@ export default function TopicLayout({ const theme = { page: darkMode ? "bg-[#050505] text-[#F5F5F5]" : "bg-[#F5F3EE] text-[#111111]", - header: darkMode ? "bg-[#050505]/85" : "bg-[#F5F3EE]/85", + header: darkMode ? "bg-[#050505]/85" : "bg-[#F5F3EE]/85", panel: darkMode ? "bg-[#050505]" : "bg-[#F5F3EE]", border: darkMode ? "border-[#1A1A1A]" : "border-[#DFE1DA]", muted: darkMode ? "text-[#8A8A8A]" : "text-[#666C73]", @@ -237,24 +274,114 @@ export default function TopicLayout({ window.scrollTo({ top, behavior: "smooth" }); }; + const scrollToTop = () => { + window.scrollTo({ top: 0, behavior: "smooth" }); + }; + + // Helper to extract the proper title for the breadcrumb + const currentTopicTitle = topics.find((t) => t.id === topicKey)?.title || frontmatter?.title || topicKey; + return (
- {/* HEADER: Strictly fixed to h-16 (64px) with a translucent background for the blur to work */} + + {/* MOBILE DRAWER OVERLAY */} + {isMobileMenuOpen && ( +
setIsMobileMenuOpen(false)} + /> + )} + + {/* MOBILE DRAWER SIDEBAR */} +
+
+
+ RefMe + docs +
+ +
+ +
+
+ +
+ +
+ {topics.map((item) => { + const isActive = item.id === topicKey; + const TopicIcon = getTopicIcon(item.id); + + return ( + + ); + })} +
+
+
+ + {/* HEADER */}
-
-
-
+
+
+ {/* MOBILE HAMBURGER BUTTON */} + + +
R_
-
+
RefMe docs
-
+
{/* SEARCH BAR */} @@ -284,7 +411,7 @@ export default function TopicLayout({ Clear ) : ( - + {modifierKey}K )} @@ -334,7 +461,7 @@ export default function TopicLayout({ @@ -343,7 +470,7 @@ export default function TopicLayout({
- {/* LEFT SIDEBAR: Strictly constrained to top-16 (64px) to perfectly match the h-16 header */} + {/* DESKTOP LEFT SIDEBAR */} {/* MAIN CONTENT AREA */} -
+
+ + {/* MOBILE IN-PAGE TOC */} + {outlineGroups.length > 0 && ( +
+ + +
+
+
+ {outlineGroups.map((group) => { + const isActiveGroup = activeOutlineId === group.id; + + return ( + + ); + })} +
+
+
+
+ )} + + {/* BREADCRUMBS */} + +
{children}
+ + {/* USER ENGAGEMENT & GITHUB EDIT */} +
+
+ Was this page helpful? +
+ + +
+
+ + + Edit this page on GitHub + +
+
{/* BOTTOM PAGINATION */} @@ -427,7 +675,7 @@ export default function TopicLayout({
- {/* RIGHT SIDEBAR: Strictly constrained to top-16 (64px) to perfectly match the h-16 header */} + {/* DESKTOP RIGHT SIDEBAR */}
+ + {/* SCROLL TO TOP FAB */} +
); } \ No newline at end of file diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 8c13d9e..9e83473 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,10 +1,10 @@ "use client"; -import DirectoryList from "./components/DirectoryList"; import { useTheme } from "../src/context/ThemeContext"; -import { useState } from "react"; +import { useState, useMemo, useEffect, useRef } from "react"; +import Link from "next/link"; +import { FaGithub, FaLinkedin } from "react-icons/fa"; import { - ArrowRight, Binary, Braces, Code2, @@ -21,202 +21,376 @@ import { export const REFERENCE_DATA = [ { - category: "Core languages", + category: "Core Languages", icon: , items: [ - { name: "C++", href: "/cpp", icon: , desc: "Low-level systems, performance, and STL patterns.", size: "normal" }, - { name: "JavaScript", href: "/javascript", icon: , desc: "Modern syntax, async flows, and browser fundamentals.", size: "large" }, - { name: "Python", href: "/python", icon: , desc: "Data work, automation, and clean backend scripting.", size: "normal" }, - { name: "TypeScript", href: "/typescript", icon: , desc: "Types, interfaces, and safer large-scale JavaScript systems.", size: "normal" }, - { name: "Go", href: "/go", icon: , desc: "Simple concurrency, tooling, and service-oriented code.", size: "normal" }, - { name: "Rust", href: "/rust", icon: , desc: "Ownership, performance, and safety-first systems design.", size: "normal" }, - { name: "Java", href: "/java", icon: , desc: "Enterprise patterns, JVM tooling, and backend architecture.", size: "normal" }, - { name: "C#", href: "/csharp", icon: , desc: "Strong typing, .NET frameworks, and application structure.", size: "normal" }, + { name: "C++", href: "/cpp", icon: , desc: "Low-level systems, performance, and STL patterns." }, + { name: "JavaScript", href: "/javascript", icon: , desc: "Modern syntax, async flows, and browser fundamentals." }, + { name: "Python", href: "/python", icon: , desc: "Data work, automation, and clean backend scripting." }, + { name: "TypeScript", href: "/typescript", icon: , desc: "Types, interfaces, and safer large-scale JavaScript systems." }, + { name: "Go", href: "/go", icon: , desc: "Simple concurrency, tooling, and service-oriented code." }, + { name: "Rust", href: "/rust", icon: , desc: "Ownership, performance, and safety-first systems design." }, + { name: "Java", href: "/java", icon: , desc: "Enterprise patterns, JVM tooling, and backend architecture." }, + { name: "C#", href: "/csharp", icon: , desc: "Strong typing, .NET frameworks, and application structure." }, ], }, { - category: "Frontend & web", + category: "Frontend & Web", icon: , items: [ - { name: "React", href: "/react", icon: , desc: "Components, state, effects, and rendering patterns.", size: "large" }, - { name: "Next.js", href: "/nextjs", icon: , desc: "App routing, server rendering, and performance patterns.", size: "normal" }, - { name: "CSS", href: "/css", icon: , desc: "Layout, design systems, and modern responsive styling.", size: "normal" }, - { name: "HTML", href: "/html", icon: , desc: "Structure, semantics, and accessible document foundations.", size: "normal" }, - { name: "Tailwind", href: "/tailwind", icon: , desc: "Utility-first styling, tokens, and rapid UI composition.", size: "normal" }, - { name: "Accessibility", href: "/accessibility", icon: , desc: "Semantic structure, keyboard flows, and inclusive UI.", size: "normal" }, + { name: "React", href: "/react", icon: , desc: "Components, state, effects, and rendering patterns." }, + { name: "Next.js", href: "/nextjs", icon: , desc: "App routing, server rendering, and performance patterns." }, + { name: "CSS", href: "/css", icon: , desc: "Layout, design systems, and modern responsive styling." }, + { name: "HTML", href: "/html", icon: , desc: "Structure, semantics, and accessible document foundations." }, + { name: "Tailwind", href: "/tailwind", icon: , desc: "Utility-first styling, tokens, and rapid UI composition." }, + { name: "Accessibility", href: "/accessibility", icon: , desc: "Semantic structure, keyboard flows, and inclusive UI." }, ], }, { - category: "Backend & systems", + category: "Backend & Systems", icon: , items: [ - { name: "Bash", href: "/bash", icon: , desc: "Shell workflows, pipelines, and automation basics.", size: "normal" }, - { name: "Node.js", href: "/nodejs", icon: , desc: "Runtime fundamentals and server-side JavaScript patterns.", size: "normal" }, - { name: "SQL", href: "/sql", icon: , desc: "Queries, joins, indexing, and data modeling essentials.", size: "normal" }, - { name: "APIs", href: "/apis", icon: , desc: "Request patterns, auth flows, and contract design.", size: "normal" }, - { name: "Docker", href: "/docker", icon: , desc: "Containers, images, and deployment isolation basics.", size: "normal" }, - { name: "Git", href: "/git", icon: , desc: "Branching, commits, and collaboration workflows.", size: "normal" }, + { name: "Bash", href: "/bash", icon: , desc: "Shell workflows, pipelines, and automation basics." }, + { name: "Node.js", href: "/nodejs", icon: , desc: "Runtime fundamentals and server-side JavaScript patterns." }, + { name: "SQL", href: "/sql", icon: , desc: "Queries, joins, indexing, and data modeling essentials." }, + { name: "APIs", href: "/apis", icon: , desc: "Request patterns, auth flows, and contract design." }, + { name: "Docker", href: "/docker", icon: , desc: "Containers, images, and deployment isolation basics." }, + { name: "Git", href: "/git", icon: , desc: "Branching, commits, and collaboration workflows." }, ], }, { - category: "Concepts", + category: "Concepts & Patterns", icon: , items: [ - { name: "Async", href: "/async", icon: , desc: "Promises, async/await, event loops, and scheduling.", size: "normal" }, - { name: "Testing", href: "/testing", icon: , desc: "Unit tests, integration coverage, and reliability habits.", size: "normal" }, - { name: "Security", href: "/security", icon: , desc: "Auth, validation, and safer defaults for production code.", size: "normal" }, - { name: "Data Structures", href: "/data-structures", icon: , desc: "Arrays, trees, hash maps, and choosing the right shape.", size: "normal" }, - { name: "Algorithms", href: "/algorithms", icon: , desc: "Sorting, traversal, recursion, and complexity trade-offs.", size: "normal" }, - { name: "System Design", href: "/system-design", icon: , desc: "Scalability, resilience, and architecture composition.", size: "normal" }, - { name: "State Management", href: "/state-management", icon: , desc: "Global UI state, immutability, and predictable app flows.", size: "normal" }, - { name: "OAuth", href: "/oauth", icon: , desc: "Delegated access, tokens, and identity flows in real apps.", size: "normal" }, - { name: "Caching", href: "/caching", icon: , desc: "Read-through patterns, invalidation, and latency reduction.", size: "normal" }, - { name: "Web Security", href: "/web-security", icon: , desc: "XSS, CSRF, headers, and safer web defaults.", size: "normal" }, - { name: "Design Patterns", href: "/design-patterns", icon: , desc: "Factory, strategy, observer, and reusable software shapes.", size: "normal" }, - { name: "Dependency Injection", href: "/dependency-injection", icon: , desc: "Loose coupling, wiring, and testable architecture.", size: "normal" }, - { name: "Validation", href: "/validation", icon: , desc: "Input checks, schema rules, and safer user data flows.", size: "normal" }, + { name: "Async", href: "/async", icon: , desc: "Promises, async/await, event loops, and scheduling." }, + { name: "Testing", href: "/testing", icon: , desc: "Unit tests, integration coverage, and reliability habits." }, + { name: "Security", href: "/security", icon: , desc: "Auth, validation, and safer defaults for production code." }, + { name: "Data Structures", href: "/data-structures", icon: , desc: "Arrays, trees, hash maps, and choosing the right shape." }, + { name: "Algorithms", href: "/algorithms", icon: , desc: "Sorting, traversal, recursion, and complexity trade-offs." }, + { name: "System Design", href: "/system-design", icon: , desc: "Scalability, resilience, and architecture composition." }, + { name: "State Management", href: "/state-management", icon: , desc: "Global UI state, immutability, and predictable app flows." }, + { name: "OAuth", href: "/oauth", icon: , desc: "Delegated access, tokens, and identity flows in real apps." }, + { name: "Caching", href: "/caching", icon: , desc: "Read-through patterns, invalidation, and latency reduction." }, + { name: "Web Security", href: "/web-security", icon: , desc: "XSS, CSRF, headers, and safer web defaults." }, + { name: "Design Patterns", href: "/design-patterns", icon: , desc: "Factory, strategy, observer, and reusable software shapes." }, + { name: "Dependency Injection", href: "/dependency-injection", icon: , desc: "Loose coupling, wiring, and testable architecture." }, + { name: "Validation", href: "/validation", icon: , desc: "Input checks, schema rules, and safer user data flows." }, ], }, { - category: "Architecture & infrastructure", + category: "Architecture & DevOps", icon: , items: [ - { name: "Microservices", href: "/microservices", icon: , desc: "Service boundaries, contracts, and deployment autonomy.", size: "normal" }, - { name: "Event-Driven", href: "/event-driven", icon: , desc: "Streams, decoupling, and asynchronous communication patterns.", size: "normal" }, - { name: "Load Balancing", href: "/load-balancing", icon: , desc: "Traffic distribution, resilience, and horizontal scale.", size: "normal" }, - { name: "Kubernetes", href: "/kubernetes", icon: , desc: "Container orchestration, scaling, and service management.", size: "normal" }, - { name: "Deployment", href: "/deployment", icon: , desc: "Release flow, environments, rollout safety, and automation.", size: "normal" }, - { name: "Monitoring", href: "/monitoring", icon: , desc: "Metrics, logs, traces, and runtime health visibility.", size: "normal" }, - { name: "Cloud Architecture", href: "/cloud-architecture", icon: , desc: "Multi-region design, edge services, and resilient platforms.", size: "normal" }, - { name: "Databases", href: "/databases", icon: , desc: "Storage models, schemas, indexes, and query trade-offs.", size: "normal" }, - { name: "Reliability", href: "/reliability", icon: , desc: "Failover, redundancy, and designing for graceful degradation.", size: "normal" }, + { name: "Microservices", href: "/microservices", icon: , desc: "Service boundaries, contracts, and deployment autonomy." }, + { name: "Event-Driven", href: "/event-driven", icon: , desc: "Streams, decoupling, and asynchronous communication patterns." }, + { name: "Load Balancing", href: "/load-balancing", icon: , desc: "Traffic distribution, resilience, and horizontal scale." }, + { name: "Kubernetes", href: "/kubernetes", icon: , desc: "Container orchestration, scaling, and service management." }, + { name: "Deployment", href: "/deployment", icon: , desc: "Release flow, environments, rollout safety, and automation." }, + { name: "Monitoring", href: "/monitoring", icon: , desc: "Metrics, logs, traces, and runtime health visibility." }, + { name: "Cloud Architecture", href: "/cloud-architecture", icon: , desc: "Multi-region design, edge services, and resilient platforms." }, + { name: "Databases", href: "/databases", icon: , desc: "Storage models, schemas, indexes, and query trade-offs." }, + { name: "Reliability", href: "/reliability", icon: , desc: "Failover, redundancy, and designing for graceful degradation." }, ], }, { category: "Data & AI", icon: , items: [ - { name: "Data Science", href: "/data-science", icon: , desc: "Analysis, notebooks, and patterns for working with data.", size: "normal" }, - { name: "ML", href: "/ml", icon: , desc: "Model basics, pipelines, and practical deployment notes.", size: "normal" }, - { name: "LLMs", href: "/llms", icon: , desc: "Prompt design, structured outputs, and app integration basics.", size: "normal" }, - { name: "Vectors", href: "/vectors", icon: , desc: "Embeddings, similarity search, and retrieval patterns.", size: "normal" }, - { name: "Prompt Engineering", href: "/prompt-engineering", icon: , desc: "Instruction design, context framing, and reliable outputs.", size: "normal" }, - { name: "RAG", href: "/rag", icon: , desc: "Grounded generation with retrieval and relevance filtering.", size: "normal" }, - { name: "Model Evaluation", href: "/model-evaluation", icon: , desc: "Benchmarking, scoring, and quality checks for outputs.", size: "normal" }, - { name: "AI Safety", href: "/ai-safety", icon: , desc: "Guardrails, risk mitigation, and responsible model usage.", size: "normal" }, - { name: "Agents", href: "/agents", icon: , desc: "Tool use, planning loops, and autonomous execution patterns.", size: "normal" }, + { name: "Data Science", href: "/data-science", icon: , desc: "Analysis, notebooks, and patterns for working with data." }, + { name: "Machine Learning", href: "/ml", icon: , desc: "Model basics, pipelines, and practical deployment notes." }, + { name: "LLMs", href: "/llms", icon: , desc: "Prompt design, structured outputs, and app integration basics." }, + { name: "Vectors", href: "/vectors", icon: , desc: "Embeddings, similarity search, and retrieval patterns." }, + { name: "Prompt Engineering", href: "/prompt-engineering", icon: , desc: "Instruction design, context framing, and reliable outputs." }, + { name: "RAG", href: "/rag", icon: , desc: "Grounded generation with retrieval and relevance filtering." }, + { name: "Model Evaluation", href: "/model-evaluation", icon: , desc: "Benchmarking, scoring, and quality checks for outputs." }, + { name: "AI Safety", href: "/ai-safety", icon: , desc: "Guardrails, risk mitigation, and responsible model usage." }, + { name: "Agents", href: "/agents", icon: , desc: "Tool use, planning loops, and autonomous execution patterns." }, ], }, { - category: "Dev tools", + category: "Developer Tools", icon: , items: [ - { name: "VS Code", href: "/vscode", icon: , desc: "Editor workflows, extensions, and keyboard efficiency.", size: "normal" }, - { name: "Linux", href: "/linux", icon: , desc: "Filesystems, processes, permissions, and command-line habits.", size: "normal" }, - { name: "CI/CD", href: "/ci-cd", icon: , desc: "Pipelines, automation, and release safety checks.", size: "normal" }, - { name: "Observability", href: "/observability", icon: , desc: "Logging, tracing, and debugging production systems.", size: "normal" }, - { name: "Nginx", href: "/nginx", icon: , desc: "Reverse proxying, performance tuning, and request routing.", size: "normal" }, - { name: "Terraform", href: "/terraform", icon: , desc: "Infrastructure as code, repeatable environments, and provisioning.", size: "normal" }, - { name: "GitHub Actions", href: "/github-actions", icon: , desc: "CI pipelines, automation, and shipping code confidently.", size: "normal" }, - { name: "Webpack", href: "/webpack", icon: , desc: "Bundling, module resolution, and frontend build optimization.", size: "normal" }, - { name: "Vercel", href: "/vercel", icon: , desc: "Edge deployment, previews, and modern app delivery.", size: "normal" }, - { name: "Debugging", href: "/debugging", icon: , desc: "Root cause analysis, traces, breakpoints, and narrowing bugs.", size: "normal" }, + { name: "VS Code", href: "/vscode", icon: , desc: "Editor workflows, extensions, and keyboard efficiency." }, + { name: "Linux", href: "/linux", icon: , desc: "Filesystems, processes, permissions, and command-line habits." }, + { name: "CI/CD", href: "/ci-cd", icon: , desc: "Pipelines, automation, and release safety checks." }, + { name: "Observability", href: "/observability", icon: , desc: "Logging, tracing, and debugging production systems." }, + { name: "Nginx", href: "/nginx", icon: , desc: "Reverse proxying, performance tuning, and request routing." }, + { name: "Terraform", href: "/terraform", icon: , desc: "Infrastructure as code, repeatable environments, and provisioning." }, + { name: "GitHub Actions", href: "/github-actions", icon: , desc: "CI pipelines, automation, and shipping code confidently." }, + { name: "Webpack", href: "/webpack", icon: , desc: "Bundling, module resolution, and frontend build optimization." }, + { name: "Vercel", href: "/vercel", icon: , desc: "Edge deployment, previews, and modern app delivery." }, + { name: "Debugging", href: "/debugging", icon: , desc: "Root cause analysis, traces, breakpoints, and narrowing bugs." }, ], }, ]; -export default function RefMeHero() { +// Helper component to highlight matched text +function HighlightText({ text, highlight, highlightStyle }: { text: string; highlight: string; highlightStyle: string }) { + if (!highlight.trim()) return <>{text}; + + const regex = new RegExp(`(${highlight})`, "gi"); + const parts = text.split(regex); + + return ( + <> + {parts.map((part, i) => + regex.test(part) ? ( + + {part} + + ) : ( + {part} + ) + )} + + ); +} + +export default function LandingPage() { const { darkMode, toggleTheme } = useTheme(); + + // Search bar states & refs const [searchQuery, setSearchQuery] = useState(""); + const searchInputRef = useRef(null); + const [isMounted, setIsMounted] = useState(false); + const [modifierKey, setModifierKey] = useState("⌘"); const theme = { - page: darkMode ? "bg-[#050505] text-[#f5f5f5]" : "bg-[#f3f0ea] text-[#111111]", - panel: darkMode ? "bg-[#0d0d0d] border-[#1f1f1f]" : "bg-[#f8f6f2] border-[#e1dfd8]", - soft: darkMode ? "bg-[#121212] border-[#262626]" : "bg-[#f3f1ec] border-[#e1dfd8]", - border: darkMode ? "border-[#1f1f1f]" : "border-[#e1dfd8]", - muted: darkMode ? "text-[#9b9b9b]" : "text-[#666a73]", - accent: darkMode ? "text-zinc-200" : "text-zinc-700", - dim: darkMode ? "text-[#b8b8b8]" : "text-[#4d4d4d]", + page: darkMode ? "bg-[#050505] text-[#f5f5f5]" : "bg-[#fcfbf9] text-[#111111]", + header: darkMode ? "bg-[#050505]/85" : "bg-[#fcfbf9]/85", + panel: darkMode ? "bg-[#0d0d0d]" : "bg-white", + border: darkMode ? "border-white/10" : "border-black/10", + muted: darkMode ? "text-zinc-400" : "text-zinc-500", + accent: darkMode ? "text-zinc-100" : "text-zinc-900", + // Clean, professional hover states without translations + cardHover: darkMode + ? "hover:border-[#C699FF]/40 hover:bg-[#C699FF]/[0.03]" + : "hover:border-[#6f45d6]/40 hover:bg-[#6f45d6]/[0.03]", + iconBox: darkMode ? "bg-white/5 border-white/10" : "bg-black/5 border-black/10", + iconBoxHover: darkMode + ? "group-hover:border-[#C699FF]/40 group-hover:text-[#C699FF] group-hover:bg-[#C699FF]/10" + : "group-hover:border-[#6f45d6]/40 group-hover:text-[#6f45d6] group-hover:bg-[#6f45d6]/10", + highlightText: darkMode ? "bg-[#C699FF]/25 text-[#C699FF]" : "bg-[#6f45d6]/15 text-[#6f45d6]", + }; + + // Keyboard shortcut detection + useEffect(() => { + setIsMounted(true); + if (typeof window !== "undefined") { + const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform); + setModifierKey(isMac ? "⌘" : "Ctrl "); + } + }, []); + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + const isModifier = event.metaKey || event.ctrlKey; + if (!isModifier || event.key.toLowerCase() !== "k") return; + + event.preventDefault(); + searchInputRef.current?.focus(); + searchInputRef.current?.select(); + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, []); + + const handleInputKeyDown = (event: React.KeyboardEvent) => { + if (event.key === "Escape") { + setSearchQuery(""); + searchInputRef.current?.blur(); + } }; + // Fast client-side filtering logic + const filteredData = useMemo(() => { + if (!searchQuery.trim()) return REFERENCE_DATA; + + const query = searchQuery.toLowerCase(); + return REFERENCE_DATA.map((category) => ({ + ...category, + items: category.items.filter( + (item) => + item.name.toLowerCase().includes(query) || + item.desc.toLowerCase().includes(query) + ), + })).filter((category) => category.items.length > 0); + }, [searchQuery]); + return ( -
-
-
-
-
- Developer reference library -
+ {/* HERO SECTION */} +
+
+

+ The developer's
quick-reference guide. +

-

- Clear reference material for modern engineering work. -

-
+

+ Find the syntax, patterns, and implementation notes you need without digging through scattered docs. Built for speed and focus. +

-

- Find the syntax, patterns, and implementation notes you need without digging through scattered docs. -

- -
- - - - Explore - - + {/* SEARCH BAR (Refactored to match TopicLayout component) */} +
+
+ + setSearchQuery(e.target.value)} + onKeyDown={handleInputKeyDown} + placeholder="Search algorithms, frameworks, or concepts..." + className="w-full bg-transparent py-4 pl-12 pr-20 text-base outline-none placeholder:opacity-50" + /> + + {/* Absolute positioned elements to prevent layout shift */} +
+ {searchQuery ? ( + + ) : ( + + {modifierKey}K + + )}
+
+
+
-
- Documentation-first - • - Concise notes - • - Built for speed -
+ {/* CATEGORY GRIDS */} +
+ {filteredData.length === 0 ? ( +
+

+ No references found for "{searchQuery}". +

- + ) : ( +
+ {filteredData.map((category) => ( +
+
+ + {category.icon} + +

+ {category.category} +

+
+ +
+ {category.items.map((item) => ( + +
+
+ {item.icon} +
+

+ +

+
+

+ +

+ + ))} +
+
+ ))} +
+ )} +
-
- -
-
+ {/* FOOTER */} +
); -} +} \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 77aa383..56c79ad 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,6 +15,7 @@ "next-mdx-remote": "^6.0.0", "react": "19.2.4", "react-dom": "19.2.4", + "react-icons": "^5.7.0", "react-syntax-highlighter": "^16.1.1" }, "devDependencies": { @@ -7026,6 +7027,15 @@ "react": "^19.2.4" } }, + "node_modules/react-icons": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz", + "integrity": "sha512-LBLy340Rzqy6+/yVhZKT3B/QpP1BZaesGqasf09HPOBzRarcDIFH0WwXlXQfE7q7ipxK4MSiC5DIBWURCny6fw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 52c5a76..9902e22 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,6 +16,7 @@ "next-mdx-remote": "^6.0.0", "react": "19.2.4", "react-dom": "19.2.4", + "react-icons": "^5.7.0", "react-syntax-highlighter": "^16.1.1" }, "devDependencies": { From 95ee0767bbac481fe9386f789c066d5c51f8b9d2 Mon Sep 17 00:00:00 2001 From: Yash Vardhan Date: Mon, 10 Aug 2026 01:54:21 +0530 Subject: [PATCH 2/3] feat: massive UI/UX overhaul, fuzzy search, and mobile navigation - Redesigned landing page with a new hero section and categorized topic grids - Built mobile-responsive navigation including a slide-out drawer and inline TOC - Upgraded MDX components with advanced code blocks and callouts/admonitions - Integrated Fuse.js for fuzzy search with exact-match text highlighting - Added breadcrumbs, a feedback widget, and 'Edit on GitHub' links --- frontend/app/[topic]/TopicLayout.tsx | 237 +++++++++++++------- frontend/app/components/Callout.tsx | 69 ++++++ frontend/app/components/ClientCodeBlock.tsx | 57 +++-- frontend/app/page.tsx | 118 +++++++--- frontend/package-lock.json | 41 ++++ frontend/package.json | 2 + 6 files changed, 396 insertions(+), 128 deletions(-) create mode 100644 frontend/app/components/Callout.tsx diff --git a/frontend/app/[topic]/TopicLayout.tsx b/frontend/app/[topic]/TopicLayout.tsx index b260e6c..ed2e962 100644 --- a/frontend/app/[topic]/TopicLayout.tsx +++ b/frontend/app/[topic]/TopicLayout.tsx @@ -2,7 +2,9 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { useTheme } from "../../src/context/ThemeContext"; import { useRouter } from "next/navigation"; -import { FaGithub} from "react-icons/fa"; +import { FaGithub } from "react-icons/fa"; +import toast, { Toaster } from "react-hot-toast"; +import Fuse from "fuse.js"; // <-- Imported Fuse.js import { ArrowLeft, ArrowUp, @@ -47,6 +49,9 @@ export default function TopicLayout({ const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const [isMobileTocOpen, setIsMobileTocOpen] = useState(false); + // User Engagement State + const [feedbackSubmitted, setFeedbackSubmitted] = useState(false); + // Scroll to Top state const [showScrollTop, setShowScrollTop] = useState(false); @@ -56,14 +61,12 @@ export default function TopicLayout({ useEffect(() => { setIsMounted(true); - // Detect if the user is on a Mac or Apple device if (typeof window !== "undefined") { const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform); setModifierKey(isMac ? "⌘" : "Ctrl "); } }, []); - // Prevent background scrolling when mobile menu is open useEffect(() => { if (isMobileMenuOpen) { document.body.style.overflow = "hidden"; @@ -75,7 +78,6 @@ export default function TopicLayout({ }; }, [isMobileMenuOpen]); - // Scroll to Top listener useEffect(() => { const handleScroll = () => { setShowScrollTop(window.scrollY > 400); @@ -98,46 +100,49 @@ export default function TopicLayout({ })); setDomHeadings(nextHeadings); + + // Reset feedback state when navigating to a new topic + setFeedbackSubmitted(false); }, [children, topicKey]); const activeHeadings = headings.length > 0 ? headings : domHeadings; - const normalizedQuery = docSearch.trim().toLowerCase(); - const hasSearchQuery = normalizedQuery.length > 0; + const hasSearchQuery = docSearch.trim().length > 0; + + // --- FUZZY SEARCH IMPLEMENTATION --- + const searchableData = useMemo(() => { + const sectionDocs = activeHeadings.map((heading) => ({ + id: heading.id, + title: heading.title, + type: "section" as const, + depth: heading.depth, + })); + + const topicDocs = topics.map((item) => ({ + id: item.id, + title: item.title || item.id, + description: item.description || "", + type: "topic" as const, + depth: 1, + })); + + return [...topicDocs, ...sectionDocs]; + }, [activeHeadings, topics]); + + const fuse = useMemo(() => { + return new Fuse(searchableData, { + keys: ["title", "description"], + includeMatches: true, + threshold: 0.3, // 0.0 is exact match, 1.0 is anything. 0.3 is perfect for slight typos + ignoreLocation: true, + }); + }, [searchableData]); const searchResults = useMemo(() => { if (!hasSearchQuery) return []; + return fuse.search(docSearch.trim()).slice(0, 12); + }, [docSearch, hasSearchQuery, fuse]); + // ----------------------------------- - const sectionMatches = activeHeadings - .filter((heading) => - heading.title.toLowerCase().includes(normalizedQuery), - ) - .map((heading) => ({ - id: heading.id, - title: heading.title, - type: "section" as const, - depth: heading.depth, - })); - - const topicMatches = topics - .filter((item) => { - const title = (item.title || item.id || "").toLowerCase(); - const description = (item.description || "").toLowerCase(); - return ( - title.includes(normalizedQuery) || - description.includes(normalizedQuery) - ); - }) - .map((item) => ({ - id: item.id, - title: item.title || item.id, - type: "topic" as const, - depth: 1, - })); - - return [...sectionMatches, ...topicMatches].slice(0, 12); - }, [activeHeadings, hasSearchQuery, normalizedQuery, topics]); - - // Decoupled outlineGroups from search state const outlineGroups = useMemo(() => { const groups: Array<{ id: string; @@ -265,10 +270,8 @@ export default function TopicLayout({ const scrollToSection = (id: string) => { if (!id) return; - const target = document.getElementById(id); if (!target) return; - const headerOffset = 88; const top = target.getBoundingClientRect().top + window.scrollY - headerOffset; window.scrollTo({ top, behavior: "smooth" }); @@ -278,12 +281,36 @@ export default function TopicLayout({ window.scrollTo({ top: 0, behavior: "smooth" }); }; - // Helper to extract the proper title for the breadcrumb + const handleFeedback = (isPositive: boolean) => { + setFeedbackSubmitted(true); + if (isPositive) { + toast.success("Glad it helped! Thanks for the feedback."); + } else { + toast("Thanks for the feedback! We'll work on improving this.", { + icon: '📝', + }); + } + }; + const currentTopicTitle = topics.find((t) => t.id === topicKey)?.title || frontmatter?.title || topicKey; return (
+ {/* TOAST CONTAINER */} + + {/* MOBILE DRAWER OVERLAY */} {isMobileMenuOpen && (
- {/* MOBILE HAMBURGER BUTTON */}
- {/* SEARCH RESULTS DROPDOWN */} + {/* FUZZY SEARCH RESULTS DROPDOWN */} {hasSearchQuery && (
{searchResults.length > 0 ? (
- {searchResults.map((result) => ( -
- - ))} + + ); + })}
) : (
@@ -614,21 +640,47 @@ export default function TopicLayout({
+ {/* AUTOMATIC DOCUMENT TITLE INJECTION */} +
+

+ {currentTopicTitle} +

+ {frontmatter?.description && ( +

+ {frontmatter.description} +

+ )} +
+ {/* MDX CONTENT */} {children}
{/* USER ENGAGEMENT & GITHUB EDIT */}
- Was this page helpful? -
- - -
+ {feedbackSubmitted ? ( + + Thank you for your feedback! 🎉 + + ) : ( + <> + Was this page helpful? +
+ + +
+ + )}
); +} + +// --- HELPER COMPONENT FOR FUZZY MATCH HIGHLIGHTING --- +function HighlightText({ text, matches }: { text: string; matches?: readonly [number, number][] }) { + if (!matches || matches.length === 0) return {text}; + + const elements: React.ReactNode[] = []; + let lastIndex = 0; + + matches.forEach(([start, end], i) => { + // Text before the match + if (start > lastIndex) { + elements.push({text.substring(lastIndex, start)}); + } + // The exact matched characters + elements.push( + + {text.substring(start, end + 1)} + + ); + lastIndex = end + 1; + }); + + // Text after the final match + if (lastIndex < text.length) { + elements.push({text.substring(lastIndex)}); + } + + return <>{elements}; } \ No newline at end of file diff --git a/frontend/app/components/Callout.tsx b/frontend/app/components/Callout.tsx new file mode 100644 index 0000000..0dd1633 --- /dev/null +++ b/frontend/app/components/Callout.tsx @@ -0,0 +1,69 @@ +"use client"; +import { useTheme } from "../../src/context/ThemeContext"; +import { Info, AlertTriangle, Lightbulb, ShieldAlert } from "lucide-react"; + +type CalloutType = "info" | "tip" | "warning" | "danger"; + +export default function Callout({ + type = "info", + title, + children, +}: { + type?: CalloutType; + title?: string; + children: React.ReactNode; +}) { + const { darkMode } = useTheme(); + + const config = { + info: { + icon: Info, + light: "bg-blue-50 border-blue-200 text-blue-900", + dark: "bg-blue-950/30 border-blue-900/50 text-blue-200", + iconColor: "text-blue-500", + }, + tip: { + icon: Lightbulb, + light: "bg-emerald-50 border-emerald-200 text-emerald-900", + dark: "bg-emerald-950/30 border-emerald-900/50 text-emerald-200", + iconColor: "text-emerald-500", + }, + warning: { + icon: AlertTriangle, + light: "bg-amber-50 border-amber-200 text-amber-900", + dark: "bg-amber-950/30 border-amber-900/50 text-amber-200", + iconColor: "text-amber-500", + }, + danger: { + icon: ShieldAlert, + light: "bg-red-50 border-red-200 text-red-900", + dark: "bg-red-950/30 border-red-900/50 text-red-200", + iconColor: "text-red-500", + }, + }; + + const activeConfig = config[type]; + const Icon = activeConfig.icon; + + return ( +
+
+ +
+
+ {title && ( +
+ {title} +
+ )} +
+ {children} +
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/app/components/ClientCodeBlock.tsx b/frontend/app/components/ClientCodeBlock.tsx index 19ff564..6c18ce6 100644 --- a/frontend/app/components/ClientCodeBlock.tsx +++ b/frontend/app/components/ClientCodeBlock.tsx @@ -1,15 +1,19 @@ "use client"; import { useTheme } from "../../src/context/ThemeContext"; import { useState, useEffect } from "react"; -import { Copy, Check } from "lucide-react"; +import { Copy, Check, FileCode2 } from "lucide-react"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; export default function ClientCodeBlock({ code, language, + filename, + highlightLines = [], }: { code: string; language: string; + filename?: string; + highlightLines?: number[]; }) { const { darkMode } = useTheme(); const [mounted, setMounted] = useState(false); @@ -168,7 +172,6 @@ export default function ClientCodeBlock({ iconHover: darkMode ? "hover:text-zinc-100" : "hover:text-indigo-600", }; - // Render a seamless skeleton placeholder while server is syncing with client if (!mounted) { return (
{/* Code Header Bar */}
- - {language} - +
+ {filename ? ( + <> + + + {filename} + + + ) : ( + + {language} + + )} +
{/* Code Area */}
{ + const isHighlighted = highlightLines.includes(lineNumber); + return { + style: { + display: "block", + backgroundColor: isHighlighted + ? (darkMode ? "rgba(198, 153, 255, 0.1)" : "rgba(111, 69, 214, 0.1)") + : "transparent", + borderLeft: isHighlighted + ? (darkMode ? "3px solid #C699FF" : "3px solid #6f45d6") + : "3px solid transparent", + paddingLeft: "13px", // 16px total minus the 3px border + paddingRight: "16px", + } + }; + }} customStyle={{ background: "transparent", padding: 0, @@ -226,4 +257,4 @@ export default function ClientCodeBlock({
); -} +} \ No newline at end of file diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 9e83473..7da1dd9 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -4,6 +4,7 @@ import { useTheme } from "../src/context/ThemeContext"; import { useState, useMemo, useEffect, useRef } from "react"; import Link from "next/link"; import { FaGithub, FaLinkedin } from "react-icons/fa"; +import Fuse from "fuse.js"; // <-- Imported Fuse.js import { Binary, Braces, @@ -125,26 +126,44 @@ export const REFERENCE_DATA = [ }, ]; -// Helper component to highlight matched text -function HighlightText({ text, highlight, highlightStyle }: { text: string; highlight: string; highlightStyle: string }) { - if (!highlight.trim()) return <>{text}; - - const regex = new RegExp(`(${highlight})`, "gi"); - const parts = text.split(regex); - - return ( - <> - {parts.map((part, i) => - regex.test(part) ? ( - - {part} - - ) : ( - {part} - ) - )} - - ); +// --- FUZZY SEARCH HIGHLIGHT HELPER --- +function HighlightText({ + text, + matches, + fieldKey, + highlightStyle, +}: { + text: string; + matches?: any[]; + fieldKey: string; + highlightStyle: string; +}) { + const match = matches?.find((m) => m.key === fieldKey); + if (!match || !match.indices || match.indices.length === 0) return {text}; + + const elements: React.ReactNode[] = []; + let lastIndex = 0; + + match.indices.forEach(([start, end]: [number, number], i: number) => { + // Text before the match + if (start > lastIndex) { + elements.push({text.substring(lastIndex, start)}); + } + // The exact matched characters + elements.push( + + {text.substring(start, end + 1)} + + ); + lastIndex = end + 1; + }); + + // Text after the final match + if (lastIndex < text.length) { + elements.push({text.substring(lastIndex)}); + } + + return <>{elements}; } export default function LandingPage() { @@ -163,7 +182,6 @@ export default function LandingPage() { border: darkMode ? "border-white/10" : "border-black/10", muted: darkMode ? "text-zinc-400" : "text-zinc-500", accent: darkMode ? "text-zinc-100" : "text-zinc-900", - // Clean, professional hover states without translations cardHover: darkMode ? "hover:border-[#C699FF]/40 hover:bg-[#C699FF]/[0.03]" : "hover:border-[#6f45d6]/40 hover:bg-[#6f45d6]/[0.03]", @@ -204,20 +222,48 @@ export default function LandingPage() { } }; - // Fast client-side filtering logic + // --- FUZZY SEARCH SETUP --- + const flattenedItems = useMemo(() => { + return REFERENCE_DATA.flatMap(cat => + cat.items.map(item => ({ + ...item, + categoryName: cat.category, + categoryIcon: cat.icon + })) + ); + }, []); + + const fuse = useMemo(() => new Fuse(flattenedItems, { + keys: ["name", "desc"], + includeMatches: true, + threshold: 0.3, // Allows slight typos like 'reac' or 'jaavscript' + ignoreLocation: true, + }), [flattenedItems]); + const filteredData = useMemo(() => { if (!searchQuery.trim()) return REFERENCE_DATA; - const query = searchQuery.toLowerCase(); - return REFERENCE_DATA.map((category) => ({ - ...category, - items: category.items.filter( - (item) => - item.name.toLowerCase().includes(query) || - item.desc.toLowerCase().includes(query) - ), - })).filter((category) => category.items.length > 0); - }, [searchQuery]); + const results = fuse.search(searchQuery.trim()); + + // Re-group the search results back into their original UI categories + const grouped = new Map(); + results.forEach(({ item, matches }) => { + if (!grouped.has(item.categoryName)) { + grouped.set(item.categoryName, { + category: item.categoryName, + icon: item.categoryIcon, + items: [] + }); + } + grouped.get(item.categoryName).items.push({ + ...item, + matches, // Append the match indices to pass to our Highlight component + }); + }); + + return Array.from(grouped.values()); + }, [searchQuery, fuse]); + // -------------------------- return (
@@ -256,7 +302,6 @@ export default function LandingPage() { Find the syntax, patterns, and implementation notes you need without digging through scattered docs. Built for speed and focus.

- {/* SEARCH BAR (Refactored to match TopicLayout component) */}
- {/* Absolute positioned elements to prevent layout shift */}
{searchQuery ? ( +
+ + + {hasSearchQuery && searchResults.length === 0 && !showHomeAction && ( + + No results found for "{docSearch}". + + )} + + {!hasSearchQuery && ( + Quick Actions
}> + { + router.push("/"); + setCmdOpen(false); + }} + className={`flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-current/10 aria-selected:bg-current/10 ${theme.navItem}`} + > + + Go to Directory (Home) + + { toggleTheme(); setCmdOpen(false); }} + className={`flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-current/10 aria-selected:bg-current/10 ${theme.navItem}`} + > + + Switch to {darkMode ? "Light" : "Dark"} Mode + + { + navigator.clipboard.writeText(window.location.href); + toast.success("URL copied to clipboard!"); + setCmdOpen(false); + }} + className={`flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-current/10 aria-selected:bg-current/10 ${theme.navItem}`} + > + + Copy Current URL + + { + window.open("https://github.com/yash-pluto/refme", "_blank"); + setCmdOpen(false); + }} + className={`flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-current/10 aria-selected:bg-current/10 ${theme.navItem}`} + > + + Go to GitHub Repo + + + )} + + {/* Specific match for "Home" or "Directory" */} + {showHomeAction && ( + Navigation
}> + { + router.push("/"); + setDocSearch(""); + setCmdOpen(false); + }} + className={`flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-current/10 aria-selected:bg-current/10 ${theme.navItem}`} + > + + Go to Directory (Home) + + + )} + + {hasSearchQuery && searchResults.length > 0 && ( + Documentation
}> + {searchResults.map(({ item, matches }) => { + const titleMatches = matches?.find(m => m.key === "title")?.indices; + return ( + { + if (item.type === "topic") { + router.push(`/${item.id}`); + } else { + scrollToSection(item.id); + } + setDocSearch(""); + setCmdOpen(false); + }} + className={`flex cursor-pointer items-center justify-between gap-3 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-current/10 aria-selected:bg-current/10 ${theme.navItem}`} + > +
+
+ +
+
+
+ {item.type === "topic" ? "Topic" : item.depth === 2 ? "Section" : "Subsection"} +
+
+ ); + })} + + )} + + +
+
+ )} + {/* MOBILE DRAWER OVERLAY */} {isMobileMenuOpen && (
-
-
-
- - setDocSearch(event.target.value)} - onKeyDown={handleInputKeyDown} - aria-label="Search docs" - placeholder="Search topics..." - className="w-full bg-transparent py-2 pl-10 pr-16 text-sm outline-none placeholder:text-current placeholder:opacity-50" - /> -
- {docSearch ? ( - - ) : ( - - {modifierKey}K - - )} -
+
+ - ); - })} -
- ) : ( -
- No matching topics or sections -
- )} -
- )} -
+ + {modifierKey}K + +
+
+ + + {docSearch.trim().length > 0 && searchResults.length === 0 && !showHomeAction && ( + + No results found for "{docSearch}". + + )} + + {/* Default Quick Actions when search is empty */} + {!docSearch.trim() && ( + Quick Actions
}> + { + router.push("/"); + setCmdOpen(false); + }} + className="flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-current/10 aria-selected:bg-current/10 text-current" + > + + Go to Directory (Home) + + { toggleTheme(); setCmdOpen(false); }} + className="flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-current/10 aria-selected:bg-current/10 text-current" + > + + Switch to {darkMode ? "Light" : "Dark"} Mode + + { + window.open("https://github.com/yash-pluto/refme", "_blank"); + setCmdOpen(false); + }} + className="flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-current/10 aria-selected:bg-current/10 text-current" + > + + Go to GitHub Repo + + + )} + + {/* Specific match for "Home" or "Directory" */} + {showHomeAction && ( + Navigation
}> + { + router.push("/"); + setDocSearch(""); + setCmdOpen(false); + }} + className="flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-current/10 aria-selected:bg-current/10 text-current" + > + + Go to Directory (Home) + + + )} + + {/* General Topic Results */} + {docSearch.trim().length > 0 && searchResults.length > 0 && ( + Topics}> + {searchResults.map(({ item, matches }) => ( + { + router.push(item.href); + setDocSearch(""); + setCmdOpen(false); + }} + className="flex cursor-pointer items-center justify-between gap-3 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-current/10 aria-selected:bg-current/10 text-current" + > +
+
+ {item.icon} +
+
+
+ +
+
+ {item.desc} +
+
+
+
+ {item.categoryName} +
+
+ ))} +
+ )} + + + + + )} + {/* HEADER */}
@@ -303,91 +430,66 @@ export default function LandingPage() {

-
setCmdOpen(true)} + className={`relative flex w-full items-center justify-between rounded-2xl border bg-transparent py-4 pl-4 pr-4 shadow-sm transition-all duration-200 ${theme.border} ${theme.panel} hover:border-[#C699FF]/60 hover:ring-4 hover:ring-[#C699FF]/10 text-left group`} > - - setSearchQuery(e.target.value)} - onKeyDown={handleInputKeyDown} - placeholder="Search algorithms, frameworks, or concepts..." - className="w-full bg-transparent py-4 pl-12 pr-20 text-base outline-none placeholder:opacity-50" - /> +
+ + Search algorithms, frameworks, or concepts... +
-
- {searchQuery ? ( - - ) : ( - - {modifierKey}K - - )} +
+ + {modifierKey}K +
-
+
{/* CATEGORY GRIDS */}
- {filteredData.length === 0 ? ( -
-

- No references found for "{searchQuery}". -

-
- ) : ( -
- {filteredData.map((category) => ( -
-
- - {category.icon} - -

- {category.category} -

-
+
+ {REFERENCE_DATA.map((category) => ( +
+
+ + {category.icon} + +

+ {category.category} +

+
-
- {category.items.map((item: any) => ( - -
-
- {item.icon} -
-

- -

+
+ {category.items.map((item: any) => ( + +
+
+ {item.icon}
-

- -

- - ))} -
-
- ))} -
- )} +

+ {item.name} +

+
+

+ {item.desc} +

+ + ))} +
+ + ))} + {/* FOOTER */} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 319300c..a6e720e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend", "version": "0.1.0", "dependencies": { + "cmdk": "^1.1.1", "framer-motion": "^12.38.0", "fuse.js": "^7.5.0", "gray-matter": "^4.0.3", @@ -1310,6 +1311,320 @@ "node": ">=12.4.0" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz", + "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", + "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz", + "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz", + "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz", + "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-effect-event": "0.0.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz", + "integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz", + "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", + "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz", + "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz", + "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz", + "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.3.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz", + "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz", + "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz", + "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", + "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", + "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1705,7 +2020,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -2358,6 +2673,18 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -2830,6 +3157,22 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/collapse-white-space": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", @@ -3058,6 +3401,12 @@ "node": ">=8" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -4175,6 +4524,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -7084,6 +7442,75 @@ "dev": true, "license": "MIT" }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-syntax-highlighter": { "version": "16.1.1", "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.1.tgz", @@ -8436,6 +8863,49 @@ "punycode": "^2.1.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index 32cc3a5..48a5734 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,6 +9,7 @@ "lint": "eslint" }, "dependencies": { + "cmdk": "^1.1.1", "framer-motion": "^12.38.0", "fuse.js": "^7.5.0", "gray-matter": "^4.0.3",