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 ( - {children} + + {children} + diff --git a/src/components/search.tsx b/src/components/search.tsx new file mode 100644 index 00000000..ebdfbe65 --- /dev/null +++ b/src/components/search.tsx @@ -0,0 +1,100 @@ +"use client"; +import { useDocsSearch } from "fumadocs-core/search/client"; +import { + SearchDialog, + SearchDialogClose, + SearchDialogContent, + SearchDialogFooter, + SearchDialogHeader, + SearchDialogIcon, + SearchDialogInput, + SearchDialogList, + SearchDialogOverlay, + type SharedProps, + TagsList, + TagsListItem, +} from "fumadocs-ui/components/dialog/search"; +import { useI18n } from "fumadocs-ui/contexts/i18n"; +import { ArrowDownIcon, ArrowUpIcon, CornerDownLeftIcon } from "lucide-react"; +import { useState } from "react"; + +const items = [ + { + name: "All", + value: "", + }, + { + name: "Docs", + description: "Only results about documentation", + value: "docs", + }, + { + name: "Programming", + description: "Only results about programming languages", + value: "programs", + }, +]; + +export default function CustomSearchDialog(props: SharedProps) { + const { locale } = useI18n(); // (optional) for i18n + const [tag, setTag] = useState(); + const { search, setSearch, query } = useDocsSearch({ + type: "fetch", + locale, + tag, + delayMs: 500, + }); + + return ( + + + + +
+ + + +
+ + {items.map((item) => ( + + {item.name} + + ))} + +
+ + +
+
+ + + + + + + Navigate +
+
+ + + + Select +
+
+ + ESC + + Close +
+
+
+
+
+ ); +} From a4a5d0f4a160764f3368ccfe54ab4e5f890114fe Mon Sep 17 00:00:00 2001 From: Arghya Ghosh <71373838+uiuxarghya@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:14:07 +0530 Subject: [PATCH 4/5] refactor(route): enhance type safety for page data in mapping functions --- src/app/static.json/route.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/app/static.json/route.ts b/src/app/static.json/route.ts index 151b6d69..0ef09864 100644 --- a/src/app/static.json/route.ts +++ b/src/app/static.json/route.ts @@ -4,8 +4,21 @@ import { source, programsSource } from "@/lib/source"; export const revalidate = false; +// Type for page data with structured data +interface PageWithStructuredData { + url: string; + data: { + title: string; + description?: string; + structuredData: any; + }; +} + // Helper function to map pages to DocumentRecord format -function mapPageToDocumentRecord(page: any, tag: string): DocumentRecord { +function mapPageToDocumentRecord( + page: PageWithStructuredData, + tag: string, +): DocumentRecord { return { _id: page.url, structured: page.data.structuredData, @@ -56,4 +69,3 @@ export function GET() { ); } } - From 0b782fd12a8301b42202f5027760af2900ff408f Mon Sep 17 00:00:00 2001 From: Arghya Ghosh <71373838+uiuxarghya@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:17:15 +0530 Subject: [PATCH 5/5] refactor(route): improve type safety for structured data in page interface --- src/app/static.json/route.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/static.json/route.ts b/src/app/static.json/route.ts index 0ef09864..88dfda3a 100644 --- a/src/app/static.json/route.ts +++ b/src/app/static.json/route.ts @@ -1,6 +1,7 @@ -import { NextResponse } from "next/server"; +import { programsSource, source } from "@/lib/source"; +import { StructuredData } from "fumadocs-core/mdx-plugins"; import { type DocumentRecord } from "fumadocs-core/search/algolia"; -import { source, programsSource } from "@/lib/source"; +import { NextResponse } from "next/server"; export const revalidate = false; @@ -10,7 +11,7 @@ interface PageWithStructuredData { data: { title: string; description?: string; - structuredData: any; + structuredData: StructuredData; }; }