Skip to content

feat: support order in meta.json and folder redirect#62

Merged
rsbh merged 4 commits into
mainfrom
feat/meta-json-order
May 13, 2026
Merged

feat: support order in meta.json and folder redirect#62
rsbh merged 4 commits into
mainfrom
feat/meta-json-order

Conversation

@rsbh
Copy link
Copy Markdown
Member

@rsbh rsbh commented May 13, 2026

Summary

  • Add order field to meta.json for sorting folders in sidebar
  • Lower order = appears first, no order = end of list
  • Folder order from meta.json takes priority over index page frontmatter order
  • Visiting a folder URL (e.g., /docs/guides) redirects to its first sorted child page

Usage

// docs/getting-started/meta.json
{
  "title": "Getting Started",
  "order": 1
}

// docs/guides/meta.json
{
  "title": "Guides",
  "order": 2
}

Test plan

  • Add order to meta.json files — sidebar respects order
  • Folder without order appears after ordered folders
  • Visit folder URL without index page — redirects to first child
  • Existing page order frontmatter still works
  • Breadcrumbs show title from meta.json

🤖 Generated with Claude Code

rsbh and others added 4 commits May 13, 2026 11:34
Set index_page in chronicle.yaml content entry to specify which page
to redirect to when visiting a content root (e.g., /docs).
Falls back to first page in tree when not set.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- meta.json order field sorts folders in sidebar
- Visiting a folder URL redirects to its first sorted child page
- Folder order from meta.json takes priority over index page frontmatter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 13, 2026

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

Project Deployment Actions Updated (UTC)
chronicle Ready Ready Preview, Comment May 13, 2026 6:59am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for configuring custom index pages for content sections.
    • Extended ordering capabilities to support both pages and folders.
  • Bug Fixes

    • Improved 404 page handling with enhanced route resolution and folder-specific redirect logic.

Walkthrough

Index page configuration and folder-aware tree ordering have been added. A new optional index_page field in content entries enables per-section landing page specification. Page tree sorting now respects both page and folder-level order metadata from meta.json files. 404 redirect logic uses configured index pages, content-root fallbacks, and folder-based resolution via recursive pathname matching.

Changes

Index Page Routing and Folder Ordering

Layer / File(s) Summary
Config contract for index_page field
packages/chronicle/src/types/config.ts
Adds optional index_page: string field to contentEntrySchema, enabling per-content-entry landing page configuration.
Folder ordering map and tree sorting infrastructure
packages/chronicle/src/lib/source.ts
Introduces getOrder helper to resolve effective node ordering from separate page and folder order maps (preferring folder index order when available). Adds buildFolderOrderMap to extract folder order values from meta.json entries. Updates sortTreeByOrder to accept meta files, build both order maps, and apply dual page+folder sorting.
Page tree meta file collection
packages/chronicle/src/lib/source.ts
Modifies getPageTree to filter and collect meta files (type === 'meta'), passing them into sortTreeByOrder to enable folder-level ordering during tree caching.
404 redirect resolution with index_page and folder-first fallback
packages/chronicle/src/pages/DocsPage.tsx, packages/chronicle/src/types/config.ts
Adds findFolderFirstPage recursive helper to locate a folder node by matching its index.url against the current pathname, returning the first page URL within that folder. Reworks DocsPage 404 error handling to redirect to configured index_page when present, fall back to content-root first page, or recursively resolve folder-specific first pages using pathname matching.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • raystack/chronicle#19: Overlapping updates to folder and frontmatter ordering logic in the same getPageTree module and related sorting functions.

Suggested reviewers

  • rohilsurana
  • rohanchkrabrty
