@terrafirmagreg-team/recipe 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 (240) hide show
  1. package/package.json +16 -16
  2. package/src/components/button-copy.tsx +1 -0
  3. package/src/components/cards/card/index.tsx +5 -3
  4. package/src/components/cards/card/scene.tsx +4 -2
  5. package/src/components/cards/card/strip.tsx +4 -2
  6. package/src/components/cards/card/text.tsx +2 -1
  7. package/src/components/cards/card/tips.tsx +1 -0
  8. package/src/components/cards/default-item.tsx +6 -2
  9. package/src/components/cards/glare-filter.tsx +1 -0
  10. package/src/components/cards/paged-card.tsx +1 -0
  11. package/src/components/cards/slot-hitbox.tsx +4 -2
  12. package/src/components/cards/strip-card/buttons.tsx +7 -2
  13. package/src/components/cards/strip-card/index.tsx +3 -1
  14. package/src/components/cards/strip-card/member-cell.tsx +4 -2
  15. package/src/components/cards/strip-card/sprite.tsx +1 -0
  16. package/src/components/embed-card.tsx +44 -0
  17. package/src/components/mdx/recipe.tsx +34 -16
  18. package/src/components/tool-list.tsx +1 -0
  19. package/src/{lib/tree.ts → components/tree.tsx} +20 -12
  20. package/src/components/views/alloy/index.tsx +14 -7
  21. package/src/components/views/alloy/list.tsx +3 -2
  22. package/src/components/views/alloy/mix/index.tsx +1 -0
  23. package/src/components/views/alloy/mix/ratio.tsx +7 -6
  24. package/src/components/views/alloy/mix/rows.tsx +6 -7
  25. package/src/components/views/alloy/mix/status.tsx +8 -7
  26. package/src/components/views/alloy/mix/total.tsx +13 -12
  27. package/src/components/views/alloy/range-bar.tsx +2 -1
  28. package/src/components/views/alloy/stack-bar.tsx +4 -2
  29. package/src/components/views/browser/axis-switcher.tsx +13 -12
  30. package/src/components/views/browser/card-grid.tsx +2 -1
  31. package/src/components/views/browser/favorite-button.tsx +2 -1
  32. package/src/components/views/browser/frame.tsx +3 -2
  33. package/src/components/views/browser/index.tsx +20 -2
  34. package/src/components/views/browser/item-panel/index.tsx +11 -9
  35. package/src/components/views/browser/item-panel/skeleton.tsx +1 -0
  36. package/src/components/views/browser/item-panel-grid/cell.tsx +5 -3
  37. package/src/components/views/browser/item-panel-grid/favorite-cell.tsx +4 -2
  38. package/src/components/views/browser/item-panel-grid/index.tsx +4 -2
  39. package/src/components/views/browser/item-view/index.tsx +7 -12
  40. package/src/components/views/browser/item-view/tags.tsx +7 -5
  41. package/src/components/views/browser/item-view-header.tsx +10 -9
  42. package/src/components/views/browser/pane-tabs.tsx +1 -0
  43. package/src/components/views/browser/place-header.tsx +4 -3
  44. package/src/components/views/browser/query-controls.tsx +3 -1
  45. package/src/components/views/browser/recipe-item-header.tsx +5 -4
  46. package/src/components/views/browser/settings.tsx +4 -3
  47. package/src/components/views/browser/toolbar.tsx +3 -2
  48. package/src/components/views/browser/type-tabs.tsx +5 -3
  49. package/src/components/views/browser/type-view.tsx +7 -12
  50. package/src/components/views/browser/workstation-rail.tsx +4 -3
  51. package/src/components/views/calc/calculator/actions.tsx +4 -14
  52. package/src/components/views/calc/calculator/help.tsx +69 -28
  53. package/src/components/views/calc/calculator/index.tsx +34 -82
  54. package/src/components/views/calc/dot-overlay.tsx +1 -0
  55. package/src/components/views/calc/edge.tsx +134 -32
  56. package/src/components/views/calc/flow/index.tsx +92 -54
  57. package/src/components/views/calc/flow/picker-dialogs.tsx +84 -6
  58. package/src/components/views/calc/flow/toolbar.tsx +9 -21
  59. package/src/components/views/calc/flow/view-menu.tsx +16 -120
  60. package/src/components/views/calc/member-picker.tsx +8 -6
  61. package/src/components/views/calc/node-card/actions.tsx +28 -106
  62. package/src/components/views/calc/node-card/badges.tsx +10 -28
  63. package/src/components/views/calc/node-card/chips.tsx +8 -40
  64. package/src/components/views/calc/node-card/far.tsx +13 -65
  65. package/src/components/views/calc/node-card/full.tsx +133 -83
  66. package/src/components/views/calc/node-card/index.tsx +1 -0
  67. package/src/components/views/calc/node-card/menu.tsx +93 -22
  68. package/src/components/views/calc/node-card/port-cells.tsx +186 -0
  69. package/src/components/views/calc/node-card/port-rails.tsx +251 -0
  70. package/src/components/views/calc/node-card/ports.tsx +86 -0
  71. package/src/components/views/calc/recipe-picker/body.tsx +17 -14
  72. package/src/components/views/calc/recipe-picker/footer.tsx +5 -3
  73. package/src/components/views/calc/recipe-picker/index.tsx +5 -0
  74. package/src/components/views/calc/sidebar.tsx +19 -10
  75. package/src/components/views/calc/trees/icon-picker.tsx +3 -2
  76. package/src/components/views/calc/trees/index.tsx +5 -4
  77. package/src/components/views/calc/trees/label.tsx +6 -4
  78. package/src/components/views/calc/trees/rows.tsx +6 -5
  79. package/src/components/views/forge/gui.tsx +2 -1
  80. package/src/components/views/forge/index.tsx +14 -7
  81. package/src/components/views/forge/list.tsx +3 -2
  82. package/src/components/views/forge/workbench/controls.tsx +13 -6
  83. package/src/components/views/forge/workbench/index.tsx +3 -2
  84. package/src/components/views/forge/workbench/panel.tsx +1 -0
  85. package/src/components/views/forge/workbench/ruler.tsx +5 -4
  86. package/src/components/views/forge/workbench/status.tsx +6 -5
  87. package/src/components/views/forge/workbench/tape.tsx +5 -4
  88. package/src/hooks/alloy/use-fluid-colors.ts +12 -9
  89. package/src/hooks/alloy/use-mix-state.ts +3 -2
  90. package/src/hooks/calc/flow/use-engage.ts +129 -0
  91. package/src/hooks/calc/flow/use-handlers.ts +192 -0
  92. package/src/hooks/calc/flow/use-layout.ts +105 -0
  93. package/src/hooks/calc/flow/{mount.ts → use-mount.ts} +1 -0
  94. package/src/hooks/calc/flow/{nodes.ts → use-nodes.ts} +46 -37
  95. package/src/hooks/calc/flow/use-pickers.ts +57 -0
  96. package/src/hooks/calc/flow/{viewport.ts → use-viewport.ts} +13 -26
  97. package/src/hooks/calc/flow/{window.ts → use-window.ts} +1 -0
  98. package/src/hooks/calc/use-actions.ts +208 -229
  99. package/src/hooks/calc/use-amounts.ts +23 -60
  100. package/src/hooks/calc/use-data.ts +106 -192
  101. package/src/hooks/calc/use-defaults.ts +4 -2
  102. package/src/hooks/calc/use-far-mode.ts +1 -0
  103. package/src/hooks/calc/use-focus.ts +8 -7
  104. package/src/hooks/calc/use-maps.ts +1 -0
  105. package/src/hooks/calc/use-reset.ts +1 -0
  106. package/src/hooks/calc/use-resolver.ts +32 -196
  107. package/src/hooks/calc/use-session.ts +1 -0
  108. package/src/hooks/calc/use-share.ts +5 -2
  109. package/src/hooks/calc/use-storage.ts +5 -4
  110. package/src/hooks/calc/use-tier.ts +1 -0
  111. package/src/hooks/calc/use-view-mode.ts +1 -0
  112. package/src/hooks/calc/use-worker.ts +56 -121
  113. package/src/hooks/forge/use-anvil-gui.ts +4 -2
  114. package/src/hooks/forge/use-forge-state.ts +1 -0
  115. package/src/hooks/forge/use-ruler.ts +1 -0
  116. package/src/hooks/use-add-to-calc.ts +23 -19
  117. package/src/hooks/use-categories-by-id.ts +18 -0
  118. package/src/hooks/use-favorite-hotkey.ts +1 -0
  119. package/src/hooks/use-favorites.ts +1 -0
  120. package/src/hooks/use-group-outlines.ts +1 -0
  121. package/src/hooks/use-hide-unused.ts +2 -1
  122. package/src/hooks/use-index.ts +6 -2
  123. package/src/hooks/use-item-feeds.ts +3 -1
  124. package/src/hooks/use-item-pane.ts +6 -4
  125. package/src/hooks/use-item-press.ts +27 -23
  126. package/src/hooks/use-item-tag-groups.ts +4 -2
  127. package/src/hooks/use-item-tooltips.ts +3 -1
  128. package/src/hooks/use-left-sidebar-tree.ts +11 -3
  129. package/src/hooks/use-mod-names.ts +4 -2
  130. package/src/hooks/use-mount-limit.ts +1 -0
  131. package/src/hooks/use-panel-cells.ts +3 -1
  132. package/src/hooks/use-panel-page.ts +9 -6
  133. package/src/hooks/use-panel-tabs.ts +2 -1
  134. package/src/hooks/use-query.ts +8 -6
  135. package/src/hooks/use-session.ts +6 -2
  136. package/src/hooks/use-sidebar-group.ts +3 -7
  137. package/src/hooks/use-tag-members.ts +3 -1
  138. package/src/hooks/use-tool-recipes.ts +1 -0
  139. package/src/hooks/use-type-filter.ts +1 -0
  140. package/src/lib/alloy/mix.ts +1 -0
  141. package/src/lib/alloy/parse.ts +1 -0
  142. package/src/lib/calc/defaults.ts +6 -4
  143. package/src/lib/calc/flow.ts +143 -107
  144. package/src/lib/calc/focus.ts +1 -0
  145. package/src/lib/calc/meta.ts +3 -1
  146. package/src/lib/calc/pack.ts +47 -154
  147. package/src/lib/calc/ports.ts +43 -0
  148. package/src/lib/calc/resolve.ts +6 -38
  149. package/src/lib/calc/route/buckets.ts +1 -0
  150. package/src/lib/calc/route/geometry.ts +13 -24
  151. package/src/lib/calc/route/index.ts +1 -0
  152. package/src/lib/calc/route/polyline.ts +3 -3
  153. package/src/lib/calc/route/router.ts +181 -57
  154. package/src/lib/calc/scheme.ts +226 -0
  155. package/src/lib/calc/storage/entry.ts +27 -19
  156. package/src/lib/calc/storage/index.ts +2 -0
  157. package/src/lib/calc/storage/keys.ts +4 -8
  158. package/src/lib/calc/storage/store.ts +11 -36
  159. package/src/lib/calc/tree/index.ts +2 -16
  160. package/src/lib/calc/tree/materials.ts +1 -0
  161. package/src/lib/calc/tree/recipe.ts +1 -0
  162. package/src/lib/calc/tree/share.ts +86 -149
  163. package/src/lib/calc/worker/api.ts +3 -11
  164. package/src/lib/calc/worker/fetch.ts +1 -0
  165. package/src/lib/calc/worker/flush.ts +7 -20
  166. package/src/lib/calc/worker/index.ts +1 -1
  167. package/src/lib/calc/worker/protocol.ts +16 -13
  168. package/src/lib/calc/worker/resolve.ts +32 -84
  169. package/src/lib/calc/worker/state.ts +5 -6
  170. package/src/{components/cards/card/model.ts → lib/card-model.ts} +1 -0
  171. package/src/lib/client/recipe-categories.ts +11 -93
  172. package/src/lib/client/recipe.ts +13 -11
  173. package/src/lib/client-index/catalog.ts +33 -27
  174. package/src/lib/client-index/chrome.ts +30 -30
  175. package/src/lib/client-index/index.ts +1 -0
  176. package/src/lib/client-index/registry.ts +70 -59
  177. package/src/lib/client-index/rows.ts +63 -57
  178. package/src/lib/client-index/tooltips.ts +28 -34
  179. package/src/lib/config.ts +2 -1
  180. package/src/lib/data/category/dto.ts +4 -3
  181. package/src/lib/data/category/load.ts +9 -8
  182. package/src/lib/data/category/parse.ts +4 -3
  183. package/src/lib/data/dto.ts +4 -3
  184. package/src/lib/data/grid.ts +2 -1
  185. package/src/lib/data/parse/entry.ts +3 -2
  186. package/src/lib/data/parse/index.ts +1 -0
  187. package/src/lib/data/parse/layout.ts +5 -2
  188. package/src/lib/data/parse/slots.ts +3 -2
  189. package/src/lib/data/parse/widgets.ts +3 -2
  190. package/src/lib/data/scene.ts +1 -1
  191. package/src/lib/data/slot-io.ts +1 -0
  192. package/src/lib/dump.ts +4 -3
  193. package/src/lib/embed.ts +22 -0
  194. package/src/lib/favorite-hover.ts +1 -0
  195. package/src/lib/fluid-outputs.ts +10 -12
  196. package/src/lib/forge/actions.ts +2 -1
  197. package/src/lib/forge/data.ts +2 -1
  198. package/src/lib/forge/solve.ts +1 -0
  199. package/src/lib/group.ts +1 -0
  200. package/src/lib/index-store.ts +15 -9
  201. package/src/lib/links.ts +15 -9
  202. package/src/lib/lookup.ts +10 -8
  203. package/src/lib/panel-outline.ts +1 -0
  204. package/src/lib/panel.ts +3 -2
  205. package/src/lib/preview.ts +12 -8
  206. package/src/lib/recipe-slots.ts +1 -0
  207. package/src/lib/search.ts +1 -0
  208. package/src/lib/sidebar.ts +1 -0
  209. package/src/lib/used-items.ts +1 -0
  210. package/src/locales/en_us.json +170 -226
  211. package/src/locales/ru_ru.json +170 -226
  212. package/src/locales.ts +1 -0
  213. package/src/types/alloy.ts +1 -0
  214. package/src/{hooks/calc/flow/props.ts → types/calc-flow-props.ts} +18 -14
  215. package/src/types/calc.ts +41 -44
  216. package/src/types/forge.ts +1 -0
  217. package/src/types/index.ts +4 -2
  218. package/styles/calc.css +308 -47
  219. package/src/components/cards/fit-card.tsx +0 -26
  220. package/src/components/views/calc/target.tsx +0 -271
  221. package/src/hooks/calc/flow/engage.ts +0 -89
  222. package/src/hooks/calc/flow/handlers.ts +0 -115
  223. package/src/hooks/calc/flow/layout.ts +0 -149
  224. package/src/hooks/calc/flow/pickers.ts +0 -50
  225. package/src/hooks/calc/use-calc-fill.ts +0 -91
  226. package/src/hooks/calc/use-density.ts +0 -28
  227. package/src/hooks/calc/use-derived.ts +0 -258
  228. package/src/hooks/calc/use-direction.ts +0 -23
  229. package/src/hooks/calc/use-edge-style.ts +0 -40
  230. package/src/hooks/calc/use-fill.ts +0 -213
  231. package/src/lib/alloy/alloy.test.ts +0 -186
  232. package/src/lib/calc/tree/build.ts +0 -196
  233. package/src/lib/calc/tree/context.ts +0 -42
  234. package/src/lib/calc/tree/goal.ts +0 -64
  235. package/src/lib/calc/tree/ideal.ts +0 -36
  236. package/src/lib/calc/tree/pick.ts +0 -68
  237. package/src/lib/calc/tree/tree.test.ts +0 -169
  238. package/src/lib/calc/worker/fill.ts +0 -123
  239. package/src/lib/client-index/cache.ts +0 -13
  240. package/src/lib/forge/forge.test.ts +0 -123
