@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
@@ -0,0 +1,17 @@
1
+ import path from 'node:path';
2
+
3
+ /**
4
+ * Resolve a URL path within a base directory, preventing path traversal.
5
+ * Returns null if the resolved path escapes the base directory.
6
+ */
7
+ export function safePath(baseDir: string, urlPath: string): string | null {
8
+ const decoded = decodeURIComponent(urlPath.split('?')[0]);
9
+ const resolved = path.resolve(baseDir, '.' + decoded);
10
+ if (
11
+ !resolved.startsWith(path.resolve(baseDir) + path.sep) &&
12
+ resolved !== path.resolve(baseDir)
13
+ ) {
14
+ return null;
15
+ }
16
+ return resolved;
17
+ }
@@ -1,71 +1,111 @@
1
- import path from 'path'
2
- import { type InlineConfig } from 'vite'
3
- import react from '@vitejs/plugin-react'
4
- import mdx from '@mdx-js/rollup'
5
- import remarkDirective from 'remark-directive'
6
- import remarkGfm from 'remark-gfm'
7
- import remarkFrontmatter from 'remark-frontmatter'
8
- import remarkMdxFrontmatter from 'remark-mdx-frontmatter'
9
- import rehypeShiki from '@shikijs/rehype'
1
+ import react from '@vitejs/plugin-react';
2
+ import { remarkDirectiveAdmonition, remarkMdxMermaid } from 'fumadocs-core/mdx-plugins';
3
+ import { defineConfig as defineFumadocsConfig } from 'fumadocs-mdx/config';
4
+ import mdx from 'fumadocs-mdx/vite';
5
+ import { nitro } from 'nitro/vite';
6
+ import path from 'node:path';
7
+ import remarkDirective from 'remark-directive';
8
+ import { type InlineConfig } from 'vite';
9
+ import remarkUnusedDirectives from '../lib/remark-unused-directives';
10
+
11
+ function resolveOutputDir(projectRoot: string, preset?: string): string {
12
+ if (preset === 'vercel' || preset === 'vercel-static') return path.resolve(projectRoot, '.vercel/output');
13
+ return path.resolve(projectRoot, '.output');
14
+ }
10
15
 
11
16
  export interface ViteConfigOptions {
12
- root: string
13
- contentDir: string
14
- isDev?: boolean
17
+ packageRoot: string;
18
+ projectRoot: string;
19
+ contentDir: string;
20
+ preset?: string;
15
21
  }
16
22
 
17
- export async function createViteConfig(options: ViteConfigOptions): Promise<InlineConfig> {
18
- const { root, contentDir, isDev = false } = options
23
+ export async function createViteConfig(
24
+ options: ViteConfigOptions
25
+ ): Promise<InlineConfig> {
26
+ const { packageRoot, projectRoot, contentDir, preset } = options;
19
27
 
20
28
  return {
21
- root,
29
+ root: packageRoot,
22
30
  configFile: false,
31
+ plugins: [
32
+ nitro({
33
+ serverDir: path.resolve(packageRoot, 'src/server'),
34
+ ...(preset && { preset }),
35
+ }),
36
+ mdx({
37
+ default: defineFumadocsConfig({
38
+ mdxOptions: {
39
+ remarkPlugins: [
40
+ remarkDirective,
41
+ [remarkDirectiveAdmonition, {
42
+ tags: {
43
+ CalloutContainer: 'Callout',
44
+ CalloutTitle: 'CalloutTitle',
45
+ CalloutDescription: 'CalloutDescription',
46
+ },
47
+ types: {
48
+ note: 'accent',
49
+ tip: 'accent',
50
+ info: 'accent',
51
+ warn: 'attention',
52
+ warning: 'attention',
53
+ danger: 'alert',
54
+ caution: 'alert',
55
+ success: 'success',
56
+ },
57
+ }],
58
+ remarkUnusedDirectives,
59
+ remarkMdxMermaid,
60
+ ],
61
+ },
62
+ }),
63
+ }, { index: false }),
64
+ react()
65
+ ],
23
66
  resolve: {
24
67
  alias: {
25
- '@': path.resolve(root, 'src'),
26
- '@content': contentDir,
68
+ '@': path.resolve(packageRoot, 'src'),
27
69
  },
28
- dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime'],
70
+ conditions: ['module-sync', 'import', 'node'],
71
+ dedupe: [
72
+ 'react',
73
+ 'react-dom',
74
+ 'react/jsx-runtime',
75
+ 'react/jsx-dev-runtime',
76
+ 'react-router',
77
+ ]
29
78
  },
30
79
  server: {
31
80
  fs: {
32
- allow: [root, contentDir],
33
- },
81
+ allow: [packageRoot, projectRoot, contentDir]
82
+ }
34
83
  },
35
- plugins: [
36
- mdx({
37
- remarkPlugins: [
38
- remarkFrontmatter,
39
- remarkMdxFrontmatter,
40
- remarkGfm,
41
- remarkDirective,
42
- ],
43
- rehypePlugins: [
44
- [rehypeShiki, { themes: { light: 'github-light', dark: 'github-dark' }, defaultColor: false }],
45
- ],
46
- mdExtensions: ['.md'],
47
- mdxExtensions: ['.mdx'],
48
- }),
49
- react(),
50
- ],
51
84
  define: {
52
- 'process.env.CHRONICLE_CONTENT_DIR': JSON.stringify(contentDir),
53
- 'process.env.CHRONICLE_PROJECT_ROOT': JSON.stringify(root),
85
+ __CHRONICLE_CONTENT_DIR__: JSON.stringify(contentDir),
86
+ __CHRONICLE_PROJECT_ROOT__: JSON.stringify(projectRoot)
54
87
  },
55
88
  css: {
56
89
  modules: {
57
- localsConvention: 'camelCase',
58
- },
90
+ localsConvention: 'camelCase'
91
+ }
59
92
  },
60
93
  ssr: {
61
- noExternal: ['@raystack/apsara'],
94
+ noExternal: ['@raystack/apsara', 'dayjs', 'fumadocs-core']
95
+ },
96
+ environments: {
97
+ client: {
98
+ build: {
99
+ rollupOptions: {
100
+ input: path.resolve(packageRoot, 'src/server/entry-client.tsx')
101
+ }
102
+ }
103
+ }
62
104
  },
63
- build: {
64
- rolldownOptions: {
65
- input: isDev ? undefined : {
66
- client: path.resolve(root, 'src/server/index.html'),
67
- },
105
+ nitro: {
106
+ output: {
107
+ dir: resolveOutputDir(projectRoot, preset),
68
108
  },
69
109
  },
70
- }
110
+ };
71
111
  }
@@ -1,64 +1,87 @@
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 { Node } from 'fumadocs-core/page-tree';
18
+ import type { ThemeLayoutProps } from '@/types';
19
+ import styles from './Layout.module.css';
14
20
 
15
21
  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" />,
22
+ 'rectangle-stack': <RectangleStackIcon width={16} height={16} />,
23
+ 'method-get': <MethodBadge method='GET' size='micro' />,
24
+ 'method-post': <MethodBadge method='POST' size='micro' />,
25
+ 'method-put': <MethodBadge method='PUT' size='micro' />,
26
+ 'method-delete': <MethodBadge method='DELETE' size='micro' />,
27
+ 'method-patch': <MethodBadge method='PATCH' size='micro' />
22
28
  };
23
29
 
24
30
  let savedScrollTop = 0;
25
31
 
