Skip to content

Commit e0336dc

Browse files
rsbhclaude
andauthored
feat: default theme layout with tab bar + page nav types (#37)
* feat: add prev/next nav fields to Page type Prepare Page type for backend-provided prev/next links used by the docs sub-navbar. Adds PageNavLink/PageNav types, extends Page via extends PageNav, unifies PageData with Page in page context, and threads null placeholders through SSR + hydration until backend computes real values. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: add std-env as root dep to unblock nitro beta nitro@3.0.260311-beta lists std-env in devDependencies but its runtime imports it. Node ESM resolution from nitro's isolated cache path fails without std-env at top-level node_modules. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: restructure default theme layout with tab bar Drop the top Navbar; move navigation into a per-section tab bar alongside the sidebar. Resize sidebar to 262px, swap button-based nav links for pill-style tabs using mini typography tokens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: switch docs theme to default Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: compute page prev/next on the server Add getPageNav helper that flattens the page tree and returns the adjacent PageNavLinks for a slug. Both the /api/page handler and SSR pageData now populate prev/next so the client can render navigation without re-flattening the tree. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: wrap content in card with prev/next sub-navbar Default theme now renders the main content inside a rounded, bordered card with a sub-navbar at the top containing prev/next IconButtons driven by the server-provided page nav, followed by breadcrumbs. Breadcrumbs move out of Page.tsx since the Layout owns the chrome. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: sidebar top navbar and layout polish - Add sidebar top navbar with placeholder logo, icon-only search, and IconButton-based theme switcher using resolvedTheme so system mode lands on the correct icon. - Simplify Search component to IconButton only, drop cmdk kbd trigger. - Wrap content in a bordered card, remove TOC from Page, align heading + paragraph typography with Aurora tokens. - Secondary bg on sidebar + card wrapper, primary bg on content area. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: floating TOC with hover panel TOC renders as short horizontal markers floating right-center, each width scaled from the heading text length (base + per char, clamped). Hovering the aside fades the markers out and reveals a panel with the full heading list, active state highlighted. Shadows switch to the Apsara --rs-shadow-soft token. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: address PR review feedback - page-context: coerce prev/next from API response to null so the state always matches the Page type instead of carrying undefined. - source.getPageNav: guard PageNavLink.title to string-only page names so ReactNode names don't stringify to "[object Object]". - Layout tabs: compute per-entry active state from api.basePath instead of the shared isApiRoute boolean, so only the matching API tab highlights when multiple APIs are configured. - Layout: skip breadcrumbs on API routes since the docs tree doesn't include API paths. - Search: add title="Search (⌘K)" so the keyboard shortcut stays discoverable after the kbd hint was removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: show sidebar on landing, fix sidebar header height Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: address copilot review feedback (phase 1) - search shortcut tooltip: platform-neutral "Ctrl/⌘K" - TOC: mirror :hover with :focus-within for keyboard users - getPageNav: derive PageNavLink.title from URL when page name is non-string Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: address copilot review feedback (phase 2) - getPageNav: accept pre-loaded tree to avoid redundant tree rebuild - entry-server: skip getPageNav when page is null; share tree with nav - entry-client: nullable frontmatter/relativePath/originalPath in EmbeddedData; narrow at usage Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(default theme): nested collapsible sidebar groups - SidebarNode recurses to depth 1 with data-depth attr on section - Depth 0: non-collapsible section header - Depth 1: collapsible sub-group with right-aligned chevron - Grandchildren folders (depth > 1) ignored - Hide sidebar scrollbar - Style navGroup header/trigger/label/chevron via apsara classNames slots Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: rename Tabs.Trigger to Tabs.Tab in components doc Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(default theme): align sidebar + card to Figma - Drop tabBar (ContentDirButtons + API tabs) and subNav card-chrome - Sidebar header: logo + search + theme only (version switcher moved out) - Sidebar footer: holds VersionSwitcher, hidden when no versions - Sidebar bg uses --rs-color-background-base-secondary - Card: border-left only, no border-radius - cardWrapper: drop horizontal padding - TOC: drop extra right offset Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(default theme): content-dir + API cells at top of sidebar - Render content-dir entries and API entries as Sidebar.Item cells at the top of Sidebar.Main, above the page tree - Hidden when total (content + API) count is ≤ 1 - Drop Apsara's default header margin-bottom Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(default theme): align sidebar text + spacing to Figma - Top content links: tertiary text by default, primary when active, transparent bg - Nav group label uses secondary for section headers, primary for sub-groups - Nav group: zero out default margin-top, apply space-7 only to top-level (data-depth=0) - Zero out gap on Sidebar.Main and nav-group-items - Breadcrumbs: use render={<RouterLink>} so SPA navigation works (not-current items) - Wrap top content/API links in .topLinks div with bottom margin Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(default theme): config-driven sidebar logo - New SidebarLogo component reads config.logo.light/dark - Falls back to BookOpenIcon when no logo is configured - Picks src based on resolvedTheme from Apsara useTheme() Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(config): optional icon per content dir and API - Schema accepts icon string on content entries and api entries - Icon can be a URL or an inline SVG string (uses currentColor) - renderConfigIcon helper picks <img> for URL or inlines SVG - Fallback to DocumentTextIcon / CodeBracketSquareIcon when absent Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: always enable raw .md and llms.txt endpoints - Drop llms config section; routes serve unconditionally - Remove llms.enabled gating from .md slug route and llms.txt routes - Clean up llms: enabled: true from docs and versioned example yamls Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: drop Footer component and footer config - Remove footerSchema + FooterConfig from config schema - Delete Footer component and CSS - Remove Footer usage from paper theme Layout - Clean footer: sections from docs and basic example yamls Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: add <link rel="alternate"> markdown version on docs pages Head accepts optional markdownHref; DocsPage passes `/${slug}.md` so crawlers and AI assistants can discover the raw markdown source. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(default theme): Open in AI dropdown Adds OpenInAI menu in the subNav with four actions: - Copy as MD (fetches the page's .md and writes to clipboard) - View MD (opens the raw .md in a new tab) - Open in ChatGPT (chatgpt.com/?q=Read <mdUrl>) - Open in Claude (claude.ai/new?q=Read <mdUrl>) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(search): migrate to apsara 1.0.0-rc.4 Command API - Bump @raystack/apsara to 1.0.0-rc.4 - Drop manual Dialog wrapper; use Command.Dialog + Command.DialogContent - Command.List -> Command.Content - Command.Group heading -> <Command.Label> - Item onSelect -> onClick; Input leadingIcon replaces external icon - Style dialogContent (width/radius/top) to position at 20% from top Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(default theme): extract --navbar-height CSS variable Replace four 48px usages (sidebar header, sidebar footer, subNav, etc) with var(--navbar-height) defined on .layout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(OpenInAI): add ChatGPT and Claude brand icons to menu items Inline SVG icons using currentColor so they inherit the text color. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: address coderabbit review feedback - Layout: tighten isApiRoute match (exact /apis or /apis/ prefix) so breadcrumb suppression doesn't catch unrelated paths - Sidebar: flex: 0 0 262px + flex-column so it doesn't shrink; Main gets overflow-y: auto + min-height: 0 for long trees - Card: overflow visible so floating TOC + focus rings aren't clipped - Toc markers: aria-hidden + tabIndex=-1 so the panel nav is the single accessible entry - Toc: hide below 900px viewport via media query Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b6ef4db commit e0336dc

37 files changed

Lines changed: 847 additions & 433 deletions

TODO.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# TODO
2+
3+
## Default theme
4+
5+
- [x] Replace sidebar logo placeholder (`CubeIcon`) with configurable logo from `config.logo` (light/dark)
6+
- [x] Add optional `icon` field per content dir and API in `chronicle.yaml` (accepts URL or inline SVG string)
7+
- [x] "Open in AI" dropdown (Copy/View MD, Open in ChatGPT/Claude) in subNav
8+
- [x] Sidebar nesting supports 2 levels; 3rd+ level items are ignored

bun.lock

Lines changed: 5 additions & 64 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/chronicle.yaml

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ content:
77
label: Docs
88

99
theme:
10-
name: paper
10+
name: default
1111

1212
navigation:
1313
links:
@@ -18,16 +18,7 @@ search:
1818
enabled: true
1919
placeholder: Search docs...
2020

21-
llms:
22-
enabled: true
23-
2421
telemetry:
2522
enabled: true
2623

2724
preset: "vercel"
28-
29-
footer:
30-
copyright: "© 2026 Raystack. All rights reserved."
31-
links:
32-
- label: GitHub
33-
href: https://github.com/raystack/chronicle

docs/content/docs/components.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ Tabbed content panels using Apsara's `Tabs` component.
7878
````mdx
7979
<Tabs defaultValue="npm">
8080
<Tabs.List>
81-
<Tabs.Trigger value="npm">npm</Tabs.Trigger>
82-
<Tabs.Trigger value="bun">bun</Tabs.Trigger>
81+
<Tabs.Tab value="npm">npm</Tabs.Tab>
82+
<Tabs.Tab value="bun">bun</Tabs.Tab>
8383
</Tabs.List>
8484
<Tabs.Content value="npm">
8585

examples/basic/chronicle.yaml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,3 @@ analytics:
3838
googleAnalytics:
3939
measurementId: G-XXXXXXXXXX
4040

41-
footer:
42-
copyright: "© 2024 Chronicle. All rights reserved."
43-
links:
44-
- label: GitHub
45-
href: https://github.com/raystack/chronicle
46-
- label: Documentation
47-
href: /

examples/versioned/chronicle.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,3 @@ versions:
3737
search:
3838
enabled: true
3939
placeholder: Search...
40-
41-
llms:
42-
enabled: true

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@
1717
"build:examples:basic": "./packages/chronicle/bin/chronicle.js build --config examples/basic/chronicle.yaml",
1818
"dev:examples:versioned": "./packages/chronicle/bin/chronicle.js dev --config examples/versioned/chronicle.yaml",
1919
"build:examples:versioned": "./packages/chronicle/bin/chronicle.js build --config examples/versioned/chronicle.yaml"
20+
},
21+
"dependencies": {
22+
"std-env": "^4.0.0"
2023
}
2124
}

