docs-i18n 0.6.3 → 0.7.0
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/{src/admin/ui → admin/app}/components/JobDialog.tsx +21 -2
- package/{src/admin/ui → admin/app}/components/JobPanel.tsx +1 -1
- package/{src/admin/ui → admin/app}/components/Preview.tsx +2 -5
- package/{src/admin/ui → admin/app}/lib/api.ts +18 -39
- package/admin/app/routeTree.gen.ts +68 -0
- package/admin/app/router.tsx +23 -0
- package/admin/app/routes/__root.tsx +55 -0
- package/admin/app/routes/index.tsx +416 -0
- package/{src/admin/ui → admin/app}/styles.css +36 -3
- package/admin/package.json +27 -0
- package/admin/server/functions/jobs.ts +53 -0
- package/admin/server/functions/misc.ts +84 -0
- package/{src/admin/server/routes → admin/server/functions}/models.ts +16 -29
- package/admin/server/functions/status.ts +61 -0
- package/admin/server/index.ts +35 -0
- package/admin/server/init.ts +46 -0
- package/{src/admin → admin}/server/services/job-manager.ts +39 -10
- package/{src/admin → admin}/server/services/status.ts +6 -6
- package/admin/tsconfig.json +19 -0
- package/{src/admin → admin}/vite.config.ts +8 -2
- package/dist/{assemble-7H4QCW35.js → assemble-CP2BRYQJ.js} +6 -4
- package/dist/{chunk-A3YQNPKZ.js → chunk-CLYUAWZE.js} +1 -1
- package/dist/{chunk-YN4VJHCQ.js → chunk-JHBSHTXC.js} +1 -1
- package/dist/chunk-L64GJ4OB.js +32 -0
- package/dist/{chunk-SKKZIV3L.js → chunk-PNKVD2UK.js} +1 -29
- package/dist/{chunk-XEOYZUHS.js → chunk-QKIR7RKQ.js} +4 -31
- package/dist/chunk-TRURQFP4.js +31 -0
- package/dist/cli.js +108 -7
- package/dist/index.d.ts +41 -1
- package/dist/index.js +92 -3
- package/dist/{rescan-O5D3CYC2.js → rescan-HXMWFAOC.js} +5 -3
- package/dist/{status-F4MYIAAY.js → status-AGZDXOTZ.js} +4 -2
- package/dist/{translate-ZIVKNAC4.js → translate-A5X6MX4Y.js} +14 -7
- package/dist/upload-XL6KG6S2.js +132 -0
- package/package.json +17 -15
- package/template/app/components/BlogArticle.tsx +159 -0
- package/template/app/components/BlogList.tsx +88 -0
- package/template/app/components/Breadcrumbs.tsx +81 -0
- package/template/app/components/Card.tsx +31 -0
- package/template/app/components/Doc.tsx +191 -0
- package/template/app/components/DocBreadcrumb.tsx +60 -0
- package/template/app/components/DocContainer.tsx +13 -0
- package/template/app/components/DocTitle.tsx +11 -0
- package/template/app/components/DocsLayout.tsx +715 -0
- package/template/app/components/Dropdown.tsx +116 -0
- package/template/app/components/FallbackBanner.tsx +36 -0
- package/template/app/components/Footer.tsx +29 -0
- package/template/app/components/FrameworkSelect.tsx +150 -0
- package/template/app/components/LibraryCard.tsx +178 -0
- package/template/app/components/LocaleSwitcher.tsx +43 -0
- package/template/app/components/Navbar.tsx +430 -0
- package/template/app/components/PostNotFound.tsx +20 -0
- package/template/app/components/SearchButton.tsx +32 -0
- package/template/app/components/Select.tsx +103 -0
- package/template/app/components/Spinner.tsx +18 -0
- package/template/app/components/ThemeProvider.tsx +141 -0
- package/template/app/components/ThemeToggle.tsx +31 -0
- package/template/app/components/Toc.tsx +86 -0
- package/template/app/components/VersionSelect.tsx +118 -0
- package/template/app/components/icons/BSkyIcon.tsx +27 -0
- package/template/app/components/icons/BaseballCapIcon.tsx +25 -0
- package/template/app/components/icons/BrandXIcon.tsx +28 -0
- package/template/app/components/icons/CheckCircleIcon.tsx +28 -0
- package/template/app/components/icons/CogsIcon.tsx +25 -0
- package/template/app/components/icons/DiscordIcon.tsx +24 -0
- package/template/app/components/icons/GithubIcon.tsx +24 -0
- package/template/app/components/icons/GoogleIcon.tsx +24 -0
- package/template/app/components/icons/InstagramIcon.tsx +24 -0
- package/template/app/components/icons/NpmIcon.tsx +26 -0
- package/template/app/components/icons/YinYangIcon.tsx +26 -0
- package/template/app/components/icons/YouTubeIcon.tsx +24 -0
- package/template/app/components/markdown/CodeBlock.tsx +254 -0
- package/template/app/components/markdown/FileTabs.tsx +58 -0
- package/template/app/components/markdown/FrameworkContent.tsx +76 -0
- package/template/app/components/markdown/Markdown.tsx +216 -0
- package/template/app/components/markdown/MarkdownContent.tsx +89 -0
- package/template/app/components/markdown/MarkdownFrameworkHandler.tsx +66 -0
- package/template/app/components/markdown/MarkdownHeadingContext.tsx +35 -0
- package/template/app/components/markdown/MarkdownLink.tsx +46 -0
- package/template/app/components/markdown/MarkdownTabsHandler.tsx +109 -0
- package/template/app/components/markdown/PackageManagerTabs.tsx +95 -0
- package/template/app/components/markdown/Tabs.tsx +139 -0
- package/template/app/components/markdown/index.ts +15 -0
- package/template/app/components/ui/Button.tsx +141 -0
- package/template/app/components/ui/InlineCode.tsx +16 -0
- package/template/app/components/ui/MarkdownImg.tsx +21 -0
- package/template/app/config/frameworks.ts +93 -0
- package/template/app/contexts/SearchContext.tsx +36 -0
- package/template/app/db/index.ts +17 -0
- package/template/app/db/schema.ts +74 -0
- package/template/app/hooks/useClickOutside.ts +106 -0
- package/template/app/routeTree.gen.ts +584 -0
- package/template/app/router.tsx +29 -0
- package/template/app/routes/$lang.$project.$version.docs.$.tsx +128 -0
- package/template/app/routes/$lang.$project.$version.docs.framework.$framework.$.tsx +106 -0
- package/template/app/routes/$lang.$project.$version.docs.framework.$framework.index.tsx +27 -0
- package/template/app/routes/$lang.$project.$version.docs.framework.index.tsx +44 -0
- package/template/app/routes/$lang.$project.$version.docs.index.tsx +27 -0
- package/template/app/routes/$lang.$project.$version.docs.tsx +70 -0
- package/template/app/routes/$lang.$project.$version.tsx +69 -0
- package/template/app/routes/$lang.$project.docs.$.tsx +104 -0
- package/template/app/routes/$lang.$project.docs.index.tsx +20 -0
- package/template/app/routes/$lang.$project.docs.tsx +79 -0
- package/template/app/routes/$lang.$project.tsx +89 -0
- package/template/app/routes/$lang.blog.$.tsx +82 -0
- package/template/app/routes/$lang.blog.index.tsx +56 -0
- package/template/app/routes/$lang.blog.tsx +26 -0
- package/template/app/routes/$lang.docs.$.tsx +100 -0
- package/template/app/routes/$lang.docs.framework.$framework.$.tsx +104 -0
- package/template/app/routes/$lang.docs.framework.$framework.index.tsx +32 -0
- package/template/app/routes/$lang.docs.framework.index.tsx +47 -0
- package/template/app/routes/$lang.docs.index.tsx +20 -0
- package/template/app/routes/$lang.docs.tsx +90 -0
- package/template/app/routes/$lang.tsx +16 -0
- package/template/app/routes/__root.tsx +180 -0
- package/template/app/routes/index.tsx +89 -0
- package/template/app/site.config.ts +182 -0
- package/template/app/styles/app.css +1029 -0
- package/template/app/types/index.ts +77 -0
- package/template/app/utils/blog.server.ts +193 -0
- package/template/app/utils/blog.ts +42 -0
- package/template/app/utils/config.ts +120 -0
- package/template/app/utils/content-loader.ts +400 -0
- package/template/app/utils/dates.ts +29 -0
- package/template/app/utils/docs.server.ts +150 -0
- package/template/app/utils/markdown/filterFrameworkContent.ts +233 -0
- package/template/app/utils/markdown/index.ts +2 -0
- package/template/app/utils/markdown/installCommand.ts +143 -0
- package/template/app/utils/markdown/plugins/collectHeadings.ts +104 -0
- package/template/app/utils/markdown/plugins/extractCodeMeta.ts +57 -0
- package/template/app/utils/markdown/plugins/helpers.ts +33 -0
- package/template/app/utils/markdown/plugins/index.ts +8 -0
- package/template/app/utils/markdown/plugins/parseCommentComponents.ts +103 -0
- package/template/app/utils/markdown/plugins/transformCommentComponents.ts +23 -0
- package/template/app/utils/markdown/plugins/transformFrameworkComponent.ts +217 -0
- package/template/app/utils/markdown/plugins/transformTabsComponent.ts +359 -0
- package/template/app/utils/markdown/processor.ts +75 -0
- package/template/app/utils/site-config.tsx +11 -0
- package/template/app/utils/upload.ts +232 -0
- package/template/app/utils/useLocalStorage.ts +65 -0
- package/template/app/utils/utils.ts +23 -0
- package/template/package.json +54 -0
- package/template/public/favicon.svg +1 -0
- package/template/public/fonts/Inter-latin-ext.woff2 +0 -0
- package/template/public/fonts/Inter-latin.woff2 +0 -0
- package/template/public/images/frameworks/angular-logo.svg +1 -0
- package/template/public/images/frameworks/js-logo.svg +1 -0
- package/template/public/images/frameworks/lit-logo.svg +1 -0
- package/template/public/images/frameworks/preact-logo.svg +6 -0
- package/template/public/images/frameworks/qwik-logo.svg +1 -0
- package/template/public/images/frameworks/react-logo.svg +1 -0
- package/template/public/images/frameworks/solid-logo.svg +1 -0
- package/template/public/images/frameworks/svelte-logo.svg +1 -0
- package/template/public/images/frameworks/vue-logo.svg +4 -0
- package/template/tsconfig.json +24 -0
- package/template/vite.config.ts +43 -0
- package/template/wrangler.jsonc +16 -0
- package/README.md +0 -161
- package/dist/server-73AVSOL5.js +0 -598
- package/src/admin/index.html +0 -13
- package/src/admin/server/index.ts +0 -138
- package/src/admin/server/routes/jobs.ts +0 -113
- package/src/admin/server/routes/status.ts +0 -57
- package/src/admin/ui/App.tsx +0 -332
- package/src/admin/ui/main.tsx +0 -19
- /package/{src/admin/ui → admin/app}/components/FileList.tsx +0 -0
- /package/{src/admin/ui → admin/app}/components/LangGrid.tsx +0 -0
- /package/{src/admin/ui → admin/app}/components/ProgressBar.tsx +0 -0
- /package/{src/admin/ui → admin/app}/lib/flags.ts +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createFileRoute,
|
|
3
|
+
notFound,
|
|
4
|
+
redirect,
|
|
5
|
+
useLocation,
|
|
6
|
+
} from '@tanstack/react-router'
|
|
7
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
8
|
+
import { siteConfig, findProject, isSingleProject } from '~/site.config'
|
|
9
|
+
import { Doc } from '~/components/Doc'
|
|
10
|
+
|
|
11
|
+
const fetchDoc = createServerFn({ method: 'GET' })
|
|
12
|
+
.inputValidator(
|
|
13
|
+
(data: { project: string; version: string; lang: string; slug: string }) =>
|
|
14
|
+
data,
|
|
15
|
+
)
|
|
16
|
+
.handler(async ({ data }) => {
|
|
17
|
+
const { loadDoc } = await import('~/utils/docs.server')
|
|
18
|
+
return loadDoc(data.project, data.version, data.lang, data.slug)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
export const Route = createFileRoute('/$lang/$project/$version/docs/$')({
|
|
22
|
+
staleTime: 1000 * 60 * 5,
|
|
23
|
+
beforeLoad: ({ params }) => {
|
|
24
|
+
// Redirect full URLs to simplified URLs for single-project sites
|
|
25
|
+
if (isSingleProject()) {
|
|
26
|
+
const { lang, _splat } = params
|
|
27
|
+
throw redirect({
|
|
28
|
+
to: '/$lang/docs/$',
|
|
29
|
+
params: { lang, _splat: _splat || '' },
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// When hideLatestVersion is enabled, redirect latest version URLs to versionless
|
|
34
|
+
if (siteConfig.hideLatestVersion) {
|
|
35
|
+
const { lang, project: projectId, version, _splat } = params
|
|
36
|
+
const project = findProject(projectId)
|
|
37
|
+
if (project && (version === project.latestVersion || version === 'latest')) {
|
|
38
|
+
throw redirect({
|
|
39
|
+
to: '/$lang/$project/docs/$',
|
|
40
|
+
params: { lang, project: projectId, _splat: _splat || '' },
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
loader: async ({ params }) => {
|
|
46
|
+
const { lang, project, version, _splat: slug } = params
|
|
47
|
+
if (!slug) throw notFound()
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
return await fetchDoc({ data: { project, version, lang, slug } })
|
|
51
|
+
} catch (error) {
|
|
52
|
+
throw notFound()
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
head: ({ loaderData, params }) => {
|
|
56
|
+
const project = findProject(params.project)
|
|
57
|
+
const title = loaderData?.meta?.title
|
|
58
|
+
const projectName = project?.name || siteConfig.name
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
meta: [
|
|
62
|
+
{
|
|
63
|
+
title: title ? `${title} | ${projectName} Docs` : `${projectName} Docs`,
|
|
64
|
+
},
|
|
65
|
+
...(loaderData?.meta?.description
|
|
66
|
+
? [{ name: 'description', content: loaderData.meta.description }]
|
|
67
|
+
: []),
|
|
68
|
+
],
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
headers: ({ params }) => {
|
|
72
|
+
const { version, project: projectId } = params
|
|
73
|
+
const project = findProject(projectId)
|
|
74
|
+
|
|
75
|
+
const isLatestVersion =
|
|
76
|
+
project &&
|
|
77
|
+
(version === 'latest' || version === project.latestVersion)
|
|
78
|
+
|
|
79
|
+
if (isLatestVersion) {
|
|
80
|
+
return {
|
|
81
|
+
'cache-control': 'public, max-age=60, must-revalidate',
|
|
82
|
+
'cdn-cache-control':
|
|
83
|
+
'max-age=600, stale-while-revalidate=3600, durable',
|
|
84
|
+
vary: 'Accept-Encoding',
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
return {
|
|
88
|
+
'cache-control': 'public, max-age=3600, must-revalidate',
|
|
89
|
+
'cdn-cache-control':
|
|
90
|
+
'max-age=86400, stale-while-revalidate=604800, durable',
|
|
91
|
+
vary: 'Accept-Encoding',
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
component: DocsPage,
|
|
96
|
+
errorComponent: DocNotFound,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
function DocsPage() {
|
|
100
|
+
const loaderData = Route.useLoaderData()
|
|
101
|
+
const { project: projectId } = Route.useParams()
|
|
102
|
+
const project = findProject(projectId)
|
|
103
|
+
const location = useLocation()
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<Doc
|
|
107
|
+
title={loaderData.meta?.title || ''}
|
|
108
|
+
content={loaderData.content}
|
|
109
|
+
isFallback={loaderData.isFallback}
|
|
110
|
+
filePath={loaderData.filePath}
|
|
111
|
+
repo={project?.repo}
|
|
112
|
+
pagePath={location.pathname}
|
|
113
|
+
/>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function DocNotFound() {
|
|
118
|
+
return (
|
|
119
|
+
<div className="flex-1 flex items-center justify-center py-20">
|
|
120
|
+
<div className="text-center">
|
|
121
|
+
<h1 className="text-2xl font-bold mb-2">Page Not Found</h1>
|
|
122
|
+
<p className="text-gray-500 dark:text-gray-400">
|
|
123
|
+
The document you are looking for does not exist.
|
|
124
|
+
</p>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createFileRoute,
|
|
3
|
+
redirect,
|
|
4
|
+
useLocation,
|
|
5
|
+
} from '@tanstack/react-router'
|
|
6
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
7
|
+
import { siteConfig, findProject } from '~/site.config'
|
|
8
|
+
import { Doc } from '~/components/Doc'
|
|
9
|
+
|
|
10
|
+
const fetchFrameworkDoc = createServerFn({ method: 'GET' })
|
|
11
|
+
.inputValidator(
|
|
12
|
+
(data: {
|
|
13
|
+
project: string
|
|
14
|
+
version: string
|
|
15
|
+
lang: string
|
|
16
|
+
framework: string
|
|
17
|
+
slug: string
|
|
18
|
+
}) => data,
|
|
19
|
+
)
|
|
20
|
+
.handler(async ({ data }) => {
|
|
21
|
+
const { loadDoc } = await import('~/utils/docs.server')
|
|
22
|
+
return loadDoc(
|
|
23
|
+
data.project,
|
|
24
|
+
data.version,
|
|
25
|
+
data.lang,
|
|
26
|
+
`framework/${data.framework}/${data.slug}`,
|
|
27
|
+
)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
export const Route = createFileRoute(
|
|
31
|
+
'/$lang/$project/$version/docs/framework/$framework/$',
|
|
32
|
+
)({
|
|
33
|
+
staleTime: 1000 * 60 * 5,
|
|
34
|
+
loader: async ({ params }) => {
|
|
35
|
+
const { lang, project, version, framework, _splat: slug } = params
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
return await fetchFrameworkDoc({
|
|
39
|
+
data: { project, version, lang, framework, slug: slug || '' },
|
|
40
|
+
})
|
|
41
|
+
} catch (error) {
|
|
42
|
+
// If doc not found, redirect to framework index
|
|
43
|
+
throw redirect({
|
|
44
|
+
to: '/$lang/$project/$version/docs/framework/$framework',
|
|
45
|
+
params: { lang, project, version, framework },
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
head: ({ loaderData, params }) => {
|
|
50
|
+
const project = findProject(params.project)
|
|
51
|
+
const projectName = project?.name || siteConfig.name
|
|
52
|
+
const framework =
|
|
53
|
+
params.framework.charAt(0).toUpperCase() + params.framework.slice(1)
|
|
54
|
+
const title = loaderData?.meta?.title
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
meta: [
|
|
58
|
+
{
|
|
59
|
+
title: title
|
|
60
|
+
? `${title} | ${projectName} ${framework} Docs`
|
|
61
|
+
: `${projectName} ${framework} Docs`,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
headers: ({ params }) => {
|
|
67
|
+
const project = findProject(params.project)
|
|
68
|
+
const isLatest =
|
|
69
|
+
project &&
|
|
70
|
+
(params.version === 'latest' || params.version === project.latestVersion)
|
|
71
|
+
|
|
72
|
+
return isLatest
|
|
73
|
+
? {
|
|
74
|
+
'cache-control': 'public, max-age=60, must-revalidate',
|
|
75
|
+
'cdn-cache-control':
|
|
76
|
+
'max-age=600, stale-while-revalidate=3600, durable',
|
|
77
|
+
vary: 'Accept-Encoding',
|
|
78
|
+
}
|
|
79
|
+
: {
|
|
80
|
+
'cache-control': 'public, max-age=3600, must-revalidate',
|
|
81
|
+
'cdn-cache-control':
|
|
82
|
+
'max-age=86400, stale-while-revalidate=604800, durable',
|
|
83
|
+
vary: 'Accept-Encoding',
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
component: FrameworkDocsPage,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
function FrameworkDocsPage() {
|
|
90
|
+
const loaderData = Route.useLoaderData()
|
|
91
|
+
const { project: projectId, framework } = Route.useParams()
|
|
92
|
+
const project = findProject(projectId)
|
|
93
|
+
const location = useLocation()
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<Doc
|
|
97
|
+
title={loaderData.meta?.title || ''}
|
|
98
|
+
content={loaderData.content}
|
|
99
|
+
isFallback={loaderData.isFallback}
|
|
100
|
+
filePath={loaderData.filePath}
|
|
101
|
+
repo={project?.repo}
|
|
102
|
+
pagePath={location.pathname}
|
|
103
|
+
framework={framework}
|
|
104
|
+
/>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { findProject } from '~/site.config'
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute(
|
|
5
|
+
'/$lang/$project/$version/docs/framework/$framework/',
|
|
6
|
+
)({
|
|
7
|
+
component: FrameworkIndex,
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
function FrameworkIndex() {
|
|
11
|
+
const { project: projectId, framework } = Route.useParams()
|
|
12
|
+
const project = findProject(projectId)
|
|
13
|
+
const frameworkLabel =
|
|
14
|
+
framework.charAt(0).toUpperCase() + framework.slice(1)
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="py-8 max-w-3xl mx-auto">
|
|
18
|
+
<h1 className="text-3xl font-bold mb-4">
|
|
19
|
+
{project?.name} {frameworkLabel} Documentation
|
|
20
|
+
</h1>
|
|
21
|
+
<div className="h-px bg-gray-200 dark:bg-gray-700 mb-6" />
|
|
22
|
+
<p className="text-gray-600 dark:text-gray-400">
|
|
23
|
+
Use the sidebar to select a documentation page.
|
|
24
|
+
</p>
|
|
25
|
+
</div>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createFileRoute, Link } from '@tanstack/react-router'
|
|
2
|
+
import { findProject } from '~/site.config'
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute(
|
|
5
|
+
'/$lang/$project/$version/docs/framework/',
|
|
6
|
+
)({
|
|
7
|
+
component: FrameworkSelectionPage,
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
function FrameworkSelectionPage() {
|
|
11
|
+
const { lang, project: projectId, version } = Route.useParams()
|
|
12
|
+
const project = findProject(projectId)
|
|
13
|
+
const frameworks = project?.frameworks || []
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div className="py-8 max-w-3xl mx-auto">
|
|
17
|
+
<h1 className="text-3xl font-bold mb-4">
|
|
18
|
+
{project?.name} Frameworks
|
|
19
|
+
</h1>
|
|
20
|
+
<div className="h-px bg-gray-200 dark:bg-gray-700 mb-6" />
|
|
21
|
+
|
|
22
|
+
{frameworks.length > 0 ? (
|
|
23
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
24
|
+
{frameworks.map((fw) => (
|
|
25
|
+
<Link
|
|
26
|
+
key={fw}
|
|
27
|
+
to="/$lang/$project/$version/docs/framework/$framework"
|
|
28
|
+
params={{ lang, project: projectId, version, framework: fw }}
|
|
29
|
+
className="p-4 rounded-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
|
|
30
|
+
>
|
|
31
|
+
<span className="text-lg font-medium">
|
|
32
|
+
{fw.charAt(0).toUpperCase() + fw.slice(1)}
|
|
33
|
+
</span>
|
|
34
|
+
</Link>
|
|
35
|
+
))}
|
|
36
|
+
</div>
|
|
37
|
+
) : (
|
|
38
|
+
<p className="text-gray-600 dark:text-gray-400">
|
|
39
|
+
No frameworks are configured for this project.
|
|
40
|
+
</p>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createFileRoute, redirect } from '@tanstack/react-router'
|
|
2
|
+
import { getProject, isSingleProject } from '~/site.config'
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute('/$lang/$project/$version/docs/')({
|
|
5
|
+
beforeLoad: ({ params }) => {
|
|
6
|
+
const project = getProject(params.project)
|
|
7
|
+
|
|
8
|
+
// Redirect to simplified URL for single-project sites
|
|
9
|
+
if (isSingleProject()) {
|
|
10
|
+
throw redirect({
|
|
11
|
+
to: '/$lang/docs/$',
|
|
12
|
+
params: {
|
|
13
|
+
lang: params.lang,
|
|
14
|
+
_splat: project.defaultDocs || 'overview',
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
throw redirect({
|
|
20
|
+
from: '/$lang/$project/$version/docs',
|
|
21
|
+
to: './$',
|
|
22
|
+
params: {
|
|
23
|
+
_splat: project.defaultDocs || 'overview',
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
},
|
|
27
|
+
})
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
createFileRoute,
|
|
4
|
+
Outlet,
|
|
5
|
+
} from '@tanstack/react-router'
|
|
6
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
7
|
+
import { siteConfig, findProject } from '~/site.config'
|
|
8
|
+
import { DocsLayout } from '~/components/DocsLayout'
|
|
9
|
+
import { frameworkOptions } from '~/config/frameworks'
|
|
10
|
+
import type { Framework } from '~/config/frameworks'
|
|
11
|
+
|
|
12
|
+
const fetchDocsConfig = createServerFn({ method: 'GET' })
|
|
13
|
+
.inputValidator((data: { project: string; version: string }) => data)
|
|
14
|
+
.handler(async ({ data }) => {
|
|
15
|
+
const { loadDocsConfig } = await import('~/utils/docs.server')
|
|
16
|
+
return loadDocsConfig(data.project, data.version)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export const Route = createFileRoute('/$lang/$project/$version/docs')({
|
|
20
|
+
staleTime: 1000 * 60 * 5,
|
|
21
|
+
loader: async ({ params }) => {
|
|
22
|
+
const config = await fetchDocsConfig({
|
|
23
|
+
data: { project: params.project, version: params.version },
|
|
24
|
+
})
|
|
25
|
+
return { config }
|
|
26
|
+
},
|
|
27
|
+
component: DocsLayoutRoute,
|
|
28
|
+
head: ({ params }) => {
|
|
29
|
+
const project = findProject(params.project)
|
|
30
|
+
return {
|
|
31
|
+
meta: [{ title: project ? `${project.name} Docs` : siteConfig.name }],
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
headers: () => ({
|
|
35
|
+
'cache-control': 'public, max-age=0, must-revalidate',
|
|
36
|
+
'cdn-cache-control': 'max-age=300, stale-while-revalidate=300, durable',
|
|
37
|
+
}),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
function DocsLayoutRoute() {
|
|
41
|
+
const { project: projectId, version } = Route.useParams()
|
|
42
|
+
const { config } = Route.useLoaderData()
|
|
43
|
+
const project = findProject(projectId)
|
|
44
|
+
|
|
45
|
+
if (!project) {
|
|
46
|
+
return <div>Project not found: {projectId}</div>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Map string framework IDs to Framework type
|
|
50
|
+
const frameworks: Framework[] = (project.frameworks ?? []).filter(
|
|
51
|
+
(f): f is Framework =>
|
|
52
|
+
frameworkOptions.some((opt) => opt.value === f),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<DocsLayout
|
|
57
|
+
name={project.name}
|
|
58
|
+
version={version}
|
|
59
|
+
colorFrom={project.colorFrom}
|
|
60
|
+
colorTo={project.colorTo}
|
|
61
|
+
textColor={project.textColor ?? ''}
|
|
62
|
+
config={config}
|
|
63
|
+
frameworks={frameworks}
|
|
64
|
+
versions={project.availableVersions}
|
|
65
|
+
repo={project.repo}
|
|
66
|
+
>
|
|
67
|
+
<Outlet />
|
|
68
|
+
</DocsLayout>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createFileRoute,
|
|
3
|
+
notFound,
|
|
4
|
+
redirect,
|
|
5
|
+
Outlet,
|
|
6
|
+
Link,
|
|
7
|
+
} from '@tanstack/react-router'
|
|
8
|
+
import { findProject, siteConfig, isSingleProject } from '~/site.config'
|
|
9
|
+
|
|
10
|
+
export const Route = createFileRoute('/$lang/$project/$version')({
|
|
11
|
+
staleTime: 1000 * 60 * 5,
|
|
12
|
+
beforeLoad: ({ params }) => {
|
|
13
|
+
const { lang, project: projectId, version } = params
|
|
14
|
+
const project = findProject(projectId)
|
|
15
|
+
|
|
16
|
+
if (!project) {
|
|
17
|
+
throw notFound()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Redirect "latest" to actual latest version (skip if latestVersion is literally 'latest')
|
|
21
|
+
if (version === 'latest' && project.latestVersion !== 'latest') {
|
|
22
|
+
throw redirect({
|
|
23
|
+
params: { ...params, version: project.latestVersion } as never,
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Validate version
|
|
28
|
+
if (version !== 'latest' && !project.availableVersions.includes(version)) {
|
|
29
|
+
throw redirect({
|
|
30
|
+
params: { ...params, version: project.latestVersion } as never,
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
component: () => <Outlet />,
|
|
35
|
+
staticData: {
|
|
36
|
+
Title: () => {
|
|
37
|
+
const { project: projectId, version, lang } = Route.useParams()
|
|
38
|
+
const project = findProject(projectId)
|
|
39
|
+
|
|
40
|
+
if (!project) {
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const projectName = project.name
|
|
45
|
+
const resolvedVersion =
|
|
46
|
+
version === 'latest' ? project.latestVersion : version!
|
|
47
|
+
const gradientText = `inline-block text-transparent bg-clip-text bg-linear-to-r ${project.colorFrom} ${project.colorTo}`
|
|
48
|
+
return (
|
|
49
|
+
<Link
|
|
50
|
+
to={`/$lang/$project`}
|
|
51
|
+
params={{ lang, project: projectId }}
|
|
52
|
+
className="relative whitespace-nowrap"
|
|
53
|
+
>
|
|
54
|
+
<span className={`${gradientText}`}>{projectName}</span>{' '}
|
|
55
|
+
{resolvedVersion && resolvedVersion !== 'latest' && (
|
|
56
|
+
<>
|
|
57
|
+
<span className="text-sm absolute right-0 top-0 font-normal normal-case">
|
|
58
|
+
{resolvedVersion}
|
|
59
|
+
</span>
|
|
60
|
+
<span className="text-sm opacity-0 normal-case">
|
|
61
|
+
{resolvedVersion}
|
|
62
|
+
</span>
|
|
63
|
+
</>
|
|
64
|
+
)}
|
|
65
|
+
</Link>
|
|
66
|
+
)
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
})
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createFileRoute,
|
|
3
|
+
notFound,
|
|
4
|
+
} from '@tanstack/react-router'
|
|
5
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
6
|
+
import { siteConfig, findProject } from '~/site.config'
|
|
7
|
+
import { Doc } from '~/components/Doc'
|
|
8
|
+
|
|
9
|
+
const fetchDoc = createServerFn({ method: 'GET' })
|
|
10
|
+
.inputValidator(
|
|
11
|
+
(data: { project: string; version: string; lang: string; slug: string }) =>
|
|
12
|
+
data,
|
|
13
|
+
)
|
|
14
|
+
.handler(async ({ data }) => {
|
|
15
|
+
const { loadDoc } = await import('~/utils/docs.server')
|
|
16
|
+
return loadDoc(data.project, data.version, data.lang, data.slug)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export const Route = createFileRoute('/$lang/$project/docs/$')({
|
|
20
|
+
staleTime: 1000 * 60 * 5,
|
|
21
|
+
beforeLoad: ({ params }) => {
|
|
22
|
+
const project = findProject(params.project)
|
|
23
|
+
|
|
24
|
+
// This route only works when hideLatestVersion is enabled
|
|
25
|
+
if (!project || !siteConfig.hideLatestVersion) {
|
|
26
|
+
throw notFound()
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
loader: async ({ params }) => {
|
|
30
|
+
const { lang, project: projectId, _splat: slug } = params
|
|
31
|
+
if (!slug) throw notFound()
|
|
32
|
+
|
|
33
|
+
const project = findProject(projectId)!
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
return await fetchDoc({
|
|
37
|
+
data: {
|
|
38
|
+
project: projectId,
|
|
39
|
+
version: project.latestVersion,
|
|
40
|
+
lang,
|
|
41
|
+
slug,
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
} catch (error) {
|
|
45
|
+
throw notFound()
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
head: ({ loaderData, params }) => {
|
|
49
|
+
const project = findProject(params.project)
|
|
50
|
+
const title = loaderData?.meta?.title
|
|
51
|
+
const projectName = project?.name || siteConfig.name
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
meta: [
|
|
55
|
+
{
|
|
56
|
+
title: title ? `${title} | ${projectName} Docs` : `${projectName} Docs`,
|
|
57
|
+
},
|
|
58
|
+
...(loaderData?.meta?.description
|
|
59
|
+
? [{ name: 'description', content: loaderData.meta.description }]
|
|
60
|
+
: []),
|
|
61
|
+
],
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
headers: () => {
|
|
65
|
+
return {
|
|
66
|
+
'cache-control': 'public, max-age=60, must-revalidate',
|
|
67
|
+
'cdn-cache-control':
|
|
68
|
+
'max-age=600, stale-while-revalidate=3600, durable',
|
|
69
|
+
vary: 'Accept-Encoding',
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
component: VersionlessDocsPage,
|
|
73
|
+
errorComponent: DocNotFound,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
function VersionlessDocsPage() {
|
|
77
|
+
const loaderData = Route.useLoaderData()
|
|
78
|
+
const { project: projectId } = Route.useParams()
|
|
79
|
+
const project = findProject(projectId)!
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<Doc
|
|
83
|
+
title={loaderData.meta?.title || ''}
|
|
84
|
+
content={loaderData.content}
|
|
85
|
+
isFallback={loaderData.isFallback}
|
|
86
|
+
filePath={loaderData.filePath}
|
|
87
|
+
repo={project.repo}
|
|
88
|
+
branch={project.latestBranch}
|
|
89
|
+
/>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function DocNotFound() {
|
|
94
|
+
return (
|
|
95
|
+
<div className="flex-1 flex items-center justify-center py-20">
|
|
96
|
+
<div className="text-center">
|
|
97
|
+
<h1 className="text-2xl font-bold mb-2">Page Not Found</h1>
|
|
98
|
+
<p className="text-gray-500 dark:text-gray-400">
|
|
99
|
+
The document you are looking for does not exist.
|
|
100
|
+
</p>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createFileRoute, notFound, redirect } from '@tanstack/react-router'
|
|
2
|
+
import { siteConfig, findProject } from '~/site.config'
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute('/$lang/$project/docs/')({
|
|
5
|
+
beforeLoad: ({ params }) => {
|
|
6
|
+
const project = findProject(params.project)
|
|
7
|
+
|
|
8
|
+
if (!project || !siteConfig.hideLatestVersion) {
|
|
9
|
+
throw notFound()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
throw redirect({
|
|
13
|
+
from: '/$lang/$project/docs',
|
|
14
|
+
to: './$',
|
|
15
|
+
params: {
|
|
16
|
+
_splat: project.defaultDocs || 'overview',
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
},
|
|
20
|
+
})
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createFileRoute,
|
|
3
|
+
notFound,
|
|
4
|
+
Outlet,
|
|
5
|
+
} from '@tanstack/react-router'
|
|
6
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
7
|
+
import { siteConfig, findProject } from '~/site.config'
|
|
8
|
+
import { DocsLayout } from '~/components/DocsLayout'
|
|
9
|
+
import { frameworkOptions } from '~/config/frameworks'
|
|
10
|
+
import type { Framework } from '~/config/frameworks'
|
|
11
|
+
|
|
12
|
+
const fetchDocsConfig = createServerFn({ method: 'GET' })
|
|
13
|
+
.inputValidator((data: { project: string; version: string }) => data)
|
|
14
|
+
.handler(async ({ data }) => {
|
|
15
|
+
const { loadDocsConfig } = await import('~/utils/docs.server')
|
|
16
|
+
return loadDocsConfig(data.project, data.version)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export const Route = createFileRoute('/$lang/$project/docs')({
|
|
20
|
+
staleTime: 1000 * 60 * 5,
|
|
21
|
+
beforeLoad: ({ params }) => {
|
|
22
|
+
const project = findProject(params.project)
|
|
23
|
+
|
|
24
|
+
// This route only works when hideLatestVersion is enabled
|
|
25
|
+
if (!project || !siteConfig.hideLatestVersion) {
|
|
26
|
+
throw notFound()
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
loader: async ({ params }) => {
|
|
30
|
+
const project = findProject(params.project)!
|
|
31
|
+
const config = await fetchDocsConfig({
|
|
32
|
+
data: { project: project.id, version: project.latestVersion },
|
|
33
|
+
})
|
|
34
|
+
return { config }
|
|
35
|
+
},
|
|
36
|
+
component: VersionlessDocsLayout,
|
|
37
|
+
head: ({ params }) => {
|
|
38
|
+
const project = findProject(params.project)
|
|
39
|
+
return {
|
|
40
|
+
meta: [{ title: project ? `${project.name} Docs` : siteConfig.name }],
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
headers: () => ({
|
|
44
|
+
'cache-control': 'public, max-age=0, must-revalidate',
|
|
45
|
+
'cdn-cache-control': 'max-age=300, stale-while-revalidate=300, durable',
|
|
46
|
+
}),
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
function VersionlessDocsLayout() {
|
|
50
|
+
const { project: projectId } = Route.useParams()
|
|
51
|
+
const { config } = Route.useLoaderData()
|
|
52
|
+
const project = findProject(projectId)
|
|
53
|
+
|
|
54
|
+
if (!project) {
|
|
55
|
+
return <div>Project not found: {projectId}</div>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const frameworks: Framework[] = (project.frameworks ?? []).filter(
|
|
59
|
+
(f): f is Framework =>
|
|
60
|
+
frameworkOptions.some((opt) => opt.value === f),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<DocsLayout
|
|
65
|
+
name={project.name}
|
|
66
|
+
version={project.latestVersion}
|
|
67
|
+
colorFrom={project.colorFrom}
|
|
68
|
+
colorTo={project.colorTo}
|
|
69
|
+
textColor={project.textColor ?? ''}
|
|
70
|
+
config={config}
|
|
71
|
+
frameworks={frameworks}
|
|
72
|
+
versions={project.availableVersions}
|
|
73
|
+
repo={project.repo}
|
|
74
|
+
docsBasePath="/$lang/$project/docs"
|
|
75
|
+
>
|
|
76
|
+
<Outlet />
|
|
77
|
+
</DocsLayout>
|
|
78
|
+
)
|
|
79
|
+
}
|