package/package.json CHANGED
@@ -1,25 +1,22 @@
1
1
  {
2
2
  "name": "@terrafirmagreg-team/recipe",
3
- "version": "0.1.0",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
+ "description": "Recipe viewer and calculator",
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",
@@ -27,17 +24,20 @@
27
24
  "./locales": "./src/locales.ts"
28
25
  },
29
26
  "dependencies": {
30
- "cn": "^0.5.0",
27
+ "@terrafirmagreg-team/data": "1.2.0",
28
+ "@terrafirmagreg-team/dump": "1.2.0",
29
+ "@terrafirmagreg-team/game": "1.2.0",
30
+ "@terrafirmagreg-team/kit": "1.2.0",
31
+ "@terrafirmagreg-team/mdx": "1.2.0",
32
+ "@terrafirmagreg-team/preview": "1.2.0",
33
+ "@terrafirmagreg-team/sites": "1.2.0",
34
+ "@terrafirmagreg-team/translate": "1.2.0",
35
+ "@terrafirmagreg-team/ui": "1.2.0",
31
36
  "comlink": "^4.4.2",
32
37
  "fflate": "0.8.3",
33
38
  "fumadocs-core": "16.17.8",
34
- "lucide-react": "^1.63.0",
35
- "pathfinding": "0.4.18",
36
- "potpack": "^2.1.0",
37
- "@terrafirmagreg-team/game": "0.1.0",
38
- "@terrafirmagreg-team/translate": "0.1.0",
39
- "@terrafirmagreg-team/ui": "0.1.0",
40
- "@terrafirmagreg-team/kit": "0.1.0"
39
+ "lucide-react": "^0.577.0",
40
+ "pathfinding": "0.4.18"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "next": ">=15",
@@ -40,3 +40,4 @@ export function ButtonCopy({
40
40
  );
41
41
  return title ? <Hint label={title}>{button}</Hint> : button;
42
42
  }
43
+
@@ -7,7 +7,7 @@ import {
7
7
  cardStripGeo,
8
8
  countCoveredText,
9
9
  textInStrip,
10
- } from '@terrafirmagreg-team/recipe/components/cards/card/model';
10
+ } from '@terrafirmagreg-team/recipe/lib/card-model';
11
11
  import { CardText } from '@terrafirmagreg-team/recipe/components/cards/card/text';
