@terrafirmagreg-team/guide 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.
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
@@ -2,9 +2,9 @@ import {
2
2
  asTaggedReference,
3
3
  stackIdFromRef,
4
4
  type TaggedReference,
5
- } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
6
- import { asString } from '@terrafirmagreg-team/game/lib/dump/contract/read';
7
- import { referenceFrom, type ResourceKind } from '@terrafirmagreg-team/game/lib/dump/contract/kind';
5
+ } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
6
+ import { asString } from '@terrafirmagreg-team/dump/lib/dump/contract/read';
7
+ import { referenceFrom, type ResourceKind } from '@terrafirmagreg-team/dump/lib/dump/contract/kind';
8
8
 
9
9
  function itemstackIds(value: string): string[] {
10
10
  return value
@@ -91,3 +91,4 @@ export function classifyGuidePageType(type: string | undefined): ResourceKind |
91
91
  return undefined;
92
92
  }
93
93
  }
94
+
package/src/lib/dump.ts CHANGED
@@ -1,5 +1,6 @@
1
- import type { DumpIndex } from '@terrafirmagreg-team/game/lib/dump/mods/dto';
2
-
3
- export function guideDumpHasContent(dump: DumpIndex): boolean {
1
+ export function guideDumpHasContent(dump: {
2
+ mods: readonly { books: readonly unknown[] }[];
3
+ }): boolean {
4
4
  return dump.mods.some((mod) => mod.books.length > 0);
5
5
  }
6
+
@@ -0,0 +1,12 @@
1
+ import { createConfig } from '@terrafirmagreg-team/kit/lib/config';
2
+ import type { SectionEmbeds } from '@terrafirmagreg-team/kit/types/embeds';
3
+
4
+ export type {
5
+ EmbedRecipe,
6
+ EmbedRecipeCardProps,
7
+ EmbedQuestLocation,
8
+ SectionEmbeds,
9
+ } from '@terrafirmagreg-team/kit/types/embeds';
10
+
11
+ export const { register: registerGuideEmbeds, get: guideEmbeds } =
12
+ createConfig<SectionEmbeds>('GuideEmbeds');
package/src/lib/format.ts CHANGED
@@ -3,8 +3,8 @@ import type {
3
3
  GuideFormatOptions,
4
4
  PageRendererProps,
5
5
  } from '@terrafirmagreg-team/guide/types';
6
- import { MC_FORMAT_COLORS } from '@terrafirmagreg-team/game/lib/dump/contract/text';
7
- import { langString } from '@terrafirmagreg-team/game/lib/dump/assets/lang/names';
6
+ import { MC_FORMAT_COLORS } from '@terrafirmagreg-team/dump/lib/dump/contract/text';
7
+ import { langString } from '@terrafirmagreg-team/dump/lib/dump/assets/lang/names';
8
8
 
9
9
  type State = {
10
10
  styles: string[];
@@ -229,3 +229,4 @@ export function keybindLabels(
229
229
  title: bindName,
230
230
  };
231
231
  }
232
+
package/src/lib/links.ts CHANGED
@@ -1,2 +1,2 @@
1
-
2
1
  export { guideHref } from '@terrafirmagreg-team/game/lib/entry/href';
2
+
@@ -71,3 +71,4 @@ export function guideCategoryMarkdown(
71
71
  if (items.length) parts.push(items.join('\n'));
72
72
  return `${parts.filter(Boolean).join('\\')}\n`;
73
73
  }
74
+
@@ -1,8 +1,9 @@
1
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
1
2
  import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
2
3
  import type { GuideReferenceContext } from '@terrafirmagreg-team/guide/types';
3
4
 
4
5
  export type GuideMarkdownCtx = {
5
- dataset: string;
6
+ dataset: DatasetRef;
6
7
  lang: Lang;
7
8
  langMap: Record<string, string>;
8
9
  keybinds?: Record<string, string>;
@@ -24,3 +25,4 @@ export function refCtx(ctx: GuideMarkdownCtx): GuideReferenceContext {
24
25
  questLinks: ctx.questLinks,
25
26
  };
26
27
  }
28
+
@@ -6,3 +6,4 @@ export {
6
6
  export type { GuideMarkdownCtx } from './ctx';
7
7
  export { guidePageMarkdown } from './page';
8
8
  export { guideTextMarkdown } from './text';
9
+
@@ -5,12 +5,10 @@ import type {
5
5
  import { classify, isIndexPage, pageTexts } from '@terrafirmagreg-team/guide/lib/page-kind';
6
6
  import { guideLink } from '@terrafirmagreg-team/guide/lib/reference';
7
7
  import { bareId } from '@terrafirmagreg-team/guide/lib/paths';
8
- import { recipeHref } from '@terrafirmagreg-team/recipe/lib/links';
9
- import { questHref } from '@terrafirmagreg-team/quest/lib/links';
10
- import { recipeTitle } from '@terrafirmagreg-team/recipe/lib/data/parse';
11
- import { texturePublicUrl } from '@terrafirmagreg-team/game/lib/dump/assets/urls';
8
+ import { guideEmbeds } from '@terrafirmagreg-team/guide/lib/embeds';
9
+ import { texturePublicUrl } from '@terrafirmagreg-team/dump/lib/dump/assets/urls';
12
10
  import { collectItemIds } from '@terrafirmagreg-team/guide/lib/data/parse';
13
- import { stackDisplayName } from '@terrafirmagreg-team/game/lib/dump/assets/lang/names';
11
+ import { stackDisplayName } from '@terrafirmagreg-team/dump/lib/dump/assets/lang/names';
14
12
  import { type GuideMarkdownCtx, refCtx } from './ctx';
15
13
  import { guideTextMarkdown } from './text';
16
14
  import { tableMarkdown } from './table';
@@ -70,8 +68,8 @@ export function guidePageMarkdown(
70
68
  ];
71
69
  for (const id of ids) {
72
70
  const recipe = payload?.extras?.recipes[id];
73
- const label = recipe ? recipeTitle(recipe, ctx.langMap) : id;
74
- parts.push(`- [${label}](${recipeHref(ctx.dataset, undefined, { id })})`);
71
+ const label = recipe ? guideEmbeds().recipe?.title(recipe, ctx.langMap) : id;
72
+ parts.push(`- [${label}](${guideEmbeds().recipe?.href(ctx.dataset, undefined, { id })})`);
75
73
  }
76
74
  for (const raw of pageTexts(page)) parts.push(guideTextMarkdown(raw, ctx));
77
75
  break;
@@ -131,7 +129,7 @@ export function guidePageMarkdown(
131
129
  const url =
132
130
  href ??
133
131
  (hit
134
- ? questHref(ctx.dataset, undefined, {
132
+ ? guideEmbeds().quest?.href(ctx.dataset, undefined, {
135
133
  category: hit.filename,
136
134
  id: hit.questId,
137
135
  })
@@ -152,3 +150,4 @@ export function guidePageMarkdown(
152
150
  }
153
151
  return parts.filter(Boolean).join('\n\n');
154
152
  }
153
+
@@ -33,3 +33,4 @@ export function tableMarkdown(page: GuidePage, ctx: GuideMarkdownCtx): string {
33
33
  lines.splice(1, 0, `| ${Array.from({ length: width }, () => '---').join(' | ')} |`);
34
34
  return lines.join('\n');
35
35
  }
36
+
@@ -37,3 +37,4 @@ export function guideTextMarkdown(
37
37
  .replace(/\n{3,}/g, '\n\n')
38
38
  .trim();
39
39
  }
40
+
@@ -1,6 +1,7 @@
1
- import { registerMarkdownProvider } from '@terrafirmagreg-team/kit/lib/markdown';
1
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
+ import { registerMarkdownProvider } from '@terrafirmagreg-team/mdx/lib/markdown';
2
3
  import { loadClientGuidePayload } from '@terrafirmagreg-team/guide/lib/client-payload';
3
- import { clientDumpLang } from '@terrafirmagreg-team/game/lib/dump/client/dump';
4
+ import { clientDumpLang } from '@terrafirmagreg-team/dump/lib/dump/client/dump';
4
5
  import { categoryMap } from '@terrafirmagreg-team/guide/lib/breadcrumb';
5
6
  import {
6
7
  guideCategoryMarkdown,
@@ -13,7 +14,7 @@ import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
13
14
 
14
15
  export async function guideMarkdown(
15
16
  req: Request,
16
- dataset: string | undefined,
17
+ dataset: DatasetRef | undefined,
17
18
  lang: Lang,
18
19
  ): Promise<Response | undefined> {
19
20
  if (!dataset) return undefined;
@@ -68,5 +69,6 @@ export async function guideMarkdown(
68
69
  }
69
70
 
70
71
  registerMarkdownProvider('guide', (req, { slugs, lang }) =>
71
- guideMarkdown(req, slugs[0], lang),
72
+ guideMarkdown(req, slugs[0] ? { id: slugs[0] } : undefined, lang),
72
73
  );
74
+
@@ -11,3 +11,4 @@ export function guideMessage(
11
11
  }
12
12
  return text;
13
13
  }
14
+
package/src/lib/meta.ts CHANGED
@@ -7,3 +7,4 @@ export function asMultiblock(
7
7
  if (typeof value === 'string') return { id: value };
8
8
  return { data: value };
9
9
  }
10
+
@@ -6,3 +6,4 @@ export function multiblockKey(
6
6
  if (!spec) return undefined;
7
7
  return spec.id ?? (spec.data ? `data:${JSON.stringify(spec.data)}` : undefined);
8
8
  }
9
+
@@ -1,12 +1,15 @@
1
- import { asRecord, asString } from '@terrafirmagreg-team/game/lib/dump/contract/read';
2
- import { splitDumpId } from '@terrafirmagreg-team/game/lib/dump/paths';
3
- import { modelPublicUrl } from '@terrafirmagreg-team/game/lib/dump/assets/urls';
1
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
+ import { asRecord, asString } from '@terrafirmagreg-team/dump/lib/dump/contract/read';
3
+ import { splitDumpId } from '@terrafirmagreg-team/dump/lib/dump/paths';
4
+ import { modelPublicUrl } from '@terrafirmagreg-team/dump/lib/dump/assets/urls';
4
5
  import {
5
6
  clientDumpJson,
6
7
  dumpModelUrl,
7
- loadClientDump,
8
- } from '@terrafirmagreg-team/game/lib/dump/client/dump';
9
- import { createAsyncCache } from '@terrafirmagreg-team/ui/lib/utils/async-cache';
8
+ } from '@terrafirmagreg-team/dump/lib/dump/client/dump';
9
+ import { loadDatasetDump } from '@terrafirmagreg-team/dump/lib/dump/mods/load';
10
+ import { createDatasetCache } from '@terrafirmagreg-team/dump/lib/dump/reader';
11
+ import { loadMultiblockNameIndex } from '@terrafirmagreg-team/guide/lib/client/guides';
12
+ import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
10
13
  import type { GuideMultiblock } from '@terrafirmagreg-team/guide/types';
11
14
 
12
15
  async function sha256Hex(text: string): Promise<string> {
@@ -21,8 +24,8 @@ function inlineMultiblockHash(data: unknown): Promise<string> {
21
24
  return sha256Hex(JSON.stringify(data));
22
25
  }
23
26
 
24
- const multiblockIndex = createAsyncCache(async (dataset: string) => {
25
- const dump = await loadClientDump(dataset);
27
+ const multiblockIndex = createDatasetCache(async (dataset: DatasetRef) => {
28
+ const dump = await loadDatasetDump(dataset);
26
29
  const entries = await Promise.all(
27
30
  (dump?.mods ?? []).map(async (ns) => {
28
31
  const index = asRecord(
@@ -40,11 +43,21 @@ const multiblockIndex = createAsyncCache(async (dataset: string) => {
40
43
  });
41
44
 
42
45
  export async function glbFor(
43
- dataset: string,
46
+ dataset: DatasetRef,
44
47
  _bookModId: string,
45
48
  spec: { id?: string; data?: GuideMultiblock },
49
+ lang?: string,
46
50
  ): Promise<string | null> {
47
- if (spec.id) return (await dumpModelUrl(dataset, spec.id)) ?? null;
51
+ if (spec.id) {
52
+ const direct = await dumpModelUrl(dataset, spec.id);
53
+ if (direct) return direct;
54
+ if (lang) {
55
+ const names = await loadMultiblockNameIndex(dataset, lang as Lang);
56
+ const named = names[spec.id.trim().toLowerCase()];
57
+ if (named) return glbFor(dataset, _bookModId, { data: named }, lang);
58
+ }
59
+ return null;
60
+ }
48
61
  if (!spec.data) return null;
49
62
  const hash = await inlineMultiblockHash(spec.data);
50
63
  const index = await multiblockIndex(dataset);
@@ -59,3 +72,4 @@ export async function glbFor(
59
72
  }
60
73
  return null;
61
74
  }
75
+
package/src/lib/names.ts CHANGED
@@ -1,11 +1,12 @@
1
- import type { AtlasBundle } from '@terrafirmagreg-team/game/lib/dump/assets/atlas/dto';
2
- import { lookupSpriteInBundle } from '@terrafirmagreg-team/game/lib/dump/assets/atlas/lookup';
1
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
+ import type { AtlasBundle } from '@terrafirmagreg-team/dump/lib/dump/assets/atlas/dto';
3
+ import { lookupSpriteInBundle } from '@terrafirmagreg-team/dump/lib/dump/assets/atlas/lookup';
3
4
  import { stackDisplayName } from '@terrafirmagreg-team/game/lib/entry/display';
4
5
  import { matchGuideEntryKey } from '@terrafirmagreg-team/guide/lib/reference/resolver';
5
6
  import type { GuideEntryRef } from '@terrafirmagreg-team/guide/types';
6
7
 
7
8
  export function spriteName(
8
- dataset: string,
9
+ dataset: DatasetRef,
9
10
  langMap: Record<string, string>,
10
11
  atlases: Record<string, AtlasBundle> | undefined,
11
12
  id: string,
@@ -33,3 +34,4 @@ export function entryLabel(
33
34
  ): string {
34
35
  return entryRef(refs, id)?.name ?? stackDisplayName(langMap, id);
35
36
  }
37
+
@@ -1,5 +1,5 @@
1
1
  import type { GuidePage } from '@terrafirmagreg-team/guide/types';
2
- import { langString } from '@terrafirmagreg-team/game/lib/dump/assets/lang/names';
2
+ import { langString } from '@terrafirmagreg-team/dump/lib/dump/assets/lang/names';
3
3
 
4
4
  export type PageKind =
5
5
  | 'empty'
@@ -115,3 +115,4 @@ export function isIndexPage(
115
115
  return linkListOnly(text);
116
116
  });
117
117
  }
118
+
package/src/lib/paths.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
1
2
  import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
2
3
  import type { GuideCategoryRef } from '@terrafirmagreg-team/guide/types';
3
4
  import { guideHref as guideQueryHref } from '@terrafirmagreg-team/guide/lib/links';
@@ -14,7 +15,7 @@ export function bookOptionId(modId: string, bookPath: string): string {
14
15
  }
15
16
 
16
17
  export function guideBookHref(
17
- dataset: string,
18
+ dataset: DatasetRef,
18
19
  lang: Lang | undefined,
19
20
  modId?: string,
20
21
  bookPath?: string,
@@ -27,7 +28,7 @@ export function guideBookHref(
27
28
  }
28
29
 
29
30
  export function guideCategoryHref(
30
- dataset: string,
31
+ dataset: DatasetRef,
31
32
  lang: Lang | undefined,
32
33
  modId: string,
33
34
  bookPath: string,
@@ -50,3 +51,4 @@ export function isRootCategory(
50
51
  ): boolean {
51
52
  return true;
52
53
  }
54
+
@@ -1,7 +1,7 @@
1
- import { registerPreviewResolver } from '@terrafirmagreg-team/kit/lib/preview';
1
+ import { registerPreviewResolver } from '@terrafirmagreg-team/preview/lib';
2
2
  import { loadClientGuideBook } from '@terrafirmagreg-team/guide/lib/client/guides';
3
3
  import { entryPreviewIcon } from '@terrafirmagreg-team/game/lib/preview';
4
- import type { PreviewResolverCtx } from '@terrafirmagreg-team/kit/lib/preview';
4
+ import type { PreviewResolverCtx } from '@terrafirmagreg-team/preview/lib';
5
5
  import type { PagePreviewData } from '@terrafirmagreg-team/ui/types/preview';
6
6
 
7
7
  export async function guideLinkPreview({
@@ -9,9 +9,12 @@ export async function guideLinkPreview({
9
9
  query,
10
10
  lang,
11
11
  }: PreviewResolverCtx): Promise<PagePreviewData | undefined> {
12
- const dataset = parts[1];
12
+ const dataset = {
13
+ id: parts[1] ?? '',
14
+ version: query.get('version') ?? undefined,
15
+ };
13
16
  const bookId = query.get('book');
14
- if (!dataset || !bookId) return undefined;
17
+ if (!dataset.id || !bookId) return undefined;
15
18
  const index = await loadClientGuideBook(dataset, lang, bookId);
16
19
  if (!index) return undefined;
17
20
  const entryId = query.get('entry');
@@ -31,3 +34,4 @@ export async function guideLinkPreview({
31
34
  }
32
35
 
33
36
  registerPreviewResolver('guide', guideLinkPreview);
37
+
package/src/lib/query.ts CHANGED
@@ -26,3 +26,4 @@ export function firstGuideEntryId(index: GuideIndex): string | undefined {
26
26
  )[0] ?? index.entries[0]?.id
27
27
  );
28
28
  }
29
+
package/src/lib/recipe.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { GuidePage } from '@terrafirmagreg-team/guide/types';
2
- import type { Recipe } from '@terrafirmagreg-team/recipe/lib/data/dto';
2
+ import type { EmbedRecipe } from '@terrafirmagreg-team/guide/lib/embeds';
3
3
  import { stackDisplayName } from '@terrafirmagreg-team/game/lib/entry/display';
4
4
 
5
5
  export function recipeIds(page: GuidePage): string[] {
@@ -10,7 +10,7 @@ export function recipeIds(page: GuidePage): string[] {
10
10
 
11
11
  export function recipeNameScope(
12
12
  langMap: Record<string, string>,
13
- recipe: Recipe,
13
+ recipe: EmbedRecipe,
14
14
  ): Record<string, string> {
15
15
  const ids = new Set<string>([recipe.id]);
16
16
  for (const ing of [...recipe.inputs, ...recipe.outputs]) ids.add(ing.id);
@@ -28,3 +28,4 @@ export function recipeNameScope(
28
28
  for (const id of ids) names[id] = stackDisplayName(langMap, id);
29
29
  return names;
30
30
  }
31
+
@@ -38,3 +38,4 @@ export function guideLink(
38
38
  external: false,
39
39
  };
40
40
  }
41
+
@@ -1,5 +1,4 @@
1
- import { recipeHref } from '@terrafirmagreg-team/recipe/lib/links';
2
- import { questHref } from '@terrafirmagreg-team/quest/lib/links';
1
+ import { guideEmbeds } from '@terrafirmagreg-team/guide/lib/embeds';
3
2
  import {
4
3
  bareId,
5
4
  guideBookHref,
@@ -67,13 +66,13 @@ export function resolveGuideReference(
67
66
  case 'recipe': {
68
67
  const href =
69
68
  context.targets?.recipe?.(reference.id) ??
70
- recipeHref(context.dataset, undefined, { id: reference.id });
69
+ guideEmbeds().recipe?.href(context.dataset, undefined, { id: reference.id });
71
70
  return { ...reference, href, available: Boolean(href) };
72
71
  }
73
72
  case 'quest': {
74
73
  const hit = context.questLinks?.[reference.id];
75
74
  const href = hit
76
- ? questHref(context.dataset, undefined, {
75
+ ? guideEmbeds().quest?.href(context.dataset, undefined, {
77
76
  category: hit.filename,
78
77
  id: hit.questId,
79
78
  })
@@ -86,3 +85,4 @@ export function resolveGuideReference(
86
85
  }
87
86
  }
88
87
  }
88
+
@@ -1,4 +1,4 @@
1
- import { registerSearchHitFilter } from '@terrafirmagreg-team/kit/lib/search';
1
+ import { registerSearchHitFilter } from '@terrafirmagreg-team/search/lib';
2
2
  import type { SortedResult } from '@terrafirmagreg-team/ui/types/search';
3
3
 
4
4
  export function guideHitFilter(
@@ -15,3 +15,4 @@ export function guideHitFilter(
15
15
  }
16
16
 
17
17
  registerSearchHitFilter('guide', guideHitFilter);
18
+
@@ -1,7 +1,9 @@
1
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
1
2
  import type { AdvancedIndex } from 'fumadocs-core/search/server';
2
- import { dumpTextString, langString } from '@terrafirmagreg-team/game/lib/dump/assets/lang/names';
3
+ import { dumpTextString, langString } from '@terrafirmagreg-team/dump/lib/dump/assets/lang/names';
4
+ import { formatGuide } from '@terrafirmagreg-team/guide/lib/format';
3
5
  import { isIndexPage } from '@terrafirmagreg-team/guide/lib/page-kind';
4
- import type { DumpIndex } from '@terrafirmagreg-team/game/lib/dump/mods/dto';
6
+ import type { DumpIndex } from '@terrafirmagreg-team/dump/lib/dump/mods/dto';
5
7
 
6
8
  export type GuideIndex = AdvancedIndex & { locale: string };
7
9
 
@@ -12,25 +14,41 @@ export interface DumpSource {
12
14
 
13
15
  type DumpTextNode = Parameters<typeof dumpTextString>[1];
14
16
 
17
+ function plainText(raw: string, macros?: Record<string, string>): string {
18
+ let out = '';
19
+ for (const node of formatGuide(raw, { macros })) {
20
+ if (node.type === 'break') out += '\n';
21
+ else out += (node.type === 'command' ? node.label : undefined) ?? node.value;
22
+ }
23
+ return out
24
+ .replace(/\s*\$\([^)]*$/, '')
25
+ .replace(/[ \t]*\n[ \t]*/g, '\n')
26
+ .replace(/\n{3,}/g, '\n\n')
27
+ .trim();
28
+ }
29
+
15
30
  function resolveText(
16
31
  map: Record<string, string>,
17
32
  node: unknown,
33
+ macros?: Record<string, string>,
18
34
  ): string | undefined {
19
35
  if (typeof node === 'string') {
20
36
  const bare =
21
37
  node.startsWith('{') && node.endsWith('}')
22
38
  ? node.slice(1, -1).trim()
23
39
  : undefined;
24
- return langString(map, node) ?? bare ?? (bare ? undefined : node);
40
+ const resolved = langString(map, node) ?? bare ?? (bare ? undefined : node);
41
+ return resolved ? plainText(resolved, macros) || undefined : undefined;
25
42
  }
26
43
  if (Array.isArray(node)) {
27
44
  const parts = node
28
- .map((item) => resolveText(map, item))
45
+ .map((item) => resolveText(map, item, macros))
29
46
  .filter((part): part is string => Boolean(part));
30
47
  return parts.length ? parts.join('\n') : undefined;
31
48
  }
32
49
  if (node && typeof node === 'object') {
33
- return dumpTextString(map, node as DumpTextNode) || undefined;
50
+ const resolved = dumpTextString(map, node as DumpTextNode);
51
+ return resolved ? plainText(resolved, macros) || undefined : undefined;
34
52
  }
35
53
  return undefined;
36
54
  }
@@ -49,40 +67,47 @@ interface RawEntry {
49
67
  interface RawBook {
50
68
  id?: unknown;
51
69
  entries?: Record<string, string>;
70
+ macros?: Record<string, string>;
52
71
  }
53
72
 
54
73
  function entryIndex(
55
- dataset: string,
74
+ dataset: DatasetRef,
56
75
  bookId: string,
57
76
  entryId: string,
58
77
  entry: RawEntry,
59
78
  map: Record<string, string>,
60
79
  lang: string,
80
+ macros?: Record<string, string>,
61
81
  ) {
62
82
  const title =
63
- resolveText(map, entry.display?.name) ?? entryId.split('/').pop() ?? entryId;
83
+ resolveText(map, entry.display?.name, macros) ??
84
+ entryId.split('/').pop() ??
85
+ entryId;
64
86
  const contents = (entry.pages ?? [])
65
87
  .map((page) => {
66
88
  if (isIndexPage(page, map)) return null;
67
- const heading = resolveText(map, page.title);
68
- const content = resolveText(map, page.texts);
89
+ const heading = resolveText(map, page.title, macros);
90
+ const content = resolveText(map, page.texts, macros);
69
91
  return content ? { heading: heading ?? title, content } : null;
70
92
  })
71
93
  .filter((c): c is { heading: string; content: string } => c !== null);
72
94
  const body = contents.map((c) => c.content).join('\n');
73
95
  if (!body && !title) return null;
96
+ const params = new URLSearchParams({ book: bookId, entry: entryId });
97
+ if (dataset.version) params.set('version', dataset.version);
98
+ const refKey = `${dataset.id}/${dataset.version ?? ''}`;
74
99
  return {
75
100
  title,
76
- url: `/guide/${dataset}?book=${bookId}&entry=${entryId}`,
77
- id: `guide:${dataset}:${bookId}:${entryId}:${lang}`,
78
- tag: `guide:${dataset}`,
101
+ url: `/guide/${dataset.id}?${params}`,
102
+ id: `guide:${refKey}:${bookId}:${entryId}:${lang}`,
103
+ tag: `guide:${refKey}`,
79
104
  locale: lang,
80
105
  structuredData: { headings: [], contents },
81
106
  };
82
107
  }
83
108
 
84
109
  async function bookIndexes(
85
- dataset: string,
110
+ dataset: DatasetRef,
86
111
  dir: string,
87
112
  bookId: string,
88
113
  langs: string[],
@@ -123,6 +148,7 @@ async function bookIndexes(
123
148
  entry,
124
149
  maps.get(lang) ?? {},
125
150
  lang,
151
+ book.macros,
126
152
  );
127
153
  if (index) out.push(index);
128
154
  }
@@ -131,7 +157,7 @@ async function bookIndexes(
131
157
  }
132
158
 
133
159
  export async function guideSearchIndexes(
134
- dataset: string,
160
+ dataset: DatasetRef,
135
161
  dump: DumpIndex,
136
162
  source: DumpSource,
137
163
  ): Promise<GuideIndex[]> {
@@ -154,3 +180,4 @@ export async function guideSearchIndexes(
154
180
  return true;
155
181
  });
156
182
  }
183
+
@@ -15,3 +15,4 @@ export function spriteIdsFromEntry(entry: GuideEntry): string[] {
15
15
  }
16
16
  return ids;
17
17
  }
18
+
package/src/lib/table.ts CHANGED
@@ -8,3 +8,4 @@ export function cellTexts(cell: GuideTableCell): string[] {
8
8
  if (Array.isArray(cell.texts)) return cell.texts.filter(Boolean);
9
9
  return typeof cell.text === 'string' && cell.text ? [cell.text] : [];
10
10
  }
11
+
package/src/lib/toc.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { TOCItemType } from 'fumadocs-core/toc';
2
- import type { TaggedReference } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
3
- import { langString } from '@terrafirmagreg-team/game/lib/dump/assets/lang/names';
2
+ import type { TaggedReference } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
3
+ import { langString } from '@terrafirmagreg-team/dump/lib/dump/assets/lang/names';
4
4
  import { stackDisplayName } from '@terrafirmagreg-team/game/lib/entry/display';
5
5
  import {
6
6
  classifyGuidePageType,
@@ -135,3 +135,4 @@ export function guideToc(
135
135
  return [{ title, url: `#${anchors[i]}`, depth: 2 }];
136
136
  });
137
137
  }
138
+
@@ -1,22 +1,14 @@
1
1
  {
2
- "guide": {
3
- "entry_count": "{count} entries",
4
- "page_count": "{count} pages",
5
- "entry_missing": "Entry not found",
6
- "entity_unavailable": "Entity preview unavailable",
7
- "link": "Open",
8
- "objective": "Objective",
9
- "quest": "Quest",
10
- "recipe_empty": "No recipes",
11
- "related": "Related"
12
- },
13
- "ui": {
14
- "select_book": "Select book"
15
- },
16
- "sections": {
17
- "guide": "Guide",
18
- "desc": {
19
- "guide": "Gameplay guide"
20
- }
21
- }
2
+ "Entity preview unavailable(guide.entity_unavailable)": "Entity preview unavailable",
3
+ "Entry not found(guide.entry_missing)": "Entry not found",
4
+ "Gameplay guide(sections.desc.guide)": "Gameplay guide",
5
+ "Guide(sections.guide)": "Guide",
6
+ "No recipes(guide.recipe_empty)": "No recipes",
7
+ "Objective(guide.objective)": "Objective",
8
+ "Open(guide.link)": "Open",
9
+ "Quest(guide.quest)": "Quest",
10
+ "Related(guide.related)": "Related",
11
+ "Select book(ui.select_book)": "Select book",
12
+ "{count} entries(guide.entry_count)": "{count} entries",
13
+ "{count} pages(guide.page_count)": "{count} pages"
22
14
  }