@terrafirmagreg-team/guide 1.2.6 → 1.2.8
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terrafirmagreg-team/guide",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Guide/books section",
|
|
6
6
|
"license": "LGPL-3.0-only",
|
|
@@ -22,17 +22,17 @@
|
|
|
22
22
|
"./types": "./src/types/index.ts"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@terrafirmagreg-team/data": "^1.2.
|
|
26
|
-
"@terrafirmagreg-team/discussions": "^1.2.
|
|
27
|
-
"@terrafirmagreg-team/dump": "^1.2.
|
|
28
|
-
"@terrafirmagreg-team/game": "^1.2.
|
|
29
|
-
"@terrafirmagreg-team/kit": "^1.2.
|
|
30
|
-
"@terrafirmagreg-team/mdx": "^1.2.
|
|
31
|
-
"@terrafirmagreg-team/preview": "^1.2.
|
|
32
|
-
"@terrafirmagreg-team/search": "^1.2.
|
|
25
|
+
"@terrafirmagreg-team/data": "^1.2.2",
|
|
26
|
+
"@terrafirmagreg-team/discussions": "^1.2.6",
|
|
27
|
+
"@terrafirmagreg-team/dump": "^1.2.4",
|
|
28
|
+
"@terrafirmagreg-team/game": "^1.2.4",
|
|
29
|
+
"@terrafirmagreg-team/kit": "^1.2.9",
|
|
30
|
+
"@terrafirmagreg-team/mdx": "^1.2.4",
|
|
31
|
+
"@terrafirmagreg-team/preview": "^1.2.3",
|
|
32
|
+
"@terrafirmagreg-team/search": "^1.2.5",
|
|
33
33
|
"@terrafirmagreg-team/sites": "^1.2.1",
|
|
34
|
-
"@terrafirmagreg-team/translate": "^1.2.
|
|
35
|
-
"@terrafirmagreg-team/ui": "^1.2.
|
|
34
|
+
"@terrafirmagreg-team/translate": "^1.2.6",
|
|
35
|
+
"@terrafirmagreg-team/ui": "^1.2.7",
|
|
36
36
|
"fumadocs-core": "16.17.10",
|
|
37
37
|
"lucide-react": "^1.63.0"
|
|
38
38
|
},
|
|
@@ -54,8 +54,9 @@ function bookLeaf(
|
|
|
54
54
|
slug: string,
|
|
55
55
|
raw: GuideBookJson | undefined,
|
|
56
56
|
map: Record<string, string>,
|
|
57
|
-
): PageTree.Item {
|
|
57
|
+
): PageTree.Item | undefined {
|
|
58
58
|
const book = toBook(raw, ref, slug, map);
|
|
59
|
+
if (!book.name.trim()) return undefined;
|
|
59
60
|
const bookId = bookOptionId(ref.modId, ref.bookPath);
|
|
60
61
|
return keyTreeNode({
|
|
61
62
|
type: 'page',
|
|
@@ -73,6 +74,7 @@ async function datasetBookLeaves(
|
|
|
73
74
|
const dump = await loadDatasetDump(dataset);
|
|
74
75
|
if (!dump) return [];
|
|
75
76
|
const taken = new Set<string>();
|
|
77
|
+
const seen = new Set<string>();
|
|
76
78
|
const leaves: PageTree.Node[] = [];
|
|
77
79
|
for (const mod of dump.mods) {
|
|
78
80
|
const raws = await Promise.all(
|
|
@@ -85,14 +87,19 @@ async function datasetBookLeaves(
|
|
|
85
87
|
for (const [i, ref] of mod.books.entries()) {
|
|
86
88
|
const raw = raws[i];
|
|
87
89
|
if (!raw) continue;
|
|
90
|
+
const bookId = bookOptionId(ref.modId, ref.bookPath);
|
|
91
|
+
if (seen.has(bookId)) continue;
|
|
88
92
|
let slug = bookSlug(raw, ref);
|
|
89
93
|
if (taken.has(slug)) {
|
|
90
94
|
let n = 2;
|
|
91
95
|
while (taken.has(`${slug}-${n}`)) n += 1;
|
|
92
96
|
slug = `${slug}-${n}`;
|
|
93
97
|
}
|
|
98
|
+
const leaf = bookLeaf(dataset, ref, slug, raw, langMap);
|
|
99
|
+
if (!leaf) continue;
|
|
94
100
|
taken.add(slug);
|
|
95
|
-
|
|
101
|
+
seen.add(bookId);
|
|
102
|
+
leaves.push(leaf);
|
|
96
103
|
}
|
|
97
104
|
}
|
|
98
105
|
return leaves;
|
|
@@ -2,12 +2,20 @@
|
|
|
2
2
|
import { useEffect, useMemo } from 'react';
|
|
3
3
|
import { useRouter } from 'next/navigation';
|
|
4
4
|
import { useLocationSearchParams } from '@terrafirmagreg-team/ui/hooks/page/use-location-search';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
useAsyncResult,
|
|
7
|
+
useAsyncValue,
|
|
8
|
+
} from '@terrafirmagreg-team/ui/hooks/use-async-value';
|
|
9
|
+
import { useLastResult } from '@terrafirmagreg-team/ui/hooks/use-last-result';
|
|
6
10
|
import { useVersionedDataset } from '@terrafirmagreg-team/kit/hooks/use-versioned-dataset';
|
|
11
|
+
import { useDumpRetryNonce } from '@terrafirmagreg-team/kit/hooks/use-dump-status';
|
|
12
|
+
import { DumpUnavailable } from '@terrafirmagreg-team/kit/components/views/unavailable';
|
|
13
|
+
import { StaleBadge } from '@terrafirmagreg-team/kit/components/dump-status/stale-badge';
|
|
7
14
|
import { useLangMap } from '@terrafirmagreg-team/game/hooks/use-lang-map';
|
|
8
15
|
import { useResolvedLang } from '@terrafirmagreg-team/translate/hooks/use-resolved-lang';
|
|
9
16
|
import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
|
|
10
17
|
import { Skeleton } from '@terrafirmagreg-team/ui/components/ui/skeleton';
|
|
18
|
+
import { EmptyState } from '@terrafirmagreg-team/ui/components/blocks/page/empty-state';
|
|
11
19
|
import { WorkspaceBody } from '@terrafirmagreg-team/ui/components/layouts/workspace';
|
|
12
20
|
import { GuidePageView } from '@terrafirmagreg-team/guide/components/views/page/view';
|
|
13
21
|
import {
|
|
@@ -59,9 +67,13 @@ export function GuideBrowser({
|
|
|
59
67
|
[params],
|
|
60
68
|
);
|
|
61
69
|
const source = getGuideSource();
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
const nonce = useDumpRetryNonce();
|
|
71
|
+
const books = useLastResult(
|
|
72
|
+
useAsyncResult(
|
|
73
|
+
() => source.books(dataset, lang),
|
|
74
|
+
[dataset, lang, nonce],
|
|
75
|
+
),
|
|
76
|
+
dataset.id,
|
|
65
77
|
);
|
|
66
78
|
const langMap = useLangMap(dataset, lang);
|
|
67
79
|
const messages = useMemo<GuideMessages>(
|
|
@@ -87,47 +99,79 @@ export function GuideBrowser({
|
|
|
87
99
|
|
|
88
100
|
const resolved = useResolvedLang();
|
|
89
101
|
const canonical = useAsyncValue(
|
|
90
|
-
books && !bookId
|
|
102
|
+
books.value && !bookId
|
|
91
103
|
? async () => {
|
|
92
|
-
const book = books[0];
|
|
104
|
+
const book = books.value?.[0];
|
|
93
105
|
if (!book) return undefined;
|
|
94
106
|
const index = await source.book(dataset, lang, book.bookId);
|
|
95
107
|
const first = index ? firstGuideEntryId(index) : undefined;
|
|
96
108
|
return guideHref(dataset, resolved, book.bookId, first);
|
|
97
109
|
}
|
|
98
110
|
: undefined,
|
|
99
|
-
[books, bookId, dataset, lang, resolved],
|
|
111
|
+
[books.value, bookId, dataset, lang, resolved],
|
|
100
112
|
);
|
|
101
113
|
|
|
102
114
|
useEffect(() => {
|
|
103
115
|
if (canonical) router.replace(canonical);
|
|
104
116
|
}, [canonical, router]);
|
|
105
117
|
|
|
106
|
-
const payload =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
const payload = useLastResult(
|
|
119
|
+
useAsyncResult(
|
|
120
|
+
langMap && bookId
|
|
121
|
+
? () =>
|
|
122
|
+
source.payload(
|
|
123
|
+
dataset,
|
|
124
|
+
lang,
|
|
125
|
+
bookId,
|
|
126
|
+
entryId,
|
|
127
|
+
categoryId,
|
|
128
|
+
langMap,
|
|
129
|
+
messages,
|
|
130
|
+
)
|
|
131
|
+
: undefined,
|
|
132
|
+
[dataset, lang, bookId, entryId, categoryId, langMap, messages, nonce],
|
|
133
|
+
),
|
|
134
|
+
`${dataset.id}:${bookId ?? ''}`,
|
|
120
135
|
);
|
|
121
136
|
|
|
122
137
|
useEffect(() => {
|
|
123
|
-
if (payload?.title) {
|
|
124
|
-
document.title = `${payload.title} · ${datasetTitle} | ${guideConfig().appName}`;
|
|
138
|
+
if (payload.value?.title) {
|
|
139
|
+
document.title = `${payload.value.title} · ${datasetTitle} | ${guideConfig().appName}`;
|
|
125
140
|
}
|
|
126
|
-
}, [payload?.title, datasetTitle]);
|
|
141
|
+
}, [payload.value?.title, datasetTitle]);
|
|
142
|
+
|
|
143
|
+
const unavailable = (
|
|
144
|
+
<WorkspaceBody>
|
|
145
|
+
<DumpUnavailable>
|
|
146
|
+
{t('Guide data is temporarily unavailable. Try again later.', {
|
|
147
|
+
note: 'guide.unavailable',
|
|
148
|
+
})}
|
|
149
|
+
</DumpUnavailable>
|
|
150
|
+
</WorkspaceBody>
|
|
151
|
+
);
|
|
127
152
|
|
|
128
|
-
if (
|
|
153
|
+
if (books.error || (!books.pending && !books.value?.length)) {
|
|
154
|
+
return unavailable;
|
|
155
|
+
}
|
|
156
|
+
if (!bookId || !langMap || payload.pending) {
|
|
129
157
|
return <GuideBrowserFallback />;
|
|
130
158
|
}
|
|
131
|
-
|
|
159
|
+
if (!payload.value) {
|
|
160
|
+
return payload.error ? (
|
|
161
|
+
unavailable
|
|
162
|
+
) : (
|
|
163
|
+
<WorkspaceBody>
|
|
164
|
+
<EmptyState variant="missing">
|
|
165
|
+
{t('Entry not found', { note: 'guide.entry_missing' })}
|
|
166
|
+
</EmptyState>
|
|
167
|
+
</WorkspaceBody>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return (
|
|
171
|
+
<div className="relative flex-1">
|
|
172
|
+
{books.stale || payload.stale ? <StaleBadge /> : null}
|
|
173
|
+
<GuidePageView payload={payload.value} langMap={langMap} />
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
132
176
|
}
|
|
133
177
|
|
|
@@ -5,6 +5,7 @@ import type * as PageTree from 'fumadocs-core/page-tree';
|
|
|
5
5
|
import { useLocationSearchParams } from '@terrafirmagreg-team/ui/hooks/page/use-location-search';
|
|
6
6
|
import { useResolvedLang } from '@terrafirmagreg-team/translate/hooks/use-resolved-lang';
|
|
7
7
|
import { useLastValue } from '@terrafirmagreg-team/ui/hooks/use-last-value';
|
|
8
|
+
import { skeletonTree } from '@terrafirmagreg-team/ui/lib/tree';
|
|
8
9
|
import { useGuideTree } from '@terrafirmagreg-team/guide/hooks/use-tree';
|
|
9
10
|
import { guideQueryFromParams } from '@terrafirmagreg-team/guide/lib/nav/query';
|
|
10
11
|
import { parseSectionLocation } from '@terrafirmagreg-team/sites/location';
|
|
@@ -19,10 +20,8 @@ export function useGuideLeftSidebarTree(tree: PageTree.Root): PageTree.Root {
|
|
|
19
20
|
() => (datasetId ? { id: datasetId, version } : undefined),
|
|
20
21
|
[datasetId, version],
|
|
21
22
|
);
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
);
|
|
23
|
+
const result = useGuideTree(dataset, lang, bookId);
|
|
24
|
+
const runtime = useLastValue(result.value, dataset?.id ?? '');
|
|
25
|
+
if (!runtime && result.pending) return skeletonTree();
|
|
26
26
|
return runtime ?? tree;
|
|
27
27
|
}
|
|
28
|
-
|
package/src/hooks/use-tree.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
3
3
|
|
|
4
4
|
import type * as PageTree from 'fumadocs-core/page-tree';
|
|
5
|
-
import {
|
|
5
|
+
import { useAsyncResult } from '@terrafirmagreg-team/ui/hooks/use-async-value';
|
|
6
|
+
import { useDumpRetryNonce } from '@terrafirmagreg-team/kit/hooks/use-dump-status';
|
|
6
7
|
import { loadClientGuideBook } from '@terrafirmagreg-team/guide/lib/client/books';
|
|
7
8
|
import { listGuideBooks } from '@terrafirmagreg-team/guide/lib/book/list';
|
|
8
9
|
import { guideTreeChildren } from '@terrafirmagreg-team/guide/components/tree/children';
|
|
@@ -12,8 +13,9 @@ export function useGuideTree(
|
|
|
12
13
|
dataset: DatasetRef | undefined,
|
|
13
14
|
lang: Lang,
|
|
14
15
|
bookId: string | undefined,
|
|
15
|
-
): PageTree.Root | undefined {
|
|
16
|
-
|
|
16
|
+
): { value: PageTree.Root | undefined; pending: boolean; error: unknown } {
|
|
17
|
+
const nonce = useDumpRetryNonce();
|
|
18
|
+
return useAsyncResult(
|
|
17
19
|
dataset
|
|
18
20
|
? async () => {
|
|
19
21
|
const summaries = await listGuideBooks(dataset, lang);
|
|
@@ -29,7 +31,6 @@ export function useGuideTree(
|
|
|
29
31
|
} satisfies PageTree.Root;
|
|
30
32
|
}
|
|
31
33
|
: undefined,
|
|
32
|
-
[dataset, lang, bookId],
|
|
34
|
+
[dataset, lang, bookId, nonce],
|
|
33
35
|
);
|
|
34
36
|
}
|
|
35
|
-
|
package/src/lib/config.ts
CHANGED
|
@@ -31,7 +31,7 @@ export type GuideConfig = {
|
|
|
31
31
|
const { register: registerGuideConfig, get: guideConfig } = createConfigRegistry<
|
|
32
32
|
GuideConfigInput,
|
|
33
33
|
GuideConfig
|
|
34
|
-
>((overrides) => ({
|
|
34
|
+
>('guide', (overrides) => ({
|
|
35
35
|
appName: overrides.appName ?? uiConfig().brand?.name ?? '',
|
|
36
36
|
components: {
|
|
37
37
|
PageActions: (overrides.components?.PageActions ??
|