Skip to content

feat: navigate to heading from search results#94

Merged
rsbh merged 4 commits into
mainfrom
feat/search-headings
May 20, 2026
Merged

feat: navigate to heading from search results#94
rsbh merged 4 commits into
mainfrom
feat/search-headings

Conversation

@rsbh

@rsbh rsbh commented May 20, 2026

Copy link
Copy Markdown
Member

Summary

  • Heading search matches now include #slug anchor in URL
  • Clicking a heading result scrolls directly to that section
  • Uses inline slugify matching github-slugger algorithm

Test plan

  • Search for a term that matches a heading (e.g. "install")
  • Verify heading result URL contains #anchor
  • Click heading result — page navigates and scrolls to heading

🤖 Generated with Claude Code

Append #slug to URL for heading matches so clicking a search result
scrolls directly to the matched heading.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@vercel

vercel Bot commented May 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
chronicle Ready Ready Preview, Comment May 20, 2026 9:20am

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2f3637d2-f8aa-45e8-867e-44a7d973ea85

📥 Commits

Reviewing files that changed from the base of the PR and between 0679bdf and dec4eb9.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • packages/chronicle/package.json
  • packages/chronicle/src/server/api/search.ts

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Enhanced search results to include direct anchor links for heading matches, allowing users to jump directly to specific sections within documents.

Walkthrough

Search results for heading matches now include stable URL fragments. The search API uses github-slugger to generate deterministic heading slugs and appends them as #fragment identifiers to both the result id and url fields, enabling direct navigation to matched headings.

Changes

Heading Fragment Support

Layer / File(s) Summary
Dependency and import setup
packages/chronicle/package.json, packages/chronicle/src/server/api/search.ts
github-slugger ^2.0.0 is added to dependencies and imported in the search module.
Slug generation and result formatting
packages/chronicle/src/server/api/search.ts
findMatch generates stable heading slugs via GithubSlugger and returns them; the API handler appends slugs as #${slug} fragments to both id and url for heading matches.

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: navigate to heading from search results' accurately summarizes the main change: enabling users to navigate directly to headings via search results with anchor links.
Description check ✅ Passed The description is directly related to the changeset, explaining the feature addition with clear details about anchor navigation, expected behavior, and a test plan.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/search-headings

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

@rohilsurana rohilsurana left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Critical

  1. Custom slugify doesn't match github-slugger — fumadocs uses github-slugger (via remark-heading.js) to generate heading id attributes in the rendered HTML. The PR introduces a custom slugify function that behaves differently. Key differences:

    • github-slugger preserves non-ASCII characters (e.g., accented letters, CJK); the custom regex /[^\w\s-]/g strips them.
    • github-slugger handles duplicate headings by appending -1, -2, etc. The custom function doesn't.
    • github-slugger converts spaces differently for certain edge cases.

    This means clicking a heading search result will navigate to #wrong-slug and the page won't scroll to the heading. Use github-slugger directly — it's already a transitive dependency via fumadocs-core:

    import Slugger from 'github-slugger';
    const slugger = new Slugger();
    // reset per document, then: slugger.slug(headingText)
  2. Duplicate heading disambiguation is missing — If a page has two headings with the same text (e.g., two "Example" headings), github-slugger generates #example and #example-1. The custom slugify generates #example for both, so the second heading result would scroll to the wrong one. Using github-slugger with proper per-document reset fixes this.

Issues

  1. Slugger state must be per-document, not per-headinggithub-slugger tracks seen slugs to deduplicate. If you create one slugger instance and use it across all documents in the search results, the dedup counter will carry across pages. You need to instantiate a new Slugger() (or call slugger.reset()) for each document/page when building the index. The correct place to compute heading slugs is during buildDocs (indexing time), not in findMatch (query time), since you need the full list of headings per page to get dedup right.

  2. deduplicateByUrl in search.tsx strips anchors — The client-side deduplicateByUrl function (line 188-196) does r.url.split('#')[0] and deduplicates by the base URL. This means if the same page has two heading matches, only the first one survives. Before this PR that was fine (all results for one page had the same URL), but now heading results have distinct #anchor URLs and should probably not be deduplicated against each other. Consider deduplicating by the full URL (with anchor) when match type is heading, or removing the dedup for heading results.

Minor

  1. Headings in the search index may contain markdown formatting — The heading extraction regex line.match(/^#{1,6}\s+(.+)/) captures raw markdown text. A heading like ## Using \config.yaml`stores the backticks in the headings column. The custom slugify strips them, butgithub-sluggerwould produce a different slug than the rendered HTML where backticks become` and are stripped during flattening. Consider stripping inline markdown formatting before slugifying.

  2. Body match could also benefit from anchor links — If the query matches body text near a heading, you could include the nearest preceding heading's anchor. Not required for this PR but would be a good follow-up.

Replace inline slugify with github-slugger to match fumadocs
heading ID generation exactly.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@rsbh rsbh merged commit 78373c4 into main May 20, 2026
4 checks passed
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.

3 participants