@terrafirmagreg-team/guide 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.
Files changed (68) hide show
  1. package/package.json +18 -19
  2. package/src/components/book-switcher.tsx +12 -6
  3. package/src/components/mdx/guide-entry.tsx +48 -0
  4. package/src/components/mdx/multiblock.tsx +86 -0
  5. package/src/{lib/tree/children.ts → components/tree/children.tsx} +13 -8
  6. package/src/{lib/tree/index.ts → components/tree/index.tsx} +25 -15
  7. package/src/components/views/browser.tsx +26 -17
  8. package/src/components/views/entry/pages.tsx +4 -2
  9. package/src/components/views/entry/view.tsx +3 -1
  10. package/src/components/views/kind/links.tsx +3 -2
  11. package/src/components/views/kind/media.tsx +6 -5
  12. package/src/components/views/kind/recipe.tsx +6 -51
  13. package/src/components/views/kind/table.tsx +4 -2
  14. package/src/components/views/kind/unknown.tsx +1 -0
  15. package/src/components/views/page/category-body.tsx +3 -1
  16. package/src/components/views/page/entry-body.tsx +3 -1
  17. package/src/components/views/page/overview-body.tsx +4 -2
  18. package/src/components/views/page/text.tsx +1 -0
  19. package/src/components/views/page/view.tsx +6 -6
  20. package/src/components/views/text.tsx +4 -2
  21. package/src/hooks/use-left-sidebar-tree.ts +15 -6
  22. package/src/hooks/use-tree.ts +5 -3
  23. package/src/lib/book/convert.ts +2 -1
  24. package/src/lib/book/index-rows.ts +3 -1
  25. package/src/lib/book/index.ts +1 -0
  26. package/src/lib/book/localize.ts +3 -2
  27. package/src/lib/book/raw.ts +5 -4
  28. package/src/lib/books.ts +4 -2
  29. package/src/lib/breadcrumb.ts +3 -1
  30. package/src/lib/client/guides.ts +86 -13
  31. package/src/lib/client-payload/extras.ts +10 -8
  32. package/src/lib/client-payload/index.ts +9 -8
  33. package/src/lib/client-payload/links.ts +10 -8
  34. package/src/lib/config.ts +1 -0
  35. package/src/lib/data/dto.ts +5 -4
  36. package/src/lib/data/parse.ts +4 -3
  37. package/src/lib/dump.ts +4 -3
  38. package/src/lib/embeds.ts +12 -0
  39. package/src/lib/format.ts +3 -2
  40. package/src/lib/links.ts +1 -1
  41. package/src/lib/markdown/book.ts +1 -0
  42. package/src/lib/markdown/ctx.ts +3 -1
  43. package/src/lib/markdown/index.ts +1 -0
  44. package/src/lib/markdown/page.ts +7 -8
  45. package/src/lib/markdown/table.ts +1 -0
  46. package/src/lib/markdown/text.ts +1 -0
  47. package/src/lib/markdown-page.ts +6 -4
  48. package/src/lib/messages.ts +1 -0
  49. package/src/lib/meta.ts +1 -0
  50. package/src/lib/multiblock/index.ts +1 -0
  51. package/src/lib/multiblock/url.ts +24 -10
  52. package/src/lib/names.ts +5 -3
  53. package/src/lib/page-kind.ts +2 -1
  54. package/src/lib/paths.ts +4 -2
  55. package/src/lib/preview.ts +8 -4
  56. package/src/lib/query.ts +1 -0
  57. package/src/lib/recipe.ts +3 -2
  58. package/src/lib/reference/index.ts +1 -0
  59. package/src/lib/reference/resolver.ts +4 -4
  60. package/src/lib/search/filter.ts +2 -1
  61. package/src/lib/search/index.ts +41 -14
  62. package/src/lib/sprites.ts +1 -0
  63. package/src/lib/table.ts +1 -0
  64. package/src/lib/toc.ts +3 -2
  65. package/src/locales/en_us.json +12 -20
  66. package/src/locales/ru_ru.json +12 -20
  67. package/src/locales.ts +1 -0
  68. package/src/types/index.ts +12 -10
package/package.json CHANGED
@@ -1,40 +1,41 @@
1
1
  {
2
2
  "name": "@terrafirmagreg-team/guide",
3
- "version": "0.1.0",
3
+ "version": "1.2.1",
4
4
  "type": "module",
5
+ "description": "Guide/books section",
6
+ "license": "LGPL-3.0-only",
5
7
  "exports": {
6
8
  "./components/*": [
7
9
  "./src/components/*.tsx",
8
- "./src/components/*.ts",
9
10
  "./src/components/*/index.tsx",
10
11
  "./src/components/*/index.ts"
11
12
  ],
12
13
  "./hooks/*": [
13
14
  "./src/hooks/*.ts",
14
- "./src/hooks/*.tsx",
15
- "./src/hooks/*/index.ts",
16
- "./src/hooks/*/index.tsx"
15
+ "./src/hooks/*/index.ts"
17
16
  ],
18
17
  "./lib/*": [
19
18
  "./src/lib/*.ts",
20
- "./src/lib/*.tsx",
21
- "./src/lib/*/index.ts",
22
- "./src/lib/*/index.tsx"
19
+ "./src/lib/*/index.ts"
23
20
  ],
24
21
  "./types/*": "./src/types/*.ts",
25
22
  "./types": "./src/types/index.ts",
26
23
  "./locales": "./src/locales.ts"
27
24
  },
28
25
  "dependencies": {
26
+ "@terrafirmagreg-team/data": "1.2.1",
27
+ "@terrafirmagreg-team/discussions": "1.2.1",
28
+ "@terrafirmagreg-team/dump": "1.2.1",
29
+ "@terrafirmagreg-team/game": "1.2.1",
30
+ "@terrafirmagreg-team/kit": "1.2.1",
31
+ "@terrafirmagreg-team/mdx": "1.2.1",
32
+ "@terrafirmagreg-team/preview": "1.2.1",
33
+ "@terrafirmagreg-team/search": "1.2.1",
34
+ "@terrafirmagreg-team/sites": "1.2.1",
35
+ "@terrafirmagreg-team/translate": "1.2.1",
36
+ "@terrafirmagreg-team/ui": "1.2.1",
29
37
  "fumadocs-core": "16.17.8",
30
- "lucide-react": "^1.63.0",
31
- "@terrafirmagreg-team/game": "0.1.0",
32
- "@terrafirmagreg-team/quest": "0.1.0",
33
- "@terrafirmagreg-team/recipe": "0.1.0",
34
- "@terrafirmagreg-team/translate": "0.1.0",
35
- "@terrafirmagreg-team/ui": "0.1.0",
36
- "@terrafirmagreg-team/kit": "0.1.0",
37
- "@terrafirmagreg-team/comments": "0.1.0"
38
+ "lucide-react": "^0.577.0"
38
39
  },
39
40
  "peerDependencies": {
40
41
  "next": ">=15",
@@ -54,8 +55,6 @@
54
55
  "access": "public"
55
56
  },
56
57
  "files": [
57
- "src",
58
- "styles",
59
- "styles.css"
58
+ "src"
60
59
  ]
61
60
  }
@@ -1,15 +1,16 @@
1
1
  'use client';
2
2
 
3
3
  import { usePathname } from 'next/navigation';
4
+ import { useMemo } from 'react';
4
5
  import { BookOpen } from 'lucide-react';
5
6
  import { useAsyncValue } from '@terrafirmagreg-team/ui/hooks/use-async-value';
6
7
  import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
7
8
  import { useResolvedLang } from '@terrafirmagreg-team/translate/hooks/use-resolved-lang';
8
- import { useLocationSearch } from '@terrafirmagreg-team/ui/hooks/page/use-location-search';
9
+ import { useLocationSearchParams } from '@terrafirmagreg-team/ui/hooks/page/use-location-search';
9
10
  import { guideQueryFromParams } from '@terrafirmagreg-team/guide/lib/query';
10
11
  import { listGuideBooks } from '@terrafirmagreg-team/guide/lib/books';
11
12
  import { guideHref } from '@terrafirmagreg-team/guide/lib/links';
12
- import { parseSectionLocation } from '@terrafirmagreg-team/kit/lib/location';
13
+ import { parseSectionLocation } from '@terrafirmagreg-team/sites/location';
13
14
  import { Switcher } from '@terrafirmagreg-team/ui/components/blocks/nav/switcher';
14
15
 
15
16
  function oneLineLanding(text: string | undefined) {
@@ -20,10 +21,14 @@ function oneLineLanding(text: string | undefined) {
20
21
 
21
22
  export function GuideBookSwitcher() {
22
23
  const t = useTranslations();
23
- const { dataset } = parseSectionLocation(usePathname());
24
+ const { dataset: datasetId } = parseSectionLocation(usePathname());
24
25
  const lang = useResolvedLang();
25
- const { bookId } = guideQueryFromParams(
26
- new URLSearchParams(useLocationSearch()),
26
+ const params = useLocationSearchParams();
27
+ const { bookId } = guideQueryFromParams(params);
28
+ const version = params.get('version') ?? undefined;
29
+ const dataset = useMemo(
30
+ () => (datasetId ? { id: datasetId, version } : undefined),
31
+ [datasetId, version],
27
32
  );
28
33
  const books = useAsyncValue(
29
34
  dataset ? () => listGuideBooks(dataset, lang) : undefined,
@@ -40,7 +45,8 @@ export function GuideBookSwitcher() {
40
45
  url: guideHref(dataset, lang, book.bookId),
41
46
  }))}
42
47
  selected={bookId}
43
- placeholder={t('ui.select_book')}
48
+ placeholder={t('Select book', { note: 'ui.select_book' })}
44
49
  />
45
50
  );
46
51
  }
52
+
@@ -0,0 +1,48 @@
1
+ 'use client';
2
+
3
+ import { usePathname } from 'next/navigation';
4
+ import { useContext } from 'react';
5
+ import { parseSectionLocation } from '@terrafirmagreg-team/sites/location';
6
+ import { Entry } from '@terrafirmagreg-team/game/components/entry/entry';
7
+ import { useEntryContext } from '@terrafirmagreg-team/game/hooks/use-entry-context';
8
+ import { ResolvedLangContext } from '@terrafirmagreg-team/translate/hooks/use-resolved-lang';
9
+ import { useAsyncValue } from '@terrafirmagreg-team/ui/hooks/use-async-value';
10
+ import { findClientGuideEntry } from '@terrafirmagreg-team/guide/lib/client/guides';
11
+ import type { GuideDirectiveProps } from '@terrafirmagreg-team/game/types/entry';
12
+
13
+ export function GuideEntry({
14
+ id,
15
+ dataset: datasetProp,
16
+ name,
17
+ modId,
18
+ bookPath,
19
+ entry: entryOpt,
20
+ ...rest
21
+ }: GuideDirectiveProps) {
22
+ const ctx = useEntryContext();
23
+ const pathname = usePathname();
24
+ const locId = parseSectionLocation(pathname).dataset;
25
+ const dataset =
26
+ datasetProp ?? ctx?.dataset ?? (locId ? { id: locId } : undefined);
27
+ const lang = useContext(ResolvedLangContext)?.lang;
28
+ const hit = useAsyncValue(
29
+ dataset && lang && id
30
+ ? () => findClientGuideEntry(dataset, lang, id)
31
+ : false,
32
+ [dataset, lang, id],
33
+ );
34
+ return (
35
+ <Entry
36
+ kind="guide"
37
+ id={hit?.entry.id ?? id}
38
+ dataset={dataset}
39
+ name={name ?? hit?.entry.name}
40
+ icon={hit?.entry.icon}
41
+ modId={modId ?? hit?.modId}
42
+ bookPath={bookPath ?? hit?.bookPath}
43
+ entry={entryOpt ?? hit?.entry.id}
44
+ {...rest}
45
+ />
46
+ );
47
+ }
48
+
@@ -0,0 +1,86 @@
1
+ 'use client';
2
+
3
+ import { useContext, useMemo } from 'react';
4
+ import { usePathname } from 'next/navigation';
5
+ import {
6
+ type DatasetRef,
7
+ parseSectionLocation,
8
+ } from '@terrafirmagreg-team/sites/location';
9
+ import { useAsyncValue } from '@terrafirmagreg-team/ui/hooks/use-async-value';
10
+ import { useDumpLocation } from '@terrafirmagreg-team/game/hooks/use-dump-location';
11
+ import { ResolvedLangContext } from '@terrafirmagreg-team/translate/hooks/use-resolved-lang';
12
+ import { createDumpAssetResolver } from '@terrafirmagreg-team/dump/lib/dump/assets/urls';
13
+ import { asMultiblock } from '@terrafirmagreg-team/guide/lib/meta';
14
+ import { glbFor } from '@terrafirmagreg-team/guide/lib/multiblock/url';
15
+ import { loadMultiblockNameIndex } from '@terrafirmagreg-team/guide/lib/client/guides';
16
+ import { multiblockKey } from '@terrafirmagreg-team/guide/lib/multiblock';
17
+ import { MultiblockVisual } from '@terrafirmagreg-team/guide/components/views/kind/media';
18
+ import type { GuideMultiblock } from '@terrafirmagreg-team/guide/types';
19
+
20
+ export type MdxMultiblockProps = {
21
+ id?: string;
22
+ name?: string;
23
+ spec?: string;
24
+ label?: string;
25
+ dataset?: DatasetRef;
26
+ title?: string;
27
+ };
28
+
29
+ export function MdxMultiblock({
30
+ id,
31
+ name,
32
+ spec,
33
+ label,
34
+ dataset: datasetProp,
35
+ title,
36
+ }: MdxMultiblockProps) {
37
+ const pathname = usePathname();
38
+ const locId = parseSectionLocation(pathname).dataset;
39
+ const dataset = useMemo<DatasetRef>(
40
+ () => datasetProp ?? { id: locId ?? '' },
41
+ [datasetProp, locId],
42
+ );
43
+ const lang = useContext(ResolvedLangContext)?.lang;
44
+ const dumpLoc = useDumpLocation(dataset);
45
+ const resolveResource = useMemo(
46
+ () => createDumpAssetResolver(dataset),
47
+ [dataset],
48
+ );
49
+ const namedSpec = useAsyncValue(
50
+ dataset && lang && name
51
+ ? async () =>
52
+ (await loadMultiblockNameIndex(dataset, lang))[
53
+ name.trim().toLowerCase()
54
+ ]
55
+ : false,
56
+ [dataset, lang, name],
57
+ );
58
+ const value = useMemo<string | GuideMultiblock | undefined>(() => {
59
+ if (spec) {
60
+ try {
61
+ return JSON.parse(spec) as GuideMultiblock;
62
+ } catch {
63
+ return undefined;
64
+ }
65
+ }
66
+ if (name) return namedSpec;
67
+ return id;
68
+ }, [id, name, spec, namedSpec]);
69
+ const key = multiblockKey(asMultiblock(value));
70
+ const src = useAsyncValue(
71
+ dataset && key
72
+ ? () => glbFor(dataset, '', asMultiblock(value), lang)
73
+ : false,
74
+ [dataset, key, lang, dumpLoc],
75
+ );
76
+ const glbs = src && key ? { [key]: src } : undefined;
77
+ return (
78
+ <MultiblockVisual
79
+ value={value}
80
+ title={title ?? label ?? id ?? name}
81
+ glbs={glbs}
82
+ resolveResource={resolveResource}
83
+ />
84
+ );
85
+ }
86
+
@@ -2,13 +2,17 @@ import { createElement } from 'react';
2
2
  import type * as PageTree from 'fumadocs-core/page-tree';