12
12
  import { CardSceneOverlay } from '@terrafirmagreg-team/recipe/components/cards/card/scene';
13
13
  import { CardWidgetTips } from '@terrafirmagreg-team/recipe/components/cards/card/tips';
@@ -17,8 +17,9 @@ import {
17
17
  recipeMultiblock,
18
18
  recipeSceneEntities,
19
19
  } from '@terrafirmagreg-team/recipe/lib/data/scene';
20
- import { dumpRelPublicUrl } from '@terrafirmagreg-team/game/lib/dump/paths';
20
+ import { dumpRelPublicUrl } from '@terrafirmagreg-team/dump/lib/dump/paths';
21
21
  import { useDumpLocation } from '@terrafirmagreg-team/game/hooks/use-dump-location';
22
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
22
23
  import { useInView } from '@terrafirmagreg-team/ui/hooks/use-in-view';
23
24
  import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
24
25
  import { GlbEmbedViewer } from '@terrafirmagreg-team/ui/components/blocks/media/glb/viewer';
@@ -48,7 +49,7 @@ export function RecipeCard({
48
49
  frameOnly,
49
50
  }: {
50
51
  recipe: Recipe;
51
- dataset: string;
52
+ dataset: DatasetRef;
52
53
  lang: string;
53
54
  langMap?: Record<string, string>;
54
55
  nav?: Pick<RecipeHrefOpts, 'tab' | 'search'>;
@@ -210,3 +211,4 @@ export function RecipeCard({
210
211
  </div>
211
212
  );
212
213
  }
214
+
@@ -1,8 +1,9 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { EntryTooltip } from '@terrafirmagreg-team/game/components/entry/tooltip';
4
5
  import { fetchEntityTexture } from '@terrafirmagreg-team/game/lib/entry/icon-load';
5
- import { stackDisplayName } from '@terrafirmagreg-team/game/lib/dump/assets/lang/names';
6
+ import { stackDisplayName } from '@terrafirmagreg-team/dump/lib/dump/assets/lang/names';
6
7
  import { useAsyncResult } from '@terrafirmagreg-team/ui/hooks/use-async-value';
7
8
  import type {
8
9
  RecipeSceneEntity,
@@ -35,7 +36,7 @@ export function CardSceneOverlay({
35
36
  interactive = true,
36
37
  }: {
37
38
  entity: RecipeSceneEntity;
38
- dataset: string;
39
+ dataset: DatasetRef;
39
40
  lang: string;
40
41
  langMap?: Record<string, string>;
41
42
  scale: number;
@@ -103,3 +104,4 @@ export function CardSceneOverlay({
103
104
  </EntryTooltip>
104
105
  );
105
106
  }
107
+
@@ -1,10 +1,11 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { Fragment, useRef, useState } from 'react';
4
5
  import { SlotHitbox } from '@terrafirmagreg-team/recipe/components/cards/slot-hitbox';
5
6
  import { CardText } from '@terrafirmagreg-team/recipe/components/cards/card/text';
6
7
  import { formatCount } from '@terrafirmagreg-team/ui/lib/utils/format';
7
- import type { CardStripGeo } from '@terrafirmagreg-team/recipe/components/cards/card/model';
8
+ import type { CardStripGeo } from '@terrafirmagreg-team/recipe/lib/card-model';
8
9
  import type { EntryTooltipLevel } from '@terrafirmagreg-team/game/components/entry/tooltip';
9
10
  import type { RecipeTextWidget } from '@terrafirmagreg-team/recipe/lib/data/dto';
10
11
  import type { RecipeHrefOpts } from '@terrafirmagreg-team/recipe/types';
@@ -27,7 +28,7 @@ export function CardStrip({
27
28
  margin: number;
28
29
  scale: number;
29
30
  contentGuiW: number;
30
- dataset: string;
31
+ dataset: DatasetRef;
31
32
  lang: string;
32
33
  langMap?: Record<string, string>;
33
34
  nav?: Pick<RecipeHrefOpts, 'tab' | 'search'>;
@@ -194,3 +195,4 @@ export function CardStrip({
194
195
  </>
195
196
  );
196
197
  }
198
+
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { McText } from '@terrafirmagreg-team/game/components/entry/mc-text';
4
- import { dumpTextString } from '@terrafirmagreg-team/game/lib/dump/assets/lang/names';
4
+ import { dumpTextString } from '@terrafirmagreg-team/dump/lib/dump/assets/lang/names';
5
5
  import type { RecipeTextWidget } from '@terrafirmagreg-team/recipe/lib/data/dto';
6
6
 
7
7
  export function CardText({
@@ -43,3 +43,4 @@ export function CardText({
43
43
  </span>
44
44
  );
45
45
  }
46
+
@@ -60,3 +60,4 @@ export function CardWidgetTips({
60
60
  );
61
61
  });
62
62
  }
63
+
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { Pin, PinOff } from 'lucide-react';
4
5
  import { useEffect, useState } from 'react';
@@ -14,7 +15,7 @@ export function RecipeDefaultMenuItem({
14
15
  materialId,
15
16
  recipeId,
16
17
  }: {
17
- dataset: string;
18
+ dataset: DatasetRef;
18
19
  materialId?: string;
19
20
  recipeId: string;
20
21
  }) {
@@ -37,7 +38,10 @@ export function RecipeDefaultMenuItem({
37
38
  onClick={() => writeRecipeDefault(dataset, materialId, pinned ? null : recipeId)}
38
39
  >
39
40
  {pinned ? <PinOff /> : <Pin />}
40
- {t(pinned ? 'recipe.calc.clear.default' : 'recipe.calc.set_default')}
41
+ {pinned
42
+ ? t('Unpin default recipe', { note: 'recipe.calc.clear.default' })
43
+ : t('Pin as default recipe', { note: 'recipe.calc.set_default' })}
41
44
  </ContextMenuItem>
42
45
  );
43
46
  }
47
+
@@ -26,3 +26,4 @@ export function RecipeGlareFilter() {
26
26
  </svg>
27
27
  );
28
28
  }
29
+
@@ -219,3 +219,4 @@ export function PagedCard({
219
219
  </div>
220
220
  );
221
221
  }
222
+
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import Link from 'next/link';
4
5
  import { ENTRY_CYCLE_MS, EntryIcon } from '@terrafirmagreg-team/game/components/entry/icon';
@@ -12,7 +13,7 @@ import { entryHref } from '@terrafirmagreg-team/game/lib/entry/href';
12
13
  import { loadStackDisplay } from '@terrafirmagreg-team/game/lib/entry/tooltip';
13
14
  import { favoriteHover } from '@terrafirmagreg-team/recipe/lib/favorite-hover';
14
15
  import { useAsyncResult } from '@terrafirmagreg-team/ui/hooks/use-async-value';
15
- import type { ReferenceKind } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
16
+ import type { ReferenceKind } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
16
17
  import type { RecipeSlotWidget } from '@terrafirmagreg-team/recipe/lib/data/dto';
17
18
  import type { RecipeHrefOpts } from '@terrafirmagreg-team/recipe/types';
18
19
 
@@ -64,7 +65,7 @@ export function SlotHitbox({
64
65
  tooltipLevel,
65
66
  }: {
66
67
  widget: RecipeSlotWidget;
67
- dataset: string;
68
+ dataset: DatasetRef;
68
69
  lang: string;
69
70
  langMap?: Record<string, string>;
70
71
  nav?: Pick<RecipeHrefOpts, 'tab' | 'search'>;
@@ -209,3 +210,4 @@ export function SlotHitbox({
209
210
  </EntryTooltip>
210
211
  );
211
212
  }
213
+
@@ -26,7 +26,11 @@ export function StripCardTagPager({
26
26
  return (
27
27
  <button
28
28
  type="button"
29
- aria-label={t(dir < 0 ? 'ui.prev_page' : 'ui.next_page')}
29
+ aria-label={
30
+ dir < 0
31
+ ? t('Previous Page', { note: 'ui.prev_page' })
32
+ : t('Next Page', { note: 'ui.next_page' })
33
+ }
30
34
  disabled={disabled}
31
35
  onClick={onClick}
32
36
  onMouseEnter={() => setHover(true)}
@@ -78,7 +82,7 @@ export function StripCardTagCopy({
78
82
  return (
79
83
  <button
80
84
  type="button"
81
- aria-label={t('recipe.copy_recipe_id')}
85
+ aria-label={t('Copy recipe id', { note: 'recipe.copy_recipe_id' })}
82
86
  onClick={onClick}
83
87
  onMouseEnter={() => setHover(true)}
84
88
  onMouseLeave={() => setHover(false)}
@@ -99,3 +103,4 @@ export function StripCardTagCopy({
99
103
  </button>
100
104
  );
101
105
  }
106
+
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { Fragment, useRef, useState } from 'react';
4
5
  import { SlotHitbox } from '@terrafirmagreg-team/recipe/components/cards/slot-hitbox';
@@ -36,7 +37,7 @@ export function StripCard({
36
37
  frameOnly,
37
38
  }: {
38
39
  recipe: Recipe;
39
- dataset: string;
40
+ dataset: DatasetRef;
40
41
  lang: string;
41
42
  langMap?: Record<string, string>;
42
43
  nav?: Pick<RecipeHrefOpts, 'tab' | 'search'>;
@@ -210,3 +211,4 @@ export function StripCard({
210
211
  </div>
211
212
  );
212
213
  }
214
+
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import Link from 'next/link';
4
5
  import { EntryIcon } from '@terrafirmagreg-team/game/components/entry/icon';
@@ -11,7 +12,7 @@ import { entryHref } from '@terrafirmagreg-team/game/lib/entry/href';
11
12
  import { loadStackDisplay } from '@terrafirmagreg-team/game/lib/entry/tooltip';
12
13
  import { favoriteHover } from '@terrafirmagreg-team/recipe/lib/favorite-hover';
13
14
  import { useAsyncResult } from '@terrafirmagreg-team/ui/hooks/use-async-value';
14
- import type { ReferenceKind } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
15
+ import type { ReferenceKind } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
15
16
  import type { RecipeGridMember } from '@terrafirmagreg-team/recipe/lib/data/dto';
16
17
  import type { RecipeHrefOpts } from '@terrafirmagreg-team/recipe/types';
17
18
 
@@ -34,7 +35,7 @@ export function StripCardMember({
34
35
  tooltipLevel,
35
36
  }: {
36
37
  member: RecipeGridMember;
37
- dataset: string;
38
+ dataset: DatasetRef;
38
39
  lang: string;
39
40
  langMap?: Record<string, string>;
40
41
  nav?: Pick<RecipeHrefOpts, 'tab' | 'search'>;
@@ -163,3 +164,4 @@ export function StripCardMember({
163
164
  </EntryTooltip>
164
165
  );
165
166
  }
167
+
@@ -33,3 +33,4 @@ export function StripCardSprite({
33
33
  />
34
34
  );
35
35
  }
36
+
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+ import { useState } from 'react';
3
+ import Link from 'next/link';
4
+ import { PagedCard } from '@terrafirmagreg-team/recipe/components/cards/paged-card';
5
+ import { FitBox } from '@terrafirmagreg-team/ui/components/blocks/fit-box';
6
+ import { recipeHref } from '@terrafirmagreg-team/recipe/lib/links';
7
+ import { recipeCardImageUrl } from '@terrafirmagreg-team/recipe/lib/links';
8
+ import { recipeImageUrl } from '@terrafirmagreg-team/dump/lib/dump/assets/urls';
9
+ import { recipeTitle } from '@terrafirmagreg-team/recipe/lib/data/parse';
10
+ import type { Recipe } from '@terrafirmagreg-team/recipe/lib/data/dto';
11
+ import type { EmbedRecipeCardProps } from '@terrafirmagreg-team/kit/types/embeds';
12
+
13
+ export function RecipeEmbedCard({
14
+ id,
15
+ recipe,
16
+ dataset,
17
+ lang,
18
+ langMap,
19
+ }: EmbedRecipeCardProps) {
20
+ const [failed, setFailed] = useState(false);
21
+ if (failed) return null;
22
+ const full = recipe as unknown as Recipe;
23
+ const src = recipeImageUrl(dataset, full) ?? recipeCardImageUrl(dataset, id);
24
+ if (!src) return null;
25
+ const title = recipeTitle(full, langMap);
26
+ return (
27
+ <Link
28
+ href={recipeHref(dataset, lang, { id })}
29
+ className="not-prose block w-fit max-w-full"
30
+ >
31
+ <FitBox w={full.w ?? 0}>
32
+ <PagedCard
33
+ recipe={full}
34
+ dataset={dataset}
35
+ lang={lang}
36
+ langMap={langMap}
37
+ title={title}
38
+ src={src}
39
+ onFail={() => setFailed(true)}
40
+ />
41
+ </FitBox>
42
+ </Link>
43
+ );
44
+ }
@@ -1,9 +1,11 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import Link from 'next/link';
5
+ import { useRouter } from 'next/navigation';
4
6
  import { useState } from 'react';
5
7
  import { PagedCard } from '@terrafirmagreg-team/recipe/components/cards/paged-card';
6
- import { FitCard } from '@terrafirmagreg-team/recipe/components/cards/fit-card';
8
+ import { FitBox } from '@terrafirmagreg-team/ui/components/blocks/fit-box';
7
9
  import { EmptyState } from '@terrafirmagreg-team/ui/components/blocks/page/empty-state';
8
10
  import { Skeleton } from '@terrafirmagreg-team/ui/components/ui/skeleton';
9
11
  import { useAsyncResult } from '@terrafirmagreg-team/ui/hooks/use-async-value';
@@ -17,25 +19,31 @@ import {
17
19
  resolveRecipeRows,
18
20
  } from '@terrafirmagreg-team/recipe/lib/client-index';
19
21
  import { loadClientRecipe } from '@terrafirmagreg-team/recipe/lib/client/recipe';
20
- import { clientDumpLang } from '@terrafirmagreg-team/game/lib/dump/client/dump';
22
+ import { clientDumpLang } from '@terrafirmagreg-team/dump/lib/dump/client/dump';
21
23
  import { recipeTitle } from '@terrafirmagreg-team/recipe/lib/data/parse';
22
- import { recipeImageUrl } from '@terrafirmagreg-team/game/lib/dump/assets/urls';
24
+ import { recipeImageUrl } from '@terrafirmagreg-team/dump/lib/dump/assets/urls';
23
25
  import { recipeCardImageUrl, recipeHref } from '@terrafirmagreg-team/recipe/lib/links';
24
26
 
27
+ export type RecipeProps = {
28
+ id?: string;
29
+ dataset?: DatasetRef;
30
+ maxH?: number;
31
+ };
32
+
25
33
  export function Recipe({
26
34
  id,
27
35
  dataset: datasetProp,
28
36
  maxH = 360,
29
- }: {
30
- id?: string;
31
- dataset?: string;
32
- maxH?: number;
33
- }) {
37
+ }: RecipeProps) {
34
38
  const catalog = useSectionCatalog();
35
39
  const current = useCurrentDataset(catalog.recipe);
36
- const dataset = datasetProp ?? current ?? catalog.recipe[0]?.id;
40
+ const dataset =
41
+ datasetProp ??
42
+ current ??
43
+ (catalog.recipe[0] ? { id: catalog.recipe[0].id } : undefined);
37
44
  const lang = useResolvedLang();
38
- const { index, indexLoading } = useRecipeIndex(dataset ?? '', lang);
45
+ const router = useRouter();
46
+ const { index, indexLoading } = useRecipeIndex(dataset, lang);
39
47
  const [failed, setFailed] = useState(false);
40
48
 
41
49
  const { value, pending } = useAsyncResult(
@@ -82,13 +90,22 @@ export function Recipe({
82
90
  }
83
91
  const { row, recipe, langMap, title, src } = value;
84
92
  return (
85
- <Link
86
- href={href}
87
- className="not-prose mb-4 block w-fit max-w-full rounded-md"
93
+ <div
94
+ role="link"
95
+ tabIndex={0}
88
96
  title={title}
97
+ className="not-prose mb-4 block w-fit max-w-full cursor-pointer rounded-md"
98
+ onClick={(event) => {
99
+ if ((event.target as HTMLElement).closest('a,button')) return;
100
+ router.push(href);
101
+ }}
102
+ onKeyDown={(event) => {
103
+ if (event.key === 'Enter' && event.target === event.currentTarget)
104
+ router.push(href);
105
+ }}
89
106
  >
90
107
  {src && !failed ? (
91
- <FitCard w={recipe.w ?? row.d?.w ?? 0}>
108
+ <FitBox w={recipe.w ?? row.d?.w ?? 0}>
92
109
  <PagedCard
93
110
  recipe={recipe}
94
111
  dataset={dataset}
@@ -102,10 +119,11 @@ export function Recipe({
102
119
  maxH={Number(maxH) || 360}
103
120
  interactive
104
121
  />
105
- </FitCard>
122
+ </FitBox>
106
123
  ) : (
107
124
  <EmptyState variant="missing">{title}</EmptyState>
108
125
  )}
109
- </Link>
126
+ </div>
110
127
  );
111
128
  }
129
+
@@ -46,3 +46,4 @@ export function ToolList({
46
46
  </LeftSidebarPortal>
47
47
  );
48
48
  }
49
+
@@ -1,9 +1,12 @@
1
1
  import 'server-only';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
  import { createElement } from 'react';
3
4
  import type * as PageTree from 'fumadocs-core/page-tree';
4
5
  import { Entry } from '@terrafirmagreg-team/game/components/entry/entry';
5
- import { loadMergedLangMap, creativeTabName } from '@terrafirmagreg-team/game/lib/dump/assets/lang/load';
6
- import { loadClientManifest } from '@terrafirmagreg-team/game/lib/dump/client/dump';
6
+ import { loadMergedLangMap, creativeTabName } from '@terrafirmagreg-team/dump/lib/dump/assets/lang/load';
7
+ import { loadClientManifest } from '@terrafirmagreg-team/dump/lib/dump/client/dump';
8
+ import { DUMP_POINTER_TTL } from '@terrafirmagreg-team/dump/lib/dump/json-reader';
9
+ import { createAsyncCache } from '@terrafirmagreg-team/data/lib/async-cache';
7
10
  import { loadDatasetRecipeCategories } from '@terrafirmagreg-team/recipe/lib/data/category/load';
8
11
  import { keyTreeNode } from '@terrafirmagreg-team/translate/server';
9
12
  import { loadLocaleRegistry } from '@terrafirmagreg-team/translate/server';
@@ -14,7 +17,7 @@ import type { DumpRecipeCategory } from '@terrafirmagreg-team/recipe/lib/data/ca
14
17
  import type { SectionTrees } from '@terrafirmagreg-team/kit/types';
15
18
 
16
19
  function categoryIcon(
17
- dataset: string,
20
+ dataset: DatasetRef,
18
21
  category: DumpRecipeCategory,
19
22
  ): React.ReactNode {
20
23
  if (!category.icon) return undefined;
@@ -34,7 +37,7 @@ function categoryIcon(
34
37
  }
35
38
 
36
39
  function categoryLeaf(
37
- dataset: string,
40
+ dataset: DatasetRef,
38
41
  category: DumpRecipeCategory,
39
42
  map: Record<string, string>,
40
43
  ): PageTree.Item {
@@ -43,15 +46,18 @@ function categoryLeaf(
43
46
  name: creativeTabName(map, category.display?.name),
44
47
  url: `${recipeBasePath(dataset)}?type=${encodeQueryValue(category.id)}`,
45
48
  icon: categoryIcon(dataset, category),
46
- $id: `${dataset}/type/${category.id}`,
49
+ $id: `${dataset.id}/${dataset.version ?? ''}/type/${category.id}`,
47
50
  }) as PageTree.Item;
48
51
  }
49
52
 
50
- let cached: Promise<SectionTrees> | undefined;
53
+ const recipeTreesCache = createAsyncCache(
54
+ async (): Promise<SectionTrees> => buildRecipeTrees(),
55
+ () => 'recipe-trees',
56
+ DUMP_POINTER_TTL,
57
+ );
51
58
 
52
59
  export function recipeTrees(): Promise<SectionTrees> {
53
- cached ??= buildRecipeTrees();
54
- return cached;
60
+ return recipeTreesCache();
55
61
  }
56
62
 
57
63
  async function buildRecipeTrees(): Promise<SectionTrees> {
@@ -60,9 +66,10 @@ async function buildRecipeTrees(): Promise<SectionTrees> {
60
66
  const trees: SectionTrees = {};
61
67
  for (const { id, title: datasetTitle, languages } of datasets) {
62
68
  for (const lang of languages ?? [defaultLanguage]) {
63
- const map = await loadMergedLangMap(id, lang);
64
- const categories = await loadDatasetRecipeCategories(id);
65
- const types = (await loadClientManifest(id))?.indexes.recipes.types;
69
+ const ref = { id };
70
+ const map = await loadMergedLangMap(ref, lang);
71
+ const categories = await loadDatasetRecipeCategories(ref);
72
+ const types = (await loadClientManifest(ref))?.indexes.recipes.types;
66
73
  const nonEmpty = types
67
74
  ? new Set(
68
75
  types
@@ -78,7 +85,7 @@ async function buildRecipeTrees(): Promise<SectionTrees> {
78
85
  creativeTabName(map, b.display?.name),
79
86
  ) || a.id.localeCompare(b.id),
80
87
  )
81
- .map((category) => categoryLeaf(id, category, map));
88
+ .map((category) => categoryLeaf(ref, category, map));
82
89
  (trees[lang] ??= {})[id] = {
83
90
  name: datasetTitle ?? id,
84
91
  children: leaves,
@@ -88,3 +95,4 @@ async function buildRecipeTrees(): Promise<SectionTrees> {
88
95
  }
89
96
  return trees;
90
97
  }
98
+
@@ -9,7 +9,7 @@ import { RecipeFrame } from '@terrafirmagreg-team/recipe/components/views/browse
9
9
  import { useToolRecipes } from '@terrafirmagreg-team/recipe/hooks/use-tool-recipes';
10
10
  import { toAlloyRecipe } from '@terrafirmagreg-team/recipe/lib/alloy/parse';
11
11
  import { RecipeCard } from '@terrafirmagreg-team/recipe/components/cards/card';
12
- import { FitCard } from '@terrafirmagreg-team/recipe/components/cards/fit-card';
12
+ import { FitBox } from '@terrafirmagreg-team/ui/components/blocks/fit-box';
13
13
  import {
14
14
  recipeCardImageUrl,
15
15
  recipeImageUrl,
@@ -67,16 +67,16 @@ export function AlloyCalculator({ session }: { session: RecipeSession }) {
67
67
  active ? (
68
68
  <RecipeItemHeader recipe={active.recipe} session={session} />
69
69
  ) : (
70
- <ItemRow layout="header" title={t('recipe.alloy.title')} />
70
+ <ItemRow layout="header" title={t('Alloys', { note: 'recipe.alloy.title' })} />
71
71
  )
72
72
  }
73
73
  >
74
- <div className="flex-1 space-y-5 overflow-y-auto p-2 sm:p-4">
74
+ <div className="scrollbar-thin flex-1 space-y-5 overflow-y-auto p-2 sm:p-4">
75
75
  {loading ? (
76
76
  <EmptyState variant="loading" />
77
77
  ) : value === undefined ? (
78
78
  <EmptyState variant="missing">
79
- {t('recipe.alloy.load_error')}
79
+ {t('Could not load recipes. Reload the page to retry.', { note: 'recipe.alloy.load_error' })}
80
80
  </EmptyState>
81
81
  ) : active ? (
82
82
  <>
@@ -85,17 +85,23 @@ export function AlloyCalculator({ session }: { session: RecipeSession }) {
85
85
  recipe={active}
86
86
  session={session}
87
87
  />
88
- <FitCard w={active.recipe.w ?? 0}>
88
+ <FitBox w={active.recipe.w ?? 0}>
89
89
  <AlloyRecipeCard
90
90
  key={active.recipe.id}
91
91
  recipe={active.recipe}
92
92
  session={session}
93
93
  />
94
- </FitCard>
94
+ </FitBox>
95
95
  </>
96
96
  ) : (
97
97
  <EmptyState>
98
- {t(activeId ? 'recipe.alloy.missing' : 'recipe.alloy.empty')}
98
+ {activeId
99
+ ? t('Recipe not found in this dump. Select another alloy.', {
100
+ note: 'recipe.alloy.missing',
101
+ })
102
+ : t('No matching recipes in this dump.', {
103
+ note: 'recipe.alloy.empty',
104
+ })}
99
105
  </EmptyState>
100
106
  )}
101
107
  </div>
@@ -103,3 +109,4 @@ export function AlloyCalculator({ session }: { session: RecipeSession }) {
103
109
  </>
104
110
  );
105
111
  }
112
+
@@ -77,8 +77,9 @@ export function AlloyList({ session }: { session: RecipeSession }) {
77
77
  loaded={value !== undefined}
78
78
  query={query}
79
79
  onQuery={setQuery}
80
- searchLabel={t('recipe.alloy.search')}
81
- emptyLabel={t('recipe.alloy.empty')}
80
+ searchLabel={t('Search alloys', { note: 'recipe.alloy.search' })}
81
+ emptyLabel={t('No matching recipes in this dump.', { note: 'recipe.alloy.empty' })}
82
82
  />
83
83
  );
84
84
  }
85
+
@@ -32,3 +32,4 @@ export function AlloyMix({
32
32
  </div>
33
33
  );
34
34
  }
35
+