Skip to content

feat: add search#8

Merged
rsbh merged 10 commits intomainfrom
feat_add_search
Feb 7, 2026
Merged

feat: add search#8
rsbh merged 10 commits intomainfrom
feat_add_search

Conversation

@rsbh
Copy link
Copy Markdown
Member

@rsbh rsbh commented Feb 5, 2026

Summary by CodeRabbit

  • New Features

    • Breadcrumb navigation on pages.
    • Interactive command-style Search dialog with keyboard shortcut and result navigation.
    • Advanced server-side search endpoint.
    • Footer component with configurable links and copyright.
  • Improvements

    • Search widget added to the site header when enabled.
    • Reduced hydration warnings by suppressing on layout root.
  • Style

    • New styles for search UI and footer; removed legacy footer styles.
  • Chores

    • Config loader now derives content directory from env and exposes footer in config.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 5, 2026

📝 Walkthrough

Walkthrough

Adds breadcrumb UI and integrates it into the Page theme, introduces a client Search component + styles and a new advanced search API route, expands ThemePageProps to include tree, adds Footer component and config/footer support, updates loadConfig signature to no-args, minor layout/hydration and dependency updates.

Changes

Cohort / File(s) Summary
Breadcrumbs
packages/chronicle/src/components/ui/breadcrumbs.tsx
New client-side Breadcrumbs export. Resolves nodes and first-page URLs from PageTree, builds label/href items from slug, and renders breadcrumb UI with separators.
Page theme & types
packages/chronicle/src/themes/default/Page.tsx, packages/chronicle/src/types/theme.ts, packages/chronicle/src/app/[[...slug]]/page.tsx
ThemePageProps now includes tree; Page now accepts tree and renders <Breadcrumbs slug={page.slug} tree={tree} />. Docs page passes the computed tree into Page (added tree prop).
Search UI & styles
packages/chronicle/src/components/ui/search.tsx, packages/chronicle/src/components/ui/search.module.css
New client-side Search component (modal/command UI, global shortcut, fetch-based results, navigation) and corresponding CSS module for trigger, dialog, list, items, and selected states.
Search API
packages/chronicle/src/app/api/search/route.ts
New advanced search API route exporting GET via createSearchAPI('advanced', { indexes }). Builds indexes from source.getPages(), normalizes title/description/structuredData and invokes page load() when needed.
Layout & Footer
packages/chronicle/src/themes/default/Layout.tsx, packages/chronicle/src/components/ui/footer.tsx, packages/chronicle/src/components/ui/footer.module.css, packages/chronicle/src/themes/default/Layout.module.css
Navbar now renders <Search /> when enabled; new Footer component and CSS added; removed legacy .footer and .footerText from theme CSS; layout uses Footer and includes a console.log(config).
App layout hydration
packages/chronicle/src/app/layout.tsx
Adds suppressHydrationWarning to the <body> element.
Config loader
packages/chronicle/src/lib/config.ts
loadConfig signature changed to loadConfig(): ChronicleConfig (no parameters), resolves CHRONICLE_CONTENT_DIR or cwd for content path, and returns footer from user config in the merged ChronicleConfig.
Package manifest
packages/chronicle/package.json
Added dependencies: @heroicons/react, lodash, react-device-detect; added devDependency @types/lodash.
Example config
examples/basic/chronicle.yaml
Added footer configuration with copyright and links.

Sequence Diagram

sequenceDiagram
    participant Client as Client (Search UI)
    participant API as /api/search (Route)
    participant Source as Content Source
    participant Page as Page module (structuredData loader)

    Client->>API: GET /api/search?q=...
    API->>Source: source.getPages()
    Source-->>API: pages[]
    API->>Page: for page with load() -> invoke load()
    Page-->>API: structuredData (or error)
    API-->>Client: search indexes (id,url,title,description,structuredData)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through nodes and slugs today,
Breadcrumb crumbs to light my way,
A search bell rings, a modal bright,
Footers hum beneath the site,
Hop, read, and nibble knowledge away! 📚✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add search' is partially related to the changeset. While search functionality is added, the PR encompasses significantly more features including breadcrumbs navigation, footer component, and configuration updates. The title captures only one aspect of the work.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat_add_search

Comment @coderabbitai help to get the list of available commands and usage tips.

@rsbh rsbh changed the title feat: add breadcumbs feat: add search Feb 5, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/chronicle/src/components/ui/breadcrumbs.tsx`:
- Around line 11-23: findInTree currently compares only the last URL segment
(item.url?.split('/').pop()) which causes incorrect matches for pages with the
same filename in different folders; modify findInTree to accept the currentPath
(or an accumulatedPath) and match full URLs (e.g., compare item.url to the
corresponding prefix/suffix of currentPath) instead of just the final segment,
update the function signature (findInTree(items: PageTreeItem[], segment:
string, currentPath: string) or findInTree(items, accumulatedPath)) and adjust
all call sites to pass currentPath so the lookup uses the full path context and
returns the correct PageTreeItem.
🧹 Nitpick comments (1)
packages/chronicle/src/components/ui/breadcrumbs.tsx (1)

28-28: Consider making the base path configurable.

The /docs base path is hardcoded. If the documentation root ever changes or needs to be configurable per deployment, this would require code changes.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/chronicle/src/app/api/search/route.ts`:
- Around line 34-37: The block that calls data.load() inside the Promise.all can
throw and will reject the whole batch; wrap the await data.load() call in a
try-catch so failures for a single page are handled without breaking the entire
Promise.all. Specifically, inside the async mapper where you access
structuredData and call data.load(), catch errors from await data.load() (or
from loaded.structuredData) and either skip that page, set structuredData to
null/empty, and log the error with context (e.g., page id or data source) so the
search route remains available while individual load failures are isolated.
🧹 Nitpick comments (2)
packages/chronicle/src/components/ui/search.tsx (2)