🚥 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 clearly summarizes the main changes: adding order field support to meta.json for folder sorting and implementing folder redirect functionality.
Description check ✅ Passed The description is directly related to the changeset, providing clear examples, usage instructions, and a test plan that align with the implemented features.
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/meta-json-order

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.

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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/chronicle/src/lib/source.ts`:
- Around line 121-131: The comparator currently ignores folder-level order from
meta.json when a Folder node has no index because getOrder(node, pageOrderMap,
folderOrderMap) only looks up folder order via node.index.url; update the logic
to use the folder's own URL (built during tree traversal) when resolving folder
order. Change the call sites that compute ordering (where getOrder is invoked
during traversal/sorting) to pass the folderUrl (the folder path derived from
meta.path) and then modify getOrder (or add a new parameter) so when node.type
=== 'folder' and node.index is undefined it looks up
folderOrderMap.get(folderUrl) before falling back to pageOrderMap. Ensure you
reference and update buildFolderOrderMap keys (which are derived from meta.path)
and the Node/Folder handling so folder meta.order is applied even without an
index file.

In `@packages/chronicle/src/pages/DocsPage.tsx`:
- Around line 38-50: The current logic performs an unconditional redirect to
contentConfig.index_page on any 404 whose first slug matches a content dir,
which masks real 404s and can create a redirect loop; update the block that
checks contentConfig.index_page to only run when isContentRoot is true and
additionally guard the redirect target (constructed from contentConfig.dir +
contentConfig.index_page) to ensure it resolves to a real page (or is not equal
to the current pathname) before returning <Navigate />. Locate the check around
index_page in DocsPage (references: slug, contentConfig, isContentRoot,
pathname, tree, getFirstPageUrl, findFolderFirstPage) and modify it so
index_page redirect is gated by isContentRoot and validated against existing
pages or current pathname to prevent loops.
- Around line 19-29: findFolderFirstPage fails for folders that lack an index
because it only compares node.index?.url to pathname; update findFolderFirstPage
to be path-aware by adding a currentPath (or prefix) parameter and construct the
folder's URL when index is missing (e.g., folderUrl = node.index?.url ??
`${currentPath}/${nodeSlugOrName}`), then compare that folderUrl to pathname;
pass the updated currentPath into recursive calls for children and use
getFirstPageUrl(node.children) unchanged when a match is found. Ensure you
update all call sites to supply the initial empty/current path.

In `@packages/chronicle/src/types/config.ts`:
- Line 89: Rename the public config field from index_page to indexPage in the
schema (update the zod entry in config.ts that currently uses index_page:
z.string().optional()), and update all consumers to the new camelCase name
(notably the code that reads it in DocsPage.tsx and any docs/examples) so
configs remain consistent; ensure backward compatibility if needed by optionally
accepting index_page and mapping it to indexPage during parsing/migration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d47b4f9a-aded-4e63-be54-e65b09ee3896

📥 Commits

Reviewing files that changed from the base of the PR and between 7ab1885 and 82bf974.

📒 Files selected for processing (3)
  • packages/chronicle/src/lib/source.ts
  • packages/chronicle/src/pages/DocsPage.tsx
  • packages/chronicle/src/types/config.ts

Comment on lines +121 to 131
function getOrder(node: Node, pageOrderMap: Map<string, number>, folderOrderMap: Map<string, number>): number | undefined {
if (node.type === 'page') return pageOrderMap.get(node.url);
if (node.type === 'folder') {
if (node.index) {
const fromMeta = folderOrderMap.get(node.index.url);
if (fromMeta !== undefined) return fromMeta;
return pageOrderMap.get(node.index.url);
}
}
return undefined;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether fumadocs Folder nodes expose any path/url identifier independent of `index`.
rg -nP --type=ts -C3 '\bFolder\b' node_modules/fumadocs-core/dist 2>/dev/null | head -200
fd -i 'page-tree' node_modules/fumadocs-core 2>/dev/null | head -20

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

# First, check the full source.ts file to understand the context
wc -l packages/chronicle/src/lib/source.ts

Repository: raystack/chronicle

Length of output: 103


🏁 Script executed:

# Read the relevant section around getOrder and buildFolderOrderMap
sed -n '115,160p' packages/chronicle/src/lib/source.ts

Repository: raystack/chronicle

Length of output: 1712


🏁 Script executed:

# Check for Folder/Node type definitions in the repository
fd -i 'types\|index' packages/chronicle/src --type f | head -20

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

# Look for where Node type is defined
rg -n 'type Node\b|interface Node' packages/chronicle/src

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

head -50 packages/chronicle/src/lib/source.ts

Repository: raystack/chronicle

Length of output: 1709


🏁 Script executed:

# Search for Node, Folder, Root type definitions in the entire src directory
rg -n 'type Node|interface Node|type Folder|interface Folder|type Root|interface Root' packages/chronicle/src --type ts

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

# Look in node_modules for fumadocs types
find node_modules -name "*.d.ts" -type f 2>/dev/null | xargs grep -l "type Folder\|interface Folder" 2>/dev/null | head -5

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

# Find fumadocs-core in node_modules
find node_modules/fumadocs-core -name "*.d.ts" -type f 2>/dev/null | grep -E "(page-tree|index)" | head -10

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

# Look for page-tree type definitions
find node_modules -path "*fumadocs-core*page-tree*" -type f 2>/dev/null | head -20

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

# Try to find the actual fumadocs-core types
fd -t f "fumadocs-core" node_modules 2>/dev/null | head -20

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

# Check if fumadocs-core is in package.json
rg 'fumadocs-core' packages/chronicle/package.json

Repository: raystack/chronicle

Length of output: 93


🏁 Script executed:

# Also check root package.json
rg 'fumadocs-core' package.json

Repository: raystack/chronicle

Length of output: 44


🌐 Web query:

fumadocs-core Folder type definition page-tree

💡 Result:

In fumadocs-core, the “folder” node in the Page Tree is defined as a TypeScript interface with the node type discriminator type: 'folder'. A Folder node contains name, children, and optional folder-specific metadata like index, description, and collapsible/root/defaultOpen/icon settings. [1][2] Example shape (from the “Page Tree API” docs): interface Folder { $id?: string; $ref?: { metaFile?: string }; type: 'folder'; name: ReactNode; description?: ReactNode; root?: boolean; defaultOpen?: boolean; collapsible?: boolean; index?: Item; icon?: ReactNode; children: Node[]; } [1] How this maps to folder behavior: - A page tree is described as navigation links that includes separators and folders. [2] - A Folder node is explicitly “a node containing multiple children nodes.” [2] - Folder-specific “meta.json” options (title/icon/root/defaultOpen/collapsible/description/pages) are part of the meta data used to control folder behavior in the page tree. [3][4] Related utility types/docs: - The “Page Tree Utils” docs show that fumadocs-core exports helpers operating on the page tree root type Root from fumadocs-core/page-tree. [5] Citations: [1] https://www.mintlify.com/fuma-nama/fumadocs/api/core/page-tree [2] https://www.fumadocs.dev/docs/headless/page-tree [5] https://www.fumadocs.dev/docs/headless/utils/page-tree [3] https://www.fumadocs.dev/docs/headless/page-conventions [4] https://www.mintlify.com/fuma-nama/fumadocs/api/core/source

Citations:


Folder order in meta.json is silently ignored when the folder has no index page.

getOrder only resolves a folder's order via node.index?.url, but buildFolderOrderMap keys entries by the folder's URL derived from meta.path. For folders without an index.mdx/readme.mdx, node.index is undefined, so the meta-based order never gets consulted and the folder falls into the "no order" bucket (sorted last). The PR's stated goal of using order in meta.json to control folder sorting therefore only holds for folders that happen to have an index page.

The Folder type from fumadocs-core has no standalone path/url property—only index and children. To fix this, the comparator needs the folder's own URL (built and passed down during tree traversal) rather than relying on node.index.url.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/chronicle/src/lib/source.ts` around lines 121 - 131, The comparator
currently ignores folder-level order from meta.json when a Folder node has no
index because getOrder(node, pageOrderMap, folderOrderMap) only looks up folder
order via node.index.url; update the logic to use the folder's own URL (built
during tree traversal) when resolving folder order. Change the call sites that
compute ordering (where getOrder is invoked during traversal/sorting) to pass
the folderUrl (the folder path derived from meta.path) and then modify getOrder
(or add a new parameter) so when node.type === 'folder' and node.index is
undefined it looks up folderOrderMap.get(folderUrl) before falling back to
pageOrderMap. Ensure you reference and update buildFolderOrderMap keys (which
are derived from meta.path) and the Node/Folder handling so folder meta.order is
applied even without an index file.