3
3
  import { Entry } from '@terrafirmagreg-team/game/components/entry/entry';
4
4
  import { encodeQueryValue } from '@terrafirmagreg-team/translate/lib/url';
5
- import { sectionUrl } from '@terrafirmagreg-team/kit/lib/location';
5
+ import {
6
+ type DatasetRef,
7
+ sectionUrl,
8
+ versionSuffix,
9
+ } from '@terrafirmagreg-team/sites/location';
6
10
  import { bookOptionId, guideCategoryHref } from '@terrafirmagreg-team/guide/lib/paths';
7
11
  import type { GuideIndex } from '@terrafirmagreg-team/guide/types';
8
- import type { TaggedReference } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
12
+ import type { TaggedReference } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
9
13
 
10
14
  function nodeIcon(
11
- dataset: string,
15
+ dataset: DatasetRef,
12
16
  icon: TaggedReference | undefined,
13
17
  name: string,
14
18
  ): React.ReactNode {
@@ -34,7 +38,7 @@ function nodeIcon(
34
38
  }
35
39
 
36
40
  export function guideTreeChildren(
37
- dataset: string,
41
+ dataset: DatasetRef,
38
42
  index: GuideIndex,
39
43
  ): PageTree.Node[] {
40
44
  const bookId =
@@ -56,15 +60,16 @@ export function guideTreeChildren(
56
60
  index.bookPath ?? '',
57
61
  category,
58
62
  ),
59
- $id: `${dataset}/${index.modId}/${index.bookPath}/${category.id}#index`,
63
+ $id: `${dataset.id}/${dataset.version ?? ''}/${index.modId}/${index.bookPath}/${category.id}#index`,
60
64
  },
61
- $id: `${dataset}/${index.modId}/${index.bookPath}/${category.id}`,
65
+ $id: `${dataset.id}/${dataset.version ?? ''}/${index.modId}/${index.bookPath}/${category.id}`,
62
66
  children: category.entries.map((entry): PageTree.Item => ({
63
67
  type: 'page',
64
68
  name: entry.name,
65
- url: `${sectionUrl('guide', dataset)}?book=${encodeQueryValue(bookId ?? '')}&entry=${encodeQueryValue(entry.id)}`,
69
+ url: `${sectionUrl('guide', dataset.id)}?book=${encodeQueryValue(bookId ?? '')}&entry=${encodeQueryValue(entry.id)}${versionSuffix(dataset)}`,
66
70
  icon: nodeIcon(dataset, entry.icon, entry.name),
67
- $id: `${dataset}/${index.modId}/${index.bookPath}/${category.id}/${entry.id}`,
71
+ $id: `${dataset.id}/${dataset.version ?? ''}/${index.modId}/${index.bookPath}/${category.id}/${entry.id}`,
68
72
  })),
69
73
  }));
