@raystack/chronicle 0.1.0-canary.5a730d4 → 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 +217 -412
- package/package.json +13 -10
- package/src/cli/commands/build.ts +30 -48
- package/src/cli/commands/dev.ts +24 -13
- package/src/cli/commands/init.ts +38 -123
- package/src/cli/commands/serve.ts +35 -50
- package/src/cli/commands/start.ts +20 -16
- package/src/cli/index.ts +14 -14
- package/src/cli/utils/config.ts +25 -26
- package/src/cli/utils/index.ts +3 -2
- package/src/cli/utils/resolve.ts +7 -3
- package/src/cli/utils/scaffold.ts +14 -16
- package/src/components/mdx/details.module.css +0 -2
- package/src/components/mdx/image.tsx +5 -20
- package/src/components/mdx/index.tsx +18 -4
- package/src/components/mdx/link.tsx +24 -20
- package/src/components/ui/breadcrumbs.tsx +8 -42
- package/src/components/ui/footer.tsx +2 -3
- package/src/components/ui/search.tsx +116 -72
- package/src/lib/api-routes.ts +6 -8
- package/src/lib/config.ts +31 -29
- package/src/lib/get-llm-text.ts +10 -0
- package/src/lib/head.tsx +26 -22
- package/src/lib/mdx-loader.ts +21 -0
- package/src/lib/openapi.ts +8 -8
- package/src/lib/page-context.tsx +74 -58
- package/src/lib/source.ts +136 -114
- package/src/pages/ApiLayout.tsx +22 -18
- package/src/pages/ApiPage.tsx +32 -27
- package/src/pages/DocsLayout.tsx +7 -7
- package/src/pages/DocsPage.tsx +11 -11
- package/src/pages/NotFound.tsx +11 -4
- package/src/server/App.tsx +35 -27
- package/src/server/api/apis-proxy.ts +69 -0
- package/src/server/api/health.ts +5 -0
- package/src/server/api/page/[...slug].ts +17 -0
- package/src/server/api/search.ts +170 -0
- package/src/server/api/specs.ts +9 -0
- package/src/server/build-search-index.ts +117 -0
- package/src/server/entry-client.tsx +47 -55
- package/src/server/entry-server.tsx +100 -35
- package/src/server/routes/llms.txt.ts +61 -0
- package/src/server/routes/og.tsx +75 -0
- package/src/server/routes/robots.txt.ts +11 -0
- package/src/server/routes/sitemap.xml.ts +40 -0
- package/src/server/utils/safe-path.ts +17 -0
- package/src/server/vite-config.ts +90 -44
- package/src/themes/default/Layout.tsx +78 -47
- package/src/themes/default/Page.module.css +0 -12
- package/src/themes/default/Page.tsx +9 -11
- package/src/themes/default/Toc.tsx +25 -39
- package/src/themes/default/index.ts +7 -9
- package/src/themes/paper/ChapterNav.tsx +63 -43
- package/src/themes/paper/Layout.module.css +1 -1
- package/src/themes/paper/Layout.tsx +24 -12
- package/src/themes/paper/Page.module.css +16 -4
- package/src/themes/paper/Page.tsx +56 -62
- package/src/themes/paper/ReadingProgress.tsx +160 -139
- package/src/themes/paper/index.ts +5 -5
- package/src/themes/registry.ts +7 -7
- package/src/types/content.ts +5 -21
- package/src/types/globals.d.ts +3 -0
- package/src/types/theme.ts +4 -3
- package/src/cli/__tests__/config.test.ts +0 -25
- package/src/cli/__tests__/scaffold.test.ts +0 -10
- package/src/pages/__tests__/head.test.tsx +0 -57
- package/src/server/__tests__/entry-server.test.tsx +0 -35
- package/src/server/__tests__/handlers.test.ts +0 -77
- package/src/server/__tests__/og.test.ts +0 -23
- package/src/server/__tests__/router.test.ts +0 -72
- package/src/server/__tests__/vite-config.test.ts +0 -25
- package/src/server/dev.ts +0 -156
- package/src/server/entry-prod.ts +0 -127
- package/src/server/handlers/apis-proxy.ts +0 -52
- package/src/server/handlers/health.ts +0 -3
- package/src/server/handlers/llms.ts +0 -58
- package/src/server/handlers/og.ts +0 -87
- package/src/server/handlers/robots.ts +0 -11
- package/src/server/handlers/search.ts +0 -140
- package/src/server/handlers/sitemap.ts +0 -39
- package/src/server/handlers/specs.ts +0 -9
- package/src/server/index.html +0 -12
- package/src/server/prod.ts +0 -18
- package/src/server/router.ts +0 -42
- package/src/themes/default/font.ts +0 -4
package/src/lib/source.ts
CHANGED
|
@@ -1,138 +1,160 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
export interface SourcePage {
|
|
14
|
-
url: string
|
|
15
|
-
slugs: string[]
|
|
16
|
-
filePath: string
|
|
17
|
-
frontmatter: Frontmatter
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { loader } from 'fumadocs-core/source';
|
|
4
|
+
import type { Root, Node, Folder } from 'fumadocs-core/page-tree';
|
|
5
|
+
import matter from 'gray-matter';
|
|
6
|
+
import type { MDXContent } from 'mdx/types';
|
|
7
|
+
import type { TableOfContents } from 'fumadocs-core/toc';
|
|
8
|
+
import type { Frontmatter } from '@/types';
|
|
9
|
+
|
|
10
|
+
function getContentDir(): string {
|
|
11
|
+
return __CHRONICLE_CONTENT_DIR__ || path.join(process.cwd(), 'content');
|
|
18
12
|
}
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
14
|
+
async function scanFiles(contentDir: string) {
|
|
15
|
+
const files: {
|
|
16
|
+
type: 'page' | 'meta';
|
|
17
|
+
path: string;
|
|
18
|
+
data: Record<string, unknown>;
|
|
19
|
+
}[] = [];
|
|
20
|
+
|
|
21
|
+
async function scan(dir: string, prefix: string[] = []) {
|
|
22
|
+
try {
|
|
23
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules')
|
|
26
|
+
continue;
|
|
27
|
+
const fullPath = path.join(dir, entry.name);
|
|
28
|
+
const relativePath = [...prefix, entry.name].join('/');
|
|
29
|
+
|
|
30
|
+
if (entry.isDirectory()) {
|
|
31
|
+
await scan(fullPath, [...prefix, entry.name]);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (entry.name.endsWith('.mdx') || entry.name.endsWith('.md')) {
|
|
36
|
+
const raw = await fs.readFile(fullPath, 'utf-8');
|
|
37
|
+
const { data } = matter(raw);
|
|
38
|
+
files.push({
|
|
39
|
+
type: 'page',
|
|
40
|
+
path: relativePath,
|
|
41
|
+
data: { ...data, _relativePath: relativePath }
|
|
42
|
+
});
|
|
43
|
+
} else if (entry.name === 'meta.json' || entry.name === 'meta.yaml') {
|
|
44
|
+
try {
|
|
45
|
+
const raw = await fs.readFile(fullPath, 'utf-8');
|
|
46
|
+
const data = entry.name.endsWith('.json')
|
|
47
|
+
? JSON.parse(raw)
|
|
48
|
+
: matter(raw).data;
|
|
49
|
+
files.push({ type: 'meta', path: relativePath, data });
|
|
50
|
+
} catch {
|
|
51
|
+
/* malformed meta file */
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
/* directory not readable */
|
|
31
57
|
}
|
|
32
58
|
}
|
|
33
|
-
return first.slice(0, depth).join('/') + '/'
|
|
34
|
-
}
|
|
35
59
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
function filePathToSlugs(filePath: string): string[] {
|
|
39
|
-
const relative = filePath.slice(prefix.length)
|
|
40
|
-
const withoutExt = relative.replace(/\.(mdx|md)$/, '')
|
|
41
|
-
const parts = withoutExt.split('/').filter(Boolean)
|
|
42
|
-
if (parts[parts.length - 1] === 'index') parts.pop()
|
|
43
|
-
return parts
|
|
60
|
+
await scan(contentDir);
|
|
61
|
+
return files;
|
|
44
62
|
}
|
|
45
63
|
|
|
46
|
-
|
|
47
|
-
|
|
64
|
+
let cachedSource: ReturnType<typeof loader> | null = null;
|
|
65
|
+
|
|
66
|
+
async function getSource() {
|
|
67
|
+
if (cachedSource) return cachedSource;
|
|
68
|
+
const contentDir = getContentDir();
|
|
69
|
+
const files = await scanFiles(contentDir);
|
|
70
|
+
cachedSource = loader({
|
|
71
|
+
source: { files },
|
|
72
|
+
baseUrl: '/'
|
|
73
|
+
});
|
|
74
|
+
return cachedSource;
|
|
48
75
|
}
|
|
49
76
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export async function getPages(): Promise<SourcePage[]> {
|
|
53
|
-
if (cachedPages) return cachedPages
|
|
54
|
-
|
|
55
|
-
cachedPages = Object.entries(meta).map(([filePath, fm]) => {
|
|
56
|
-
const slugs = filePathToSlugs(filePath)
|
|
57
|
-
const baseName = slugs[slugs.length - 1] ?? 'index'
|
|
58
|
-
return {
|
|
59
|
-
url: slugsToUrl(slugs),
|
|
60
|
-
slugs,
|
|
61
|
-
filePath,
|
|
62
|
-
frontmatter: {
|
|
63
|
-
title: fm?.title ?? baseName,
|
|
64
|
-
description: fm?.description,
|
|
65
|
-
order: fm?.order,
|
|
66
|
-
icon: fm?.icon,
|
|
67
|
-
lastModified: fm?.lastModified,
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
})
|
|
77
|
+
export { getSource as source };
|
|
71
78
|
|
|
72
|
-
|
|
79
|
+
export function invalidate() {
|
|
80
|
+
cachedSource = null;
|
|
73
81
|
}
|
|
74
82
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return
|
|
83
|
+
function getOrder(node: Node, orderMap: Map<string, number>): number | undefined {
|
|
84
|
+
if (node.type === 'page') return orderMap.get(node.url);
|
|
85
|
+
if (node.type === 'folder' && node.index) return orderMap.get(node.index.url);
|
|
86
|
+
return undefined;
|
|
79
87
|
}
|
|
80
88
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
function sortNodes(nodes: Node[], orderMap: Map<string, number>): Node[] {
|
|
90
|
+
return [...nodes]
|
|
91
|
+
.map(n =>
|
|
92
|
+
n.type === 'folder'
|
|
93
|
+
? ({ ...n, children: sortNodes(n.children, orderMap) } as Folder)
|
|
94
|
+
: n
|
|
95
|
+
)
|
|
96
|
+
.sort(
|
|
97
|
+
(a, b) =>
|
|
98
|
+
(getOrder(a, orderMap) ?? Number.MAX_SAFE_INTEGER) -
|
|
99
|
+
(getOrder(b, orderMap) ?? Number.MAX_SAFE_INTEGER)
|
|
100
|
+
);
|
|
86
101
|
}
|
|
87
102
|
|
|
88
|
-
|
|
89
|
-
|
|
103
|
+
function sortTreeByOrder(tree: Root, pages: { url: string; data: unknown }[]): Root {
|
|
104
|
+
const orderMap = new Map<string, number>();
|
|
105
|
+
for (const page of pages) {
|
|
106
|
+
const d = page.data as Record<string, unknown>;
|
|
107
|
+
const order = d.order as number | undefined;
|
|
108
|
+
if (order !== undefined) orderMap.set(page.url, order);
|
|
109
|
+
if (page.url === '/') orderMap.set('/', order ?? 0);
|
|
110
|
+
}
|
|
111
|
+
return { ...tree, children: sortNodes(tree.children, orderMap) };
|
|
90
112
|
}
|
|
91
113
|
|
|
92
|
-
export async function
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
pages.forEach((page) => {
|
|
98
|
-
const isIndex = page.url === '/'
|
|
99
|
-
const item: PageTreeItem = {
|
|
100
|
-
type: 'page',
|
|
101
|
-
name: page.frontmatter.title,
|
|
102
|
-
url: page.url,
|
|
103
|
-
order: page.frontmatter.order ?? (isIndex ? 0 : undefined),
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (page.slugs.length > 1) {
|
|
107
|
-
const folder = page.slugs[0]
|
|
108
|
-
if (!folders.has(folder)) {
|
|
109
|
-
folders.set(folder, [])
|
|
110
|
-
}
|
|
111
|
-
folders.get(folder)?.push(item)
|
|
112
|
-
} else {
|
|
113
|
-
rootPages.push(item)
|
|
114
|
-
}
|
|
115
|
-
})
|
|
114
|
+
export async function getPageTree(): Promise<Root> {
|
|
115
|
+
const s = await getSource();
|
|
116
|
+
return sortTreeByOrder(s.pageTree as Root, s.getPages());
|
|
117
|
+
}
|
|
116
118
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
export async function getPages() {
|
|
120
|
+
const s = await getSource();
|
|
121
|
+
return s.getPages();
|
|
122
|
+
}
|
|
119
123
|
|
|
120
|
-
|
|
124
|
+
export async function getPage(slugs?: string[]) {
|
|
125
|
+
const s = await getSource();
|
|
126
|
+
return s.getPage(slugs);
|
|
127
|
+
}
|
|
121
128
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
})
|
|
133
|
-
})
|
|
129
|
+
export function extractFrontmatter(page: { data: unknown }, fallbackTitle?: string): Frontmatter {
|
|
130
|
+
const d = page.data as Record<string, unknown>;
|
|
131
|
+
return {
|
|
132
|
+
title: (d.title as string) ?? fallbackTitle ?? 'Untitled',
|
|
133
|
+
description: d.description as string | undefined,
|
|
134
|
+
order: d.order as number | undefined,
|
|
135
|
+
icon: d.icon as string | undefined,
|
|
136
|
+
lastModified: d.lastModified as string | undefined,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
134
139
|
|
|
135
|
-
|
|
140
|
+
export function getRelativePath(page: { data: unknown }): string {
|
|
141
|
+
return ((page.data as Record<string, unknown>)._relativePath as string) ?? '';
|
|
142
|
+
}
|
|
136
143
|
|
|
137
|
-
|
|
144
|
+
const ssrModules = import.meta.glob<{ default?: MDXContent; toc?: TableOfContents }>(
|
|
145
|
+
'../../.content/**/*.{mdx,md}'
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
export async function loadPageModule(
|
|
149
|
+
relativePath: string
|
|
150
|
+
): Promise<{ default: MDXContent | null; toc: TableOfContents }> {
|
|
151
|
+
if (!relativePath || relativePath.includes('..')) return { default: null, toc: [] };
|
|
152
|
+
const withoutExt = relativePath.replace(/\.(mdx|md)$/, '');
|
|
153
|
+
const key = relativePath.endsWith('.md')
|
|
154
|
+
? `../../.content/${withoutExt}.md`
|
|
155
|
+
: `../../.content/${withoutExt}.mdx`;
|
|
156
|
+
const loader = ssrModules[key];
|
|
157
|
+
if (!loader) return { default: null, toc: [] };
|
|
158
|
+
const mod = await loader();
|
|
159
|
+
return { default: mod.default ?? null, toc: mod.toc ?? [] };
|
|
138
160
|
}
|
package/src/pages/ApiLayout.tsx
CHANGED
|
@@ -1,29 +1,33 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { buildApiPageTree } from '@/lib/api-routes'
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import styles from './ApiLayout.module.css'
|
|
1
|
+
import { cx } from 'class-variance-authority';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import { Search } from '@/components/ui/search';
|
|
4
|
+
import { buildApiPageTree } from '@/lib/api-routes';
|
|
5
|
+
import { usePageContext } from '@/lib/page-context';
|
|
6
|
+
import { getTheme } from '@/themes/registry';
|
|
7
|
+
import styles from './ApiLayout.module.css';
|
|
8
8
|
|
|
9
9
|
interface ApiLayoutProps {
|
|
10
|
-
children: ReactNode
|
|
10
|
+
children: ReactNode;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function ApiLayout({ children }: ApiLayoutProps) {
|
|
14
|
-
const { config, apiSpecs } = usePageContext()
|
|
15
|
-
const { Layout, className } = getTheme(config.theme?.name)
|
|
16
|
-
const tree = buildApiPageTree(apiSpecs)
|
|
14
|
+
const { config, apiSpecs } = usePageContext();
|
|
15
|
+
const { Layout, className } = getTheme(config.theme?.name);
|
|
16
|
+
const tree = buildApiPageTree(apiSpecs);
|
|
17
17
|
|
|
18
18
|
return (
|
|
19
|
-
<Layout
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
<Layout
|
|
20
|
+
config={config}
|
|
21
|
+
tree={tree}
|
|
22
|
+
classNames={{
|
|
23
|
+
layout: cx(styles.layout, className),
|
|
24
|
+
body: styles.body,
|
|
25
|
+
sidebar: styles.sidebar,
|
|
26
|
+
content: styles.content
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
25
29
|
<Search className={styles.hiddenSearch} />
|
|
26
30
|
{children}
|
|
27
31
|
</Layout>
|
|
28
|
-
)
|
|
32
|
+
);
|
|
29
33
|
}
|
package/src/pages/ApiPage.tsx
CHANGED
|
@@ -1,44 +1,41 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { findApiOperation } from '@/lib/api-routes'
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
1
|
+
import { Flex, Headline, Text } from '@raystack/apsara';
|
|
2
|
+
import type { OpenAPIV3 } from 'openapi-types';
|
|
3
|
+
import { EndpointPage } from '@/components/api';
|
|
4
|
+
import { findApiOperation } from '@/lib/api-routes';
|
|
5
|
+
import { Head } from '@/lib/head';
|
|
6
|
+
import type { ApiSpec } from '@/lib/openapi';
|
|
7
|
+
import { usePageContext } from '@/lib/page-context';
|
|
8
8
|
|
|
9
9
|
interface ApiPageProps {
|
|
10
|
-
slug: string[]
|
|
10
|
+
slug: string[];
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function ApiPage({ slug }: ApiPageProps) {
|
|
14
|
-
const { config, apiSpecs } = usePageContext()
|
|
14
|
+
const { config, apiSpecs } = usePageContext();
|
|
15
15
|
|
|
16
16
|
if (slug.length === 0) {
|
|
17
17
|
return (
|
|
18
18
|
<>
|
|
19
19
|
<Head
|
|
20
|
-
title=
|
|
20
|
+
title='API Reference'
|
|
21
21
|
description={`API documentation for ${config.title}`}
|
|
22
22
|
config={config}
|
|
23
23
|
/>
|
|
24
24
|
<ApiLanding specs={apiSpecs} />
|
|
25
25
|
</>
|
|
26
|
-
)
|
|
26
|
+
);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const match = findApiOperation(apiSpecs, slug)
|
|
30
|
-
if (!match) return null
|
|
29
|
+
const match = findApiOperation(apiSpecs, slug);
|
|
30
|
+
if (!match) return null;
|
|
31
31
|
|
|
32
|
-
const operation = match.operation as OpenAPIV3.OperationObject
|
|
33
|
-
const title =
|
|
32
|
+
const operation = match.operation as OpenAPIV3.OperationObject;
|
|
33
|
+
const title =
|
|
34
|
+
operation.summary ?? `${match.method.toUpperCase()} ${match.path}`;
|
|
34
35
|
|
|
35
36
|
return (
|
|
36
37
|
<>
|
|
37
|
-
<Head
|
|
38
|
-
title={title}
|
|
39
|
-
description={operation.description}
|
|
40
|
-
config={config}
|
|
41
|
-
/>
|
|
38
|
+
<Head title={title} description={operation.description} config={config} />
|
|
42
39
|
<EndpointPage
|
|
43
40
|
method={match.method}
|
|
44
41
|
path={match.path}
|
|
@@ -48,21 +45,29 @@ export function ApiPage({ slug }: ApiPageProps) {
|
|
|
48
45
|
auth={match.spec.auth}
|
|
49
46
|
/>
|
|
50
47
|
</>
|
|
51
|
-
)
|
|
48
|
+
);
|
|
52
49
|
}
|
|
53
50
|
|
|
54
51
|
function ApiLanding({ specs }: { specs: ApiSpec[] }) {
|
|
55
52
|
return (
|
|
56
|
-
<Flex
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
<Flex
|
|
54
|
+
direction='column'
|
|
55
|
+
gap='large'
|
|
56
|
+
style={{ padding: 'var(--rs-space-7)' }}
|
|
57
|
+
>
|
|
58
|
+
<Headline size='medium' as='h1'>
|
|
59
|
+
API Reference
|
|
60
|
+
</Headline>
|
|
61
|
+
{specs.map(spec => (
|
|
62
|
+
<Flex key={spec.name} direction='column' gap='small'>
|
|
63
|
+
<Headline size='small' as='h2'>
|
|
64
|
+
{spec.name}
|
|
65
|
+
</Headline>
|
|
61
66
|
{spec.document.info.description && (
|
|
62
67
|
<Text size={3}>{spec.document.info.description}</Text>
|
|
63
68
|
)}
|
|
64
69
|
</Flex>
|
|
65
70
|
))}
|
|
66
71
|
</Flex>
|
|
67
|
-
)
|
|
72
|
+
);
|
|
68
73
|
}
|
package/src/pages/DocsLayout.tsx
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import type { ReactNode } from 'react'
|
|
2
|
-
import { usePageContext } from '@/lib/page-context'
|
|
3
|
-
import { getTheme } from '@/themes/registry'
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { usePageContext } from '@/lib/page-context';
|
|
3
|
+
import { getTheme } from '@/themes/registry';
|
|
4
4
|
|
|
5
5
|
interface DocsLayoutProps {
|
|
6
|
-
children: ReactNode
|
|
6
|
+
children: ReactNode;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function DocsLayout({ children }: DocsLayoutProps) {
|
|
10
|
-
const { config, tree } = usePageContext()
|
|
11
|
-
const { Layout, className } = getTheme(config.theme?.name)
|
|
10
|
+
const { config, tree } = usePageContext();
|
|
11
|
+
const { Layout, className } = getTheme(config.theme?.name);
|
|
12
12
|
|
|
13
13
|
return (
|
|
14
14
|
<Layout config={config} tree={tree} classNames={{ layout: className }}>
|
|
15
15
|
{children}
|
|
16
16
|
</Layout>
|
|
17
|
-
)
|
|
17
|
+
);
|
|
18
18
|
}
|
package/src/pages/DocsPage.tsx
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { Head } from '@/lib/head';
|
|
2
|
+
import { usePageContext } from '@/lib/page-context';
|
|
3
|
+
import { getTheme } from '@/themes/registry';
|
|
4
4
|
|
|
5
5
|
interface DocsPageProps {
|
|
6
|
-
slug: string[]
|
|
6
|
+
slug: string[];
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function DocsPage({ slug }: DocsPageProps) {
|
|
10
|
-
const { config, tree, page } = usePageContext()
|
|
10
|
+
const { config, tree, page } = usePageContext();
|
|
11
11
|
|
|
12
|
-
if (!page) return null
|
|
12
|
+
if (!page) return null;
|
|
13
13
|
|
|
14
|
-
const { Page } = getTheme(config.theme?.name)
|
|
15
|
-
const pageUrl = config.url ? `${config.url}/${slug.join('/')}` : undefined
|
|
14
|
+
const { Page } = getTheme(config.theme?.name);
|
|
15
|
+
const pageUrl = config.url ? `${config.url}/${slug.join('/')}` : undefined;
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
18
|
<>
|
|
@@ -25,7 +25,7 @@ export function DocsPage({ slug }: DocsPageProps) {
|
|
|
25
25
|
'@type': 'Article',
|
|
26
26
|
headline: page.frontmatter.title,
|
|
27
27
|
description: page.frontmatter.description,
|
|
28
|
-
...(pageUrl && { url: pageUrl })
|
|
28
|
+
...(pageUrl && { url: pageUrl })
|
|
29
29
|
}}
|
|
30
30
|
/>
|
|
31
31
|
<Page
|
|
@@ -33,11 +33,11 @@ export function DocsPage({ slug }: DocsPageProps) {
|
|
|
33
33
|
slug,
|
|
34
34
|
frontmatter: page.frontmatter,
|
|
35
35
|
content: page.content,
|
|
36
|
-
toc:
|
|
36
|
+
toc: page.toc
|
|
37
37
|
}}
|
|
38
38
|
config={config}
|
|
39
39
|
tree={tree}
|
|
40
40
|
/>
|
|
41
41
|
</>
|
|
42
|
-
)
|
|
42
|
+
);
|
|
43
43
|
}
|
package/src/pages/NotFound.tsx
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import { Flex, Headline, Text } from '@raystack/apsara'
|
|
1
|
+
import { Flex, Headline, Text } from '@raystack/apsara';
|
|
2
2
|
|
|
3
3
|
export function NotFound() {
|
|
4
4
|
return (
|
|
5
|
-
<Flex
|
|
6
|
-
|
|
5
|
+
<Flex
|
|
6
|
+
direction='column'
|
|
7
|
+
align='center'
|
|
8
|
+
justify='center'
|
|
9
|
+
style={{ minHeight: '60vh' }}
|
|
10
|
+
>
|
|
11
|
+
<Headline size='large' as='h1'>
|
|
12
|
+
404
|
|
13
|
+
</Headline>
|
|
7
14
|
<Text size={3}>Page not found</Text>
|
|
8
15
|
</Flex>
|
|
9
|
-
)
|
|
16
|
+
);
|
|
10
17
|
}
|
package/src/server/App.tsx
CHANGED
|
@@ -1,29 +1,33 @@
|
|
|
1
|
-
import '@raystack/apsara/normalize.css'
|
|
2
|
-
import '@raystack/apsara/style.css'
|
|
3
|
-
import { ThemeProvider } from '@raystack/apsara'
|
|
4
|
-
import { useLocation } from 'react-router
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import type { ChronicleConfig } from '@/types'
|
|
1
|
+
import '@raystack/apsara/normalize.css';
|
|
2
|
+
import '@raystack/apsara/style.css';
|
|
3
|
+
import { ThemeProvider } from '@raystack/apsara';
|
|
4
|
+
import { useLocation } from 'react-router';
|
|
5
|
+
import { Head } from '@/lib/head';
|
|
6
|
+
import { usePageContext } from '@/lib/page-context';
|
|
7
|
+
import { ApiLayout } from '@/pages/ApiLayout';
|
|
8
|
+
import { ApiPage } from '@/pages/ApiPage';
|
|
9
|
+
import { DocsLayout } from '@/pages/DocsLayout';
|
|
10
|
+
import { DocsPage } from '@/pages/DocsPage';
|
|
11
|
+
import type { ChronicleConfig } from '@/types';
|
|
12
12
|
|
|
13
13
|
function resolveRoute(pathname: string) {
|
|
14
14
|
if (pathname.startsWith('/apis')) {
|
|
15
|
-
const slug = pathname
|
|
16
|
-
|
|
15
|
+
const slug = pathname
|
|
16
|
+
.replace(/^\/apis\/?/, '')
|
|
17
|
+
.split('/')
|
|
18
|
+
.filter(Boolean);
|
|
19
|
+
return { type: 'api' as const, slug };
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
const slug =
|
|
20
|
-
|
|
22
|
+
const slug =
|
|
23
|
+
pathname === '/' ? [] : pathname.slice(1).split('/').filter(Boolean);
|
|
24
|
+
return { type: 'docs' as const, slug };
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
export function App() {
|
|
24
|
-
const { pathname } = useLocation()
|
|
25
|
-
const { config } = usePageContext()
|
|
26
|
-
const route = resolveRoute(pathname)
|
|
28
|
+
const { pathname } = useLocation();
|
|
29
|
+
const { config } = usePageContext();
|
|
30
|
+
const route = resolveRoute(pathname);
|
|
27
31
|
|
|
28
32
|
return (
|
|
29
33
|
<ThemeProvider enableSystem>
|
|
@@ -38,7 +42,7 @@ export function App() {
|
|
|
38
42
|
</DocsLayout>
|
|
39
43
|
)}
|
|
40
44
|
</ThemeProvider>
|
|
41
|
-
)
|
|
45
|
+
);
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
function RootHead({ config }: { config: ChronicleConfig }) {
|
|
@@ -47,13 +51,17 @@ function RootHead({ config }: { config: ChronicleConfig }) {
|
|
|
47
51
|
title={config.title}
|
|
48
52
|
description={config.description}
|
|
49
53
|
config={config}
|
|
50
|
-
jsonLd={
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
jsonLd={
|
|
55
|
+
config.url
|
|
56
|
+
? {
|
|
57
|
+
'@context': 'https://schema.org',
|
|
58
|
+
'@type': 'WebSite',
|
|
59
|
+
name: config.title,
|
|
60
|
+
description: config.description,
|
|
61
|
+
url: config.url
|
|
62
|
+
}
|
|
63
|
+
: undefined
|
|
64
|
+
}
|
|
57
65
|
/>
|
|
58
|
-
)
|
|
66
|
+
);
|
|
59
67
|
}
|