@terrafirmagreg-team/guide 1.2.5 → 1.2.7

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.5",
3
+ "version": "1.2.7",
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.1",
26
- "@terrafirmagreg-team/discussions": "1.2.3",
27
- "@terrafirmagreg-team/dump": "1.2.2",
28
- "@terrafirmagreg-team/game": "1.2.3",
29
- "@terrafirmagreg-team/kit": "1.2.4",
30
- "@terrafirmagreg-team/mdx": "1.2.2",
31
- "@terrafirmagreg-team/preview": "1.2.1",
32
- "@terrafirmagreg-team/search": "1.2.3",
33
- "@terrafirmagreg-team/sites": "1.2.1",
34
- "@terrafirmagreg-team/translate": "1.2.4",
35
- "@terrafirmagreg-team/ui": "1.2.4",
25
+ "@terrafirmagreg-team/data": "^1.2.2",
26
+ "@terrafirmagreg-team/discussions": "^1.2.5",
27
+ "@terrafirmagreg-team/dump": "^1.2.3",
28
+ "@terrafirmagreg-team/game": "^1.2.4",
29
+ "@terrafirmagreg-team/kit": "^1.2.8",
30
+ "@terrafirmagreg-team/mdx": "^1.2.3",
31
+ "@terrafirmagreg-team/preview": "^1.2.2",
32
+ "@terrafirmagreg-team/search": "^1.2.4",
33
+ "@terrafirmagreg-team/sites": "^1.2.1",
34
+ "@terrafirmagreg-team/translate": "^1.2.5",
35
+ "@terrafirmagreg-team/ui": "^1.2.6",
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
- leaves.push(bookLeaf(dataset, ref, slug, raw, langMap));
101
+ seen.add(bookId);
102
+ leaves.push(leaf);
96
103
  }
97
104
  }
98
105
  return leaves;
@@ -2,12 +2,16 @@
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 { useAsyncValue } from '@terrafirmagreg-team/ui/hooks/use-async-value';
5
+ import {
6
+ useAsyncResult,
7
+ useAsyncValue,
8
+ } from '@terrafirmagreg-team/ui/hooks/use-async-value';
6
9
  import { useVersionedDataset } from '@terrafirmagreg-team/kit/hooks/use-versioned-dataset';
7
10
  import { useLangMap } from '@terrafirmagreg-team/game/hooks/use-lang-map';
8
11
  import { useResolvedLang } from '@terrafirmagreg-team/translate/hooks/use-resolved-lang';
9
12
  import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
10
13
  import { Skeleton } from '@terrafirmagreg-team/ui/components/ui/skeleton';
14
+ import { EmptyState } from '@terrafirmagreg-team/ui/components/blocks/page/empty-state';
11
15
  import { WorkspaceBody } from '@terrafirmagreg-team/ui/components/layouts/workspace';
12
16
  import { GuidePageView } from '@terrafirmagreg-team/guide/components/views/page/view';
13
17
  import {
@@ -59,7 +63,7 @@ export function GuideBrowser({
59
63
  [params],
60
64
  );
61
65
  const source = getGuideSource();
62
- const books = useAsyncValue(
66
+ const books = useAsyncResult(
63
67
  () => source.books(dataset, lang),
64
68
  [dataset, lang],
65
69
  );
@@ -87,23 +91,23 @@ export function GuideBrowser({
87
91
 
88
92
  const resolved = useResolvedLang();
89
93
  const canonical = useAsyncValue(
90
- books && !bookId
94
+ books.value && !bookId
91
95
  ? async () => {
92
- const book = books[0];
96
+ const book = books.value?.[0];
93
97
  if (!book) return undefined;
94
98
  const index = await source.book(dataset, lang, book.bookId);
95
99
  const first = index ? firstGuideEntryId(index) : undefined;
96
100
  return guideHref(dataset, resolved, book.bookId, first);
97
101
  }
98
102
  : undefined,
99
- [books, bookId, dataset, lang, resolved],
103
+ [books.value, bookId, dataset, lang, resolved],
100
104
  );
101
105
 
102
106
  useEffect(() => {
103
107
  if (canonical) router.replace(canonical);
104
108
  }, [canonical, router]);
105
109
 
106
- const payload = useAsyncValue(
110
+ const payload = useAsyncResult(
107
111
  langMap && bookId
108
112
  ? () =>
109
113
  source.payload(
@@ -120,14 +124,38 @@ export function GuideBrowser({
120
124
  );
121
125
 
122
126
  useEffect(() => {
123
- if (payload?.title) {
124
- document.title = `${payload.title} · ${datasetTitle} | ${guideConfig().appName}`;
127
+ if (payload.value?.title) {
128
+ document.title = `${payload.value.title} · ${datasetTitle} | ${guideConfig().appName}`;
125
129
  }
126
- }, [payload?.title, datasetTitle]);
130
+ }, [payload.value?.title, datasetTitle]);
131
+
132
+ const unavailable = (
133
+ <WorkspaceBody>
134
+ <EmptyState variant="missing">
135
+ {t('Guide data is temporarily unavailable. Try again later.', {
136
+ note: 'guide.unavailable',
137
+ })}
138
+ </EmptyState>
139
+ </WorkspaceBody>
140
+ );
127
141
 
128
- if (!bookId || !payload || !langMap) {
142
+ if (books.error || (!books.pending && !books.value?.length)) {
143
+ return unavailable;
144
+ }
145
+ if (!bookId || !langMap || payload.pending) {
129
146
  return <GuideBrowserFallback />;
130
147
  }
131
- return <GuidePageView payload={payload} langMap={langMap} />;
148
+ if (!payload.value) {
149
+ return payload.error ? (
150
+ unavailable
151
+ ) : (
152
+ <WorkspaceBody>
153
+ <EmptyState variant="missing">
154
+ {t('Entry not found', { note: 'guide.entry_missing' })}
155
+ </EmptyState>
156
+ </WorkspaceBody>
157
+ );
158
+ }
159
+ return <GuidePageView payload={payload.value} langMap={langMap} />;
132
160
  }
133
161
 
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 ??