packages/chronicle/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"@opentelemetry/resources": "^2.6.1",
4343
"@opentelemetry/sdk-metrics": "^2.6.1",
4444
"@opentelemetry/semantic-conventions": "^1.40.0",
45-
"@raystack/apsara": "1.0.0-rc.3",
45+
"@raystack/apsara": "1.0.0-rc.4",
4646
"@shikijs/rehype": "^4.0.2",
4747
"@vitejs/plugin-react": "^6.0.1",
4848
"chalk": "^5.6.2",

packages/chronicle/src/components/ui/breadcrumbs.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { Breadcrumb } from '@raystack/apsara'
44
import { getBreadcrumbItems } from 'fumadocs-core/breadcrumb'
55
import type { Root } from 'fumadocs-core/page-tree'
6+
import { Link as RouterLink } from 'react-router'
67

78
interface BreadcrumbsProps {
89
slug: string[]
@@ -18,11 +19,12 @@ export function Breadcrumbs({ slug, tree }: BreadcrumbsProps) {
1819
return (
1920
<Breadcrumb size="small">
2021
{items.flatMap((item, index) => {
22+
const isCurrent = index === items.length - 1
2123
const breadcrumbItem = (
2224
<Breadcrumb.Item
2325
key={`item-${index}`}
24-
href={item.url}
25-
current={index === items.length - 1}
26+
current={isCurrent}
27+
render={isCurrent ? undefined : <RouterLink to={item.url} />}
2628
>
2729
{item.name}
2830
</Breadcrumb.Item>
Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,35 @@
11
'use client'
22

3-
import { ThemeSwitcher } from '@raystack/apsara'
4-
import { useState, useEffect } from 'react'
3+
import { MoonIcon, SunIcon } from '@heroicons/react/24/outline'
4+
import { IconButton, useTheme } from '@raystack/apsara'
5+
import { useEffect, useState } from 'react'
56

67
interface ClientThemeSwitcherProps {
78
size?: number
89
}
910

10-
export function ClientThemeSwitcher({ size }: ClientThemeSwitcherProps) {
11+
export function ClientThemeSwitcher({ size = 16 }: ClientThemeSwitcherProps) {
1112
const [isClient, setIsClient] = useState(false)
13+
const { resolvedTheme, setTheme } = useTheme()
1214

1315
useEffect(() => {
1416
setIsClient(true)
1517
}, [])
1618

17-
return isClient ? <ThemeSwitcher size={size} /> : null
19+
if (!isClient) return null
20+
21+
const isDark = resolvedTheme === 'dark'
22+
return (
23+
<IconButton
24+
size={3}
25+
aria-label={isDark ? 'Switch to light theme' : 'Switch to dark theme'}
26+
onClick={() => setTheme(isDark ? 'light' : 'dark')}
27+
>
28+
{isDark ? (
29+
<SunIcon width={size} height={size} />
30+
) : (
31+
<MoonIcon width={size} height={size} />
32+
)}
33+
</IconButton>
34+
)
1835
}

0 commit comments

Comments
 (0)