@raystack/chronicle 0.1.0-canary.a320792 → 0.1.0-canary.ac60f9f

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 (82) hide show
  1. package/dist/cli/index.js +150 -416
  2. package/package.json +13 -9
  3. package/src/cli/commands/build.ts +30 -48
  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/code.tsx +1 -10
  14. package/src/components/mdx/details.module.css +24 -1
  15. package/src/components/mdx/details.tsx +3 -2
  16. package/src/components/mdx/image.tsx +5 -20
  17. package/src/components/mdx/index.tsx +3 -3
  18. package/src/components/mdx/link.tsx +24 -20
  19. package/src/components/ui/footer.tsx +2 -3
  20. package/src/components/ui/search.tsx +116 -71
  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 +76 -57
  26. package/src/lib/source.ts +144 -96
  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 +18 -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 +117 -0
  39. package/src/server/entry-client.tsx +52 -56
  40. package/src/server/entry-server.tsx +95 -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 +39 -0
  45. package/src/server/utils/safe-path.ts +17 -0
  46. package/src/server/vite-config.ts +50 -49
  47. package/src/themes/default/Layout.tsx +69 -41
  48. package/src/themes/default/Page.module.css +0 -60
  49. package/src/themes/default/Page.tsx +9 -11
  50. package/src/themes/default/Toc.tsx +30 -28
  51. package/src/themes/default/index.ts +7 -9
  52. package/src/themes/paper/ChapterNav.tsx +59 -39
  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 +11 -4
  56. package/src/themes/paper/Page.tsx +67 -47
  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/globals.d.ts +4 -0
  61. package/src/cli/__tests__/config.test.ts +0 -25
  62. package/src/cli/__tests__/scaffold.test.ts +0 -10
  63. package/src/pages/__tests__/head.test.tsx +0 -57
  64. package/src/server/__tests__/entry-server.test.tsx +0 -35
  65. package/src/server/__tests__/handlers.test.ts +0 -77
  66. package/src/server/__tests__/og.test.ts +0 -23
  67. package/src/server/__tests__/router.test.ts +0 -72
  68. package/src/server/__tests__/vite-config.test.ts +0 -25
  69. package/src/server/dev.ts +0 -156
  70. package/src/server/entry-prod.ts +0 -127
  71. package/src/server/handlers/apis-proxy.ts +0 -52
  72. package/src/server/handlers/health.ts +0 -3
  73. package/src/server/handlers/llms.ts +0 -58
  74. package/src/server/handlers/og.ts +0 -87
  75. package/src/server/handlers/robots.ts +0 -11
  76. package/src/server/handlers/search.ts +0 -140
  77. package/src/server/handlers/sitemap.ts +0 -39
  78. package/src/server/handlers/specs.ts +0 -9
  79. package/src/server/index.html +0 -12
  80. package/src/server/prod.ts +0 -18
  81. package/src/server/router.ts +0 -42
  82. package/src/themes/default/font.ts +0 -4
@@ -1,64 +1,86 @@
1
- "use client";
2
-
3
- import { useMemo, useEffect, useRef } from "react";
4
- import { useLocation, Link } from "react-router-dom";
5
- import { cx } from "class-variance-authority";
6
- import { Flex, Navbar, Headline, Sidebar, Button } from "@raystack/apsara";
7
- import { RectangleStackIcon } from "@heroicons/react/24/outline";
8
- import { ClientThemeSwitcher } from "@/components/ui/client-theme-switcher";
9
- import { Search } from "@/components/ui/search";
10
- import { Footer } from "@/components/ui/footer";
11
- import { MethodBadge } from "@/components/api/method-badge";
12
- import type { ThemeLayoutProps, PageTreeItem } from "@/types";
13
- import styles from "./Layout.module.css";
1
+ import { RectangleStackIcon } from '@heroicons/react/24/outline';
2
+ import {
3
+ Button,
4
+ Flex,
5
+ Headline,
6
+ Link,
7
+ Navbar,
8
+ Sidebar
9
+ } from '@raystack/apsara';
10
+ import { cx } from 'class-variance-authority';
11
+ import { useEffect, useMemo, useRef } from 'react';
12
+ import { Link as RouterLink, useLocation } from 'react-router';
13
+ import { MethodBadge } from '@/components/api/method-badge';
14
+ import { ClientThemeSwitcher } from '@/components/ui/client-theme-switcher';
15
+ import { Footer } from '@/components/ui/footer';
16
+ import { Search } from '@/components/ui/search';
17
+ import type { PageTreeItem, ThemeLayoutProps } from '@/types';
18
+ import styles from './Layout.module.css';
14
19
 
