From 4ae66829784e35a24dec539b0c04182877f60e4e Mon Sep 17 00:00:00 2001 From: Arghya Ghosh <71373838+uiuxarghya@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:05:13 +0530 Subject: [PATCH 1/5] feat(search): implement GET handler for search JSON data with error handling --- src/app/static.json/route.ts | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/app/static.json/route.ts diff --git a/src/app/static.json/route.ts b/src/app/static.json/route.ts new file mode 100644 index 00000000..151b6d69 --- /dev/null +++ b/src/app/static.json/route.ts @@ -0,0 +1,59 @@ +import { NextResponse } from "next/server"; +import { type DocumentRecord } from "fumadocs-core/search/algolia"; +import { source, programsSource } from "@/lib/source"; + +export const revalidate = false; + +// Helper function to map pages to DocumentRecord format +function mapPageToDocumentRecord(page: any, tag: string): DocumentRecord { + return { + _id: page.url, + structured: page.data.structuredData, + url: page.url, + title: page.data.title, + description: page.data.description || "", + tag, + }; +} + +// Helper function to safely get pages and map them +function mapPagesToDocuments( + sourceInstance: typeof source, + tag: string, +): DocumentRecord[] { + try { + return sourceInstance + .getPages() + .map((page) => mapPageToDocumentRecord(page, tag)); + } catch (error) { + console.error(`Error mapping ${tag} pages:`, error); + return []; + } +} + +export function GET() { + try { + // Process both sources efficiently with better error handling + const docsPages = mapPagesToDocuments(source, "docs"); + const programPages = mapPagesToDocuments(programsSource, "programs"); + + // Combine arrays efficiently + const results: DocumentRecord[] = [...docsPages, ...programPages]; + + // Add cache headers for better performance + return new NextResponse(JSON.stringify(results), { + status: 200, + headers: { + "Content-Type": "application/json", + "Cache-Control": "public, max-age=3600, stale-while-revalidate=86400", + }, + }); + } catch (error) { + console.error("Error generating static.json:", error); + return NextResponse.json( + { error: "Failed to generate search data" }, + { status: 500 }, + ); + } +} + From a59100932855555fd536583e21f9c17ec98f454d Mon Sep 17 00:00:00 2001 From: Arghya Ghosh <71373838+uiuxarghya@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:05:38 +0530 Subject: [PATCH 2/5] feat(search/api): update search API to use advanced configuration with program sources --- src/app/api/search/route.ts | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/app/api/search/route.ts b/src/app/api/search/route.ts index df889626..89faa89c 100644 --- a/src/app/api/search/route.ts +++ b/src/app/api/search/route.ts @@ -1,4 +1,24 @@ -import { source } from '@/lib/source'; -import { createFromSource } from 'fumadocs-core/search/server'; +import { programsSource, source } from "@/lib/source"; +import { createSearchAPI } from "fumadocs-core/search/server"; -export const { GET } = createFromSource(source); +export const { GET } = createSearchAPI("advanced", { + language: "english", + indexes: [ + ...source.getPages().map((page) => ({ + title: page.data.title, + description: page.data.description, + url: page.url, + id: page.url, + structuredData: page.data.structuredData, + tag: "docs", + })), + ...programsSource.getPages().map((page) => ({ + title: page.data.title, + description: page.data.description, + url: page.url, + id: page.url, + structuredData: page.data.structuredData, + tag: "programs", + })), + ], +}); From ff1eb9eafe01631287376bf25fe320ad5a3853ba Mon Sep 17 00:00:00 2001 From: Arghya Ghosh <71373838+uiuxarghya@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:07:39 +0530 Subject: [PATCH 3/5] feat(search): add custom search dialog component with tags and keyboard navigation --- src/app/global.css | 6 ++- src/app/layout.tsx | 28 +++++++++-- src/components/search.tsx | 100 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 src/components/search.tsx diff --git a/src/app/global.css b/src/app/global.css index 369c9291..a073f9da 100644 --- a/src/app/global.css +++ b/src/app/global.css @@ -7,7 +7,7 @@ @theme inline { --font-sans: var(--font-inter); - --font-mono: var(--font-inter-mono); + --font-mono: var(--font-jetbrains-mono); --font-funnel-display: var(--font-funnel-display); --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); @@ -123,3 +123,7 @@ @apply bg-background text-foreground; } } + +kbd { + @apply font-mono; +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8d897ffa..301cc8e7 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,19 +1,33 @@ +import SearchDialog from "@/components/search"; import { GoogleAnalytics } from "@next/third-parties/google"; import { RootProvider } from "fumadocs-ui/provider"; import type { Metadata } from "next"; -import { Funnel_Display, Inter } from "next/font/google"; +import { Funnel_Display, Inter, JetBrains_Mono } from "next/font/google"; import type { ReactNode } from "react"; import "./global.css"; const inter = Inter({ subsets: ["latin"], variable: "--font-inter", + weight: "variable", + display: "swap", + preload: true, }); const funnelDisplay = Funnel_Display({ subsets: ["latin", "latin-ext"], weight: "variable", variable: "--font-funnel-display", + display: "swap", + preload: true, +}); + +const jetbrainsMono = JetBrains_Mono({ + subsets: ["latin", "latin-ext"], + weight: "variable", + variable: "--font-jetbrains-mono", + display: "swap", + preload: true, }); export const metadata: Metadata = { @@ -26,13 +40,19 @@ export const metadata: Metadata = { }, }; -export default function Layout({ children }: { children: ReactNode }) { +export default function RootLayout({ children }: { children: ReactNode }) { return (
-