@raystack/chronicle 0.1.0-canary.111b55a → 0.1.0-canary.1e5fdae

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.
Files changed (87) hide show
  1. package/dist/cli/index.js +212 -833
  2. package/package.json +13 -9
  3. package/src/cli/commands/build.ts +30 -70
  4. package/src/cli/commands/dev.ts +24 -13
  5. package/src/cli/commands/init.ts +38 -123
  6. package/src/cli/commands/serve.ts +35 -50
  7. package/src/cli/commands/start.ts +20 -16
  8. package/src/cli/index.ts +14 -14
  9. package/src/cli/utils/config.ts +25 -26
  10. package/src/cli/utils/index.ts +3 -2
  11. package/src/cli/utils/resolve.ts +7 -3
  12. package/src/cli/utils/scaffold.ts +14 -16
  13. package/src/components/mdx/details.module.css +0 -2
  14. package/src/components/mdx/image.tsx +5 -20
  15. package/src/components/mdx/index.tsx +18 -4
  16. package/src/components/mdx/link.tsx +24 -20
  17. package/src/components/ui/breadcrumbs.tsx +8 -42
  18. package/src/components/ui/footer.tsx +2 -3
  19. package/src/components/ui/search.tsx +116 -71
  20. package/src/lib/api-routes.ts +6 -8
  21. package/src/lib/config.ts +31 -29
  22. package/src/lib/get-llm-text.ts +10 -0
  23. package/src/lib/head.tsx +26 -22
  24. package/src/lib/openapi.ts +8 -8
  25. package/src/lib/page-context.tsx +74 -58
  26. package/src/lib/source.ts +136 -114
  27. package/src/pages/ApiLayout.tsx +22 -18
  28. package/src/pages/ApiPage.tsx +32 -27
  29. package/src/pages/DocsLayout.tsx +7 -7
  30. package/src/pages/DocsPage.tsx +11 -11
  31. package/src/pages/NotFound.tsx +11 -4
  32. package/src/server/App.tsx +35 -27
  33. package/src/server/api/apis-proxy.ts +69 -0
  34. package/src/server/api/health.ts +5 -0
  35. package/src/server/api/page/[...slug].ts +17 -0
  36. package/src/server/api/search.ts +170 -0
  37. package/src/server/api/specs.ts +9 -0
  38. package/src/server/build-search-index.ts +78 -68
  39. package/src/server/entry-client.tsx +67 -55
  40. package/src/server/entry-server.tsx +100 -35
  41. package/src/server/routes/llms.txt.ts +61 -0
  42. package/src/server/routes/og.tsx +75 -0
  43. package/src/server/routes/robots.txt.ts +11 -0
  44. package/src/server/routes/sitemap.xml.ts +40 -0
  45. package/src/server/utils/safe-path.ts +17 -0
  46. package/src/server/vite-config.ts +87 -47
  47. package/src/themes/default/Layout.tsx +78 -47
  48. package/src/themes/default/Page.module.css +0 -16
  49. package/src/themes/default/Page.tsx +9 -11
  50. package/src/themes/default/Toc.tsx +25 -39
  51. package/src/themes/default/index.ts +7 -9
  52. package/src/themes/paper/ChapterNav.tsx +63 -43
  53. package/src/themes/paper/Layout.module.css +1 -1
  54. package/src/themes/paper/Layout.tsx +24 -12
  55. package/src/themes/paper/Page.module.css +16 -4
  56. package/src/themes/paper/Page.tsx +56 -62
  57. package/src/themes/paper/ReadingProgress.tsx +160 -139
  58. package/src/themes/paper/index.ts +5 -5
  59. package/src/themes/registry.ts +7 -7
  60. package/src/types/content.ts +5 -21
  61. package/src/types/globals.d.ts +3 -0
  62. package/src/types/theme.ts +4 -3
  63. package/src/cli/__tests__/config.test.ts +0 -25
  64. package/src/cli/__tests__/scaffold.test.ts +0 -10
  65. package/src/pages/__tests__/head.test.tsx +0 -57
  66. package/src/server/__tests__/entry-server.test.tsx +0 -35
  67. package/src/server/__tests__/handlers.test.ts +0 -77
  68. package/src/server/__tests__/og.test.ts +0 -23
  69. package/src/server/__tests__/router.test.ts +0 -72
  70. package/src/server/__tests__/vite-config.test.ts +0 -25
  71. package/src/server/adapters/vercel.ts +0 -133
  72. package/src/server/dev.ts +0 -156
  73. package/src/server/entry-prod.ts +0 -97
  74. package/src/server/entry-vercel.ts +0 -28
  75. package/src/server/handlers/apis-proxy.ts +0 -52
  76. package/src/server/handlers/health.ts +0 -3
  77. package/src/server/handlers/llms.ts +0 -58
  78. package/src/server/handlers/og.ts +0 -87
  79. package/src/server/handlers/robots.ts +0 -11
  80. package/src/server/handlers/search.ts +0 -172
  81. package/src/server/handlers/sitemap.ts +0 -39
  82. package/src/server/handlers/specs.ts +0 -9
  83. package/src/server/index.html +0 -12
  84. package/src/server/prod.ts +0 -18
  85. package/src/server/request-handler.ts +0 -63
  86. package/src/server/router.ts +0 -42
  87. package/src/themes/default/font.ts +0 -4
