@raystack/chronicle 0.1.0-canary.6511afe → 0.1.0-canary.67113f8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +72 -18
- package/package.json +1 -1
- package/src/components/mdx/code.tsx +10 -1
- package/src/components/mdx/details.module.css +1 -26
- package/src/components/mdx/details.tsx +2 -3
- package/src/components/mdx/index.tsx +15 -1
- package/src/components/ui/breadcrumbs.tsx +8 -42
- package/src/lib/api-routes.ts +6 -8
- package/src/lib/mdx-loader.ts +21 -0
- package/src/lib/page-context.tsx +13 -16
- package/src/lib/source.ts +79 -105
- package/src/pages/DocsPage.tsx +1 -1
- package/src/server/api/page/[...slug].ts +5 -6
- package/src/server/entry-client.tsx +21 -25
- package/src/server/entry-server.tsx +28 -23
- package/src/server/routes/sitemap.xml.ts +3 -2
- package/src/server/vite-config.ts +45 -17
- package/src/themes/default/Layout.tsx +13 -10
- package/src/themes/default/Page.module.css +44 -0
- package/src/themes/default/Toc.tsx +14 -30
- package/src/themes/paper/ChapterNav.tsx +14 -14
- package/src/themes/paper/Page.module.css +5 -0
- package/src/themes/paper/Page.tsx +15 -41
- package/src/themes/paper/ReadingProgress.tsx +2 -2
- package/src/types/content.ts +5 -21
- package/src/types/globals.d.ts +0 -1
- package/src/types/theme.ts +4 -3
|
@@ -1,46 +1,30 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Text } from '@raystack/apsara';
|
|
4
|
-
import {
|
|
5
|
-
import type {
|
|
4
|
+
import { AnchorProvider, useActiveAnchor } from 'fumadocs-core/toc';
|
|
5
|
+
import type { TableOfContents, TOCItemType } from 'fumadocs-core/toc';
|
|
6
6
|
import styles from './Toc.module.css';
|
|
7
7
|
|
|
8
8
|
interface TocProps {
|
|
9
|
-
items:
|
|
9
|
+
items: TableOfContents;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function Toc({ items }: TocProps) {
|
|
13
|
-
const [activeId, setActiveId] = useState<string>('');
|
|
14
|
-
|
|
15
|
-
// Filter to only show h2 and h3 headings
|
|
16
13
|
const filteredItems = items.filter(
|
|
17
14
|
item => item.depth >= 2 && item.depth <= 3
|
|
18
15
|
);
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
const headingIds = filteredItems.map(item => item.url.replace('#', ''));
|
|
22
|
-
|
|
23
|
-
const observer = new IntersectionObserver(
|
|
24
|
-
entries => {
|
|
25
|
-
entries.forEach(entry => {
|
|
26
|
-
if (entry.isIntersecting) {
|
|
27
|
-
setActiveId(entry.target.id);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
},
|
|
31
|
-
// -80px top: offset for fixed header, -80% bottom: trigger when heading is in top 20% of viewport
|
|
32
|
-
{ rootMargin: '-80px 0px -80% 0px' }
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
headingIds.forEach(id => {
|
|
36
|
-
const element = document.getElementById(id);
|
|
37
|
-
if (element) observer.observe(element);
|
|
38
|
-
});
|
|
17
|
+
if (filteredItems.length === 0) return null;
|
|
39
18
|
|
|
40
|
-
|
|
41
|
-
|
|
19
|
+
return (
|
|
20
|
+
<AnchorProvider toc={filteredItems} single>
|
|
21
|
+
<TocContent items={filteredItems} />
|
|
22
|
+
</AnchorProvider>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
42
25
|
|
|
43
|
-
|
|
26
|
+
function TocContent({ items }: { items: TOCItemType[] }) {
|
|
27
|
+
const activeAnchor = useActiveAnchor();
|
|
44
28
|
|
|
45
29
|
return (
|
|
46
30
|
<aside className={styles.toc}>
|
|
@@ -48,9 +32,9 @@ export function Toc({ items }: TocProps) {
|
|
|
48
32
|
On this page
|
|
49
33
|
</Text>
|
|
50
34
|
<nav className={styles.nav}>
|
|
51
|
-
{
|
|
35
|
+
{items.map(item => {
|
|
52
36
|
const id = item.url.replace('#', '');
|
|
53
|
-
const isActive =
|
|
37
|
+
const isActive = activeAnchor === id;
|
|
54
38
|
const isNested = item.depth > 2;
|
|
55
39
|
return (
|
|
56
40
|
<a
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Link as RouterLink, useLocation } from 'react-router';
|
|
2
2
|
import { MethodBadge } from '@/components/api/method-badge';
|
|
3
|
-
import type {
|
|
3
|
+
import type { Root, Node } from 'fumadocs-core/page-tree';
|
|
4
4
|
import styles from './ChapterNav.module.css';
|
|
5
5
|
|
|
6
6
|
const iconMap: Record<string, React.ReactNode> = {
|
|
@@ -12,16 +12,16 @@ const iconMap: Record<string, React.ReactNode> = {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
interface ChapterNavProps {
|
|
15
|
-
tree:
|
|
15
|
+
tree: Root;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function buildChapterIndices(
|
|
19
|
-
children:
|
|
20
|
-
): Map<
|
|
21
|
-
const indices = new Map<
|
|
19
|
+
children: Node[]
|
|
20
|
+
): Map<Node, number> {
|
|
21
|
+
const indices = new Map<Node, number>();
|
|
22
22
|
let index = 0;
|
|
23
23
|
for (const item of children) {
|
|
24
|
-
if (item.type === 'folder'
|
|
24
|
+
if (item.type === 'folder') {
|
|
25
25
|
index++;
|
|
26
26
|
indices.set(item, index);
|
|
27
27
|
}
|
|
@@ -39,17 +39,17 @@ export function ChapterNav({ tree }: ChapterNavProps) {
|
|
|
39
39
|
{tree.children.map(item => {
|
|
40
40
|
if (item.type === 'separator') return null;
|
|
41
41
|
|
|
42
|
-
if (item.type === 'folder'
|
|
42
|
+
if (item.type === 'folder') {
|
|
43
43
|
const chapterIndex = chapterIndices.get(item) ?? 0;
|
|
44
44
|
return (
|
|
45
|
-
<li key={item.name} className={styles.chapter}>
|
|
45
|
+
<li key={item.name?.toString()} className={styles.chapter}>
|
|
46
46
|
<span className={styles.chapterLabel}>
|
|
47
47
|
{String(chapterIndex).padStart(2, '0')}. {item.name}
|
|
48
48
|
</span>
|
|
49
49
|
<ul className={styles.chapterItems}>
|
|
50
50
|
{item.children.map(child => (
|
|
51
51
|
<ChapterItem
|
|
52
|
-
key={child.url
|
|
52
|
+
key={child.type === 'page' ? child.url : (child.name?.toString() ?? '')}
|
|
53
53
|
item={child}
|
|
54
54
|
pathname={pathname}
|
|
55
55
|
/>
|
|
@@ -61,7 +61,7 @@ export function ChapterNav({ tree }: ChapterNavProps) {
|
|
|
61
61
|
|
|
62
62
|
return (
|
|
63
63
|
<ChapterItem
|
|
64
|
-
key={item.url ?? item.name}
|
|
64
|
+
key={item.url ?? item.name?.toString() ?? ''}
|
|
65
65
|
item={item}
|
|
66
66
|
pathname={pathname}
|
|
67
67
|
/>
|
|
@@ -76,19 +76,19 @@ function ChapterItem({
|
|
|
76
76
|
item,
|
|
77
77
|
pathname
|
|
78
78
|
}: {
|
|
79
|
-
item:
|
|
79
|
+
item: Node;
|
|
80
80
|
pathname: string;
|
|
81
81
|
}) {
|
|
82
82
|
if (item.type === 'separator') return null;
|
|
83
83
|
|
|
84
|
-
if (item.type === 'folder'
|
|
84
|
+
if (item.type === 'folder') {
|
|
85
85
|
return (
|
|
86
86
|
<li>
|
|
87
87
|
<span className={styles.subLabel}>{item.name}</span>
|
|
88
88
|
<ul className={styles.chapterItems}>
|
|
89
89
|
{item.children.map(child => (
|
|
90
90
|
<ChapterItem
|
|
91
|
-
key={child.url
|
|
91
|
+
key={child.type === 'page' ? child.url : (child.name?.toString() ?? '')}
|
|
92
92
|
item={child}
|
|
93
93
|
pathname={pathname}
|
|
94
94
|
/>
|
|
@@ -99,7 +99,7 @@ function ChapterItem({
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
const isActive = pathname === item.url;
|
|
102
|
-
const icon = item.icon ? iconMap[item.icon] :
|
|
102
|
+
const icon = typeof item.icon === 'string' ? iconMap[item.icon] : item.icon;
|
|
103
103
|
|
|
104
104
|
return (
|
|
105
105
|
<li>
|
|
@@ -2,59 +2,33 @@ import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
|
|
|
2
2
|
import { Flex } from '@raystack/apsara';
|
|
3
3
|
import { useMemo } from 'react';
|
|
4
4
|
import { Link as RouterLink, useLocation } from 'react-router';
|
|
5
|
+
import { getBreadcrumbItems } from 'fumadocs-core/breadcrumb';
|
|
6
|
+
import { flattenTree } from 'fumadocs-core/page-tree';
|
|
5
7
|
import { Search } from '@/components/ui/search';
|
|
6
|
-
import type {
|
|
8
|
+
import type { ThemePageProps } from '@/types';
|
|
7
9
|
import styles from './Page.module.css';
|
|
8
10
|
import { ReadingProgress } from './ReadingProgress';
|
|
9
11
|
|
|
10
|
-
function flattenTree(items: PageTreeItem[]): PageTreeItem[] {
|
|
11
|
-
const result: PageTreeItem[] = [];
|
|
12
|
-
for (const item of items) {
|
|
13
|
-
if (item.type === 'page' && item.url) result.push(item);
|
|
14
|
-
if (item.children) result.push(...flattenTree(item.children));
|
|
15
|
-
}
|
|
16
|
-
return result;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function findBreadcrumb(
|
|
20
|
-
items: PageTreeItem[],
|
|
21
|
-
slug: string[]
|
|
22
|
-
): { label: string; href: string }[] {
|
|
23
|
-
const result: { label: string; href: string }[] = [];
|
|
24
|
-
for (let i = 0; i < slug.length; i++) {
|
|
25
|
-
const path = '/' + slug.slice(0, i + 1).join('/');
|
|
26
|
-
const found = findInTree(items, path);
|
|
27
|
-
result.push({ label: found?.name ?? slug[i], href: path });
|
|
28
|
-
}
|
|
29
|
-
return result;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function findInTree(
|
|
33
|
-
items: PageTreeItem[],
|
|
34
|
-
path: string
|
|
35
|
-
): PageTreeItem | undefined {
|
|
36
|
-
for (const item of items) {
|
|
37
|
-
if (item.url === path) return item;
|
|
38
|
-
if (item.children) {
|
|
39
|
-
const found = findInTree(item.children, path);
|
|
40
|
-
if (found) return found;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return undefined;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
12
|
export function Page({ page, config, tree }: ThemePageProps) {
|
|
47
13
|
const { pathname } = useLocation();
|
|
48
14
|
|
|
49
15
|
const { prev, next, crumbs } = useMemo(() => {
|
|
50
16
|
const pages = flattenTree(tree.children);
|
|
51
17
|
const currentIndex = pages.findIndex(p => p.url === pathname);
|
|
18
|
+
const breadcrumbItems = getBreadcrumbItems(
|
|
19
|
+
pathname,
|
|
20
|
+
tree,
|
|
21
|
+
{ includePage: true }
|
|
22
|
+
);
|
|
52
23
|
return {
|
|
53
24
|
prev: currentIndex > 0 ? pages[currentIndex - 1] : null,
|
|
54
25
|
next: currentIndex < pages.length - 1 ? pages[currentIndex + 1] : null,
|
|
55
|
-
crumbs:
|
|
26
|
+
crumbs: breadcrumbItems.map(item => ({
|
|
27
|
+
label: item.name,
|
|
28
|
+
href: item.url ?? pathname,
|
|
29
|
+
})),
|
|
56
30
|
};
|
|
57
|
-
}, [tree, pathname
|
|
31
|
+
}, [tree, pathname]);
|
|
58
32
|
|
|
59
33
|
return (
|
|
60
34
|
<>
|
|
@@ -63,7 +37,7 @@ export function Page({ page, config, tree }: ThemePageProps) {
|
|
|
63
37
|
<Flex align='center' gap='small' className={styles.navLeft}>
|
|
64
38
|
{prev ? (
|
|
65
39
|
<RouterLink
|
|
66
|
-
to={prev.url
|
|
40
|
+
to={prev.url}
|
|
67
41
|
className={styles.arrow}
|
|
68
42
|
aria-label='Previous page'
|
|
69
43
|
>
|
|
@@ -80,7 +54,7 @@ export function Page({ page, config, tree }: ThemePageProps) {
|
|
|
80
54
|
)}
|
|
81
55
|
{next ? (
|
|
82
56
|
<RouterLink
|
|
83
|
-
to={next.url
|
|
57
|
+
to={next.url}
|
|
84
58
|
className={styles.arrow}
|
|
85
59
|
aria-label='Next page'
|
|
86
60
|
>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { cx } from 'class-variance-authority';
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
-
import type {
|
|
5
|
+
import type { TOCItemType } from 'fumadocs-core/toc';
|
|
6
6
|
import styles from './ReadingProgress.module.css';
|
|
7
7
|
|
|
8
8
|
interface Heading {
|
|
@@ -68,7 +68,7 @@ function resolveOverlaps(headings: Heading[], maxPosition: number): Heading[] {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
interface ReadingProgressProps {
|
|
71
|
-
items:
|
|
71
|
+
items: TOCItemType[];
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
export function ReadingProgress({ items }: ReadingProgressProps) {
|
package/src/types/content.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
|
+
import type { TableOfContents } from 'fumadocs-core/toc'
|
|
3
|
+
|
|
4
|
+
export type { Root, Node, Item, Folder, Separator } from 'fumadocs-core/page-tree'
|
|
5
|
+
export type { TOCItemType, TableOfContents } from 'fumadocs-core/toc'
|
|
2
6
|
|
|
3
7
|
export interface Frontmatter {
|
|
4
8
|
title: string
|
|
@@ -12,25 +16,5 @@ export interface Page {
|
|
|
12
16
|
slug: string[]
|
|
13
17
|
frontmatter: Frontmatter
|
|
14
18
|
content: ReactNode
|
|
15
|
-
toc:
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface TocItem {
|
|
19
|
-
title: string
|
|
20
|
-
url: string
|
|
21
|
-
depth: number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface PageTreeItem {
|
|
25
|
-
type: 'page' | 'folder' | 'separator'
|
|
26
|
-
name: string
|
|
27
|
-
url?: string
|
|
28
|
-
order?: number
|
|
29
|
-
icon?: string
|
|
30
|
-
children?: PageTreeItem[]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface PageTree {
|
|
34
|
-
name: string
|
|
35
|
-
children: PageTreeItem[]
|
|
19
|
+
toc: TableOfContents
|
|
36
20
|
}
|
package/src/types/globals.d.ts
CHANGED
package/src/types/theme.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
|
+
import type { Root } from 'fumadocs-core/page-tree'
|
|
2
3
|
import type { ChronicleConfig } from './config'
|
|
3
|
-
import type { Page
|
|
4
|
+
import type { Page } from './content'
|
|
4
5
|
|
|
5
6
|
export interface ThemeLayoutProps {
|
|
6
7
|
children: ReactNode
|
|
7
8
|
config: ChronicleConfig
|
|
8
|
-
tree:
|
|
9
|
+
tree: Root
|
|
9
10
|
classNames?: { layout?: string; body?: string; sidebar?: string; content?: string }
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export interface ThemePageProps {
|
|
13
14
|
page: Page
|
|
14
15
|
config: ChronicleConfig
|
|
15
|
-
tree:
|
|
16
|
+
tree: Root
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export interface Theme {
|