Comment on lines +19 to +29
function findFolderFirstPage(nodes: Node[], pathname: string): string | null {
for (const node of nodes) {
if (node.type === 'folder') {
const folderUrl = node.index?.url;
if (folderUrl === pathname) return getFirstPageUrl(node.children);
const found = findFolderFirstPage(node.children, pathname);
if (found) return found;
}
}
return null;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm fumadocs Folder type has no intrinsic url/path field independent of `index`.
fd -i 'page-tree' node_modules/fumadocs-core 2>/dev/null
rg -nP --type=ts -C5 '(interface|type)\s+Folder\b' node_modules/fumadocs-core 2>/dev/null | head -120

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

cat -n packages/chronicle/src/pages/DocsPage.tsx | head -60

Repository: raystack/chronicle

Length of output: 2553


🏁 Script executed:

rg -n "type Node|interface Node|type Folder|interface Folder" packages/chronicle --type=ts --type=tsx -A 3

Repository: raystack/chronicle

Length of output: 90


🏁 Script executed:

rg -n "findFolderFirstPage" packages/chronicle --type=ts --type=tsx -B 5 -A 5

Repository: raystack/chronicle

Length of output: 90


🏁 Script executed:

rg -n "type Node|interface Node" packages/chronicle --type=ts -A 5

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

cat package.json | grep -A 5 -B 5 fumadocs

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

# Try to find Node definition in node_modules if available
find node_modules -name "page-tree*" -type f 2>/dev/null | head -20

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

cat packages/chronicle/package.json | grep -E "name|fumadocs|dependencies" -A 30

Repository: raystack/chronicle

Length of output: 2271


🏁 Script executed:

cat package.json | grep -E "fumadocs" -B 2 -A 2

Repository: raystack/chronicle

Length of output: 44


🏁 Script executed:

# Examine the logic: how pathname is constructed and used
rg -n "pathname|slug" packages/chronicle/src/pages/DocsPage.tsx -B 2 -A 2

Repository: raystack/chronicle

Length of output: 1493


🌐 Web query:

fumadocs-core page-tree Node Folder type structure url properties

💡 Result:

In fumadocs-core, the page-tree is a hierarchical structure for navigation, defined by the Node type which is a union: type Node = Item | Separator | Folder [1][2][3]. Folders represent collapsible sections containing child nodes. Folder interface [1][2]: interface Folder { $id?: string; $ref?: { metaFile?: string }; type: 'folder'; name: ReactNode; description?: ReactNode; root?: boolean; // Marks as root folder, affects navigation visibility [4][3] defaultOpen?: boolean; collapsible?: boolean; index?: Item; // Index page for the folder (not used for root folders) [2] icon?: ReactNode; children: Node[]; // Recursive child nodes } Item (page) interface, relevant for index and leaf pages [1]: interface Item { $id?: string; $ref?: { file: string }; type: 'page'; name: ReactNode; url: string; // URL/path of the page external?: boolean; description?: ReactNode; icon?: ReactNode; } The structure is built from content directories and meta.json files, where folders can define pages array for custom ordering/exclusions [4]. URLs derive from file paths/slugs [4][5]. Root is typically { type: 'root', children: Node[] } or array of root Folders [6].

Citations:


🏁 Script executed:

# Search for other usages of Node/Folder in the codebase to understand structure
rg -n "node\." packages/chronicle/src/pages/DocsPage.tsx --type=ts -B 1 -A 1

Repository: raystack/chronicle

Length of output: 560


🏁 Script executed:

# Look for page-tree usage elsewhere in the codebase
rg -n "page-tree|Node\[" packages/chronicle/src --type=ts -C 3

Repository: raystack/chronicle

Length of output: 8376


findFolderFirstPage cannot match folders without an index page — the PR's stated scenario fails.

The match condition is node.index?.url === pathname. According to fumadocs-core type definitions, Folder has no intrinsic url field; the only way to access a folder's URL is via its optional index?.url property. For folders without an index page, node.index is undefined, making folderUrl undefined and the comparison undefined === pathname always false.

The PR targets the case where visiting /docs/guides (a folder without an index page) should redirect to its first child page. Since such folders have no node.index, the redirect logic cannot trigger.

A path-aware traversal that constructs the folder's URL from accumulated path segments is needed to match and redirect folders without index pages.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/chronicle/src/pages/DocsPage.tsx` around lines 19 - 29,
findFolderFirstPage fails for folders that lack an index because it only
compares node.index?.url to pathname; update findFolderFirstPage to be
path-aware by adding a currentPath (or prefix) parameter and construct the
folder's URL when index is missing (e.g., folderUrl = node.index?.url ??
`${currentPath}/${nodeSlugOrName}`), then compare that folderUrl to pathname;
pass the updated currentPath into recursive calls for children and use
getFirstPageUrl(node.children) unchanged when a match is found. Ensure you
update all call sites to supply the initial empty/current path.

Comment on lines 38 to +50
if (errorStatus === 404) {
const isContentRoot = config.content?.some(c => slug.length === 1 && slug[0] === c.dir);
const pathname = `/${slug.join('/')}`;
const contentConfig = config.content?.find(c => c.dir === slug[0]);
const isContentRoot = slug.length === 1 && slug[0] === contentConfig?.dir;
if (contentConfig?.index_page) {
return <Navigate to={`/${contentConfig.dir}/${contentConfig.index_page}`} replace />;
}
if (isContentRoot) {
const firstUrl = getFirstPageUrl(tree.children);
if (firstUrl) return <Navigate to={firstUrl} replace />;
}
const folderFirstUrl = findFolderFirstPage(tree.children, pathname);
if (folderFirstUrl) return <Navigate to={folderFirstUrl} replace />;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

index_page redirect masks every 404 inside a configured content directory and risks a redirect loop.

The index_page check at lines 42–44 is not gated by isContentRoot. Any 404 whose first slug segment matches a configured content[].dir (e.g., a typo like /docs/intoduction, or a stale link like /docs/foo/bar) will be silently redirected to /${dir}/${index_page} instead of surfacing the real 404 to the user. The commit message itself scopes this behavior to "when visiting a content root", which matches what isContentRoot already encodes.

There is also a redirect-loop risk: if index_page itself doesn't resolve to a valid page, the redirect target lands on a path with the same first segment, comes back here as a 404, and <Navigate>s again to the same target.

🐛 Proposed fix — gate `index_page` on `isContentRoot`
   if (errorStatus === 404) {
     const pathname = `/${slug.join('/')}`;
     const contentConfig = config.content?.find(c => c.dir === slug[0]);
     const isContentRoot = slug.length === 1 && slug[0] === contentConfig?.dir;
-    if (contentConfig?.index_page) {
-      return <Navigate to={`/${contentConfig.dir}/${contentConfig.index_page}`} replace />;
-    }
     if (isContentRoot) {
+      if (contentConfig?.index_page) {
+        return <Navigate to={`/${contentConfig.dir}/${contentConfig.index_page}`} replace />;
+      }
       const firstUrl = getFirstPageUrl(tree.children);
       if (firstUrl) return <Navigate to={firstUrl} replace />;
     }
     const folderFirstUrl = findFolderFirstPage(tree.children, pathname);
     if (folderFirstUrl) return <Navigate to={folderFirstUrl} replace />;
     return <NotFound />;
   }

Consider also validating that the index_page target resolves to a real page (or guarding against the redirect target equaling the current pathname) to avoid the loop scenario above.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (errorStatus === 404) {
const isContentRoot = config.content?.some(c => slug.length === 1 && slug[0] === c.dir);
const pathname = `/${slug.join('/')}`;
const contentConfig = config.content?.find(c => c.dir === slug[0]);
const isContentRoot = slug.length === 1 && slug[0] === contentConfig?.dir;
if (contentConfig?.index_page) {
return <Navigate to={`/${contentConfig.dir}/${contentConfig.index_page}`} replace />;
}
if (isContentRoot) {
const firstUrl = getFirstPageUrl(tree.children);
if (firstUrl) return <Navigate to={firstUrl} replace />;
}
const folderFirstUrl = findFolderFirstPage(tree.children, pathname);
if (folderFirstUrl) return <Navigate to={folderFirstUrl} replace />;
if (errorStatus === 404) {
const pathname = `/${slug.join('/')}`;
const contentConfig = config.content?.find(c => c.dir === slug[0]);
const isContentRoot = slug.length === 1 && slug[0] === contentConfig?.dir;
if (isContentRoot) {
if (contentConfig?.index_page) {
return <Navigate to={`/${contentConfig.dir}/${contentConfig.index_page}`} replace />;
}
const firstUrl = getFirstPageUrl(tree.children);
if (firstUrl) return <Navigate to={firstUrl} replace />;
}
const folderFirstUrl = findFolderFirstPage(tree.children, pathname);
if (folderFirstUrl) return <Navigate to={folderFirstUrl} replace />;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/chronicle/src/pages/DocsPage.tsx` around lines 38 - 50, The current
logic performs an unconditional redirect to contentConfig.index_page on any 404
whose first slug matches a content dir, which masks real 404s and can create a
redirect loop; update the block that checks contentConfig.index_page to only run
when isContentRoot is true and additionally guard the redirect target
(constructed from contentConfig.dir + contentConfig.index_page) to ensure it
resolves to a real page (or is not equal to the current pathname) before
returning <Navigate />. Locate the check around index_page in DocsPage
(references: slug, contentConfig, isContentRoot, pathname, tree,
getFirstPageUrl, findFolderFirstPage) and modify it so index_page redirect is
gated by isContentRoot and validated against existing pages or current pathname
to prevent loops.

label: z.string().min(1),
description: z.string().optional(),
icon: z.string().optional(),
index_page: z.string().optional(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Naming inconsistency: prefer indexPage over index_page.

The rest of chronicle.yaml's public config schema uses camelCase (googleAnalytics, measurementId, basePath, serviceName). Once this field ships, renaming it will be a breaking change for user configs, so it's worth aligning now.

♻️ Proposed rename
-  index_page: z.string().optional(),
+  indexPage: z.string().optional(),

Update the corresponding read site in DocsPage.tsx (and any docs/examples) accordingly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/chronicle/src/types/config.ts` at line 89, Rename the public config
field from index_page to indexPage in the schema (update the zod entry in
config.ts that currently uses index_page: z.string().optional()), and update all
consumers to the new camelCase name (notably the code that reads it in
DocsPage.tsx and any docs/examples) so configs remain consistent; ensure
backward compatibility if needed by optionally accepting index_page and mapping
it to indexPage during parsing/migration.

Comment thread packages/chronicle/src/lib/source.ts
Comment thread packages/chronicle/src/pages/DocsPage.tsx
Comment thread packages/chronicle/src/pages/DocsPage.tsx
@rsbh rsbh merged commit 682ce9f into main May 13, 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.

2 participants