70
74
  }
75
+
@@ -2,14 +2,20 @@ import 'server-only';
2
2
  import { createElement } from 'react';
3
3
  import type * as PageTree from 'fumadocs-core/page-tree';
4
4
  import { Entry } from '@terrafirmagreg-team/game/components/entry/entry';
5
- import { loadMergedLangMap } from '@terrafirmagreg-team/game/lib/dump/assets/lang/load';
6
- import { asRecord, asString } from '@terrafirmagreg-team/game/lib/dump/contract/read';
7
- import { loadDatasetDump } from '@terrafirmagreg-team/game/lib/dump/mods/load';
8
- import { clientDumpJson } from '@terrafirmagreg-team/game/lib/dump/client/dump';
5
+ import { loadMergedLangMap } from '@terrafirmagreg-team/dump/lib/dump/assets/lang/load';
6
+ import { asRecord, asString } from '@terrafirmagreg-team/dump/lib/dump/contract/read';
7
+ import { loadDatasetDump } from '@terrafirmagreg-team/dump/lib/dump/mods/load';
8
+ import { DUMP_POINTER_TTL } from '@terrafirmagreg-team/dump/lib/dump/json-reader';
9
+ import { createAsyncCache } from '@terrafirmagreg-team/data/lib/async-cache';
10
+ import { clientDumpJson } from '@terrafirmagreg-team/dump/lib/dump/client/dump';
9
11
  import { keyTreeNode } from '@terrafirmagreg-team/translate/server';
10
12
  import { loadLocaleRegistry } from '@terrafirmagreg-team/translate/server';
11
13
  import { encodeQueryValue } from '@terrafirmagreg-team/translate/lib/url';
12
- import { sectionUrl } from '@terrafirmagreg-team/kit/lib/location';
14
+ import {
15
+ type DatasetRef,
16
+ sectionUrl,
17
+ versionSuffix,
18
+ } from '@terrafirmagreg-team/sites/location';
13
19
  import { bookOptionId } from '@terrafirmagreg-team/guide/lib/paths';
14
20
  import { bookSlug, toBook } from '@terrafirmagreg-team/guide/lib/book';
15
21
  import { sectionDatasets } from '@terrafirmagreg-team/kit/lib/datasets';
@@ -17,7 +23,7 @@ import type { GuideBookJson, GuideBookRef } from '@terrafirmagreg-team/guide/lib
17
23
  import type { SectionTrees } from '@terrafirmagreg-team/kit/types';
18
24
 