46-48: Keyboard shortcut hint only shows macOS modifier.

The trigger button displays ⌘ K which is Mac-specific, but the actual shortcut handler (line 32) supports both metaKey (Mac) and ctrlKey (Windows/Linux). Consider detecting the platform to show the appropriate hint.

💡 Suggested approach for cross-platform hint
+const isMac = typeof navigator !== 'undefined' && navigator.platform.toUpperCase().indexOf('MAC') >= 0;
+const shortcutHint = isMac ? '⌘ K' : 'Ctrl K';
+
 return (
   <>
-    <Button variant="outline" color="neutral" onClick={() => setOpen(true)} className={styles.trigger} trailingIcon={<kbd className={styles.kbd}>⌘ K</kbd>}>
+    <Button variant="outline" color="neutral" onClick={() => setOpen(true)} className={styles.trigger} trailingIcon={<kbd className={styles.kbd}>{shortcutHint}</kbd>}>
       <Text>Search...</Text>
     </Button>

Note: Platform detection should be done in an effect or memoized to avoid SSR mismatches.


117-125: Consider edge case handling in getPageTitle.

The function works for typical URLs but returns an empty string for root URLs (e.g., /). If root pages are indexed, this could result in empty titles being displayed.

💡 Suggested improvement
 function getPageTitle(url: string): string {
   const path = url.split("#")[0];
   const segments = path.split("/").filter(Boolean);
   const lastSegment = segments[segments.length - 1] || "";
+  if (!lastSegment) return "Home";
   return lastSegment
     .split("-")
     .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
     .join(" ");
 }

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/chronicle/src/app/api/search/route.ts`:
- Around line 4-24: Replace the locally redefined types StructuredDataHeading,
StructuredDataContent, StructuredData (and any local variants used by
PageData.load) with the canonical types exported by fumadocs-core: import the
types (e.g. StructuredHeading, StructuredContent, StructuredData) from
'fumadocs-core/search/server' and use those names instead of the local
interfaces; ensure StructuredContent.heading uses the library's string | null
type and make StructuredData.headings and StructuredData.contents required
arrays as per fumadocs-core so PageData.structuredData and the load() return
type align with the upstream contract.
🧹 Nitpick comments (1)
packages/chronicle/src/app/api/search/route.ts (1)

31-31: Type assertion should have runtime validation or documented schema.

The cast page.data as PageData assumes the data from fumadocs-mdx frontmatter matches the expected shape. While the code has fallbacks (?? operators), there's no validation that required properties exist. Either add explicit property checks before use or document the required frontmatter schema that all MDX files must follow. Note: fumadocs-core doesn't export a typed PageData interface; this is intentionally user-defined based on your frontmatter structure.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/chronicle/package.json`:
- Line 31: Remove unused lodash and `@types/lodash` entries from the chronicle
package.json by deleting the "lodash" dependency (currently listed in
dependencies) and the "@types/lodash" entry (currently in devDependencies);
after removing them, run the package manager's install (npm/yarn/pnpm) to update
the lockfile and ensure no import/use of lodash remains in the chronicle
codebase (search for "lodash" or "_" to confirm).
🧹 Nitpick comments (1)
packages/chronicle/src/components/ui/search.tsx (1)

47-49: Consider splitting the long button line for readability.

The trigger button JSX is quite long on a single line, which can be harder to read and maintain.

♻️ Suggested formatting
-      <Button variant="outline" color="neutral" onClick={() => setOpen(true)} className={styles.trigger} trailingIcon={<kbd className={styles.kbd}>{isMacOs ? "⌘" : "Ctrl"} K</kbd>}>
-        <Text>Search...</Text>
-      </Button>
+      <Button
+        variant="outline"
+        color="neutral"
+        onClick={() => setOpen(true)}
+        className={styles.trigger}
+        trailingIcon={
+          <kbd className={styles.kbd}>{isMacOs ? "⌘" : "Ctrl"} K</kbd>
+        }
+      >
+        <Text>Search...</Text>
+      </Button>

@rsbh rsbh requested a review from rohilsurana February 5, 2026 09:26
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/chronicle/src/lib/config.ts`:
- Around line 14-16: getConfigPath is inconsistent with loadConfig's directory
resolution: extract the directory-resolution logic into a single helper (or have
getConfigPath call loadConfig's resolver) that uses contentDir ??
process.env.CHRONICLE_CONTENT_DIR ?? './content' and then join with CONFIG_FILE;
update both getConfigPath and any other path helpers (the code around the
loadConfig function and the similar logic at lines ~37-38) to reuse that
resolver so they always point to the same config file location (reference
symbols: loadConfig, getConfigPath, CONFIG_FILE).
🧹 Nitpick comments (1)
packages/chronicle/src/themes/default/Layout.tsx (1)

12-13: Remove debug logging from the client layout.

Line 13 logs the full config on every render, which is noisy and can expose config details in the browser console.

🧹 Suggested fix
-  console.log(config);

@rsbh rsbh merged commit a3e2b45 into main Feb 7, 2026
1 check passed
@rsbh rsbh deleted the feat_add_search branch March 16, 2026 04:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants