@raystack/chronicle 0.1.0-canary.0efaef0

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 (115) hide show
  1. package/bin/chronicle.js +2 -0
  2. package/dist/cli/index.js +276 -0
  3. package/package.json +71 -0
  4. package/src/cli/commands/build.ts +34 -0
  5. package/src/cli/commands/dev.ts +32 -0
  6. package/src/cli/commands/init.ts +69 -0
  7. package/src/cli/commands/serve.ts +40 -0
  8. package/src/cli/commands/start.ts +28 -0
  9. package/src/cli/index.ts +21 -0
  10. package/src/cli/utils/config.ts +42 -0
  11. package/src/cli/utils/index.ts +3 -0
  12. package/src/cli/utils/resolve.ts +10 -0
  13. package/src/cli/utils/scaffold.ts +18 -0
  14. package/src/components/api/code-snippets.module.css +7 -0
  15. package/src/components/api/code-snippets.tsx +76 -0
  16. package/src/components/api/endpoint-page.module.css +58 -0
  17. package/src/components/api/endpoint-page.tsx +283 -0
  18. package/src/components/api/field-row.module.css +126 -0
  19. package/src/components/api/field-row.tsx +204 -0
  20. package/src/components/api/field-section.module.css +24 -0
  21. package/src/components/api/field-section.tsx +100 -0
  22. package/src/components/api/index.ts +8 -0
  23. package/src/components/api/json-editor.module.css +9 -0
  24. package/src/components/api/json-editor.tsx +61 -0
  25. package/src/components/api/key-value-editor.module.css +13 -0
  26. package/src/components/api/key-value-editor.tsx +62 -0
  27. package/src/components/api/method-badge.module.css +4 -0
  28. package/src/components/api/method-badge.tsx +29 -0
  29. package/src/components/api/response-panel.module.css +8 -0
  30. package/src/components/api/response-panel.tsx +44 -0
  31. package/src/components/common/breadcrumb.tsx +3 -0
  32. package/src/components/common/button.tsx +3 -0
  33. package/src/components/common/callout.module.css +7 -0
  34. package/src/components/common/callout.tsx +27 -0
  35. package/src/components/common/code-block.tsx +3 -0
  36. package/src/components/common/dialog.tsx +3 -0
  37. package/src/components/common/index.ts +10 -0
  38. package/src/components/common/input-field.tsx +3 -0
  39. package/src/components/common/sidebar.tsx +3 -0
  40. package/src/components/common/switch.tsx +3 -0
  41. package/src/components/common/table.tsx +3 -0
  42. package/src/components/common/tabs.tsx +3 -0
  43. package/src/components/mdx/code.module.css +42 -0
  44. package/src/components/mdx/code.tsx +27 -0
  45. package/src/components/mdx/details.module.css +37 -0
  46. package/src/components/mdx/details.tsx +18 -0
  47. package/src/components/mdx/image.tsx +9 -0
  48. package/src/components/mdx/index.tsx +35 -0
  49. package/src/components/mdx/link.tsx +41 -0
  50. package/src/components/mdx/mermaid.module.css +9 -0
  51. package/src/components/mdx/mermaid.tsx +37 -0
  52. package/src/components/mdx/paragraph.module.css +8 -0
  53. package/src/components/mdx/paragraph.tsx +19 -0
  54. package/src/components/mdx/table.tsx +40 -0
  55. package/src/components/ui/breadcrumbs.tsx +72 -0
  56. package/src/components/ui/client-theme-switcher.tsx +18 -0
  57. package/src/components/ui/footer.module.css +27 -0
  58. package/src/components/ui/footer.tsx +30 -0
  59. package/src/components/ui/search.module.css +111 -0
  60. package/src/components/ui/search.tsx +218 -0
  61. package/src/lib/api-routes.ts +120 -0
  62. package/src/lib/config.ts +58 -0
  63. package/src/lib/get-llm-text.ts +10 -0
  64. package/src/lib/head.tsx +49 -0
  65. package/src/lib/index.ts +2 -0
  66. package/src/lib/openapi.ts +188 -0
  67. package/src/lib/page-context.tsx +117 -0
  68. package/src/lib/remark-unused-directives.ts +30 -0
  69. package/src/lib/schema.ts +99 -0
  70. package/src/lib/snippet-generators.ts +87 -0
  71. package/src/lib/source.ts +186 -0
  72. package/src/pages/ApiLayout.module.css +22 -0
  73. package/src/pages/ApiLayout.tsx +33 -0
  74. package/src/pages/ApiPage.tsx +73 -0
  75. package/src/pages/DocsLayout.tsx +18 -0
  76. package/src/pages/DocsPage.tsx +43 -0
  77. package/src/pages/NotFound.tsx +17 -0
  78. package/src/server/App.tsx +67 -0
  79. package/src/server/api/apis-proxy.ts +69 -0
  80. package/src/server/api/health.ts +5 -0
  81. package/src/server/api/page/[...slug].ts +18 -0
  82. package/src/server/api/search.ts +170 -0
  83. package/src/server/api/specs.ts +9 -0
  84. package/src/server/build-search-index.ts +117 -0
  85. package/src/server/entry-client.tsx +73 -0
  86. package/src/server/entry-server.tsx +95 -0
  87. package/src/server/routes/llms.txt.ts +61 -0
  88. package/src/server/routes/og.tsx +75 -0
  89. package/src/server/routes/robots.txt.ts +11 -0
  90. package/src/server/routes/sitemap.xml.ts +39 -0
  91. package/src/server/utils/safe-path.ts +17 -0
  92. package/src/server/vite-config.ts +71 -0
  93. package/src/themes/default/Layout.module.css +81 -0
  94. package/src/themes/default/Layout.tsx +160 -0
  95. package/src/themes/default/Page.module.css +46 -0
  96. package/src/themes/default/Page.tsx +19 -0
  97. package/src/themes/default/Toc.module.css +48 -0
  98. package/src/themes/default/Toc.tsx +68 -0
  99. package/src/themes/default/index.ts +11 -0
  100. package/src/themes/paper/ChapterNav.module.css +71 -0
  101. package/src/themes/paper/ChapterNav.tsx +115 -0
  102. package/src/themes/paper/Layout.module.css +33 -0
  103. package/src/themes/paper/Layout.tsx +37 -0
  104. package/src/themes/paper/Page.module.css +181 -0
  105. package/src/themes/paper/Page.tsx +126 -0
  106. package/src/themes/paper/ReadingProgress.module.css +132 -0
  107. package/src/themes/paper/ReadingProgress.tsx +315 -0
  108. package/src/themes/paper/index.ts +8 -0
  109. package/src/themes/registry.ts +14 -0
  110. package/src/types/config.ts +80 -0
  111. package/src/types/content.ts +36 -0
  112. package/src/types/globals.d.ts +4 -0
  113. package/src/types/index.ts +3 -0
  114. package/src/types/theme.ts +22 -0
  115. package/tsconfig.json +29 -0
@@ -0,0 +1,11 @@
1
+ import type { Theme } from '@/types';
2
+ import { Layout } from './Layout';
3
+ import { Page } from './Page';
4
+ import { Toc } from './Toc';
5
+
6
+ export const defaultTheme: Theme = {
7
+ Layout,
8
+ Page
9
+ };
10
+
11
+ export { Layout, Page, Toc };
@@ -0,0 +1,71 @@
1
+ .nav {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: var(--rs-space-5);
5
+ }
6
+
7
+ .chapter {
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: var(--rs-space-2);
11
+ }
12
+
13
+ .chapterLabel {
14
+ font-size: var(--rs-font-size-small);
15
+ font-weight: 600;
16
+ text-transform: uppercase;
17
+ letter-spacing: 0.05em;
18
+ color: var(--rs-color-foreground-base-primary);
19
+ white-space: nowrap;
20
+ overflow: hidden;
21
+ text-overflow: ellipsis;
22
+ }
23
+
24
+ .chapterItems {
25
+ list-style: none;
26
+ padding: 0;
27
+ margin: 0;
28
+ display: flex;
29
+ flex-direction: column;
30
+ gap: var(--rs-space-1);
31
+ padding-left: var(--rs-space-4);
32
+ }
33
+
34
+ .link {
35
+ display: flex;
36
+ align-items: center;
37
+ gap: var(--rs-space-2);
38
+ font-size: var(--rs-font-size-small);
39
+ color: var(--rs-color-foreground-base-tertiary);
40
+ text-decoration: none;
41
+ padding: var(--rs-space-1) 0;
42
+ white-space: nowrap;
43
+ overflow: hidden;
44
+ text-overflow: ellipsis;
45
+ }
46
+
47
+ .link:hover {
48
+ color: var(--rs-color-foreground-base-primary);
49
+ }
50
+
51
+ .active {
52
+ color: var(--rs-color-foreground-accent-primary);
53
+ font-weight: 500;
54
+ }
55
+
56
+ .icon {
57
+ display: flex;
58
+ align-items: center;
59
+ flex-shrink: 0;
60
+ }
61
+
62
+ .subLabel {
63
+ font-size: var(--rs-font-size-small);
64
+ font-weight: 500;
65
+ color: var(--rs-color-foreground-base-secondary);
66
+ margin-top: var(--rs-space-3);
67
+ display: block;
68
+ white-space: nowrap;
69
+ overflow: hidden;
70
+ text-overflow: ellipsis;
71
+ }
@@ -0,0 +1,115 @@
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';
5
+
6
+ const iconMap: Record<string, React.ReactNode> = {
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
+ };
13
+
14
+ interface ChapterNavProps {
15
+ tree: PageTree;
16
+ }
17
+
18
+ function buildChapterIndices(
19
+ children: PageTreeItem[]
20
+ ): Map<PageTreeItem, number> {
21
+ const indices = new Map<PageTreeItem, number>();
22
+ let index = 0;
23
+ for (const item of children) {
24
+ if (item.type === 'folder' && item.children) {
25
+ index++;
26
+ indices.set(item, index);
27
+ }
28
+ }
29
+ return indices;
30
+ }
31
+
32
+ export function ChapterNav({ tree }: ChapterNavProps) {
33
+ const { pathname } = useLocation();
34
+ const chapterIndices = buildChapterIndices(tree.children);
35
+
36
+ return (
37
+ <nav className={styles.nav}>
38
+ <ul className={styles.chapterItems}>
39
+ {tree.children.map(item => {
40
+ if (item.type === 'separator') return null;
41
+
42
+ if (item.type === 'folder' && item.children) {
43
+ const chapterIndex = chapterIndices.get(item) ?? 0;
44
+ return (
45
+ <li key={item.name} className={styles.chapter}>
46
+ <span className={styles.chapterLabel}>
47
+ {String(chapterIndex).padStart(2, '0')}. {item.name}
48
+ </span>
49
+ <ul className={styles.chapterItems}>
50
+ {item.children.map(child => (
51
+ <ChapterItem
52
+ key={child.url ?? child.name}
53
+ item={child}
54
+ pathname={pathname}
55
+ />
56
+ ))}
57
+ </ul>
58
+ </li>
59
+ );
60
+ }
61
+
62
+ return (
63
+ <ChapterItem
64
+ key={item.url ?? item.name}
65
+ item={item}
66
+ pathname={pathname}
67
+ />
68
+ );
69
+ })}
70
+ </ul>
71
+ </nav>
72
+ );
73
+ }
74
+
75
+ function ChapterItem({
76
+ item,
77
+ pathname
78
+ }: {
79
+ item: PageTreeItem;
80
+ pathname: string;
81
+ }) {
82
+ if (item.type === 'separator') return null;
83
+
84
+ if (item.type === 'folder' && item.children) {
85
+ return (
86
+ <li>
87
+ <span className={styles.subLabel}>{item.name}</span>
88
+ <ul className={styles.chapterItems}>
89
+ {item.children.map(child => (
90
+ <ChapterItem
91
+ key={child.url ?? child.name}
92
+ item={child}
93
+ pathname={pathname}
94
+ />
95
+ ))}
96
+ </ul>
97
+ </li>
98
+ );
99
+ }
100
+
101
+ const isActive = pathname === item.url;
102
+ const icon = item.icon ? iconMap[item.icon] : null;
103
+
104
+ return (
105
+ <li>
106
+ <RouterLink
107
+ to={item.url ?? '#'}
108
+ className={`${styles.link} ${isActive ? styles.active : ''}`}
109
+ >
110
+ {icon && <span className={styles.icon}>{icon}</span>}
111
+ <span>{item.name}</span>
112
+ </RouterLink>
113
+ </li>
114
+ );
115
+ }
@@ -0,0 +1,33 @@
1
+ .layout {
2
+ --paper-sidebar-width: 260px;
3
+
4
+ min-height: 100vh;
5
+ }
6
+
7
+ .body {
8
+ flex: 1;
9
+ }
10
+
11
+ .sidebar {
12
+ width: var(--paper-sidebar-width);
13
+ padding: var(--rs-space-7) var(--rs-space-5);
14
+ background: var(--rs-color-background-neutral-primary);
15
+ overflow-y: auto;
16
+ font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", monospace;
17
+ }
18
+
19
+ .title {
20
+ text-transform: uppercase;
21
+ letter-spacing: 0.08em;
22
+ color: var(--rs-color-foreground-accent-primary);
23
+ font-family: inherit;
24
+ font-size: var(--rs-font-size-mono-large);
25
+ margin-bottom: var(--rs-space-7);
26
+ }
27
+
28
+ .content {
29
+ flex: 1;
30
+ overflow-y: auto;
31
+ background: var(--rs-color-background-neutral-primary);
32
+ padding-right: var(--paper-sidebar-width);
33
+ }
@@ -0,0 +1,37 @@
1
+ 'use client';
2
+
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
+
10
+ export function Layout({
11
+ children,
12
+ config,
13
+ tree,
14
+ classNames
15
+ }: ThemeLayoutProps) {
16
+ return (
17
+ <Flex direction='column' className={cx(styles.layout, classNames?.layout)}>
18
+ <Flex className={cx(styles.body, classNames?.body)}>
19
+ <aside className={cx(styles.sidebar, classNames?.sidebar)}>
20
+ <Headline
21
+ size='small'
22
+ weight='medium'
23
+ as='h1'
24
+ className={styles.title}
25
+ >
26
+ {config.title}
27
+ </Headline>
28
+ <ChapterNav tree={tree} />
29
+ </aside>
30
+ <div className={cx(styles.content, classNames?.content)}>
31
+ {children}
32
+ </div>
33
+ </Flex>
34
+ <Footer config={config.footer} />
35
+ </Flex>
36
+ );
37
+ }
@@ -0,0 +1,181 @@
1
+ .main {
2
+ --paper-navbar-height: 40px;
3
+ --paper-navbar-padding: var(--rs-space-3);
4
+ --paper-navbar-total: calc(
5
+ var(--paper-navbar-height) +
6
+ var(--paper-navbar-padding) *
7
+ 2 +
8
+ 1px
9
+ );
10
+
11
+ flex: 1;
12
+ max-width: 1024px;
13
+ margin: 0 auto;
14
+ }
15
+
16
+ .navbar {
17
+ height: var(--paper-navbar-height);
18
+ padding: var(--paper-navbar-padding) 0;
19
+ border-bottom: 1px solid var(--rs-color-border-base-primary);
20
+ justify-content: space-between;
21
+ width: 100%;
22
+ position: fixed;
23
+ top: 0;
24
+ background: var(--rs-color-background-neutral-primary);
25
+ z-index: 10;
26
+ max-width: 1024px;
27
+ }
28
+
29
+ .navLeft {
30
+ align-items: center;
31
+ }
32
+
33
+ .navRight {
34
+ align-items: center;
35
+ }
36
+
37
+ .arrow {
38
+ display: flex;
39
+ align-items: center;
40
+ color: var(--rs-color-foreground-base-primary);
41
+ text-decoration: none;
42
+ }
43
+
44
+ .arrow:hover {
45
+ color: var(--rs-color-foreground-accent-primary);
46
+ }
47
+
48
+ .arrowDisabled {
49
+ display: flex;
50
+ align-items: center;
51
+ color: var(--rs-color-foreground-base-tertiary);
52
+ opacity: 0.4;
53
+ cursor: default;
54
+ border: none;
55
+ background: none;
56
+ padding: 0;
57
+ }
58
+
59
+ .breadcrumb {
60
+ font-family: "SF Mono", "Fira Code", monospace;
61
+ font-size: var(--rs-font-size-small);
62
+ text-transform: uppercase;
63
+ letter-spacing: 0.05em;
64
+ margin-left: var(--rs-space-3);
65
+ }
66
+
67
+ .separator {
68
+ margin: 0 var(--rs-space-2);
69
+ color: var(--rs-color-foreground-base-tertiary);
70
+ }
71
+
72
+ .crumbLink {
73
+ color: var(--rs-color-foreground-base-tertiary);
74
+ text-decoration: none;
75
+ }
76
+
77
+ .crumbLink:hover {
78
+ color: var(--rs-color-foreground-base-primary);
79
+ }
80
+
81
+ .crumbActive {
82
+ color: var(--rs-color-foreground-base-primary);
83
+ font-weight: 600;
84
+ }
85
+
86
+ .article {
87
+ flex: 1;
88
+ min-width: 0;
89
+ margin-top: var(--paper-navbar-total);
90
+ padding: 0 var(--rs-space-7);
91
+ }
92
+
93
+ .searchButton {
94
+ height: 28px;
95
+ padding: 0 var(--rs-space-3);
96
+ font-size: var(--rs-font-size-small);
97
+ border: none;
98
+ box-shadow: none;
99
+ }
100
+
101
+ .content {
102
+ font-family: Georgia, "Times New Roman", serif;
103
+ line-height: 1.8;
104
+ background: var(--rs-color-background-base-primary);
105
+ padding: var(--rs-space-9);
106
+ border-left: 1px solid var(--rs-color-border-base-primary);
107
+ border-right: 1px solid var(--rs-color-border-base-primary);
108
+ box-shadow:
109
+ 0 1px 3px rgba(0, 0, 0, 0.08),
110
+ 0 4px 12px rgba(0, 0, 0, 0.04);
111
+ margin-bottom: var(--rs-space-9);
112
+ }
113
+
114
+ .content h1,
115
+ .content h2,
116
+ .content h3,
117
+ .content h4,
118
+ .content h5,
119
+ .content h6 {
120
+ line-height: 1.4;
121
+ }
122
+
123
+ .content h1 {
124
+ margin: 2rem 0 1rem;
125
+ font-size: 2rem;
126
+ }
127
+
128
+ .content h2 {
129
+ margin: 1.75rem 0 0.75rem;
130
+ font-size: 1.5rem;
131
+ }
132
+
133
+ .content h3 {
134
+ margin: 1.5rem 0 0.5rem;
135
+ font-size: 1.25rem;
136
+ }
137
+
138
+ .content h4 {
139
+ margin: 1.25rem 0 0.5rem;
140
+ font-size: 1.1rem;
141
+ }
142
+
143
+ .content h5 {
144
+ margin: 1rem 0 0.5rem;
145
+ font-size: 1rem;
146
+ }
147
+
148
+ .content h6 {
149
+ margin: 1rem 0 0.5rem;
150
+ font-size: 0.875rem;
151
+ }
152
+
153
+ .content p {
154
+ margin: 0.75rem 0;
155
+ }
156
+
157
+ .content ul,
158
+ .content ol {
159
+ margin: 0.75rem 0;
160
+ padding-left: 1.5rem;
161
+ margin-bottom: var(--rs-space-5);
162
+ }
163
+
164
+ .content li {
165
+ font-size: var(--rs-font-size-regular);
166
+ margin: var(--rs-space-2) 0;
167
+ }
168
+
169
+ .content table {
170
+ margin-bottom: var(--rs-space-5);
171
+ }
172
+
173
+ .content [role="tablist"] {
174
+ margin-bottom: var(--rs-space-3);
175
+ }
176
+
177
+ .content blockquote {
178
+ margin: 1rem 0;
179
+ padding-left: 1rem;
180
+ border-left: 3px solid var(--rs-color-border-base-primary);
181
+ }
@@ -0,0 +1,126 @@
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 { Search } from '@/components/ui/search';
6
+ import type { PageTreeItem, ThemePageProps } from '@/types';
7
+ import styles from './Page.module.css';
8
+ import { ReadingProgress } from './ReadingProgress';
9
+
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
+ export function Page({ page, config, tree }: ThemePageProps) {
47
+ const { pathname } = useLocation();
48
+
49
+ const { prev, next, crumbs } = useMemo(() => {
50
+ const pages = flattenTree(tree.children);
51
+ const currentIndex = pages.findIndex(p => p.url === pathname);
52
+ return {
53
+ prev: currentIndex > 0 ? pages[currentIndex - 1] : null,
54
+ next: currentIndex < pages.length - 1 ? pages[currentIndex + 1] : null,
55
+ crumbs: findBreadcrumb(tree.children, page.slug)
56
+ };
57
+ }, [tree, pathname, page.slug]);
58
+
59
+ return (
60
+ <>
61
+ <main className={styles.main}>
62
+ <Flex align='center' className={styles.navbar}>
63
+ <Flex align='center' gap='small' className={styles.navLeft}>
64
+ {prev ? (
65
+ <RouterLink
66
+ to={prev.url!}
67
+ className={styles.arrow}
68
+ aria-label='Previous page'
69
+ >
70
+ <ChevronLeftIcon width={14} height={14} />
71
+ </RouterLink>
72
+ ) : (
73
+ <button
74
+ disabled
75
+ className={styles.arrowDisabled}
76
+ aria-label='Previous page'
77
+ >
78
+ <ChevronLeftIcon width={14} height={14} />
79
+ </button>
80
+ )}
81
+ {next ? (
82
+ <RouterLink
83
+ to={next.url!}
84
+ className={styles.arrow}
85
+ aria-label='Next page'
86
+ >
87
+ <ChevronRightIcon width={14} height={14} />
88
+ </RouterLink>
89
+ ) : (
90
+ <button
91
+ disabled
92
+ className={styles.arrowDisabled}
93
+ aria-label='Next page'
94
+ >
95
+ <ChevronRightIcon width={14} height={14} />
96
+ </button>
97
+ )}
98
+ <nav className={styles.breadcrumb}>
99
+ {crumbs.map((crumb, i) => (
100
+ <span key={crumb.href}>
101
+ {i > 0 && <span className={styles.separator}>/</span>}
102
+ {i === crumbs.length - 1 ? (
103
+ <span className={styles.crumbActive}>{crumb.label}</span>
104
+ ) : (
105
+ <RouterLink to={crumb.href} className={styles.crumbLink}>
106
+ {crumb.label}
107
+ </RouterLink>
108
+ )}
109
+ </span>
110
+ ))}
111
+ </nav>
112
+ </Flex>
113
+ <Flex align='center' className={styles.navRight}>
114
+ {config.search?.enabled && (
115
+ <Search className={styles.searchButton} />
116
+ )}
117
+ </Flex>
118
+ </Flex>
119
+ <article className={styles.article} data-article-content>
120
+ <div className={styles.content}>{page.content}</div>
121
+ </article>
122
+ </main>
123
+ <ReadingProgress items={page.toc} />
124
+ </>
125
+ );
126
+ }
@@ -0,0 +1,132 @@
1
+ .container {
2
+ position: fixed;
3
+ right: var(--rs-space-4);
4
+ top: 100px;
5
+ width: 200px;
6
+ height: calc(100vh - 200px);
7
+ z-index: 10;
8
+ }
9
+
10
+ .inner {
11
+ position: absolute;
12
+ inset: 0;
13
+ }
14
+
15
+ .tickContainer {
16
+ position: absolute;
17
+ right: 0;
18
+ display: grid;
19
+ width: 6rem;
20
+ align-items: center;
21
+ justify-content: flex-end;
22
+ }
23
+
24
+ .tickLine {
25
+ height: 1px;
26
+ width: 0.5rem;
27
+ transition: all 100ms;
28
+ }
29
+
30
+ .tickLineBefore {
31
+ background-color: var(--rs-color-foreground-accent-primary);
32
+ }
33
+
34
+ .tickLineAfter {
35
+ background-color: var(--rs-color-background-neutral-tertiary);
36
+ }
37
+
38
+ .tickContainer:hover .tickLine {
39
+ width: 1rem;
40
+ }
41
+
42
+ .tickClickable {
43
+ position: absolute;
44
+ inset-inline: 0;
45
+ height: 0.5rem;
46
+ cursor: pointer;
47
+ }
48
+
49
+ .headingContainer {
50
+ transition: transform 150ms;
51
+ }
52
+
53
+ .headingContainer:hover {
54
+ transform: translateX(-0.125rem);
55
+ }
56
+
57
+ .headingLabel {
58
+ position: absolute;
59
+ display: flex;
60
+ height: 0.75rem;
61
+ align-items: center;
62
+ font-family: var(--rs-font-mono);
63
+ font-size: 0.75rem;
64
+ color: var(--rs-color-foreground-base-primary);
65
+ text-transform: capitalize;
66
+ opacity: 0;
67
+ transition: opacity 300ms;
68
+ }
69
+
70
+ .headingLink {
71
+ color: var(--rs-color-foreground-base-primary);
72
+ text-align: right;
73
+ font-family: var(--rs-font-mono);
74
+ font-size: var(--rs-font-size-mini);
75
+ font-weight: var(--rs-font-weight-regular);
76
+ line-height: var(--rs-line-height-mini);
77
+ letter-spacing: var(--rs-letter-spacing-mini);
78
+ text-decoration: none;
79
+ cursor: pointer;
80
+ }
81
+
82
+ .headingLink:hover {
83
+ color: var(--rs-color-foreground-accent-primary);
84
+ }
85
+
86
+ .container:hover .headingLabel {
87
+ opacity: 1;
88
+ }
89
+
90
+ .connectingLine {
91
+ position: absolute;
92
+ right: 0;
93
+ height: 1px;
94
+ background-color: var(--rs-color-background-neutral-emphasis);
95
+ }
96
+
97
+ .scrollMarkerContainer {
98
+ position: absolute;
99
+ right: 0;
100
+ z-index: 20;
101
+ transition: opacity 300ms;
102
+ pointer-events: none;
103
+ }
104
+
105
+ .scrollMarkerContainerReady {
106
+ opacity: 1;
107
+ }
108
+
109
+ .scrollMarkerContainerNotReady {
110
+ opacity: 0;
111
+ }
112
+
113
+ .scrollMarkerLine {
114
+ height: 1px;
115
+ width: 1rem;
116
+ background-color: var(--rs-color-foreground-accent-primary);
117
+ }
118
+
119
+ .scrollMarkerText {
120
+ position: absolute;
121
+ top: 0;
122
+ left: -2.5rem;
123
+ transform: translateY(-50%);
124
+ font-family: var(--rs-font-mono);
125
+ font-size: 0.75rem;
126
+ color: var(--rs-color-foreground-accent-primary);
127
+ transition: opacity 300ms;
128
+ }
129
+
130
+ .container:hover .scrollMarkerText {
131
+ opacity: 0;
132
+ }