@terrafirmagreg-team/kit 0.1.0 → 1.2.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/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
|
@@ -13,9 +13,11 @@ import { langContent } from '@terrafirmagreg-team/translate/server';
|
|
|
13
13
|
import { loadLocaleRegistry } from '@terrafirmagreg-team/translate/server';
|
|
14
14
|
import { readPageSource, translationState } from '@terrafirmagreg-team/translate/server';
|
|
15
15
|
import { resolvePage, type SectionSource } from '@terrafirmagreg-team/kit/lib/page';
|
|
16
|
+
import { localeLoader } from '@terrafirmagreg-team/kit/lib/locale/renderer';
|
|
16
17
|
import { findNeighbour } from 'fumadocs-core/page-tree';
|
|
17
18
|
import type {
|
|
18
19
|
DatasetCatalog,
|
|
20
|
+
SectionKey,
|
|
19
21
|
SectionPage,
|
|
20
22
|
SectionTrees,
|
|
21
23
|
} from '@terrafirmagreg-team/kit/types';
|
|
@@ -27,6 +29,7 @@ import type {
|
|
|
27
29
|
ComponentType,
|
|
28
30
|
ReactNode,
|
|
29
31
|
} from 'react';
|
|
32
|
+
import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
|
|
30
33
|
|
|
31
34
|
export interface SectionWorkspaceProps {
|
|
32
35
|
page: SectionPage;
|
|
@@ -39,7 +42,7 @@ export interface SectionWorkspaceProps {
|
|
|
39
42
|
history?: boolean;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
export function
|
|
45
|
+
export function renderSectionWorkspace({
|
|
43
46
|
source,
|
|
44
47
|
slug,
|
|
45
48
|
contentDir,
|
|
@@ -47,6 +50,8 @@ export function SectionView({
|
|
|
47
50
|
catalog,
|
|
48
51
|
history,
|
|
49
52
|
workspace: Workspace,
|
|
53
|
+
page,
|
|
54
|
+
lang,
|
|
50
55
|
}: {
|
|
51
56
|
source: SectionSource;
|
|
52
57
|
slug: string[] | undefined;
|
|
@@ -55,54 +60,93 @@ export function SectionView({
|
|
|
55
60
|
catalog: DatasetCatalog;
|
|
56
61
|
history?: boolean;
|
|
57
62
|
workspace: ComponentType<SectionWorkspaceProps>;
|
|
63
|
+
page: SectionPage;
|
|
64
|
+
lang: Lang;
|
|
58
65
|
}) {
|
|
59
66
|
const { defaultLanguage } = loadLocaleRegistry();
|
|
67
|
+
let copied: 'untranslated' | 'partial' | undefined;
|
|
68
|
+
if (lang !== defaultLanguage) {
|
|
69
|
+
const fallback = resolvePage(source, slug, defaultLanguage)?.page as
|
|
70
|
+
| SectionPage
|
|
71
|
+
| undefined;
|
|
72
|
+
if (fallback && fallback !== page) {
|
|
73
|
+
const own = readPageSource(contentDir, page.path);
|
|
74
|
+
const original = readPageSource(contentDir, fallback.path);
|
|
75
|
+
if (own && original) {
|
|
76
|
+
const state = translationState(own, original);
|
|
77
|
+
if (state !== 'ok') copied = state;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const crumbs = sectionBreadcrumbItems(source, page, lang);
|
|
82
|
+
const datasetId = page.url.split('/').filter(Boolean)[1];
|
|
83
|
+
const tree = datasetId ? trees?.[lang]?.[datasetId] : undefined;
|
|
84
|
+
const neighbours = tree ? findNeighbour(tree, page.url) : undefined;
|
|
85
|
+
return (
|
|
86
|
+
<Workspace
|
|
87
|
+
page={page}
|
|
88
|
+
crumbs={crumbs}
|
|
89
|
+
footer={neighbours}
|
|
90
|
+
previews={sectionBreadcrumbPreviews(crumbs, lang, catalog)}
|
|
91
|
+
link={createRelativeLink(
|
|
92
|
+
source as LoaderOutput<LoaderConfig>,
|
|
93
|
+
page as unknown as Page,
|
|
94
|
+
(props) => (
|
|
95
|
+
<LinkPreview
|
|
96
|
+
preview={lookupPagePreview(catalog, props.href, lang)}
|
|
97
|
+
{...props}
|
|
98
|
+
/>
|
|
99
|
+
),
|
|
100
|
+
)}
|
|
101
|
+
contentDir={contentDir}
|
|
102
|
+
copied={copied}
|
|
103
|
+
history={history}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function SectionView({
|
|
109
|
+
section,
|
|
110
|
+
getSource,
|
|
111
|
+
slug,
|
|
112
|
+
contentDir,
|
|
113
|
+
trees,
|
|
114
|
+
catalog,
|
|
115
|
+
history,
|
|
116
|
+
workspace,
|
|
117
|
+
}: {
|
|
118
|
+
section: SectionKey;
|
|
119
|
+
getSource: () => Promise<SectionSource>;
|
|
120
|
+
slug: string[] | undefined;
|
|
121
|
+
contentDir: string;
|
|
122
|
+
trees?: SectionTrees;
|
|
123
|
+
catalog: DatasetCatalog;
|
|
124
|
+
history?: boolean;
|
|
125
|
+
workspace: ComponentType<SectionWorkspaceProps>;
|
|
126
|
+
}) {
|
|
127
|
+
const source = await getSource();
|
|
60
128
|
const content = langContent(
|
|
61
129
|
slug,
|
|
62
130
|
(s, l) => resolvePage(source, s, l),
|
|
63
|
-
(page, lang) =>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (state !== 'ok') copied = state;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
const crumbs = sectionBreadcrumbItems(source, self, lang);
|
|
80
|
-
const datasetId = self.url.split('/').filter(Boolean)[1];
|
|
81
|
-
const tree = datasetId ? trees?.[lang]?.[datasetId] : undefined;
|
|
82
|
-
const neighbours = tree ? findNeighbour(tree, self.url) : undefined;
|
|
83
|
-
return (
|
|
84
|
-
<Workspace
|
|
85
|
-
page={self}
|
|
86
|
-
crumbs={crumbs}
|
|
87
|
-
footer={neighbours}
|
|
88
|
-
previews={sectionBreadcrumbPreviews(crumbs, lang, catalog)}
|
|
89
|
-
link={createRelativeLink(
|
|
90
|
-
source as LoaderOutput<LoaderConfig>,
|
|
91
|
-
page as Page,
|
|
92
|
-
(props) => (
|
|
93
|
-
<LinkPreview
|
|
94
|
-
preview={lookupPagePreview(catalog, props.href, lang)}
|
|
95
|
-
{...props}
|
|
96
|
-
/>
|
|
97
|
-
),
|
|
98
|
-
)}
|
|
99
|
-
contentDir={contentDir}
|
|
100
|
-
copied={copied}
|
|
101
|
-
history={history}
|
|
102
|
-
/>
|
|
103
|
-
);
|
|
104
|
-
},
|
|
131
|
+
(page, lang) =>
|
|
132
|
+
renderSectionWorkspace({
|
|
133
|
+
source,
|
|
134
|
+
slug,
|
|
135
|
+
contentDir,
|
|
136
|
+
trees,
|
|
137
|
+
catalog,
|
|
138
|
+
history,
|
|
139
|
+
workspace,
|
|
140
|
+
page: page as SectionPage,
|
|
141
|
+
lang,
|
|
142
|
+
}),
|
|
105
143
|
);
|
|
106
144
|
if (!content) notFound();
|
|
107
|
-
return
|
|
145
|
+
return (
|
|
146
|
+
<LangContentSwitch
|
|
147
|
+
content={content}
|
|
148
|
+
loadLang={localeLoader()?.bind(null, { section, slug })}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
108
151
|
}
|
|
152
|
+
|
|
@@ -62,7 +62,7 @@ function WorkspacePaginationItem({
|
|
|
62
62
|
{!next && (
|
|
63
63
|
<Icon className="size-3.5 transition-transform group-hover:-translate-x-0.5 rtl:rotate-180" />
|
|
64
64
|
)}
|
|
65
|
-
{next ? t('ui.next_page') : t('ui.prev_page')}
|
|
65
|
+
{next ? t('Next Page', { note: 'ui.next_page' }) : t('Previous Page', { note: 'ui.prev_page' })}
|
|
66
66
|
{next && (
|
|
67
67
|
<Icon className="size-3.5 transition-transform group-hover:translate-x-0.5 rtl:rotate-180" />
|
|
68
68
|
)}
|
|
@@ -71,3 +71,4 @@ function WorkspacePaginationItem({
|
|
|
71
71
|
</Link>
|
|
72
72
|
);
|
|
73
73
|
}
|
|
74
|
+
|
package/src/hooks/use-catalog.ts
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import { useMemo } from 'react';
|
|
2
3
|
import { usePathname } from 'next/navigation';
|
|
3
|
-
import {
|
|
4
|
+
import { useLocationSearchParams } from '@terrafirmagreg-team/ui/hooks/page/use-location-search';
|
|
5
|
+
import {
|
|
6
|
+
type DatasetRef,
|
|
7
|
+
parseSectionLocation,
|
|
8
|
+
resolveDatasetRef,
|
|
9
|
+
} from '@terrafirmagreg-team/sites/location';
|
|
4
10
|
import type { SectionDataset } from '@terrafirmagreg-team/kit/types';
|
|
5
11
|
|
|
6
12
|
export function useCurrentDataset(
|
|
7
13
|
datasets: readonly SectionDataset[],
|
|
8
|
-
):
|
|
14
|
+
): DatasetRef | undefined {
|
|
9
15
|
const pathname = usePathname();
|
|
16
|
+
const versionParam = useLocationSearchParams().get('version') ?? undefined;
|
|
10
17
|
const { dataset } = parseSectionLocation(pathname);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
const meta = datasets.find((row) => row.id === dataset);
|
|
19
|
+
return useMemo(
|
|
20
|
+
() => dataset && meta ? resolveDatasetRef(dataset, versionParam, meta).ref : undefined,
|
|
21
|
+
[dataset, versionParam, meta],
|
|
22
|
+
);
|
|
14
23
|
}
|
|
24
|
+
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import { type ReactNode, createElement, useMemo } from 'react';
|
|
3
3
|
import { DynamicIcon, type IconName } from 'lucide-react/dynamic';
|
|
4
4
|
import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
|
|
5
|
-
import {
|
|
5
|
+
import { sectionTabs } from '@terrafirmagreg-team/sites/sections';
|
|
6
6
|
import {
|
|
7
|
-
datasetRef,
|
|
8
7
|
isDumpSection,
|
|
9
|
-
|
|
8
|
+
resolveDatasetRef,
|
|
10
9
|
sectionUrl,
|
|
11
|
-
} from '@terrafirmagreg-team/
|
|
10
|
+
} from '@terrafirmagreg-team/sites/location';
|
|
12
11
|
import { useCurrentDataset } from '@terrafirmagreg-team/kit/hooks/use-current-dataset';
|
|
12
|
+
import { useSectionNames } from '@terrafirmagreg-team/kit/hooks/use-section-names';
|
|
13
13
|
import { useVersionPreference } from '@terrafirmagreg-team/kit/hooks/use-version-pref';
|
|
14
14
|
import type { LeftSidebarScope } from '@terrafirmagreg-team/kit/types';
|
|
15
15
|
import type {
|
|
@@ -30,31 +30,33 @@ export function useNavOptions({ section, catalog }: LeftSidebarScope): {
|
|
|
30
30
|
dataset: SwitcherProps;
|
|
31
31
|
} {
|
|
32
32
|
const t = useTranslations();
|
|
33
|
+
const sectionNames = useSectionNames();
|
|
33
34
|
const current = useCurrentDataset(catalog[section]);
|
|
34
|
-
const baseId = current ?
|
|
35
|
-
const pinned = current ?
|
|
35
|
+
const baseId = current ? current.id : undefined;
|
|
36
|
+
const pinned = current ? current.version : undefined;
|
|
36
37
|
const preferred = useVersionPreference(baseId);
|
|
37
38
|
const version = pinned ?? preferred;
|
|
38
39
|
|
|
39
40
|
const sectionOptions: SwitcherOption[] = useMemo(
|
|
40
41
|
() =>
|
|
41
|
-
|
|
42
|
+
sectionTabs.map(({ key, icon: Icon }) => {
|
|
42
43
|
const meta = baseId
|
|
43
44
|
? catalog[key].find(({ id }) => id === baseId)
|
|
44
45
|
: undefined;
|
|
45
|
-
const
|
|
46
|
-
isDumpSection(key) &&
|
|
47
|
-
?
|
|
48
|
-
:
|
|
46
|
+
const pinned =
|
|
47
|
+
isDumpSection(key) && baseId
|
|
48
|
+
? resolveDatasetRef(baseId, version, meta).pinned
|
|
49
|
+
: undefined;
|
|
50
|
+
const suffix = pinned ? `?version=${encodeURIComponent(pinned)}` : '';
|
|
49
51
|
return {
|
|
50
52
|
value: key,
|
|
51
|
-
title:
|
|
52
|
-
description:
|
|
53
|
+
title: sectionNames[key]?.title ?? key,
|
|
54
|
+
description: sectionNames[key]?.desc,
|
|
53
55
|
icon: Icon ? createElement(Icon) : undefined,
|
|
54
|
-
url: meta ? sectionUrl(key,
|
|
56
|
+
url: meta ? `${sectionUrl(key, baseId)}${suffix}` : sectionUrl(key),
|
|
55
57
|
};
|
|
56
58
|
}),
|
|
57
|
-
[
|
|
59
|
+
[sectionNames, baseId, version, catalog],
|
|
58
60
|
);
|
|
59
61
|
|
|
60
62
|
const datasetOptions: SwitcherOption[] = useMemo(
|
|
@@ -73,12 +75,13 @@ export function useNavOptions({ section, catalog }: LeftSidebarScope): {
|
|
|
73
75
|
section: {
|
|
74
76
|
options: sectionOptions,
|
|
75
77
|
selected: section,
|
|
76
|
-
placeholder: t('ui.select_section'),
|
|
78
|
+
placeholder: t('Select section', { note: 'ui.select_section' }),
|
|
77
79
|
},
|
|
78
80
|
dataset: {
|
|
79
81
|
options: datasetOptions,
|
|
80
82
|
selected: baseId,
|
|
81
|
-
placeholder: t('ui.select_dataset'),
|
|
83
|
+
placeholder: t('Select dataset', { note: 'ui.select_dataset' }),
|
|
82
84
|
},
|
|
83
85
|
};
|
|
84
86
|
}
|
|
87
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
|
|
4
|
+
|
|
5
|
+
export function useSectionNames(): Record<
|
|
6
|
+
string,
|
|
7
|
+
{ title: string; desc: string }
|
|
8
|
+
> {
|
|
9
|
+
const t = useTranslations();
|
|
10
|
+
return useMemo(
|
|
11
|
+
() => ({
|
|
12
|
+
docs: {
|
|
13
|
+
title: t('Docs', { note: 'sections.docs' }),
|
|
14
|
+
desc: t('Documentation', { note: 'sections.desc.docs' }),
|
|
15
|
+
},
|
|
16
|
+
changelog: {
|
|
17
|
+
title: t('Changelog', { note: 'sections.changelog' }),
|
|
18
|
+
desc: t('Release history and version notes', {
|
|
19
|
+
note: 'sections.desc.changelog',
|
|
20
|
+
}),
|
|
21
|
+
},
|
|
22
|
+
recipe: {
|
|
23
|
+
title: t('Recipes', { note: 'sections.recipe' }),
|
|
24
|
+
desc: t('Crafting and processing recipes', {
|
|
25
|
+
note: 'sections.desc.recipe',
|
|
26
|
+
}),
|
|
27
|
+
},
|
|
28
|
+
quest: {
|
|
29
|
+
title: t('Quests', { note: 'sections.quest' }),
|
|
30
|
+
desc: t('Quest chapters and objectives', { note: 'sections.desc.quest' }),
|
|
31
|
+
},
|
|
32
|
+
guide: {
|
|
33
|
+
title: t('Guide', { note: 'sections.guide' }),
|
|
34
|
+
desc: t('Gameplay guide', { note: 'sections.desc.guide' }),
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
[t],
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useLocationSearchParams } from '@terrafirmagreg-team/ui/hooks/page/use-location-search';
|
|
3
|
+
import {
|
|
4
|
+
resolveDatasetRef,
|
|
5
|
+
type DatasetVersionInfo,
|
|
6
|
+
type DatasetRef,
|
|
7
|
+
} from '@terrafirmagreg-team/sites/location';
|
|
8
|
+
|
|
9
|
+
const refs = new Map<string, DatasetRef | undefined>();
|
|
10
|
+
|
|
11
|
+
export function useVersionedDataset(
|
|
12
|
+
id: string | undefined,
|
|
13
|
+
info?: DatasetVersionInfo,
|
|
14
|
+
): DatasetRef | undefined {
|
|
15
|
+
const version = useLocationSearchParams().get('version') ?? undefined;
|
|
16
|
+
const key = `${id ?? ''} ${version ?? ''} ${info?.latest ?? ''} ${info?.versions?.join(',') ?? ''}`;
|
|
17
|
+
if (!refs.has(key))
|
|
18
|
+
refs.set(key, id ? resolveDatasetRef(id, version, info).ref : undefined);
|
|
19
|
+
return refs.get(key);
|
|
20
|
+
}
|
|
21
|
+
|
package/src/lib/breadcrumb.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import 'server-only';
|
|
2
2
|
import type * as PageTree from 'fumadocs-core/page-tree';
|
|
3
|
+
import { searchPath } from 'fumadocs-core/breadcrumb';
|
|
3
4
|
import { stripLangFromUrl } from '@terrafirmagreg-team/translate/lib/slug';
|
|
4
5
|
import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
|
|
5
6
|
import type { PagePreviewData } from '@terrafirmagreg-team/ui/types/preview';
|
|
@@ -13,21 +14,6 @@ import type { DatasetCatalog, SectionPage } from '@terrafirmagreg-team/kit/types
|
|
|
13
14
|
|
|
14
15
|
export type SectionCrumb = { name: string; url?: string };
|
|
15
16
|
|
|
16
|
-
function findTrail(
|
|
17
|
-
nodes: readonly PageTree.Node[],
|
|
18
|
-
url: string,
|
|
19
|
-
): PageTree.Node[] | undefined {
|
|
20
|
-
for (const node of nodes) {
|
|
21
|
-
if (node.type === 'page' && node.url === url) return [node];
|
|
22
|
-
if (node.type === 'folder') {
|
|
23
|
-
if (node.index?.url === url) return [node];
|
|
24
|
-
const inner = findTrail(node.children, url);
|
|
25
|
-
if (inner) return [node, ...inner];
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return undefined;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
17
|
function nodeName(name: unknown): string {
|
|
32
18
|
return typeof name === 'string' ? name : '';
|
|
33
19
|
}
|
|
@@ -52,7 +38,7 @@ export function sectionBreadcrumbItems(
|
|
|
52
38
|
const pageUrl = stripLangFromUrl(page.url, languages);
|
|
53
39
|
if (folder.index?.url === pageUrl) return [];
|
|
54
40
|
|
|
55
|
-
const trail =
|
|
41
|
+
const trail = searchPath(folder.children, pageUrl);
|
|
56
42
|
const items: SectionCrumb[] = [];
|
|
57
43
|
for (const node of trail?.slice(0, -1) ?? []) {
|
|
58
44
|
if (node.type === 'folder') {
|
|
@@ -76,3 +62,4 @@ export function sectionBreadcrumbPreviews(
|
|
|
76
62
|
}
|
|
77
63
|
return previews;
|
|
78
64
|
}
|
|
65
|
+
|
package/src/lib/config.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
2
|
import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
|
|
3
3
|
import type {
|
|
4
4
|
SectionKey,
|
|
@@ -6,13 +6,17 @@ import type {
|
|
|
6
6
|
} from '@terrafirmagreg-team/kit/types';
|
|
7
7
|
|
|
8
8
|
export type SectionDump = {
|
|
9
|
-
manifest: {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
manifest: {
|
|
10
|
+
languages?: readonly string[];
|
|
11
|
+
pack?: { version?: string | null } | null;
|
|
12
|
+
};
|
|
13
|
+
mods: readonly {
|
|
14
|
+
namespace: string;
|
|
15
|
+
books: readonly unknown[];
|
|
16
|
+
questData: string | null;
|
|
17
|
+
recipeCategories: string | null;
|
|
18
|
+
recipeIndex: string | null;
|
|
19
|
+
}[];
|
|
16
20
|
};
|
|
17
21
|
|
|
18
22
|
export type SectionConfigInput = {
|
|
@@ -21,15 +25,6 @@ export type SectionConfigInput = {
|
|
|
21
25
|
siteUrl: string;
|
|
22
26
|
previewRoute: string;
|
|
23
27
|
docsRoute: string;
|
|
24
|
-
legacyRoutes: Record<string, SectionLegacyRoute>;
|
|
25
|
-
|
|
26
|
-
sectionTabs: readonly {
|
|
27
|
-
key: SectionKey;
|
|
28
|
-
url: string;
|
|
29
|
-
icon?: ComponentType<{ className?: string }>;
|
|
30
|
-
}[];
|
|
31
|
-
|
|
32
|
-
dumpSections?: readonly SectionKey[];
|
|
33
28
|
|
|
34
29
|
datasetConfig: Record<
|
|
35
30
|
string,
|
|
@@ -39,6 +34,8 @@ export type SectionConfigInput = {
|
|
|
39
34
|
|
|
40
35
|
repo?: `${string}/${string}`;
|
|
41
36
|
|
|
37
|
+
discussions?: { repoId: string; categoryId: string };
|
|
38
|
+
|
|
42
39
|
crowdin?: string;
|
|
43
40
|
|
|
44
41
|
links?: readonly {
|
|
@@ -54,33 +51,37 @@ export type SectionConfigInput = {
|
|
|
54
51
|
sources: Partial<Record<SectionKey, SectionSourceEntry>>;
|
|
55
52
|
|
|
56
53
|
dump?: {
|
|
57
|
-
discoverDatasets(): Promise<
|
|
58
|
-
loadDataset(
|
|
54
|
+
discoverDatasets(): Promise<DatasetRef[]>;
|
|
55
|
+
loadDataset(ref: DatasetRef): Promise<SectionDump | null | undefined>;
|
|
59
56
|
hasContent: Record<string, (dump: SectionDump) => boolean>;
|
|
60
57
|
};
|
|
61
58
|
};
|
|
62
59
|
|
|
63
|
-
|
|
60
|
+
const registry = ((globalThis as Record<symbol, unknown>)[
|
|
61
|
+
Symbol.for('terrafirmagreg.config')
|
|
62
|
+
] ??= new Map<string, unknown>()) as Map<string, unknown>;
|
|
64
63
|
|
|
65
64
|
export function registerSectionConfig(
|
|
66
65
|
input: Partial<SectionConfigInput>,
|
|
67
66
|
): void {
|
|
68
|
-
|
|
67
|
+
registry.set('section', {
|
|
68
|
+
...(registry.get('section') as SectionConfigInput | undefined),
|
|
69
|
+
...input,
|
|
70
|
+
});
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
export function sectionConfig(): SectionConfigInput {
|
|
74
|
+
const config = registry.get('section') as SectionConfigInput | undefined;
|
|
72
75
|
if (!config) {
|
|
73
76
|
throw new Error(
|
|
74
|
-
'Section config is not registered
|
|
77
|
+
'Section config is not registered: call registerSectionConfig() at app startup.',
|
|
75
78
|
);
|
|
76
79
|
}
|
|
77
80
|
return config;
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
export type ClientDumpLoader = {
|
|
81
|
-
loadDump(dataset:
|
|
82
|
-
manifest: { pack?: { version?: string | null } | null };
|
|
83
|
-
} | null | undefined>;
|
|
84
|
+
loadDump(dataset: DatasetRef): Promise<SectionDump | null | undefined>;
|
|
84
85
|
loadVersions(
|
|
85
86
|
datasetId: string,
|
|
86
87
|
): Promise<{ latest?: string; versions: string[] } | null>;
|
|
@@ -92,18 +93,19 @@ export const {
|
|
|
92
93
|
} = createConfig<ClientDumpLoader>('ClientDump');
|
|
93
94
|
|
|
94
95
|
export function createConfig<T>(label: string) {
|
|
95
|
-
let config: T | undefined;
|
|
96
96
|
return {
|
|
97
97
|
register(input: T) {
|
|
98
|
-
|
|
98
|
+
registry.set(label, input);
|
|
99
99
|
},
|
|
100
100
|
get(): T {
|
|
101
|
+
const config = registry.get(label) as T | undefined;
|
|
101
102
|
if (!config) {
|
|
102
103
|
throw new Error(
|
|
103
|
-
`${label} config is not registered
|
|
104
|
+
`${label} config is not registered: call register${label}Config() at app startup.`,
|
|
104
105
|
);
|
|
105
106
|
}
|
|
106
107
|
return config;
|
|
107
108
|
},
|
|
108
109
|
};
|
|
109
110
|
}
|
|
111
|
+
|
package/src/lib/dataset-links.ts
CHANGED