@@ -1,95 +1,115 @@
1
- 'use client'
2
-
3
- import { useLocation, Link } from 'react-router-dom'
4
- import { MethodBadge } from '@/components/api/method-badge'
5
- import type { PageTree, PageTreeItem } from '@/types'
6
- import styles from './ChapterNav.module.css'
1
+ import { Link as RouterLink, useLocation } from 'react-router';
2
+ import { MethodBadge } from '@/components/api/method-badge';
3
+ import type { Root, Node } from 'fumadocs-core/page-tree';
4
+ import styles from './ChapterNav.module.css';
7
5
 
8
6
  const iconMap: Record<string, React.ReactNode> = {
9
- 'method-get': <MethodBadge method="GET" size="micro" />,
10
- 'method-post': <MethodBadge method="POST" size="micro" />,
11
- 'method-put': <MethodBadge method="PUT" size="micro" />,
12
- 'method-delete': <MethodBadge method="DELETE" size="micro" />,
13
- 'method-patch': <MethodBadge method="PATCH" size="micro" />,
14
- }
7
+ 'method-get': <MethodBadge method='GET' size='micro' />,
8
+ 'method-post': <MethodBadge method='POST' size='micro' />,
9
+ 'method-put': <MethodBadge method='PUT' size='micro' />,
10
+ 'method-delete': <MethodBadge method='DELETE' size='micro' />,
11
+ 'method-patch': <MethodBadge method='PATCH' size='micro' />
12
+ };
15
13
 
16
14
  interface ChapterNavProps {
17
- tree: PageTree
15
+ tree: Root;
18
16
  }
19
17
 