15
20
  const iconMap: Record<string, React.ReactNode> = {
16
- "rectangle-stack": <RectangleStackIcon width={16} height={16} />,
17
- "method-get": <MethodBadge method="GET" size="micro" />,
18
- "method-post": <MethodBadge method="POST" size="micro" />,
19
- "method-put": <MethodBadge method="PUT" size="micro" />,
20
- "method-delete": <MethodBadge method="DELETE" size="micro" />,
21
- "method-patch": <MethodBadge method="PATCH" size="micro" />,
21
+ 'rectangle-stack': <RectangleStackIcon width={16} height={16} />,
22
+ 'method-get': <MethodBadge method='GET' size='micro' />,
23
+ 'method-post': <MethodBadge method='POST' size='micro' />,
24
+ 'method-put': <MethodBadge method='PUT' size='micro' />,
25
+ 'method-delete': <MethodBadge method='DELETE' size='micro' />,
26
+ 'method-patch': <MethodBadge method='PATCH' size='micro' />
22
27
  };
23
28
 
24
29
  let savedScrollTop = 0;
25
30
 
26
- export function Layout({ children, config, tree, classNames }: ThemeLayoutProps) {
31
+ export function Layout({
32
+ children,
33
+ config,
34
+ tree,
35
+ classNames
36
+ }: ThemeLayoutProps) {
27
37
  const { pathname } = useLocation();
28
38
  const scrollRef = useRef<HTMLDivElement>(null);
29
39
 
30
40
  useEffect(() => {
31
41
  const el = scrollRef.current;
32
42
  if (!el) return;
33
- const onScroll = () => { savedScrollTop = el.scrollTop; };
43
+ const onScroll = () => {
44
+ savedScrollTop = el.scrollTop;
45
+ };
34
46
  el.addEventListener('scroll', onScroll);
35
47
  return () => el.removeEventListener('scroll', onScroll);
36
48
  }, []);
37
49
 
38
50
  useEffect(() => {
39
51
  const el = scrollRef.current;
40
- if (el) requestAnimationFrame(() => { el.scrollTop = savedScrollTop; });
52
+ if (el)
53
+ requestAnimationFrame(() => {
54
+ el.scrollTop = savedScrollTop;
55
+ });
41
56
  }, [pathname]);
42
57
 
43
58
  return (
44
- <Flex direction="column" className={cx(styles.layout, classNames?.layout)}>
59
+ <Flex direction='column' className={cx(styles.layout, classNames?.layout)}>
45
60
  <Navbar className={styles.header}>
46
61
  <Navbar.Start>
47
- <Link to="/" style={{ textDecoration: 'none', color: 'inherit' }}>
48
- <Headline size="small" weight="medium" as="h1">
62
+ <RouterLink
63
+ to='/'
64
+ style={{ textDecoration: 'none', color: 'inherit' }}
65
+ >
66
+ <Headline size='small' weight='medium' as='h1'>
49
67
  {config.title}
50
68
  </Headline>
51
- </Link>
69
+ </RouterLink>
52
70
  </Navbar.Start>
53
71
  <Navbar.End>
54
- <Flex gap="medium" align="center" className={styles.navActions}>
55
- {config.api?.map((api) => (
56
- <Link key={api.name} to={api.basePath} className={styles.navButton}>
72
+ <Flex gap='medium' align='center' className={styles.navActions}>
73
+ {config.api?.map(api => (
74
+ <RouterLink
75
+ key={api.basePath}
76
+ to={api.basePath}
77
+ className={styles.navButton}
78
+ >
57
79
  {api.name} API
58
- </Link>
80
+ </RouterLink>
59
81
  ))}
60
- {config.navigation?.links?.map((link) => (
61
- <Link key={link.href} to={link.href}>
82
+ {config.navigation?.links?.map(link => (
83
+ <Link key={link.href} href={link.href}>
62
84
  {link.label}
63
85
  </Link>
64
86
  ))}
@@ -68,9 +90,13 @@ export function Layout({ children, config, tree, classNames }: ThemeLayoutProps)
68
90
  </Navbar.End>
69
91
  </Navbar>
70
92
  <Flex className={cx(styles.body, classNames?.body)}>
71
- <Sidebar defaultOpen collapsible={false} className={cx(styles.sidebar, classNames?.sidebar)}>
93
+ <Sidebar
94
+ defaultOpen
95
+ collapsible={false}
96
+ className={cx(styles.sidebar, classNames?.sidebar)}
97
+ >
72
98
  <Sidebar.Main ref={scrollRef}>
73
- {tree.children.map((item) => (
99
+ {tree.children.map(item => (
74
100
  <SidebarNode
75
101
  key={item.url ?? item.name}
76
102
  item={item}
@@ -79,7 +105,9 @@ export function Layout({ children, config, tree, classNames }: ThemeLayoutProps)
79
105
  ))}
80
106
  </Sidebar.Main>
81
107
  </Sidebar>
82
- <main className={cx(styles.content, classNames?.content)}>{children}</main>
108
+ <main className={cx(styles.content, classNames?.content)}>
109
+ {children}
110
+ </main>
83
111
  </Flex>
84
112
  <Footer config={config.footer} />
85
113
  </Flex>
@@ -88,23 +116,23 @@ export function Layout({ children, config, tree, classNames }: ThemeLayoutProps)
88
116
 
89
117
  function SidebarNode({
90
118
  item,
91
- pathname,
119
+ pathname
92
120
  }: {
93
121
  item: PageTreeItem;
94
122
  pathname: string;
95
123
  }) {
96
- if (item.type === "separator") {
124
+ if (item.type === 'separator') {
97
125
  return null;
98
126
  }
99
127
 
100
- if (item.type === "folder" && item.children) {
128
+ if (item.type === 'folder' && item.children) {
101
129
  return (
102
130
  <Sidebar.Group
103
131
  label={item.name}
104
132
  leadingIcon={item.icon ? iconMap[item.icon] : undefined}
105
133
  classNames={{ items: styles.groupItems }}
106
134
  >
107
- {item.children.map((child) => (
135
+ {item.children.map(child => (
108
136
  <SidebarNode
109
137
  key={child.url ?? child.name}
110
138
  item={child}
@@ -116,8 +144,8 @@ function SidebarNode({
116
144
  }
117
145
 
118
146
  const isActive = pathname === item.url;
119
- const href = item.url ?? "#";
120
- const link = useMemo(() => <Link to={href} />, [href]);
147
+ const href = item.url ?? '#';
148
+ const link = useMemo(() => <RouterLink to={href} />, [href]);
121
149
 
122
150
  return (
123
151
  <Sidebar.Item
@@ -38,69 +38,9 @@
38
38
  margin-bottom: var(--rs-space-3);
39
39
  }
40
40
 
41
- .content :global(pre) {
42
- background-color: var(--shiki-light-bg, #fff);
43
- }
44
-
45
- .content :global(pre code span) {
46
- color: var(--shiki-light);
47
- }
48
-
49
- :global([data-theme="dark"]) .content :global(pre) {
50
- background-color: var(--shiki-dark-bg, #24292e);
51
- }
52
-
53
- :global([data-theme="dark"]) .content :global(pre code span) {
54
- color: var(--shiki-dark);
55
- }
56
-
57
- .content img {
58
- max-width: 100%;
59
- height: auto;
60
- }
61
-
62
41
  .content table {
63
42
  display: block;
64
43
  max-width: 100%;
65
44
  overflow-x: auto;
66
45
  margin-bottom: var(--rs-space-5);
67
46
  }
68
-
69
- .content details {
70
- border: 1px solid var(--rs-color-border-base-primary);
71
- border-radius: var(--rs-radius-2);
72
- margin: var(--rs-space-5) 0;
73
- overflow: hidden;
74
- }
75
-
76
- .content details summary {
77
- padding: var(--rs-space-4) var(--rs-space-5);
78
- cursor: pointer;
79
- font-weight: 500;
80
- font-size: var(--rs-font-size-small);
81
- color: var(--rs-color-text-base-primary);
82
- background: var(--rs-color-background-base-secondary);
83
- list-style: none;
84
- display: flex;
85
- align-items: center;
86
- gap: var(--rs-space-3);
87
- }
88
-
89
- .content details summary::-webkit-details-marker {
90
- display: none;
91
- }
92
-
93
- .content details summary::before {
94
- content: '▶';
95
- font-size: 10px;
96
- transition: transform 0.2s ease;
97
- color: var(--rs-color-text-base-secondary);
98
- }
99
-
100
- .content details[open] > summary::before {
101
- transform: rotate(90deg);
102
- }
103
-
104
- .content details > :not(summary) {
105
- padding: var(--rs-space-4) var(--rs-space-5);
106
- }
@@ -1,21 +1,19 @@
1
- 'use client'
1
+ 'use client';
2
2
 
3
- import { Flex } from '@raystack/apsara'
4
- import type { ThemePageProps } from '@/types'
5
- import { Breadcrumbs } from '@/components/ui/breadcrumbs'
6
- import { Toc } from './Toc'
7
- import styles from './Page.module.css'
3
+ import { Flex } from '@raystack/apsara';
4
+ import { Breadcrumbs } from '@/components/ui/breadcrumbs';
5
+ import type { ThemePageProps } from '@/types';
6
+ import styles from './Page.module.css';
7
+ import { Toc } from './Toc';
8
8
 
9
9
  export function Page({ page, tree }: ThemePageProps) {
10
10
  return (
11
11
  <Flex className={styles.page}>
12
- <article className={styles.article}>
12
+ <article className={styles.article} data-article-content>
13
13
  <Breadcrumbs slug={page.slug} tree={tree} />
14
- <div className={styles.content}>
15
- {page.content}
16
- </div>
14
+ <div className={styles.content}>{page.content}</div>
17
15
  </article>
18
16
  <Toc items={page.toc} />
19
17
  </Flex>
20
- )
18
+ );
21
19
  }
@@ -1,55 +1,57 @@
1
- 'use client'
1
+ 'use client';
2
2
 
3
- import { useEffect, useState } from 'react'
4
- import { Text } from '@raystack/apsara'
5
- import type { TocItem } from '@/types'
6
- import styles from './Toc.module.css'
3
+ import { Text } from '@raystack/apsara';
4
+ import { useEffect, useState } from 'react';
5
+ import type { TocItem } from '@/types';
6
+ import styles from './Toc.module.css';
7
7
 
8
8
  interface TocProps {
9
- items: TocItem[]
9
+ items: TocItem[];
10
10
  }
11
11
 
12
12
  export function Toc({ items }: TocProps) {
13
- const [activeId, setActiveId] = useState<string>('')
13
+ const [activeId, setActiveId] = useState<string>('');
14
14
 
15
15
  // Filter to only show h2 and h3 headings
16
- const filteredItems = items.filter((item) => item.depth >= 2 && item.depth <= 3)
16
+ const filteredItems = items.filter(
17
+ item => item.depth >= 2 && item.depth <= 3
18
+ );
17
19
 
18
20
  useEffect(() => {
19
- const headingIds = filteredItems.map((item) => item.url.replace('#', ''))
21
+ const headingIds = filteredItems.map(item => item.url.replace('#', ''));
20
22
 
21
23
  const observer = new IntersectionObserver(
22
- (entries) => {
23
- entries.forEach((entry) => {
24
+ entries => {
25
+ entries.forEach(entry => {
24
26
  if (entry.isIntersecting) {
25
- setActiveId(entry.target.id)
27
+ setActiveId(entry.target.id);
26
28
  }
27
- })
29
+ });
28
30
  },
29
31
  // -80px top: offset for fixed header, -80% bottom: trigger when heading is in top 20% of viewport
30
32
  { rootMargin: '-80px 0px -80% 0px' }
31
- )
33
+ );
32
34
 
33
- headingIds.forEach((id) => {
34
- const element = document.getElementById(id)
35
- if (element) observer.observe(element)
36
- })
35
+ headingIds.forEach(id => {
36
+ const element = document.getElementById(id);
37
+ if (element) observer.observe(element);
38
+ });
37
39
 
38
- return () => observer.disconnect()
39
- }, [filteredItems])
40
+ return () => observer.disconnect();
41
+ }, [filteredItems]);
40
42
 
41
- if (filteredItems.length === 0) return null
43
+ if (filteredItems.length === 0) return null;
42
44
 
43
45
  return (
44
46
  <aside className={styles.toc}>
45
- <Text size={1} weight="medium" className={styles.title}>
47
+ <Text size={1} weight='medium' className={styles.title}>
46
48
  On this page
47
49
  </Text>
48
50
  <nav className={styles.nav}>
49
- {filteredItems.map((item) => {
50
- const id = item.url.replace('#', '')
51
- const isActive = activeId === id
52
- const isNested = item.depth > 2
51
+ {filteredItems.map(item => {
52
+ const id = item.url.replace('#', '');
53
+ const isActive = activeId === id;
54
+ const isNested = item.depth > 2;
53
55
  return (
54
56
  <a
55
57
  key={item.url}
@@ -58,9 +60,9 @@ export function Toc({ items }: TocProps) {
58
60
  >
59
61
  {item.title}
60
62
  </a>
61
- )
63
+ );
62
64
  })}
63
65
  </nav>
64
66
  </aside>
65
- )
67
+ );
66
68
  }
@@ -1,13 +1,11 @@
1
- import { Layout } from './Layout'
2
- import { Page } from './Page'
3
- import { Toc } from './Toc'
4
- import { inter } from './font'
5
- import type { Theme } from '@/types'
1
+ import type { Theme } from '@/types';
2
+ import { Layout } from './Layout';
3
+ import { Page } from './Page';
4
+ import { Toc } from './Toc';
6
5
 
7
6
  export const defaultTheme: Theme = {
8
7
  Layout,
9
- Page,
10
- className: inter.className,
11
- }
8
+ Page
9
+ };
12
10
 
13
- export { Layout, Page, Toc }
11
+ export { Layout, Page, Toc };
@@ -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 { PageTree, PageTreeItem } from '@/types';
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: PageTree;
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: PageTreeItem[]
20
+ ): Map<PageTreeItem, number> {
21
+ const indices = new Map<PageTreeItem, number>();
22
+ let index = 0;
23
23
  for (const item of children) {
24
24
  if (item.type === 'folder' && item.children) {
25
- index++
26
- indices.set(item, index)
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
42
  if (item.type === 'folder' && item.children) {
43
- const chapterIndex = chapterIndices.get(item) ?? 0
43
+ const chapterIndex = chapterIndices.get(item) ?? 0;
44
44
  return (
45
45
  <li key={item.name} 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.url ?? child.name}
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}
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: PageTreeItem;
80
+ pathname: string;
81
+ }) {
82
+ if (item.type === 'separator') return null;
67
83
 
68
84
  if (item.type === 'folder' && item.children) {
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.url ?? child.name}
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 = item.icon ? iconMap[item.icon] : null;
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