19
25
  function bookIcon(
20
- dataset: string,
26
+ dataset: DatasetRef,
21
27
  raw: GuideBookJson | undefined,
22
28
  ): React.ReactNode {
23
29
  const cover = asRecord(raw?.cover);
@@ -43,7 +49,7 @@ function bookIcon(
43
49
  }
44
50
 
45
51
  function bookLeaf(
46
- dataset: string,
52
+ dataset: DatasetRef,
47
53
  ref: GuideBookRef,
48
54
  slug: string,
49
55
  raw: GuideBookJson | undefined,
@@ -54,14 +60,14 @@ function bookLeaf(
54
60
  return keyTreeNode({
55
61
  type: 'page',
56
62
  name: book.name,
57
- url: `${sectionUrl('guide', dataset)}?book=${encodeQueryValue(bookId)}`,
63
+ url: `${sectionUrl('guide', dataset.id)}?book=${encodeQueryValue(bookId)}${versionSuffix(dataset)}`,
58
64
  icon: bookIcon(dataset, raw),
59
- $id: `${dataset}/book/${bookId}`,
65
+ $id: `${dataset.id}/${dataset.version ?? ''}/book/${bookId}`,
60
66
  }) as PageTree.Item;
61
67
  }
62
68
 
63
69
  async function datasetBookLeaves(
64
- dataset: string,
70
+ dataset: DatasetRef,
65
71
  langMap: Record<string, string>,
66
72
  ): Promise<PageTree.Node[]> {
67
73
  const dump = await loadDatasetDump(dataset);
@@ -92,11 +98,14 @@ async function datasetBookLeaves(
92
98
  return leaves;
93
99
  }
94
100
 
95
- let cached: Promise<SectionTrees> | undefined;
101
+ const guideTreesCache = createAsyncCache(
102
+ async (): Promise<SectionTrees> => buildGuideTrees(),
103
+ () => 'guide-trees',
104
+ DUMP_POINTER_TTL,
105
+ );
96
106
 
97
107
  export function guideTrees(): Promise<SectionTrees> {
98
- cached ??= buildGuideTrees();
99
- return cached;
108
+ return guideTreesCache();
100
109
  }
101
110
 
102
111
  async function buildGuideTrees(): Promise<SectionTrees> {
@@ -105,13 +114,14 @@ async function buildGuideTrees(): Promise<SectionTrees> {
105
114
  const trees: SectionTrees = {};
106
115
  for (const { id, title: datasetTitle, languages } of datasets) {
107
116
  for (const lang of languages ?? [defaultLanguage]) {
108
- const map = await loadMergedLangMap(id, lang);
117
+ const map = await loadMergedLangMap({ id }, lang);
109
118
  (trees[lang] ??= {})[id] = {
110
119
  name: datasetTitle ?? id,
111
- children: await datasetBookLeaves(id, map),
120
+ children: await datasetBookLeaves({ id }, map),
112
121
  $id: `guide/${id}/${lang}`,
113
122
  } satisfies PageTree.Root;
114
123
  }
115
124
  }
116
125
  return trees;
117
126
  }
127
+
@@ -1,8 +1,9 @@
1
1
  'use client';
2
-
3
2
  import { useEffect, useMemo } from 'react';
4
- import { useRouter, useSearchParams } from 'next/navigation';
3
+ import { useRouter } from 'next/navigation';
4
+ import { useLocationSearchParams } from '@terrafirmagreg-team/ui/hooks/page/use-location-search';
5
5
  import { useAsyncValue } from '@terrafirmagreg-team/ui/hooks/use-async-value';
6
+ import { useVersionedDataset } from '@terrafirmagreg-team/kit/hooks/use-versioned-dataset';
6
7
  import { useLangMap } from '@terrafirmagreg-team/game/hooks/use-lang-map';
7
8
  import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
8
9
  import { Skeleton } from '@terrafirmagreg-team/ui/components/ui/skeleton';
@@ -23,30 +24,21 @@ import { guideConfig } from '@terrafirmagreg-team/guide/lib/config';
23
24
  import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
24
25
  import type { GuideMessages } from '@terrafirmagreg-team/guide/types';
25
26
 
26
- const MESSAGE_KEYS: readonly string[] = [
27
- 'guide.related',
28
- 'guide.objective',
29
- 'guide.link',
30
- 'guide.quest',
31
- 'guide.recipe_empty',
32
- 'guide.entity_unavailable',
33
- 'guide.entry_missing',
34
- 'guide.entry_count',
35
- 'guide.page_count',
36
- ];
37
-
38
27
  export function GuideBrowser({
39
- dataset,
28
+ dataset: id,
40
29
  datasetTitle,
41
30
  lang,
31
+ versions,
42
32
  }: {
43
33
  dataset: string;
44
34
  datasetTitle: string;
45
35
  lang: Lang;
36
+ versions?: readonly string[];
46
37
  }) {
47
38
  const t = useTranslations();
48
39
  const router = useRouter();
49
- const params = useSearchParams();
40
+ const params = useLocationSearchParams();
41
+ const dataset = useVersionedDataset(id, { versions }) ?? { id };
50
42
  const { bookId, entryId, categoryId } = useMemo(
51
43
  () => guideQueryFromParams(params),
52
44
  [params],
@@ -57,7 +49,23 @@ export function GuideBrowser({
57
49
  );
58
50
  const langMap = useLangMap(dataset, lang);
59
51
  const messages = useMemo<GuideMessages>(
60
- () => Object.fromEntries(MESSAGE_KEYS.map((key) => [key, t(key)])),
52
+ () => ({
53
+ 'guide.related': t('Related', { note: 'guide.related' }),
54
+ 'guide.objective': t('Objective', { note: 'guide.objective' }),
55
+ 'guide.link': t('Open', { note: 'guide.link' }),
56
+ 'guide.quest': t('Quest', { note: 'guide.quest' }),
57
+ 'guide.recipe_empty': t('No recipes', { note: 'guide.recipe_empty' }),
58
+ 'guide.entity_unavailable': t('Entity preview unavailable', {
59
+ note: 'guide.entity_unavailable',
60
+ }),
61
+ 'guide.entry_missing': t('Entry not found', {
62
+ note: 'guide.entry_missing',
63
+ }),
64
+ 'guide.entry_count': t('{count} entries', {
65
+ note: 'guide.entry_count',
66
+ }),
67
+ 'guide.page_count': t('{count} pages', { note: 'guide.page_count' }),
68
+ }),
61
69
  [t],
62
70
  );
63
71
 
@@ -122,3 +130,4 @@ export function GuideBrowser({
122
130
  }
123
131
  return <GuidePageView payload={payload} langMap={langMap} />;
124
132
  }
133
+
@@ -1,3 +1,4 @@
1
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
1
2
  import type { ReactNode } from 'react';
2
3
  import { PageText } from '@terrafirmagreg-team/guide/components/views/page/text';
3
4
  import {
@@ -19,7 +20,7 @@ import {
19
20
  import { TablePage } from '@terrafirmagreg-team/guide/components/views/kind/table';
20
21
  import { UnknownPage } from '@terrafirmagreg-team/guide/components/views/kind/unknown';
21
22
  import type { PageRendererProps } from '@terrafirmagreg-team/guide/types';
22
- import type { AtlasBundle } from '@terrafirmagreg-team/game/lib/dump/assets/atlas/dto';
23
+ import type { AtlasBundle } from '@terrafirmagreg-team/dump/lib/dump/assets/atlas/dto';
23
24
  import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
24
25
  import { guideMessage } from '@terrafirmagreg-team/guide/lib/messages';
25
26
  import type { PagePreviewData } from '@terrafirmagreg-team/ui/types/preview';
@@ -79,7 +80,7 @@ export function EntryPages({
79
80
  previews,
80
81
  messages,
81
82
  }: {
82
- dataset: string;
83
+ dataset: DatasetRef;
83
84
  lang: Lang;
84
85
  bookPath: string;
85
86
  pages: GuidePage[];
@@ -184,3 +185,4 @@ export function EntryPages({
184
185
  </div>
185
186
  );
186
187
  }
188
+
@@ -1,3 +1,4 @@
1
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
1
2
  import { EntryPages } from '@terrafirmagreg-team/guide/components/views/entry/pages';
2
3
  import type { PagePreviewData } from '@terrafirmagreg-team/ui/types/preview';
3
4
  import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
@@ -24,7 +25,7 @@ export function EntryView({
24
25
  previews,
25
26
  messages,
26
27
  }: {
27
- dataset: string;
28
+ dataset: DatasetRef;
28
29
  lang: Lang;
29
30
  bookPath: string;
30
31
  entry: GuideEntry;
@@ -58,3 +59,4 @@ export function EntryView({
58
59
  />
59
60
  );
60
61
  }
62
+
@@ -1,6 +1,6 @@
1
1
  import { LinkPreview } from '@terrafirmagreg-team/ui/components/blocks/link/link-preview';
2
2
  import { Entry } from '@terrafirmagreg-team/game/components/entry/entry';
3
- import { questHref } from '@terrafirmagreg-team/quest/lib/links';
3
+ import { guideEmbeds } from '@terrafirmagreg-team/guide/lib/embeds';
4
4
  import { guideMessage } from '@terrafirmagreg-team/guide/lib/messages';
5
5
  import {
6
6
  entryLabel,
@@ -92,7 +92,7 @@ export function QuestPage({
92
92
  const id = page.trigger || page.quest || page.link;
93
93
  const hit = id ? questLinks?.[parseGuideReference(id).id] : undefined;
94
94
  const href = hit
95
- ? questHref(dataset, lang, { category: hit.filename, id: hit.questId })
95
+ ? guideEmbeds().quest?.href(dataset, lang, { category: hit.filename, id: hit.questId })
96
96
  : undefined;
97
97
  const name = id ? spriteName(dataset, langMap, atlases, id) : '—';
98
98
  const label = id && name !== id ? name : <code>{name}</code>;
@@ -109,3 +109,4 @@ export function QuestPage({
109
109
  </p>
110
110
  );
111
111
  }
112
+
@@ -5,11 +5,11 @@ import { asMultiblock } from '@terrafirmagreg-team/guide/lib/meta';
5
5
  import {
6
6
  createDumpAssetResolver,
7
7
  texturePublicUrl,
8
- } from '@terrafirmagreg-team/game/lib/dump/assets/urls';
9
- import { dumpRelPublicUrl } from '@terrafirmagreg-team/game/lib/dump/paths';
10
- import { parseStackId } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
11
- import { langString } from '@terrafirmagreg-team/game/lib/dump/assets/lang/names';
12
- import { matchVariant } from '@terrafirmagreg-team/game/lib/dump/data/registry/parse';
8
+ } from '@terrafirmagreg-team/dump/lib/dump/assets/urls';
9
+ import { dumpRelPublicUrl } from '@terrafirmagreg-team/dump/lib/dump/paths';
10
+ import { parseStackId } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
11
+ import { langString } from '@terrafirmagreg-team/dump/lib/dump/assets/lang/names';
12
+ import { matchVariant } from '@terrafirmagreg-team/dump/lib/dump/data/registry/parse';
13
13
  import { GlbViewer } from '@terrafirmagreg-team/ui/components/blocks/media/glb/viewer';
14
14
  import { multiblockKey } from '@terrafirmagreg-team/guide/lib/multiblock';
15
15
  import { guideFormatOpts } from '@terrafirmagreg-team/guide/lib/format';
@@ -165,3 +165,4 @@ export function MultiMultiblockPage({
165
165
  <MissingVisual title={page.name} />
166
166
  );
167
167
  }
168
+
@@ -1,11 +1,7 @@
1
1
  'use client';
2
-
3
- import { useState } from 'react';
4
- import Link from 'next/link';
5
2
  import { Card } from '@terrafirmagreg-team/ui/components/ui/card';
6
3
  import { EmptyState } from '@terrafirmagreg-team/ui/components/blocks/page/empty-state';
7
- import { PagedCard } from '@terrafirmagreg-team/recipe/components/cards/paged-card';
8
- import { FitCard } from '@terrafirmagreg-team/recipe/components/cards/fit-card';
4
+ import { guideEmbeds } from '@terrafirmagreg-team/guide/lib/embeds';
9
5
  import { Entry } from '@terrafirmagreg-team/game/components/entry/entry';
10
6
  import { EntryList } from '@terrafirmagreg-team/game/components/entry/list';
11
7
  import { guideMessage } from '@terrafirmagreg-team/guide/lib/messages';
@@ -13,51 +9,8 @@ import { collectItemIds } from '@terrafirmagreg-team/guide/lib/data/parse';
13
9
  import { pageTitle } from '@terrafirmagreg-team/guide/lib/toc';
14
10
  import { spriteName } from '@terrafirmagreg-team/guide/lib/names';
15
11
  import { recipeIds, recipeNameScope } from '@terrafirmagreg-team/guide/lib/recipe';
16
- import { recipeHref } from '@terrafirmagreg-team/recipe/lib/links';
17
- import { recipeCardImageUrl } from '@terrafirmagreg-team/recipe/lib/links';
18
- import { recipeImageUrl } from '@terrafirmagreg-team/game/lib/dump/assets/urls';
19
- import { recipeTitle } from '@terrafirmagreg-team/recipe/lib/data/parse';
20
- import type { Recipe } from '@terrafirmagreg-team/recipe/lib/data/dto';
21
- import type { PageRendererProps } from '@terrafirmagreg-team/guide/types';
22
12
 
23
- function GuideRecipeCard({
24
- id,
25
- recipe,
26
- dataset,
27
- lang,
28
- langMap,
29
- }: {
30
- id: string;
31
- recipe: Recipe;
32
- dataset: string;
33
- lang: string;
34
- langMap: Record<string, string>;
35
- }) {
36
- const [failed, setFailed] = useState(false);
37
- if (failed) return null;
38
- const src =
39
- recipeImageUrl(dataset, recipe) ?? recipeCardImageUrl(dataset, id);
40
- if (!src) return null;
41
- const title = recipeTitle(recipe, langMap);
42
- return (
43
- <Link
44
- href={recipeHref(dataset, lang, { id })}
45
- className="not-prose block w-fit max-w-full"
46
- >
47
- <FitCard w={recipe.w ?? 0}>
48
- <PagedCard
49
- recipe={recipe}
50
- dataset={dataset}
51
- lang={lang}
52
- langMap={langMap}
53
- title={title}
54
- src={src}
55
- onFail={() => setFailed(true)}
56
- />
57
- </FitCard>
58
- </Link>
59
- );
60
- }
13
+ import type { PageRendererProps } from '@terrafirmagreg-team/guide/types';
61
14
 
62
15
  export function RecipePage({
63
16
  dataset,
@@ -76,13 +29,14 @@ export function RecipePage({
76
29
  </EmptyState>
77
30
  );
78
31
  }
32
+ const RecipeCard = guideEmbeds().recipe?.Card;
79
33
  return (
80
34
  <div className="space-y-4">
81
35
  {ids.map((id) => {
82
36
  const recipe = extras?.recipes?.[id];
83
- if (recipe) {
37
+ if (recipe && RecipeCard) {
84
38
  return (
85
- <GuideRecipeCard
39
+ <RecipeCard
86
40
  key={id}
87
41
  id={id}
88
42
  recipe={recipe}
@@ -132,3 +86,4 @@ export function SpotlightPage({
132
86
  />
133
87
  );
134
88
  }
89
+