diff --git a/frontend/app/[topic]/TopicLayout.tsx b/frontend/app/[topic]/TopicLayout.tsx index 389c5a3..ed2e962 100644 --- a/frontend/app/[topic]/TopicLayout.tsx +++ b/frontend/app/[topic]/TopicLayout.tsx @@ -2,15 +2,22 @@ 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 toast, { Toaster } from "react-hot-toast"; +import Fuse from "fuse.js"; // <-- Imported Fuse.js import { ArrowLeft, + ArrowUp, BookOpenText, Braces, ChevronDown, + ChevronRight, Code2, + Menu, Search, TerminalSquare, Workflow, + X, } from "lucide-react"; export default function TopicLayout({ @@ -38,6 +45,17 @@ export default function TopicLayout({ Array<{ id: string; title: string; depth: number }> >([]); + // Mobile navigation state + 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); + + // UX Enhancements: OS detection for the shortcut hint const [isMounted, setIsMounted] = useState(false); const [modifierKey, setModifierKey] = useState("⌘"); @@ -49,6 +67,26 @@ export default function TopicLayout({ } }, []); + useEffect(() => { + if (isMobileMenuOpen) { + document.body.style.overflow = "hidden"; + } else { + document.body.style.overflow = ""; + } + return () => { + document.body.style.overflow = ""; + }; + }, [isMobileMenuOpen]); + + 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 @@ -62,44 +100,48 @@ 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 []; - - 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]); + return fuse.search(docSearch.trim()).slice(0, 12); + }, [docSearch, hasSearchQuery, fuse]); + // ----------------------------------- const outlineGroups = useMemo(() => { const groups: Array<{ @@ -203,7 +245,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]", @@ -228,39 +270,147 @@ 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" }); }; + const scrollToTop = () => { + window.scrollTo({ top: 0, behavior: "smooth" }); + }; + + 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 (
- {/* HEADER: Strictly fixed to h-16 (64px) with a translucent background for the blur to work */} + + {/* TOAST CONTAINER */} + + + {/* 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 */}
-
-
-
+
+
+ + +
R_
-
+
RefMe docs
-
+
- - {/* SEARCH BAR */}
- -
{docSearch ? (
- {/* SEARCH RESULTS DROPDOWN */} + {/* FUZZY SEARCH RESULTS DROPDOWN */} {hasSearchQuery && (
{searchResults.length > 0 ? (
- {searchResults.map((result) => ( -
- - ))} + + ); + })}
) : (
@@ -334,7 +487,7 @@ export default function TopicLayout({ @@ -343,7 +496,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 */} + +
+ {/* AUTOMATIC DOCUMENT TITLE INJECTION */} +
+

+ {currentTopicTitle} +

+ {frontmatter?.description && ( +

+ {frontmatter.description} +

+ )} +
+ {/* MDX CONTENT */} {children}
+ + {/* USER ENGAGEMENT & GITHUB EDIT */} +
+
+ {feedbackSubmitted ? ( + + Thank you for your feedback! 🎉 + + ) : ( + <> + Was this page helpful? +
+ + +
+ + )} +
+ + + Edit this page on GitHub + +
+
{/* BOTTOM PAGINATION */} @@ -427,7 +727,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 */} +
); +} + +// --- 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 8c13d9e..7da1dd9 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,10 +1,11 @@ "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 Fuse from "fuse.js"; // <-- Imported Fuse.js import { - ArrowRight, Binary, Braces, Code2, @@ -21,202 +22,419 @@ 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() { +// --- 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() { 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", + 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(); + } + }; + + // --- 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 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 ( -
-
-
-
-
- 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 - - +
+
+ + 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" + /> + +
+ {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: any) => ( + +
+
+ {item.icon} +
+

+ +

+
+

+ +

+ + ))} +
+
+ ))}
- + )} +
-
- -
-
+ {/* FOOTER */} +
); -} +} \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 77aa383..319300c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,12 +9,15 @@ "version": "0.1.0", "dependencies": { "framer-motion": "^12.38.0", + "fuse.js": "^7.5.0", "gray-matter": "^4.0.3", "lucide-react": "^1.14.0", "next": "16.2.4", "next-mdx-remote": "^6.0.0", "react": "19.2.4", "react-dom": "19.2.4", + "react-hot-toast": "^2.6.0", + "react-icons": "^5.7.0", "react-syntax-highlighter": "^16.1.1" }, "devDependencies": { @@ -4114,6 +4117,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fuse.js": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.5.0.tgz", + "integrity": "sha512-sQtrEfA+ez/3G0cCZecF70oqpCRttCexYUG4mUrtWL49ULUzUyxokt5kyqwtKzj1270RaKih+hcP3qLcumccow==", + "license": "Apache-2.0", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/krisk" + } + }, "node_modules/generator-function": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", @@ -4247,6 +4263,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/goober": { + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.19.tgz", + "integrity": "sha512-U7veizMqxyKlM58+Z5j2ngJBH/r9siDmxpvNxSw0PylF6WQvrASJEZrxh1hidRBJc2jqoBVSyOban5u8m+6Rxg==", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -7026,6 +7051,32 @@ "react": "^19.2.4" } }, + "node_modules/react-hot-toast": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz", + "integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "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..32cc3a5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,12 +10,15 @@ }, "dependencies": { "framer-motion": "^12.38.0", + "fuse.js": "^7.5.0", "gray-matter": "^4.0.3", "lucide-react": "^1.14.0", "next": "16.2.4", "next-mdx-remote": "^6.0.0", "react": "19.2.4", "react-dom": "19.2.4", + "react-hot-toast": "^2.6.0", + "react-icons": "^5.7.0", "react-syntax-highlighter": "^16.1.1" }, "devDependencies": {