26
- export function Layout({ children, config, tree, classNames }: ThemeLayoutProps) {
32
+ export function Layout({
33
+ children,
34
+ config,
35
+ tree,
36
+ classNames
37
+ }: ThemeLayoutProps) {
27
38
  const { pathname } = useLocation();
28
39
  const scrollRef = useRef<HTMLDivElement>(null);
29
40
 
30
41
  useEffect(() => {
31
42
  const el = scrollRef.current;
32
43
  if (!el) return;
33
- const onScroll = () => { savedScrollTop = el.scrollTop; };
44
+ const onScroll = () => {
45
+ savedScrollTop = el.scrollTop;
46
+ };
34
47
  el.addEventListener('scroll', onScroll);
35
48
  return () => el.removeEventListener('scroll', onScroll);
36
49
  }, []);
37
50
 
38
51
  useEffect(() => {
39
52
  const el = scrollRef.current;
40
- if (el) requestAnimationFrame(() => { el.scrollTop = savedScrollTop; });
53
+ if (el)
54
+ requestAnimationFrame(() => {
55
+ el.scrollTop = savedScrollTop;
56
+ });
41
57
  }, [pathname]);
42
58
 
43
59
  return (
44
- <Flex direction="column" className={cx(styles.layout, classNames?.layout)}>
60
+ <Flex direction='column' className={cx(styles.layout, classNames?.layout)}>
45
61
  <Navbar className={styles.header}>
46
62
  <Navbar.Start>
47
- <Link to="/" style={{ textDecoration: 'none', color: 'inherit' }}>
48
- <Headline size="small" weight="medium" as="h1">
63
+ <RouterLink
64
+ to='/'
65
+ style={{ textDecoration: 'none', color: 'inherit' }}
66
+ >
67
+ <Headline size='small' weight='medium' as='h1'>
49
68
  {config.title}
50
69
  </Headline>
51
- </Link>
70
+ </RouterLink>
52
71
  </Navbar.Start>
53
72
  <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}>
73
+ <Flex gap='medium' align='center' className={styles.navActions}>
74
+ {config.api?.map(api => (
75
+ <RouterLink
76
+ key={api.basePath}
77
+ to={api.basePath}
78
+ className={styles.navButton}
79
+ >
57
80
  {api.name} API
58
- </Link>
81
+ </RouterLink>
59
82
  ))}
60
- {config.navigation?.links?.map((link) => (
61
- <Link key={link.href} to={link.href}>
83
+ {config.navigation?.links?.map(link => (
84
+ <Link key={link.href} href={link.href}>
62
85
  {link.label}
63
86
  </Link>
64
87
  ))}
@@ -68,18 +91,24 @@ export function Layout({ children, config, tree, classNames }: ThemeLayoutProps)
68
91
  </Navbar.End>
69
92
  </Navbar>
70
93
  <Flex className={cx(styles.body, classNames?.body)}>
71
- <Sidebar defaultOpen collapsible={false} className={cx(styles.sidebar, classNames?.sidebar)}>
94
+ <Sidebar
95
+ defaultOpen
96
+ collapsible={false}
97
+ className={cx(styles.sidebar, classNames?.sidebar)}
98
+ >
72
99
  <Sidebar.Main ref={scrollRef}>
73
- {tree.children.map((item) => (
100
+ {tree.children.map((item, i) => (
74
101
  <SidebarNode
75
- key={item.url ?? item.name}
102
+ key={item.type === 'page' ? item.url : (item.name?.toString() ?? i)}
76
103
  item={item}
77
104
  pathname={pathname}
78
105
  />
79
106
  ))}
80
107
  </Sidebar.Main>
81
108
  </Sidebar>
82
- <main className={cx(styles.content, classNames?.content)}>{children}</main>
109
+ <main className={cx(styles.content, classNames?.content)}>
110
+ {children}
111
+ </main>
83
112
  </Flex>
84
113
  <Footer config={config.footer} />
85
114
  </Flex>
@@ -88,25 +117,26 @@ export function Layout({ children, config, tree, classNames }: ThemeLayoutProps)
88
117
 
89
118
  function SidebarNode({
90
119
  item,
91
- pathname,
120
+ pathname
92
121
  }: {
93
- item: PageTreeItem;
122
+ item: Node;
94
123
  pathname: string;
95
124
  }) {
96
- if (item.type === "separator") {
125
+ if (item.type === 'separator') {
97
126
  return null;
98
127
  }
99
128
 
100
- if (item.type === "folder" && item.children) {
129
+ if (item.type === 'folder') {
130
+ const icon = typeof item.icon === 'string' ? iconMap[item.icon] : item.icon;
101
131
  return (
102
132
  <Sidebar.Group
103
- label={item.name}
104
- leadingIcon={item.icon ? iconMap[item.icon] : undefined}
133
+ label={item.name?.toString() ?? ''}
134
+ leadingIcon={icon ?? undefined}
105
135
  classNames={{ items: styles.groupItems }}
106
136
  >
107
- {item.children.map((child) => (
137
+ {item.children.map((child, i) => (
108
138
  <SidebarNode
109
- key={child.url ?? child.name}
139
+ key={child.type === 'page' ? child.url : (child.name?.toString() ?? i)}
110
140
  item={child}
111
141
  pathname={pathname}
112
142
  />
@@ -116,14 +146,15 @@ function SidebarNode({
116
146
  }
117
147
 
118
148
  const isActive = pathname === item.url;
119
- const href = item.url ?? "#";
120
- const link = useMemo(() => <Link to={href} />, [href]);
149
+ const href = item.url ?? '#';
150
+ const icon = typeof item.icon === 'string' ? iconMap[item.icon] : item.icon;
151
+ const link = useMemo(() => <RouterLink to={href} />, [href]);
121
152
 
122
153
  return (
123
154
  <Sidebar.Item
124
155
  href={href}
125
156
  active={isActive}
126
- leadingIcon={item.icon ? iconMap[item.icon] : undefined}
157
+ leadingIcon={icon ?? undefined}
127
158
  as={link}
128
159
  >
129
160
  {item.name}
@@ -38,22 +38,6 @@
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
41
  .content img {
58
42
  max-width: 100%;
59
43
  height: auto;
@@ -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,41 @@
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 { AnchorProvider, useActiveAnchor } from 'fumadocs-core/toc';
5
+ import type { TableOfContents, TOCItemType } from 'fumadocs-core/toc';
6
+ import styles from './Toc.module.css';
7
7
 
8
8
  interface TocProps {
9
- items: TocItem[]
9
+ items: TableOfContents;
10
10
  }
11
11
 
12
12
  export function Toc({ items }: TocProps) {
13
- const [activeId, setActiveId] = useState<string>('')
13
+ const filteredItems = items.filter(
14
+ item => item.depth >= 2 && item.depth <= 3
15
+ );
14
16
 
15
- // Filter to only show h2 and h3 headings
16
- const filteredItems = items.filter((item) => item.depth >= 2 && item.depth <= 3)
17
+ if (filteredItems.length === 0) return null;
17
18
 
18
- useEffect(() => {
19
- const headingIds = filteredItems.map((item) => item.url.replace('#', ''))
20
-
21
- const observer = new IntersectionObserver(
22
- (entries) => {
23
- entries.forEach((entry) => {
24
- if (entry.isIntersecting) {
25
- setActiveId(entry.target.id)
26
- }
27
- })
28
- },
29
- // -80px top: offset for fixed header, -80% bottom: trigger when heading is in top 20% of viewport
30
- { rootMargin: '-80px 0px -80% 0px' }
31
- )
32
-
33
- headingIds.forEach((id) => {
34
- const element = document.getElementById(id)
35
- if (element) observer.observe(element)
36
- })
37
-
38
- return () => observer.disconnect()
39
- }, [filteredItems])
19
+ return (
20
+ <AnchorProvider toc={filteredItems} single>
21
+ <TocContent items={filteredItems} />
22
+ </AnchorProvider>
23
+ );
24
+ }
40
25
 
41
- if (filteredItems.length === 0) return null
26
+ function TocContent({ items }: { items: TOCItemType[] }) {
27
+ const activeAnchor = useActiveAnchor();
42
28
 
43
29
  return (
44
30
  <aside className={styles.toc}>
45
- <Text size={1} weight="medium" className={styles.title}>
31
+ <Text size={1} weight='medium' className={styles.title}>
46
32
  On this page
47
33
  </Text>
48
34
  <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
35
+ {items.map(item => {
36
+ const id = item.url.replace('#', '');
37
+ const isActive = activeAnchor === id;
38
+ const isNested = item.depth > 2;
53
39
  return (
54
40
  <a
55
41
  key={item.url}
@@ -58,9 +44,9 @@ export function Toc({ items }: TocProps) {
58
44
  >
59
45
  {item.title}
60
46
  </a>
61
- )
47
+ );
62
48
  })}
63
49
  </nav>
64
50
  </aside>
65
- )
51
+ );
66
52
  }
@@ -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 };