@raystack/chronicle 0.5.4 → 0.6.1
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 +258 -80
- package/package.json +7 -5
- package/src/cli/commands/build.ts +5 -8
- package/src/cli/commands/dev.ts +5 -6
- package/src/cli/commands/init.test.ts +77 -0
- package/src/cli/commands/init.ts +73 -40
- package/src/cli/commands/serve.ts +6 -9
- package/src/cli/commands/start.ts +5 -5
- package/src/cli/utils/config.ts +6 -12
- package/src/cli/utils/scaffold.test.ts +179 -0
- package/src/cli/utils/scaffold.ts +70 -9
- package/src/components/api/field-row.tsx +1 -1
- package/src/components/api/field-section.tsx +2 -2
- package/src/components/mdx/index.tsx +1 -1
- package/src/components/ui/breadcrumbs.tsx +4 -2
- package/src/components/ui/client-theme-switcher.tsx +21 -4
- package/src/components/ui/search.module.css +16 -41
- package/src/components/ui/search.tsx +30 -41
- package/src/lib/config.test.ts +493 -0
- package/src/lib/config.ts +123 -22
- package/src/lib/head.tsx +23 -5
- package/src/lib/llms.test.ts +94 -0
- package/src/lib/llms.ts +41 -0
- package/src/lib/navigation.test.ts +94 -0
- package/src/lib/navigation.ts +51 -0
- package/src/lib/page-context.tsx +51 -32
- package/src/lib/route-resolver.test.ts +173 -0
- package/src/lib/route-resolver.ts +73 -0
- package/src/lib/source.ts +94 -1
- package/src/lib/version-source.test.ts +163 -0
- package/src/lib/version-source.ts +101 -0
- package/src/pages/ApiPage.tsx +1 -1
- package/src/pages/DocsLayout.tsx +24 -3
- package/src/pages/DocsPage.tsx +3 -6
- package/src/pages/LandingPage.module.css +56 -0
- package/src/pages/LandingPage.tsx +39 -0
- package/src/pages/NotFound.tsx +2 -0
- package/src/server/App.tsx +21 -23
- package/src/server/api/page.ts +5 -1
- package/src/server/api/search.ts +51 -24
- package/src/server/api/specs.ts +17 -5
- package/src/server/entry-client.tsx +42 -14
- package/src/server/entry-server.tsx +33 -11
- package/src/server/routes/[...slug].md.ts +0 -6
- package/src/server/routes/[version]/llms.txt.ts +26 -0
- package/src/server/routes/llms.txt.ts +10 -13
- package/src/server/routes/og.tsx +2 -2
- package/src/server/routes/sitemap.xml.ts +14 -6
- package/src/server/vite-config.ts +5 -5
- package/src/themes/default/ContentDirButtons.tsx +66 -0
- package/src/themes/default/Layout.module.css +187 -40
- package/src/themes/default/Layout.tsx +166 -65
- package/src/themes/default/OpenInAI.tsx +112 -0
- package/src/themes/default/Page.module.css +30 -0
- package/src/themes/default/Page.tsx +1 -3
- package/src/themes/default/SidebarLogo.tsx +26 -0
- package/src/themes/default/Toc.module.css +102 -25
- package/src/themes/default/Toc.tsx +56 -10
- package/src/themes/default/VersionSwitcher.tsx +59 -0
- package/src/themes/paper/ContentDirDropdown.tsx +47 -0
- package/src/themes/paper/Layout.module.css +7 -0
- package/src/themes/paper/Layout.tsx +20 -13
- package/src/themes/paper/VersionSwitcher.tsx +60 -0
- package/src/types/config.ts +145 -23
- package/src/types/content.ts +11 -1
- package/src/types/theme.ts +1 -0
- package/src/components/ui/footer.module.css +0 -27
- package/src/components/ui/footer.tsx +0 -30
package/src/types/config.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import uniqBy from 'lodash/uniqBy.js'
|
|
1
2
|
import { z } from 'zod'
|
|
2
3
|
|
|
3
4
|
const logoSchema = z.object({
|
|
@@ -45,19 +46,11 @@ const apiSchema = z.object({
|
|
|
45
46
|
name: z.string(),
|
|
46
47
|
spec: z.string(),
|
|
47
48
|
basePath: z.string(),
|
|
49
|
+
icon: z.string().optional(),
|
|
48
50
|
server: apiServerSchema,
|
|
49
51
|
auth: apiAuthSchema.optional(),
|
|
50
52
|
})
|
|
51
53
|
|
|
52
|
-
const footerSchema = z.object({
|
|
53
|
-
copyright: z.string().optional(),
|
|
54
|
-
links: z.array(navLinkSchema).optional(),
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
const llmsSchema = z.object({
|
|
58
|
-
enabled: z.boolean().optional(),
|
|
59
|
-
})
|
|
60
|
-
|
|
61
54
|
const googleAnalyticsSchema = z.object({
|
|
62
55
|
measurementId: z.string(),
|
|
63
56
|
})
|
|
@@ -73,24 +66,155 @@ const telemetrySchema = z.object({
|
|
|
73
66
|
port: z.number().int().min(1).max(65535).default(9090),
|
|
74
67
|
})
|
|
75
68
|
|
|
76
|
-
|
|
69
|
+
const siteSchema = z.object({
|
|
77
70
|
title: z.string(),
|
|
78
71
|
description: z.string().optional(),
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const DIR_NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/
|
|
75
|
+
|
|
76
|
+
const dirNameSchema = z
|
|
77
|
+
.string()
|
|
78
|
+
.min(1)
|
|
79
|
+
.refine((s) => DIR_NAME_PATTERN.test(s) && s !== '.' && s !== '..', {
|
|
80
|
+
message:
|
|
81
|
+
'dir must start with a letter or digit and contain only letters, digits, ".", "_", or "-"',
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const contentEntrySchema = z.object({
|
|
85
|
+
dir: dirNameSchema,
|
|
86
|
+
label: z.string().min(1),
|
|
87
|
+
icon: z.string().optional(),
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// Variants map to Apsara Badge color prop.
|
|
91
|
+
// https://apsara.raystack.org/docs/components/badge
|
|
92
|
+
const badgeVariantSchema = z.enum([
|
|
93
|
+
'accent',
|
|
94
|
+
'warning',
|
|
95
|
+
'danger',
|
|
96
|
+
'success',
|
|
97
|
+
'neutral',
|
|
98
|
+
'gradient',
|
|
99
|
+
])
|
|
100
|
+
|
|
101
|
+
const badgeSchema = z.object({
|
|
102
|
+
label: z.string().min(1),
|
|
103
|
+
variant: badgeVariantSchema.default('accent'),
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const latestSchema = z.object({
|
|
107
|
+
label: z.string().min(1),
|
|
108
|
+
landing: z.boolean().optional(),
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
const versionSchema = z.object({
|
|
112
|
+
dir: dirNameSchema,
|
|
113
|
+
label: z.string().min(1),
|
|
114
|
+
badge: badgeSchema.optional(),
|
|
115
|
+
landing: z.boolean().optional(),
|
|
116
|
+
content: z.array(contentEntrySchema).min(1),
|
|
87
117
|
api: z.array(apiSchema).optional(),
|
|
88
|
-
llms: llmsSchema.optional(),
|
|
89
|
-
analytics: analyticsSchema.optional(),
|
|
90
|
-
telemetry: telemetrySchema.optional(),
|
|
91
118
|
})
|
|
92
119
|
|
|
120
|
+
const allUnique = <T>(items: T[], key: (item: T) => string): boolean =>
|
|
121
|
+
uniqBy(items, key).length === items.length
|
|
122
|
+
|
|
123
|
+
const RESERVED_ROUTE_SEGMENTS = [
|
|
124
|
+
'api',
|
|
125
|
+
'apis',
|
|
126
|
+
'og',
|
|
127
|
+
'llms.txt',
|
|
128
|
+
'robots.txt',
|
|
129
|
+
'sitemap.xml',
|
|
130
|
+
] as const
|
|
131
|
+
|
|
132
|
+
export const chronicleConfigSchema = z
|
|
133
|
+
.object({
|
|
134
|
+
site: siteSchema,
|
|
135
|
+
url: z.string().optional(),
|
|
136
|
+
content: z.array(contentEntrySchema).min(1),
|
|
137
|
+
latest: latestSchema.optional(),
|
|
138
|
+
versions: z.array(versionSchema).optional(),
|
|
139
|
+
preset: z.string().optional(),
|
|
140
|
+
logo: logoSchema.optional(),
|
|
141
|
+
theme: themeSchema.optional(),
|
|
142
|
+
navigation: navigationSchema.optional(),
|
|
143
|
+
search: searchSchema.optional(),
|
|
144
|
+
api: z.array(apiSchema).optional(),
|
|
145
|
+
analytics: analyticsSchema.optional(),
|
|
146
|
+
telemetry: telemetrySchema.optional(),
|
|
147
|
+
})
|
|
148
|
+
.strict()
|
|
149
|
+
.refine((cfg) => allUnique(cfg.content, (c) => c.dir), {
|
|
150
|
+
message: 'content[].dir must be unique',
|
|
151
|
+
path: ['content'],
|
|
152
|
+
})
|
|
153
|
+
.refine((cfg) => !cfg.versions || allUnique(cfg.versions, (v) => v.dir), {
|
|
154
|
+
message: 'versions[].dir must be unique',
|
|
155
|
+
path: ['versions'],
|
|
156
|
+
})
|
|
157
|
+
.refine(
|
|
158
|
+
(cfg) =>
|
|
159
|
+
!cfg.versions ||
|
|
160
|
+
cfg.versions.every((v) => allUnique(v.content, (c) => c.dir)),
|
|
161
|
+
{
|
|
162
|
+
message: 'versions[].content[].dir must be unique within each version',
|
|
163
|
+
path: ['versions'],
|
|
164
|
+
},
|
|
165
|
+
)
|
|
166
|
+
.refine((cfg) => !cfg.versions || cfg.versions.length === 0 || !!cfg.latest, {
|
|
167
|
+
message: 'latest is required when versions are declared',
|
|
168
|
+
path: ['latest'],
|
|
169
|
+
})
|
|
170
|
+
.refine(
|
|
171
|
+
(cfg) => {
|
|
172
|
+
if (!cfg.versions) return true
|
|
173
|
+
const contentDirs = new Set(cfg.content.map((c) => c.dir))
|
|
174
|
+
return !cfg.versions.some((v) => contentDirs.has(v.dir))
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
message:
|
|
178
|
+
'versions[].dir must not overlap with content[].dir — the URL segment would be shadowed',
|
|
179
|
+
path: ['versions'],
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
.superRefine((cfg, ctx) => {
|
|
183
|
+
const reserved = new Set<string>(RESERVED_ROUTE_SEGMENTS)
|
|
184
|
+
const message = `dir must not be a reserved route segment: ${RESERVED_ROUTE_SEGMENTS.join(', ')}`
|
|
185
|
+
|
|
186
|
+
cfg.content.forEach((c, i) => {
|
|
187
|
+
if (reserved.has(c.dir)) {
|
|
188
|
+
ctx.addIssue({ code: 'custom', message, path: ['content', i, 'dir'] })
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
cfg.versions?.forEach((v, vi) => {
|
|
192
|
+
if (reserved.has(v.dir)) {
|
|
193
|
+
ctx.addIssue({
|
|
194
|
+
code: 'custom',
|
|
195
|
+
message,
|
|
196
|
+
path: ['versions', vi, 'dir'],
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
v.content.forEach((c, ci) => {
|
|
200
|
+
if (reserved.has(c.dir)) {
|
|
201
|
+
ctx.addIssue({
|
|
202
|
+
code: 'custom',
|
|
203
|
+
message,
|
|
204
|
+
path: ['versions', vi, 'content', ci, 'dir'],
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
|
|
93
211
|
export type ChronicleConfig = z.infer<typeof chronicleConfigSchema>
|
|
212
|
+
export type SiteConfig = z.infer<typeof siteSchema>
|
|
213
|
+
export type ContentEntry = z.infer<typeof contentEntrySchema>
|
|
214
|
+
export type BadgeConfig = z.infer<typeof badgeSchema>
|
|
215
|
+
export type BadgeVariant = z.infer<typeof badgeVariantSchema>
|
|
216
|
+
export type LatestConfig = z.infer<typeof latestSchema>
|
|
217
|
+
export type VersionConfig = z.infer<typeof versionSchema>
|
|
94
218
|
export type LogoConfig = z.infer<typeof logoSchema>
|
|
95
219
|
export type ThemeConfig = z.infer<typeof themeSchema>
|
|
96
220
|
export type NavigationConfig = z.infer<typeof navigationSchema>
|
|
@@ -100,8 +224,6 @@ export type SearchConfig = z.infer<typeof searchSchema>
|
|
|
100
224
|
export type ApiConfig = z.infer<typeof apiSchema>
|
|
101
225
|
export type ApiServerConfig = z.infer<typeof apiServerSchema>
|
|
102
226
|
export type ApiAuthConfig = z.infer<typeof apiAuthSchema>
|
|
103
|
-
export type FooterConfig = z.infer<typeof footerSchema>
|
|
104
|
-
export type LlmsConfig = z.infer<typeof llmsSchema>
|
|
105
227
|
export type AnalyticsConfig = z.infer<typeof analyticsSchema>
|
|
106
228
|
export type GoogleAnalyticsConfig = z.infer<typeof googleAnalyticsSchema>
|
|
107
229
|
export type TelemetryConfig = z.infer<typeof telemetrySchema>
|
package/src/types/content.ts
CHANGED
|
@@ -12,7 +12,17 @@ export interface Frontmatter {
|
|
|
12
12
|
lastModified?: string
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export interface
|
|
15
|
+
export interface PageNavLink {
|
|
16
|
+
url: string
|
|
17
|
+
title: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface PageNav {
|
|
21
|
+
prev: PageNavLink | null
|
|
22
|
+
next: PageNavLink | null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface Page extends PageNav {
|
|
16
26
|
slug: string[]
|
|
17
27
|
frontmatter: Frontmatter
|
|
18
28
|
content: ReactNode
|
package/src/types/theme.ts
CHANGED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
.footer {
|
|
2
|
-
border-top: 1px solid var(--rs-color-border-base-primary);
|
|
3
|
-
padding: var(--rs-space-5) var(--rs-space-7);
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
.container {
|
|
7
|
-
max-width: 1200px;
|
|
8
|
-
margin: 0 auto;
|
|
9
|
-
width: 100%;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
.copyright {
|
|
13
|
-
color: var(--rs-color-foreground-base-secondary);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.links {
|
|
17
|
-
flex-wrap: wrap;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.link {
|
|
21
|
-
color: var(--rs-color-foreground-base-secondary);
|
|
22
|
-
font-size: 14px;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.link:hover {
|
|
26
|
-
color: var(--rs-color-foreground-base-primary);
|
|
27
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { Flex, Link, Text } from "@raystack/apsara";
|
|
2
|
-
import type { FooterConfig } from "@/types";
|
|
3
|
-
import styles from "./footer.module.css";
|
|
4
|
-
|
|
5
|
-
interface FooterProps {
|
|
6
|
-
config?: FooterConfig;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function Footer({ config }: FooterProps) {
|
|
10
|
-
return (
|
|
11
|
-
<footer className={styles.footer}>
|
|
12
|
-
<Flex align="center" justify="between" className={styles.container}>
|
|
13
|
-
{config?.copyright && (
|
|
14
|
-
<Text size={2} className={styles.copyright}>
|
|
15
|
-
{config.copyright}
|
|
16
|
-
</Text>
|
|
17
|
-
)}
|
|
18
|
-
{config?.links && config.links.length > 0 && (
|
|
19
|
-
<Flex gap="medium" className={styles.links}>
|
|
20
|
-
{config.links.map((link) => (
|
|
21
|
-
<Link key={link.href} href={link.href} className={styles.link}>
|
|
22
|
-
{link.label}
|
|
23
|
-
</Link>
|
|
24
|
-
))}
|
|
25
|
-
</Flex>
|
|
26
|
-
)}
|
|
27
|
-
</Flex>
|
|
28
|
-
</footer>
|
|
29
|
-
);
|
|
30
|
-
}
|