20
- function buildChapterIndices(children: PageTreeItem[]): Map<PageTreeItem, number> {
21
- const indices = new Map<PageTreeItem, number>()
22
- let index = 0
18
+ function buildChapterIndices(
19
+ children: Node[]
20
+ ): Map<Node, number> {
21
+ const indices = new Map<Node, number>();
22
+ let index = 0;
23
23
  for (const item of children) {
24
- if (item.type === 'folder' && item.children) {
25
- index++
26
- indices.set(item, index)
24
+ if (item.type === 'folder') {
25
+ index++;
26
+ indices.set(item, index);
27
27
  }
28
28
  }
29
- return indices
29
+ return indices;
30
30
  }
31
31
 
32
32
  export function ChapterNav({ tree }: ChapterNavProps) {
33
- const { pathname } = useLocation()
34
- const chapterIndices = buildChapterIndices(tree.children)
33
+ const { pathname } = useLocation();
34
+ const chapterIndices = buildChapterIndices(tree.children);
35
35
 
36
36
  return (
37
37
  <nav className={styles.nav}>
38
38
  <ul className={styles.chapterItems}>
39
- {tree.children.map((item) => {
40
- if (item.type === 'separator') return null
39
+ {tree.children.map(item => {
40
+ if (item.type === 'separator') return null;
41
41
 
42
- if (item.type === 'folder' && item.children) {
43
- const chapterIndex = chapterIndices.get(item) ?? 0
42
+ if (item.type === 'folder') {
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
- {item.children.map((child) => (
51
- <ChapterItem key={child.url ?? child.name} item={child} pathname={pathname} />
50
+ {item.children.map(child => (
51
+ <ChapterItem
52
+ key={child.type === 'page' ? child.url : (child.name?.toString() ?? '')}
53
+ item={child}
54
+ pathname={pathname}
55
+ />
52
56
  ))}
53
57
  </ul>
54
58
  </li>
55
- )
59
+ );
56
60
  }
57
61
 
58
- return <ChapterItem key={item.url ?? item.name} item={item} pathname={pathname} />
62
+ return (
63
+ <ChapterItem
64
+ key={item.url ?? item.name?.toString() ?? ''}
65
+ item={item}
66
+ pathname={pathname}
67
+ />
68
+ );
59
69
  })}
60
70
  </ul>
61
71
  </nav>
62
- )
72
+ );
63
73
  }
64
74
 
65
- function ChapterItem({ item, pathname }: { item: PageTreeItem; pathname: string }) {
66
- if (item.type === 'separator') return null
75
+ function ChapterItem({
76
+ item,
77
+ pathname
78
+ }: {
79
+ item: Node;
80
+ pathname: string;
81
+ }) {
82
+ if (item.type === 'separator') return null;
67
83
 
68
- if (item.type === 'folder' && item.children) {
84
+ if (item.type === 'folder') {
69
85
  return (
70
86
  <li>
71
87
  <span className={styles.subLabel}>{item.name}</span>
72
88
  <ul className={styles.chapterItems}>
73
- {item.children.map((child) => (
74
- <ChapterItem key={child.url ?? child.name} item={child} pathname={pathname} />
89
+ {item.children.map(child => (
90
+ <ChapterItem
91
+ key={child.type === 'page' ? child.url : (child.name?.toString() ?? '')}
92
+ item={child}
93
+ pathname={pathname}
94
+ />
75
95
  ))}
76
96
  </ul>
77
97
  </li>
78
- )
98
+ );
79
99
  }
80
100
 
81
- const isActive = pathname === item.url
82
- const icon = item.icon ? iconMap[item.icon] : null
101
+ const isActive = pathname === item.url;
102
+ const icon = typeof item.icon === 'string' ? iconMap[item.icon] : item.icon;
83
103
 
84
104
  return (
85
105
  <li>
86
- <Link
106
+ <RouterLink
87
107
  to={item.url ?? '#'}
88
108
  className={`${styles.link} ${isActive ? styles.active : ''}`}
89
109
  >
90
110
  {icon && <span className={styles.icon}>{icon}</span>}
91
111
  <span>{item.name}</span>
92
- </Link>
112
+ </RouterLink>
93
113
  </li>
94
- )
114
+ );
95
115
  }
@@ -13,7 +13,7 @@
13
13
  padding: var(--rs-space-7) var(--rs-space-5);
14
14
  background: var(--rs-color-background-neutral-primary);
15
15
  overflow-y: auto;
16
- font-family: 'SF Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', monospace;
16
+ font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", monospace;
17
17
  }
18
18
 
19
19
  .title {
@@ -1,25 +1,37 @@
1
- 'use client'
1
+ 'use client';
2
2
 
3
- import { Flex, Headline } from '@raystack/apsara'
4
- import { cx } from 'class-variance-authority'
5
- import { Footer } from '@/components/ui/footer'
6
- import { ChapterNav } from './ChapterNav'
7
- import type { ThemeLayoutProps } from '@/types'
8
- import styles from './Layout.module.css'
3
+ import { Flex, Headline } from '@raystack/apsara';
4
+ import { cx } from 'class-variance-authority';
5
+ import { Footer } from '@/components/ui/footer';
6
+ import type { ThemeLayoutProps } from '@/types';
7
+ import { ChapterNav } from './ChapterNav';
8
+ import styles from './Layout.module.css';
9
9
 
10
- export function Layout({ children, config, tree, classNames }: ThemeLayoutProps) {
10
+ export function Layout({
11
+ children,
12
+ config,
13
+ tree,
14
+ classNames
15
+ }: ThemeLayoutProps) {
11
16
  return (
12
- <Flex direction="column" className={cx(styles.layout, classNames?.layout)}>
17
+ <Flex direction='column' className={cx(styles.layout, classNames?.layout)}>
13
18
  <Flex className={cx(styles.body, classNames?.body)}>
14
19
  <aside className={cx(styles.sidebar, classNames?.sidebar)}>
15
- <Headline size="small" weight="medium" as="h1" className={styles.title}>
20
+ <Headline
21
+ size='small'
22
+ weight='medium'
23
+ as='h1'
24
+ className={styles.title}
25
+ >
16
26
  {config.title}
17
27
  </Headline>
18
28
  <ChapterNav tree={tree} />
19
29
  </aside>
20
- <div className={cx(styles.content, classNames?.content)}>{children}</div>
30
+ <div className={cx(styles.content, classNames?.content)}>
31
+ {children}
32
+ </div>
21
33
  </Flex>
22
34
  <Footer config={config.footer} />
23
35
  </Flex>
24
- )
36
+ );
25
37
  }
@@ -1,7 +1,12 @@
1
1
  .main {
2
2
  --paper-navbar-height: 40px;
3
3
  --paper-navbar-padding: var(--rs-space-3);
4
- --paper-navbar-total: calc(var(--paper-navbar-height) + var(--paper-navbar-padding) * 2 + 1px);
4
+ --paper-navbar-total: calc(
5
+ var(--paper-navbar-height) +
6
+ var(--paper-navbar-padding) *
7
+ 2 +
8
+ 1px
9
+ );
5
10
 
6
11
  flex: 1;
7
12
  max-width: 1024px;
@@ -52,7 +57,7 @@
52
57
  }
53
58
 
54
59
  .breadcrumb {
55
- font-family: 'SF Mono', 'Fira Code', monospace;
60
+ font-family: "SF Mono", "Fira Code", monospace;
56
61
  font-size: var(--rs-font-size-small);
57
62
  text-transform: uppercase;
58
63
  letter-spacing: 0.05em;
@@ -94,13 +99,15 @@
94
99
  }
95
100
 
96
101
  .content {
97
- font-family: Georgia, 'Times New Roman', serif;
102
+ font-family: Georgia, "Times New Roman", serif;
98
103
  line-height: 1.8;
99
104
  background: var(--rs-color-background-base-primary);
100
105
  padding: var(--rs-space-9);
101
106
  border-left: 1px solid var(--rs-color-border-base-primary);
102
107
  border-right: 1px solid var(--rs-color-border-base-primary);
103
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 4px 12px rgba(0, 0, 0, 0.04);
108
+ box-shadow:
109
+ 0 1px 3px rgba(0, 0, 0, 0.08),
110
+ 0 4px 12px rgba(0, 0, 0, 0.04);
104
111
  margin-bottom: var(--rs-space-9);
105
112
  }
106
113
 
@@ -167,6 +174,11 @@
167
174
  margin-bottom: var(--rs-space-3);
168
175
  }
169
176
 
177
+ .content img {
178
+ max-width: 100%;
179
+ height: auto;
180
+ }
181
+
170
182
  .content blockquote {
171
183
  margin: 1rem 0;
172
184
  padding-left: 1rem;
@@ -1,77 +1,71 @@
1
- 'use client'
2
-
3
- import { useMemo } from 'react'
4
- import { useLocation, Link } from 'react-router-dom'
5
- import { Flex } from '@raystack/apsara'
6
- import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline'
7
- import type { ThemePageProps, PageTreeItem } from '@/types'
8
- import { Search } from '@/components/ui/search'
9
- import { ReadingProgress } from './ReadingProgress'
10
- import styles from './Page.module.css'
11
-
12
- function flattenTree(items: PageTreeItem[]): PageTreeItem[] {
13
- const result: PageTreeItem[] = []
14
- for (const item of items) {
15
- if (item.type === 'page' && item.url) result.push(item)
16
- if (item.children) result.push(...flattenTree(item.children))
17
- }
18
- return result
19
- }
20
-
21
- function findBreadcrumb(items: PageTreeItem[], slug: string[]): { label: string; href: string }[] {
22
- const result: { label: string; href: string }[] = []
23
- for (let i = 0; i < slug.length; i++) {
24
- const path = '/' + slug.slice(0, i + 1).join('/')
25
- const found = findInTree(items, path)
26
- result.push({ label: found?.name ?? slug[i], href: path })
27
- }
28
- return result
29
- }
30
-
31
- function findInTree(items: PageTreeItem[], path: string): PageTreeItem | undefined {
32
- for (const item of items) {
33
- if (item.url === path) return item
34
- if (item.children) {
35
- const found = findInTree(item.children, path)
36
- if (found) return found
37
- }
38
- }
39
- return undefined
40
- }
1
+ import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
2
+ import { Flex } from '@raystack/apsara';
3
+ import { useMemo } from 'react';
4
+ import { Link as RouterLink, useLocation } from 'react-router';
5
+ import { getBreadcrumbItems } from 'fumadocs-core/breadcrumb';
6
+ import { flattenTree } from 'fumadocs-core/page-tree';
7
+ import { Search } from '@/components/ui/search';
8
+ import type { ThemePageProps } from '@/types';
9
+ import styles from './Page.module.css';
10
+ import { ReadingProgress } from './ReadingProgress';
41
11
 
42
12
  export function Page({ page, config, tree }: ThemePageProps) {
43
- const { pathname } = useLocation()
13
+ const { pathname } = useLocation();
44
14
 
45
15
  const { prev, next, crumbs } = useMemo(() => {
46
- const pages = flattenTree(tree.children)
47
- const currentIndex = pages.findIndex((p) => p.url === pathname)
16
+ const pages = flattenTree(tree.children);
17
+ const currentIndex = pages.findIndex(p => p.url === pathname);
18
+ const breadcrumbItems = getBreadcrumbItems(
19
+ pathname,
20
+ tree,
21
+ { includePage: true }
22
+ );
48
23
  return {
49
24
  prev: currentIndex > 0 ? pages[currentIndex - 1] : null,
50
25
  next: currentIndex < pages.length - 1 ? pages[currentIndex + 1] : null,
51
- crumbs: findBreadcrumb(tree.children, page.slug),
52
- }
53
- }, [tree, pathname, page.slug])
26
+ crumbs: breadcrumbItems.map(item => ({
27
+ label: item.name,
28
+ href: item.url ?? pathname,
29
+ })),
30
+ };
31
+ }, [tree, pathname]);
54
32
 
55
33
  return (
56
34
  <>
57
35
  <main className={styles.main}>
58
- <Flex align="center" className={styles.navbar}>
59
- <Flex align="center" gap="small" className={styles.navLeft}>
36
+ <Flex align='center' className={styles.navbar}>
37
+ <Flex align='center' gap='small' className={styles.navLeft}>
60
38
  {prev ? (
61
- <Link to={prev.url!} className={styles.arrow} aria-label="Previous page">
39
+ <RouterLink
40
+ to={prev.url}
41
+ className={styles.arrow}
42
+ aria-label='Previous page'
43
+ >
62
44
  <ChevronLeftIcon width={14} height={14} />
63
- </Link>
45
+ </RouterLink>
64
46
  ) : (
65
- <button disabled className={styles.arrowDisabled} aria-label="Previous page">
47
+ <button
48
+ disabled
49
+ className={styles.arrowDisabled}
50
+ aria-label='Previous page'
51
+ >
66
52
  <ChevronLeftIcon width={14} height={14} />
67
53
  </button>
68
54
  )}
69
55
  {next ? (
70
- <Link to={next.url!} className={styles.arrow} aria-label="Next page">
56
+ <RouterLink
57
+ to={next.url}
58
+ className={styles.arrow}
59
+ aria-label='Next page'
60
+ >
71
61
  <ChevronRightIcon width={14} height={14} />
72
- </Link>
62
+ </RouterLink>
73
63
  ) : (
74
- <button disabled className={styles.arrowDisabled} aria-label="Next page">
64
+ <button
65
+ disabled
66
+ className={styles.arrowDisabled}
67
+ aria-label='Next page'
68
+ >
75
69
  <ChevronRightIcon width={14} height={14} />
76
70
  </button>
77
71
  )}
@@ -82,25 +76,25 @@ export function Page({ page, config, tree }: ThemePageProps) {
82
76
  {i === crumbs.length - 1 ? (
83
77
  <span className={styles.crumbActive}>{crumb.label}</span>
84
78
  ) : (
85
- <Link to={crumb.href} className={styles.crumbLink}>
79
+ <RouterLink to={crumb.href} className={styles.crumbLink}>
86
80
  {crumb.label}
87
- </Link>
81
+ </RouterLink>
88
82
  )}
89
83
  </span>
90
84
  ))}
91
85
  </nav>
92
86
  </Flex>
93
- <Flex align="center" className={styles.navRight}>
94
- {config.search?.enabled && <Search className={styles.searchButton} />}
87
+ <Flex align='center' className={styles.navRight}>
88
+ {config.search?.enabled && (
89
+ <Search className={styles.searchButton} />
90
+ )}
95
91
  </Flex>
96
92
  </Flex>
97
93
  <article className={styles.article} data-article-content>
98
- <div className={styles.content}>
99
- {page.content}
100
- </div>
94
+ <div className={styles.content}>{page.content}</div>
101
95
  </article>
102
96
  </main>
103
97
  <ReadingProgress items={page.toc} />
104
98
  </>
105
- )
99
+ );
106
100
  }