@terrafirmagreg-team/kit 0.1.0 → 1.2.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/package.json +15 -27
- package/src/components/blocks/nav-title.tsx +22 -25
- package/src/components/outline/dropdown.tsx +3 -2
- package/src/components/outline/index.ts +2 -1
- package/src/components/outline/list.tsx +3 -2
- package/src/components/outline/outline.tsx +4 -3
- package/src/components/outline/popover.tsx +3 -2
- package/src/components/page-actions.tsx +10 -9
- package/src/components/relative-link.tsx +1 -0
- package/src/components/toc/base.tsx +2 -1
- package/src/components/toc/block.tsx +1 -0
- package/src/components/toc/clerk.tsx +2 -1
- package/src/components/toc/default.tsx +3 -2
- package/src/components/version-menu.tsx +19 -17
- package/src/components/views/dataset.tsx +11 -2
- package/src/components/views/section.tsx +88 -44
- package/src/components/workspace/content.tsx +1 -0
- package/src/components/workspace/index.ts +1 -0
- package/src/components/workspace/pagination.tsx +2 -1
- package/src/components/workspace/workspace.tsx +1 -0
- package/src/hooks/use-catalog.ts +1 -0
- package/src/hooks/use-current-dataset.ts +15 -5
- package/src/hooks/use-dataset-accent.ts +1 -0
- package/src/hooks/use-nav-options.ts +20 -17
- package/src/hooks/use-section-names.ts +40 -0
- package/src/hooks/{toc.ts → use-toc.ts} +1 -0
- package/src/hooks/use-version-pref.ts +1 -0
- package/src/hooks/use-versioned-dataset.ts +21 -0
- package/src/lib/breadcrumb.ts +3 -16
- package/src/lib/config.ts +30 -28
- package/src/lib/dataset-links.ts +1 -0
- package/src/lib/datasets.ts +98 -68
- package/src/lib/landing.ts +4 -2
- package/src/lib/layout.ts +1 -0
- package/src/lib/locale/renderer.ts +49 -0
- package/src/lib/order.ts +1 -0
- package/src/lib/page.ts +3 -2
- package/src/lib/pin-tree.ts +11 -8
- package/src/lib/preview/lookup.ts +8 -5
- package/src/lib/section/schema.ts +2 -6
- package/src/lib/section/source.ts +59 -22
- package/src/lib/sources.ts +1 -0
- package/src/lib/tree.ts +1 -0
- package/src/lib/urls.ts +1 -0
- package/src/locales/en_us.json +7 -10
- package/src/locales/ru_ru.json +7 -10
- package/src/locales.ts +1 -0
- package/src/types/embeds.ts +54 -0
- package/src/types/index.ts +13 -9
- package/src/{components/outline/types.ts → types/outline.ts} +1 -0
- package/src/components/docs-image.tsx +0 -49
- package/src/components/search/dialog.tsx +0 -145
- package/src/lib/legacy.ts +0 -18
- package/src/lib/location.ts +0 -63
- package/src/lib/markdown/dom.ts +0 -90
- package/src/lib/markdown/index.ts +0 -13
- package/src/lib/markdown/providers.ts +0 -25
- package/src/lib/mdx/accordions.ts +0 -93
- package/src/lib/mdx/banner.ts +0 -52
- package/src/lib/mdx/directive-utils.ts +0 -125
- package/src/lib/mdx/directives.ts +0 -141
- package/src/lib/mdx/game-export.ts +0 -44
- package/src/lib/mdx/html-style.ts +0 -135
- package/src/lib/mdx/preview.ts +0 -252
- package/src/lib/mdx/references.ts +0 -56
- package/src/lib/mdx/tabs.ts +0 -80
- package/src/lib/mdx-components.tsx +0 -139
- package/src/lib/pack-assets.ts +0 -45
- package/src/lib/preview/index.ts +0 -8
- package/src/lib/preview/og.tsx +0 -115
- package/src/lib/preview/resolvers.ts +0 -98
- package/src/lib/search/config.ts +0 -13
- package/src/lib/search/filters.ts +0 -22
- package/src/lib/search/index.ts +0 -19
- package/src/lib/search/indexes.ts +0 -43
- package/src/lib/search/merge.ts +0 -22
- package/src/lib/search/server.ts +0 -18
- package/src/lib/search/sources.ts +0 -19
- package/src/lib/search/structured.ts +0 -23
- package/src/types/mdx.ts +0 -55
- package/src/types/turndown-plugin-gfm.d.ts +0 -9
package/src/lib/datasets.ts
CHANGED
|
@@ -3,8 +3,10 @@ import { dynamicIconImports } from 'lucide-react/dynamic';
|
|
|
3
3
|
import { sectionConfig } from './config';
|
|
4
4
|
import { sections } from './sources';
|
|
5
5
|
import { datasetId } from './tree';
|
|
6
|
+
import { createAsyncCache } from '@terrafirmagreg-team/data/lib/async-cache';
|
|
6
7
|
import { loadLocaleRegistry, sortLangCodes } from '@terrafirmagreg-team/translate/server';
|
|
7
|
-
import {
|
|
8
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
9
|
+
import { sectionTabs } from '@terrafirmagreg-team/sites/sections';
|
|
8
10
|
import type {
|
|
9
11
|
DatasetCatalog,
|
|
10
12
|
SectionDataset,
|
|
@@ -14,42 +16,53 @@ import type { SectionDump } from './config';
|
|
|
14
16
|
import type { IconName } from 'lucide-react/dynamic';
|
|
15
17
|
import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
|
|
16
18
|
|
|
17
|
-
const sectionMeta = new Map<
|
|
19
|
+
const sectionMeta = new Map<
|
|
20
|
+
SectionKey,
|
|
21
|
+
Promise<Map<string, Record<string, unknown>>>
|
|
22
|
+
>();
|
|
18
23
|
|
|
19
24
|
function staticSectionMeta(
|
|
20
25
|
section: SectionKey,
|
|
21
|
-
): Map<string, Record<string, unknown
|
|
26
|
+
): Promise<Map<string, Record<string, unknown>>> {
|
|
22
27
|
let meta = sectionMeta.get(section);
|
|
23
28
|
if (meta) return meta;
|
|
24
|
-
meta =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
meta = (async () => {
|
|
30
|
+
const map = new Map<string, Record<string, unknown>>();
|
|
31
|
+
const source = await sections[section]?.getSource();
|
|
32
|
+
if (source) {
|
|
33
|
+
for (const node of source.getPageTree().children) {
|
|
34
|
+
if (node.type !== 'folder') continue;
|
|
35
|
+
const id = datasetId(node);
|
|
36
|
+
const data = id
|
|
37
|
+
? (source.getNodeMeta?.(node) as { data?: Record<string, unknown> })
|
|
38
|
+
?.data
|
|
39
|
+
: undefined;
|
|
40
|
+
if (id && data) map.set(id, data);
|
|
41
|
+
}
|
|
35
42
|
}
|
|
36
|
-
|
|
43
|
+
return map;
|
|
44
|
+
})();
|
|
37
45
|
sectionMeta.set(section, meta);
|
|
46
|
+
meta.catch(() => {
|
|
47
|
+
if (sectionMeta.get(section) === meta) sectionMeta.delete(section);
|
|
48
|
+
});
|
|
38
49
|
return meta;
|
|
39
50
|
}
|
|
40
51
|
|
|
41
|
-
function contentSectionDatasets(
|
|
42
|
-
|
|
52
|
+
async function contentSectionDatasets(
|
|
53
|
+
section: SectionKey,
|
|
54
|
+
): Promise<SectionDataset[]> {
|
|
55
|
+
const source = await sections[section]?.getSource();
|
|
43
56
|
if (!source) return [];
|
|
44
57
|
const languages = new Map<string, Set<string>>();
|
|
45
58
|
for (const page of source.getPages()) {
|
|
46
|
-
const [dataset, lang] = page.
|
|
59
|
+
const [dataset, lang] = page.path.split('/');
|
|
47
60
|
if (!dataset || !lang) continue;
|
|
48
61
|
let set = languages.get(dataset);
|
|
49
62
|
if (!set) languages.set(dataset, (set = new Set()));
|
|
50
63
|
set.add(lang);
|
|
51
64
|
}
|
|
52
|
-
const meta = staticSectionMeta(section);
|
|
65
|
+
const meta = await staticSectionMeta(section);
|
|
53
66
|
return [...languages.keys()].map((id) => ({
|
|
54
67
|
id,
|
|
55
68
|
languages: datasetLanguages([...(languages.get(id) ?? [])]),
|
|
@@ -58,11 +71,11 @@ function contentSectionDatasets(section: SectionKey): SectionDataset[] {
|
|
|
58
71
|
}));
|
|
59
72
|
}
|
|
60
73
|
|
|
61
|
-
function datasetMeta(
|
|
74
|
+
async function datasetMeta(
|
|
62
75
|
section: SectionKey,
|
|
63
76
|
id: string,
|
|
64
|
-
): Omit<SectionDataset, 'id' | 'languages'
|
|
65
|
-
const meta = staticSectionMeta(section).get(id);
|
|
77
|
+
): Promise<Omit<SectionDataset, 'id' | 'languages'>> {
|
|
78
|
+
const meta = (await staticSectionMeta(section)).get(id);
|
|
66
79
|
return meta ? metaFields(meta) : {};
|
|
67
80
|
}
|
|
68
81
|
|
|
@@ -89,7 +102,23 @@ function datasetLanguages(languages: readonly string[]): Lang[] {
|
|
|
89
102
|
return [loadLocaleRegistry().defaultLanguage as Lang];
|
|
90
103
|
}
|
|
91
104
|
|
|
92
|
-
const
|
|
105
|
+
const DUMP_DATASETS_TTL = 60_000;
|
|
106
|
+
|
|
107
|
+
const dumpSectionDatasets = createAsyncCache(
|
|
108
|
+
async (section: SectionKey, hasContent: (dump: SectionDump) => boolean) =>
|
|
109
|
+
dumpDatasets(section, hasContent),
|
|
110
|
+
(section) => section,
|
|
111
|
+
DUMP_DATASETS_TTL,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const contentSectionDatasetsCached = createAsyncCache(
|
|
115
|
+
async (section: SectionKey): Promise<SectionDataset[]> => {
|
|
116
|
+
const rows = await contentSectionDatasets(section);
|
|
117
|
+
rows.sort(datasetOrder);
|
|
118
|
+
return rows;
|
|
119
|
+
},
|
|
120
|
+
(section) => section,
|
|
121
|
+
);
|
|
93
122
|
|
|
94
123
|
function datasetOrder(a: SectionDataset, b: SectionDataset): number {
|
|
95
124
|
const cfg = sectionConfig().datasetConfig;
|
|
@@ -110,6 +139,7 @@ function compareVersions(a: string, b: string): number {
|
|
|
110
139
|
}
|
|
111
140
|
|
|
112
141
|
async function dumpDatasets(
|
|
142
|
+
section: SectionKey,
|
|
113
143
|
hasContent: (dump: SectionDump) => boolean,
|
|
114
144
|
): Promise<SectionDataset[]> {
|
|
115
145
|
const dump = sectionConfig().dump;
|
|
@@ -117,17 +147,18 @@ async function dumpDatasets(
|
|
|
117
147
|
const refs = await dump.discoverDatasets();
|
|
118
148
|
const versions = new Map<string, string[]>();
|
|
119
149
|
for (const ref of refs) {
|
|
120
|
-
const { id, version } =
|
|
150
|
+
const { id, version } = ref;
|
|
121
151
|
if (version) {
|
|
122
152
|
const list = versions.get(id) ?? [];
|
|
123
153
|
list.push(version);
|
|
124
154
|
versions.set(id, list);
|
|
125
155
|
}
|
|
126
156
|
}
|
|
127
|
-
const ids = refs.filter((ref) => !
|
|
157
|
+
const ids = refs.filter((ref) => !ref.version);
|
|
128
158
|
const usable = await Promise.all(
|
|
129
|
-
ids.map(async (
|
|
130
|
-
const
|
|
159
|
+
ids.map(async (ref) => {
|
|
160
|
+
const { id } = ref;
|
|
161
|
+
const index = await dump.loadDataset(ref);
|
|
131
162
|
if (!index || !hasContent(index)) return null;
|
|
132
163
|
return {
|
|
133
164
|
id,
|
|
@@ -135,37 +166,34 @@ async function dumpDatasets(
|
|
|
135
166
|
};
|
|
136
167
|
}),
|
|
137
168
|
);
|
|
138
|
-
return
|
|
139
|
-
.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
169
|
+
return (
|
|
170
|
+
await Promise.all(
|
|
171
|
+
usable
|
|
172
|
+
.filter((row): row is { id: string; languages: Lang[] } => row !== null)
|
|
173
|
+
.map(async ({ id, languages }) => ({
|
|
174
|
+
id,
|
|
175
|
+
languages,
|
|
176
|
+
...(versions.get(id)?.length
|
|
177
|
+
? { versions: versions.get(id)!.sort(compareVersions).reverse() }
|
|
178
|
+
: {}),
|
|
179
|
+
...(await datasetMeta(section, id).then((meta) =>
|
|
180
|
+
Object.values(meta).some((value) => value !== undefined)
|
|
181
|
+
? meta
|
|
182
|
+
: datasetMeta('docs', id),
|
|
183
|
+
)),
|
|
184
|
+
...datasetAppearance(id),
|
|
185
|
+
})),
|
|
186
|
+
)
|
|
187
|
+
).sort(datasetOrder);
|
|
150
188
|
}
|
|
151
189
|
|
|
152
190
|
export function sectionDatasets(
|
|
153
191
|
section: SectionKey,
|
|
154
192
|
): Promise<SectionDataset[]> {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
cached = dumpDatasets(hasContent);
|
|
160
|
-
bySection.set(section, cached);
|
|
161
|
-
return cached;
|
|
162
|
-
}
|
|
163
|
-
const rows = contentSectionDatasets(section);
|
|
164
|
-
rows.sort(datasetOrder);
|
|
165
|
-
cached = Promise.resolve(rows);
|
|
166
|
-
bySection.set(section, cached);
|
|
167
|
-
}
|
|
168
|
-
return cached;
|
|
193
|
+
const hasContent = sectionConfig().dump?.hasContent[section];
|
|
194
|
+
return hasContent
|
|
195
|
+
? dumpSectionDatasets(section, hasContent)
|
|
196
|
+
: contentSectionDatasetsCached(section);
|
|
169
197
|
}
|
|
170
198
|
|
|
171
199
|
export async function defaultDataset(
|
|
@@ -176,36 +204,38 @@ export async function defaultDataset(
|
|
|
176
204
|
|
|
177
205
|
export function findDataset(
|
|
178
206
|
datasets: readonly SectionDataset[],
|
|
179
|
-
ref:
|
|
207
|
+
ref: DatasetRef,
|
|
180
208
|
): SectionDataset | undefined {
|
|
181
|
-
const { id, version } =
|
|
209
|
+
const { id, version } = ref;
|
|
182
210
|
const meta = datasets.find((row) => row.id === id);
|
|
183
211
|
if (!meta) return undefined;
|
|
184
212
|
if (version && !meta.versions?.includes(version)) return undefined;
|
|
185
213
|
return meta;
|
|
186
214
|
}
|
|
187
215
|
|
|
188
|
-
export async function
|
|
216
|
+
export async function resolveDatasetMeta(
|
|
189
217
|
section: SectionKey,
|
|
190
|
-
ref:
|
|
218
|
+
ref: DatasetRef,
|
|
191
219
|
): Promise<SectionDataset | undefined> {
|
|
192
220
|
return findDataset(await sectionDatasets(section), ref);
|
|
193
221
|
}
|
|
194
222
|
|
|
195
223
|
export async function datasetSlugs(section: SectionKey): Promise<string[]> {
|
|
196
|
-
return (await sectionDatasets(section)).
|
|
197
|
-
id,
|
|
198
|
-
...(versions ?? []).map((version) => `${id}@${version}`),
|
|
199
|
-
]);
|
|
224
|
+
return (await sectionDatasets(section)).map(({ id }) => id);
|
|
200
225
|
}
|
|
201
226
|
|
|
202
|
-
|
|
227
|
+
const datasetCatalogCached = createAsyncCache(
|
|
228
|
+
async (): Promise<DatasetCatalog> =>
|
|
229
|
+
Promise.all(
|
|
230
|
+
sectionTabs.map(
|
|
231
|
+
async ({ key }) => [key, await sectionDatasets(key)] as const,
|
|
232
|
+
),
|
|
233
|
+
).then((rows) => Object.fromEntries(rows) as DatasetCatalog),
|
|
234
|
+
() => 'catalog',
|
|
235
|
+
DUMP_DATASETS_TTL,
|
|
236
|
+
);
|
|
203
237
|
|
|
204
238
|
export function datasetCatalog(): Promise<DatasetCatalog> {
|
|
205
|
-
|
|
206
|
-
sectionConfig().sectionTabs.map(
|
|
207
|
-
async ({ key }) => [key, await sectionDatasets(key)] as const,
|
|
208
|
-
),
|
|
209
|
-
).then((rows) => Object.fromEntries(rows) as DatasetCatalog);
|
|
210
|
-
return catalogCached;
|
|
239
|
+
return datasetCatalogCached();
|
|
211
240
|
}
|
|
241
|
+
|
package/src/lib/landing.ts
CHANGED
|
@@ -16,16 +16,17 @@ export function getDatasetLanding(
|
|
|
16
16
|
): DatasetLanding | undefined {
|
|
17
17
|
if (!slug || slug.length !== 1) return undefined;
|
|
18
18
|
const [ref] = slug;
|
|
19
|
-
const meta = findDataset(datasets, ref);
|
|
19
|
+
const meta = findDataset(datasets, { id: ref });
|
|
20
20
|
if (!meta) return undefined;
|
|
21
21
|
const { messages, defaultLanguage } = loadLocaleRegistry();
|
|
22
22
|
const table = messages[lang] ?? messages[defaultLanguage] ?? {};
|
|
23
23
|
return {
|
|
24
24
|
title: table[`sections.${section}`] ?? section,
|
|
25
25
|
description: table[`sections.desc.${section}`],
|
|
26
|
-
dataset: ref,
|
|
26
|
+
dataset: { id: ref },
|
|
27
27
|
datasetTitle: meta.title ?? meta.id,
|
|
28
28
|
lang,
|
|
29
|
+
...(meta.versions ? { versions: meta.versions } : {}),
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
|
|
@@ -44,3 +45,4 @@ export async function generateDatasetLandingParams(
|
|
|
44
45
|
): Promise<{ slug: string[] }[]> {
|
|
45
46
|
return (await datasetSlugs(section)).map((ref) => ({ slug: [ref] }));
|
|
46
47
|
}
|
|
48
|
+
|
package/src/lib/layout.ts
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
|
|
3
|
+
|
|
4
|
+
export type LocaleScope = { section: string; slug?: string[] };
|
|
5
|
+
|
|
6
|
+
export type LocaleResolve = (
|
|
7
|
+
slug: string[] | undefined,
|
|
8
|
+
lang: Lang,
|
|
9
|
+
) => Promise<{ page: unknown; isFallback: boolean } | undefined>;
|
|
10
|
+
|
|
11
|
+
export type LocaleRender = (
|
|
12
|
+
page: unknown,
|
|
13
|
+
slug: string[] | undefined,
|
|
14
|
+
lang: Lang,
|
|
15
|
+
) => ReactNode | Promise<ReactNode>;
|
|
16
|
+
|
|
17
|
+
export type LocaleLoadResult = { node: ReactNode; missing: boolean } | null;
|
|
18
|
+
|
|
19
|
+
export type LocaleLoader = (
|
|
20
|
+
scope: LocaleScope,
|
|
21
|
+
lang: Lang,
|
|
22
|
+
) => Promise<LocaleLoadResult>;
|
|
23
|
+
|
|
24
|
+
const renderers = new Map<
|
|
25
|
+
string,
|
|
26
|
+
{ resolve: LocaleResolve; render: LocaleRender }
|
|
27
|
+
>();
|
|
28
|
+
|
|
29
|
+
export function registerLocaleRenderer(
|
|
30
|
+
section: string,
|
|
31
|
+
entry: { resolve: LocaleResolve; render: LocaleRender },
|
|
32
|
+
): void {
|
|
33
|
+
renderers.set(section, entry);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function localeRenderer(section: string) {
|
|
37
|
+
return renderers.get(section);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let loader: LocaleLoader | undefined;
|
|
41
|
+
|
|
42
|
+
export function registerLocaleLoader(next: LocaleLoader): void {
|
|
43
|
+
loader = next;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function localeLoader(): LocaleLoader | undefined {
|
|
47
|
+
return loader;
|
|
48
|
+
}
|
|
49
|
+
|
package/src/lib/order.ts
CHANGED
package/src/lib/page.ts
CHANGED
|
@@ -52,7 +52,7 @@ export async function generateDatasetParams(
|
|
|
52
52
|
section: SectionKey,
|
|
53
53
|
source: SectionSource,
|
|
54
54
|
): Promise<{ slug: string[] }[]> {
|
|
55
|
-
const params =
|
|
55
|
+
const params = generatePageParams(source);
|
|
56
56
|
const seen = new Set(params.map((row) => row.slug.join('/')));
|
|
57
57
|
for (const { id } of await sectionDatasets(section)) {
|
|
58
58
|
if (!seen.has(id)) {
|
|
@@ -63,7 +63,7 @@ export async function generateDatasetParams(
|
|
|
63
63
|
return params.length ? params : [{ slug: ['_'] }];
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
function
|
|
66
|
+
export function generatePageParams(source: SectionSource): { slug: string[] }[] {
|
|
67
67
|
const languages = [...sectionConfig().knownLanguages];
|
|
68
68
|
const seen = new Set<string>();
|
|
69
69
|
const params: { slug: string[] }[] = [];
|
|
@@ -106,3 +106,4 @@ export async function sectionMetadata(
|
|
|
106
106
|
},
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
|
+
|
package/src/lib/pin-tree.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type * as PageTree from 'fumadocs-core/page-tree';
|
|
2
2
|
|
|
3
|
-
function pinUrl(url: string, from: string,
|
|
3
|
+
function pinUrl(url: string, from: string, version: string): string {
|
|
4
4
|
const next = url[from.length];
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
if (!url.startsWith(from) ||
|
|
6
|
+
(next !== undefined && next !== '/' && next !== '?' && next !== '#')) return url;
|
|
7
|
+
const [pathQuery, hash] = url.split('#');
|
|
8
|
+
const [path, query] = pathQuery.split('?');
|
|
9
|
+
const params = new URLSearchParams(query);
|
|
10
|
+
params.set('version', version);
|
|
11
|
+
return `${path}?${params}${hash === undefined ? '' : `#${hash}`}`;
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
export function pinTreeDataset(
|
|
@@ -15,16 +18,15 @@ export function pinTreeDataset(
|
|
|
15
18
|
version: string,
|
|
16
19
|
): PageTree.Root {
|
|
17
20
|
const from = `/${section}/${datasetId}`;
|
|
18
|
-
const to = `${from}@${version}`;
|
|
19
21
|
const pinNode = (node: PageTree.Node): PageTree.Node => {
|
|
20
22
|
if (node.type === 'page') {
|
|
21
|
-
return { ...node, url: pinUrl(node.url, from,
|
|
23
|
+
return { ...node, url: pinUrl(node.url, from, version) };
|
|
22
24
|
}
|
|
23
25
|
if (node.type === 'folder') {
|
|
24
26
|
return {
|
|
25
27
|
...node,
|
|
26
28
|
index: node.index
|
|
27
|
-
? { ...node.index, url: pinUrl(node.index.url, from,
|
|
29
|
+
? { ...node.index, url: pinUrl(node.index.url, from, version) }
|
|
28
30
|
: node.index,
|
|
29
31
|
children: node.children.map(pinNode),
|
|
30
32
|
};
|
|
@@ -33,3 +35,4 @@ export function pinTreeDataset(
|
|
|
33
35
|
};
|
|
34
36
|
return { ...tree, children: tree.children.map(pinNode) };
|
|
35
37
|
}
|
|
38
|
+
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import 'server-only';
|
|
2
2
|
import { resolveLang } from '@terrafirmagreg-team/translate/lib/code';
|
|
3
3
|
import { loadLocaleRegistry } from '@terrafirmagreg-team/translate/server';
|
|
4
|
-
import { sectionConfig } from '
|
|
5
|
-
import { isSectionKey } from '
|
|
6
|
-
import { resolvePage } from '
|
|
7
|
-
import { sections } from '
|
|
4
|
+
import { sectionConfig } from '@terrafirmagreg-team/kit/lib/config';
|
|
5
|
+
import { isSectionKey } from '@terrafirmagreg-team/sites/location';
|
|
6
|
+
import { resolvePage } from '@terrafirmagreg-team/kit/lib/page';
|
|
7
|
+
import { sections } from '@terrafirmagreg-team/kit/lib/sources';
|
|
8
8
|
import type {
|
|
9
9
|
DatasetCatalog,
|
|
10
10
|
SectionKey,
|
|
@@ -37,7 +37,9 @@ export function lookupPagePreview(
|
|
|
37
37
|
dataset?.languages ?? [...sectionConfig().knownLanguages],
|
|
38
38
|
defaultLanguage,
|
|
39
39
|
);
|
|
40
|
-
const
|
|
40
|
+
const source = section.getSourceSync?.();
|
|
41
|
+
if (!source) return undefined;
|
|
42
|
+
const resolved = resolvePage(source, parts.slice(1), pageLang);
|
|
41
43
|
const page = resolved?.page as SectionPage | undefined;
|
|
42
44
|
if (!page) return undefined;
|
|
43
45
|
return {
|
|
@@ -46,3 +48,4 @@ export function lookupPagePreview(
|
|
|
46
48
|
icon: dataset?.icon ? { type: 'icon', name: dataset.icon } : undefined,
|
|
47
49
|
};
|
|
48
50
|
}
|
|
51
|
+
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { metaSchema, pageSchema } from 'fumadocs-core/source/schema';
|
|
2
2
|
import { z, type ZodRawShape } from 'zod';
|
|
3
3
|
|
|
4
|
-
export function sectionDocsConfig(
|
|
5
|
-
dir: string,
|
|
6
|
-
schemaFields: ZodRawShape = {},
|
|
7
|
-
): {
|
|
8
|
-
dir: string;
|
|
4
|
+
export function sectionDocsConfig(schemaFields: ZodRawShape = {}): {
|
|
9
5
|
docs: {
|
|
10
6
|
schema: ReturnType<typeof pageSchema.extend>;
|
|
11
7
|
postprocess: { includeProcessedMarkdown: true };
|
|
@@ -13,7 +9,6 @@ export function sectionDocsConfig(
|
|
|
13
9
|
meta: { schema: typeof metaSchema };
|
|
14
10
|
} {
|
|
15
11
|
return {
|
|
16
|
-
dir,
|
|
17
12
|
docs: {
|
|
18
13
|
schema: pageSchema.extend({
|
|
19
14
|
order: z.number().optional(),
|
|
@@ -34,3 +29,4 @@ export function sectionDocsConfig(
|
|
|
34
29
|
},
|
|
35
30
|
};
|
|
36
31
|
}
|
|
32
|
+
|
|
@@ -1,40 +1,77 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
dynamicLoader,
|
|
3
|
+
llms,
|
|
4
|
+
type DynamicSource,
|
|
5
|
+
type LoaderConfig,
|
|
6
|
+
type LoaderOutput,
|
|
7
|
+
} from 'fumadocs-core/source';
|
|
2
8
|
import { sectionDatasets } from '@terrafirmagreg-team/kit/lib/datasets';
|
|
9
|
+
import { sectionConfig } from '@terrafirmagreg-team/kit/lib/config';
|
|
3
10
|
import { orderPlugin } from '@terrafirmagreg-team/kit/lib/order';
|
|
4
|
-
import { renderSectionMarkdown } from '@terrafirmagreg-team/
|
|
11
|
+
import { renderSectionMarkdown } from '@terrafirmagreg-team/mdx/lib/markdown';
|
|
5
12
|
import { localizedDatasetTrees } from '@terrafirmagreg-team/kit/lib/tree';
|
|
6
13
|
import type { SectionTrees } from '@terrafirmagreg-team/kit/types';
|
|
14
|
+
import type { SectionKey } from '@terrafirmagreg-team/sites/location';
|
|
7
15
|
|
|
8
|
-
|
|
16
|
+
const LOCALE_SEGMENT = /^[a-z]{2,3}_[a-z]{2,4}$/;
|
|
9
17
|
|
|
10
|
-
export function createSectionSource
|
|
18
|
+
export function createSectionSource(options: {
|
|
11
19
|
route: string;
|
|
12
20
|
|
|
13
|
-
section:
|
|
14
|
-
source:
|
|
21
|
+
section: SectionKey;
|
|
22
|
+
source: DynamicSource;
|
|
15
23
|
|
|
16
24
|
postTrees?: (trees: SectionTrees) => SectionTrees;
|
|
17
25
|
}) {
|
|
18
|
-
const { route, section, source:
|
|
19
|
-
const source =
|
|
26
|
+
const { route, section, source: dynamicSource, postTrees } = options;
|
|
27
|
+
const source: DynamicSource = {
|
|
28
|
+
...dynamicSource,
|
|
29
|
+
files: async () => {
|
|
30
|
+
const known = new Set(sectionConfig().knownLanguages);
|
|
31
|
+
return (await dynamicSource.files()).filter(
|
|
32
|
+
(file) =>
|
|
33
|
+
!file.path
|
|
34
|
+
.split('/')
|
|
35
|
+
.some((seg) => LOCALE_SEGMENT.test(seg) && !known.has(seg)),
|
|
36
|
+
);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
const loader = dynamicLoader(source, {
|
|
20
40
|
baseUrl: route,
|
|
21
|
-
source: fumadocsSource,
|
|
22
41
|
plugins: [orderPlugin()],
|
|
23
42
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
43
|
+
let resolved: LoaderOutput<LoaderConfig> | undefined;
|
|
44
|
+
const getSource = async () => {
|
|
45
|
+
resolved = (await loader.get()) as unknown as LoaderOutput<LoaderConfig>;
|
|
46
|
+
return resolved;
|
|
47
|
+
};
|
|
48
|
+
const getSourceSync = () => resolved;
|
|
49
|
+
const markdown = {
|
|
50
|
+
page: async (page: unknown) =>
|
|
51
|
+
llms((await getSource()) as never, {
|
|
52
|
+
renderPage: renderSectionMarkdown as (
|
|
53
|
+
page: unknown,
|
|
54
|
+
) => Promise<string>,
|
|
55
|
+
}).page(page as never),
|
|
56
|
+
};
|
|
29
57
|
|
|
30
|
-
let cached:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
58
|
+
let cached:
|
|
59
|
+
| { source: LoaderOutput<LoaderConfig>; trees: Promise<SectionTrees> }
|
|
60
|
+
| undefined;
|
|
61
|
+
const trees = (): Promise<SectionTrees> =>
|
|
62
|
+
getSource().then((source) => {
|
|
63
|
+
if (cached?.source === source) return cached.trees;
|
|
64
|
+
const built = (async () => {
|
|
65
|
+
const result = localizedDatasetTrees(
|
|
66
|
+
source,
|
|
67
|
+
await sectionDatasets(section),
|
|
68
|
+
);
|
|
69
|
+
return postTrees ? postTrees(result) : result;
|
|
70
|
+
})();
|
|
71
|
+
cached = { source, trees: built };
|
|
72
|
+
return built;
|
|
35
73
|
});
|
|
36
|
-
return cached;
|
|
37
|
-
};
|
|
38
74
|
|
|
39
|
-
return { route,
|
|
75
|
+
return { route, getSource, getSourceSync, markdown, trees };
|
|
40
76
|
}
|
|
77
|
+
|
package/src/lib/sources.ts
CHANGED
package/src/lib/tree.ts
CHANGED
package/src/lib/urls.ts
CHANGED
package/src/locales/en_us.json
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
{
|
|
2
|
-
"ui":
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"version_latest": "latest"
|
|
9
|
-
}
|
|
10
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"Failed to copy(ui.copy_error)": "Failed to copy",
|
|
3
|
+
"Select dataset(ui.select_dataset)": "Select dataset",
|
|
4
|
+
"Select section(ui.select_section)": "Select section",
|
|
5
|
+
"View as Markdown(ui.view_markdown)": "View as Markdown",
|
|
6
|
+
"latest(ui.version_latest)": "latest"
|
|
7
|
+
}
|
package/src/locales/ru_ru.json
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
{
|
|
2
|
-
"ui":
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"version_latest": "последняя"
|
|
9
|
-
}
|
|
10
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"Failed to copy(ui.copy_error)": "Не удалось скопировать",
|
|
3
|
+
"Select dataset(ui.select_dataset)": "Выберите набор данных",
|
|
4
|
+
"Select section(ui.select_section)": "Выберите раздел",
|
|
5
|
+
"View as Markdown(ui.view_markdown)": "Просмотр Markdown",
|
|
6
|
+
"latest(ui.version_latest)": "последняя"
|
|
7
|
+
}
|
package/src/locales.ts
CHANGED