Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ web_modules/
# Next.js build output
.next
out
next-env.d.ts

# Fumadocs generated
.source
Expand Down
6 changes: 0 additions & 6 deletions packages/chronicle/next-env.d.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/chronicle/src/app/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { notFound } from 'next/navigation'
import type { MDXContent } from 'mdx/types'
import { loadConfig } from '../../lib/config'
import { source, buildPageTree } from '../../lib/source'
import { defaultTheme } from '../../themes/default'
import { getTheme } from '../../themes/registry'
import { mdxComponents } from '../../components/mdx'

interface PageProps {
Expand All @@ -26,7 +26,7 @@ export default async function DocsPage({ params }: PageProps) {
notFound()
}

const { Layout, Page } = defaultTheme
const { Layout, Page } = getTheme(config.theme?.name)

const data = page.data as PageData
const MDXBody = data.body
Expand Down
5 changes: 5 additions & 0 deletions packages/chronicle/src/app/apis/[[...slug]]/layout.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@
.content {
height: 100%;
overflow-y: auto;
padding-right: 0;
}

.hiddenSearch {
display: none;
}
5 changes: 4 additions & 1 deletion packages/chronicle/src/app/apis/[[...slug]]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { loadConfig } from '../../../lib/config'
import { loadApiSpecs } from '../../../lib/openapi'
import { buildApiPageTree } from '../../../lib/api-routes'
import { Layout } from '../../../themes/default'
import { getTheme } from '../../../themes/registry'
import { Search } from '../../../components/ui/search'
import styles from './layout.module.css'

export default function ApiLayout({ children }: { children: React.ReactNode }) {
const config = loadConfig()
const { Layout } = getTheme(config.theme?.name)
const specs = loadApiSpecs(config.api ?? [])
const tree = buildApiPageTree(specs)

Expand All @@ -16,6 +18,7 @@ export default function ApiLayout({ children }: { children: React.ReactNode }) {
sidebar: styles.sidebar,
content: styles.content,
}}>
<Search className={styles.hiddenSearch} />
{children}
</Layout>
)
Expand Down
4 changes: 2 additions & 2 deletions packages/chronicle/src/components/api/field-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export function FieldRow({ field, location, editable, value, onChange }: FieldRo
<Flex direction="column" className={styles.main}>
<Flex align="center" justify="between">
{label}
<IconButton size="small" variant="ghost" onClick={() => {
<IconButton size={1} onClick={() => {
const newItem = itemChildren ? {} : ''
onChange?.(field.name, [...items, newItem])
}}>
Expand Down Expand Up @@ -105,7 +105,7 @@ export function FieldRow({ field, location, editable, value, onChange }: FieldRo
}}
/>
)}
<IconButton size="small" variant="ghost" onClick={() => {
<IconButton size={1} onClick={() => {
const updated = items.filter((_, j) => j !== i)
onChange?.(field.name, updated)
}}>
Expand Down
2 changes: 1 addition & 1 deletion packages/chronicle/src/components/api/key-value-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function KeyValueEditor({ entries, onChange }: KeyValueEditorProps) {
onChange={(e) => updateEntry(i, 'value', e.target.value)}
/>
</div>
<IconButton size="small" variant="ghost" aria-label={`Delete ${entry.key || 'entry'}`} onClick={() => removeEntry(i)}>
<IconButton size={1} aria-label={`Delete ${entry.key || 'entry'}`} onClick={() => removeEntry(i)}>
<TrashIcon width={14} height={14} />
</IconButton>
</Flex>
Expand Down
9 changes: 7 additions & 2 deletions packages/chronicle/src/components/ui/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useState, useEffect, useCallback } from "react";
import { useRouter } from "next/navigation";
import { Button, Command, Dialog, Text } from "@raystack/apsara";
import { cx } from "class-variance-authority";
import { useDocsSearch } from "fumadocs-core/search/client";
import type { SortedResult } from "fumadocs-core/search";
import { DocumentIcon, HashtagIcon } from "@heroicons/react/24/outline";
Expand All @@ -24,7 +25,11 @@ function SearchShortcutKey({ className }: { className?: string }) {
);
}

export function Search() {
interface SearchProps {
className?: string
}

export function Search({ className }: SearchProps) {
const [open, setOpen] = useState(false);
const router = useRouter();

Expand Down Expand Up @@ -65,7 +70,7 @@ export function Search() {
variant="outline"
color="neutral"
onClick={() => setOpen(true)}
className={styles.trigger}
className={cx(styles.trigger, className)}
trailingIcon={<SearchShortcutKey className={styles.kbd} />}
>
<Text>Search...</Text>
Expand Down
71 changes: 71 additions & 0 deletions packages/chronicle/src/themes/paper/ChapterNav.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.nav {
display: flex;
flex-direction: column;
gap: var(--rs-space-5);
}

.chapter {
display: flex;
flex-direction: column;
gap: var(--rs-space-2);
}

.chapterLabel {
font-size: var(--rs-font-size-small);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--rs-color-foreground-base-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.chapterItems {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: var(--rs-space-1);
padding-left: var(--rs-space-4);
}

.link {
display: flex;
align-items: center;
gap: var(--rs-space-2);
font-size: var(--rs-font-size-small);
color: var(--rs-color-foreground-base-tertiary);
text-decoration: none;
padding: var(--rs-space-1) 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.link:hover {
color: var(--rs-color-foreground-base-primary);
}

.active {
color: var(--rs-color-foreground-accent-primary);
font-weight: 500;
}

.icon {
display: flex;
align-items: center;
flex-shrink: 0;
}

.subLabel {
font-size: var(--rs-font-size-small);
font-weight: 500;
color: var(--rs-color-foreground-base-secondary);
margin-top: var(--rs-space-3);
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
96 changes: 96 additions & 0 deletions packages/chronicle/src/themes/paper/ChapterNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use client'

import { usePathname } from 'next/navigation'
import NextLink from 'next/link'
import { MethodBadge } from '../../components/api/method-badge'
import type { PageTree, PageTreeItem } from '../../types'
import styles from './ChapterNav.module.css'

const iconMap: Record<string, React.ReactNode> = {
'method-get': <MethodBadge method="GET" size="micro" />,
'method-post': <MethodBadge method="POST" size="micro" />,
'method-put': <MethodBadge method="PUT" size="micro" />,
'method-delete': <MethodBadge method="DELETE" size="micro" />,
'method-patch': <MethodBadge method="PATCH" size="micro" />,
}

interface ChapterNavProps {
tree: PageTree
}

function buildChapterIndices(children: PageTreeItem[]): Map<PageTreeItem, number> {
const indices = new Map<PageTreeItem, number>()
let index = 0
for (const item of children) {
if (item.type === 'folder' && item.children) {
index++
indices.set(item, index)
}
}
return indices
}

export function ChapterNav({ tree }: ChapterNavProps) {
const pathname = usePathname()
const chapterIndices = buildChapterIndices(tree.children)

return (
<nav className={styles.nav}>
<ul className={styles.chapterItems}>
{tree.children.map((item) => {
if (item.type === 'separator') return null

if (item.type === 'folder' && item.children) {
const chapterIndex = chapterIndices.get(item) ?? 0
return (
<li key={item.name} className={styles.chapter}>
<span className={styles.chapterLabel}>
{String(chapterIndex).padStart(2, '0')}. {item.name}
</span>
<ul className={styles.chapterItems}>
{item.children.map((child) => (
<ChapterItem key={child.url ?? child.name} item={child} pathname={pathname} />
))}
</ul>
</li>
)
}

return <ChapterItem key={item.url ?? item.name} item={item} pathname={pathname} />
})}
</ul>
</nav>
)
}

function ChapterItem({ item, pathname }: { item: PageTreeItem; pathname: string }) {
if (item.type === 'separator') return null

if (item.type === 'folder' && item.children) {
return (
<li>
<span className={styles.subLabel}>{item.name}</span>
<ul className={styles.chapterItems}>
{item.children.map((child) => (
<ChapterItem key={child.url ?? child.name} item={child} pathname={pathname} />
))}
</ul>
</li>
)
}

const isActive = pathname === item.url
const icon = item.icon ? iconMap[item.icon] : null

return (
<li>
<NextLink
href={item.url ?? '#'}
className={`${styles.link} ${isActive ? styles.active : ''}`}
>
{icon && <span className={styles.icon}>{icon}</span>}
<span>{item.name}</span>
</NextLink>
</li>
)
}
33 changes: 33 additions & 0 deletions packages/chronicle/src/themes/paper/Layout.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.layout {
--paper-sidebar-width: 260px;

min-height: 100vh;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

.body {
flex: 1;
}

.sidebar {
width: var(--paper-sidebar-width);
padding: var(--rs-space-7) var(--rs-space-5);
background: var(--rs-color-background-neutral-primary);
overflow-y: auto;
font-family: 'SF Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', monospace;
}

.title {
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--rs-color-foreground-accent-primary);
font-family: inherit;
font-size: var(--rs-font-size-mono-large);
margin-bottom: var(--rs-space-7);
}

.content {
flex: 1;
overflow-y: auto;
background: var(--rs-color-background-neutral-primary);
padding-right: var(--paper-sidebar-width);
}
25 changes: 25 additions & 0 deletions packages/chronicle/src/themes/paper/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client'

import { Flex, Headline } from '@raystack/apsara'
import { cx } from 'class-variance-authority'
import { Footer } from '../../components/ui/footer'
import { ChapterNav } from './ChapterNav'
import type { ThemeLayoutProps } from '../../types'
import styles from './Layout.module.css'

export function Layout({ children, config, tree, classNames }: ThemeLayoutProps) {
return (
<Flex direction="column" className={cx(styles.layout, classNames?.layout)}>
<Flex className={cx(styles.body, classNames?.body)}>
<aside className={cx(styles.sidebar, classNames?.sidebar)}>
<Headline size="small" weight="medium" as="h1" className={styles.title}>
{config.title}
</Headline>
<ChapterNav tree={tree} />
</aside>
<div className={cx(styles.content, classNames?.content)}>{children}</div>
</Flex>
<Footer config={config.footer} />
</Flex>
)
}
Loading