@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
@@ -14,34 +14,38 @@ export function useAddToCalc(session: RecipeSession) {
14
14
  return useCallback(
15
15
  (item: string, kind: 'item' | 'fluid') => {
16
16
  const state = decodeCalcState(tree ?? '') ?? {
17
- target: null,
18
- selections: {},
17
+ cards: [],
18
+ wires: [],
19
19
  tagSelections: {},
20
20
  };
21
- const same =
22
- state.target?.id === item && state.target.kind === kind;
23
- const target = same
24
- ? { ...state.target!, amount: state.target!.amount + 1 }
25
- : { id: item, kind, amount: 1 };
21
+ const existing = state.cards.find(
22
+ (card) => card.materialId === item && card.kind === kind,
23
+ );
24
+ const cards = existing
25
+ ? state.cards.map((card) =>
26
+ card === existing
27
+ ? { ...card, crafts: card.crafts + 1 }
28
+ : card,
29
+ )
30
+ : [
31
+ ...state.cards,
32
+ {
33
+ id: `c${Date.now().toString(36)}${Math.random().toString(36).slice(2, 5)}`,
34
+ materialId: item,
35
+ kind,
36
+ recipeId: null,
37
+ crafts: 1,
38
+ },
39
+ ];
26
40
  replaceUrlState(
27
41
  recipeCalcHref(
28
42
  dataset,
29
43
  lang,
30
- encodeCalcState({
31
- target,
32
- ...(state.inventory ? { inventory: state.inventory } : {}),
33
- selections: same
34
- ? state.selections
35
- : Object.fromEntries(
36
- Object.entries(state.selections).filter(
37
- ([path]) => !/^g\d/.test(path),
38
- ),
39
- ),
40
- tagSelections: state.tagSelections,
41
- }),
44
+ encodeCalcState({ ...state, cards }),
42
45
  ),
43
46
  );
44
47
  },
45
48
  [tree, dataset, lang],
46
49
  );
47
50
  }
51
+
@@ -0,0 +1,18 @@
1
+ import { useMemo } from 'react';
2
+ import type {
3
+ RecipeCategory,
4
+ RecipeCompactIndex,
5
+ } from '@terrafirmagreg-team/recipe/types';
6
+
7
+ export function useCategoriesById(
8
+ index: RecipeCompactIndex | null | undefined,
9
+ ): Map<string, RecipeCategory> {
10
+ return useMemo(
11
+ () =>
12
+ new Map<string, RecipeCategory>(
13
+ (index?.categories ?? []).map((row) => [row.id, row]),
14
+ ),
15
+ [index],
16
+ );
17
+ }
18
+
@@ -36,3 +36,4 @@ export function useFavoriteHotkey() {
36
36
  return () => window.removeEventListener('keydown', onKey);
37
37
  }, [toggleItem, toggleRecipe]);
38
38
  }
39
+
@@ -132,3 +132,4 @@ export function useFavorites() {
132
132
  isRecipeFavorite,
133
133
  };
134
134
  }
135
+
@@ -62,3 +62,4 @@ export function useGroupOutlines(
62
62
 
63
63
  return { gridRef, outlines };
64
64
  }
65
+
@@ -2,10 +2,11 @@ import { boolCodec, createStoredValue } from '@terrafirmagreg-team/ui/lib/utils/
2
2
 
3
3
  const useStoredHideUnused = createStoredValue(
4
4
  'wiki.recipe.hideUnused',
5
- boolCodec(true),
5
+ boolCodec(false),
6
6
  );
7
7
 
8
8
  export function useHideUnused() {
9
9
  const [hideUnused, setHideUnused] = useStoredHideUnused();
10
10
  return { hideUnused, setHideUnused };
11
11
  }
12
+
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { startTransition, useMemo, useState } from 'react';
4
5
  import { useLang } from '@terrafirmagreg-team/translate/provider';
@@ -23,7 +24,7 @@ function idle(): Promise<void> {
23
24
  });
24
25
  }
25
26
 
26
- export function useRecipeIndex(dataset: string, lang: string) {
27
+ export function useRecipeIndex(dataset: DatasetRef | undefined, lang: string) {
27
28
  const [loaded, setLoaded] = useState<LoadedIndex | null>(null);
28
29
  const [complete, setComplete] = useState<string | null>(null);
29
30
  const [lastFull, setLastFull] = useState<RecipeCompactIndex | null>(null);
@@ -32,9 +33,10 @@ export function useRecipeIndex(dataset: string, lang: string) {
32
33
  () => items?.map((item) => item.locale) ?? [lang],
33
34
  [items, lang],
34
35
  );
35
- const key = `${dataset} ${lang}`;
36
+ const key = `${dataset ? `${dataset.id}/${dataset.version ?? ''}` : ''} ${lang}`;
36
37
 
37
38
  useAsyncEffect(async (isCancelled) => {
39
+ if (!dataset) return;
38
40
  const skeleton = await loadRecipeSkeletonIndex(dataset);
39
41
  if (isCancelled()) return;
40
42
  setLoaded({ key, index: skeleton });
@@ -51,6 +53,7 @@ export function useRecipeIndex(dataset: string, lang: string) {
51
53
  }, [dataset, lang]);
52
54
 
53
55
  useAsyncEffect(async (isCancelled) => {
56
+ if (!dataset) return;
54
57
  const others = locales.filter((entry) => entry !== lang);
55
58
  for (const entry of others) {
56
59
  if (isCancelled()) return;
@@ -66,3 +69,4 @@ export function useRecipeIndex(dataset: string, lang: string) {
66
69
  const index = active?.items.length ? active : (lastFull ?? active);
67
70
  return { index, indexLoading: complete !== key };
68
71
  }
72
+
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { useAsyncResult } from '@terrafirmagreg-team/ui/hooks/use-async-value';
4
5
  import {
@@ -16,7 +17,7 @@ export function useItemFeeds({
16
17
  item,
17
18
  kind,
18
19
  }: {
19
- dataset: string;
20
+ dataset: DatasetRef;
20
21
  index: RecipeCompactIndex | null;
21
22
  indexLoading: boolean;
22
23
  item: string;
@@ -42,3 +43,4 @@ export function useItemFeeds({
42
43
  pending: indexLoading || !index || recipesRes.pending || usesRes.pending,
43
44
  };
44
45
  }
46
+
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { createElement, useMemo } from 'react';
4
5
  import { ArrowDownToLine, ArrowUpFromLine, Tag } from 'lucide-react';
@@ -31,7 +32,7 @@ export function useItemPane({
31
32
  tagCount,
32
33
  feedsPending,
33
34
  }: {
34
- dataset: string;
35
+ dataset: DatasetRef;
35
36
  lang: Lang;
36
37
  nav: RecipeHrefOpts;
37
38
  relation?: RecipeRelation;
@@ -78,21 +79,21 @@ export function useItemPane({
78
79
  () => [
79
80
  {
80
81
  id: 'recipes',
81
- label: t('recipe.pane_recipes'),
82
+ label: t('Recipes', { note: 'recipe.pane_recipes' }),
82
83
  icon: createElement(ArrowDownToLine, { className: 'size-3.5' }),
83
84
  help: 'R',
84
85
  disabled: !available.recipes,
85
86
  },
86
87
  {
87
88
  id: 'uses',
88
- label: t('recipe.pane_uses'),
89
+ label: t('Uses', { note: 'recipe.pane_uses' }),
89
90
  icon: createElement(ArrowUpFromLine, { className: 'size-3.5' }),
90
91
  help: 'U',
91
92
  disabled: !available.uses,
92
93
  },
93
94
  {
94
95
  id: 'tags',
95
- label: t('recipe.pane_tags'),
96
+ label: t('Tags', { note: 'recipe.pane_tags' }),
96
97
  icon: createElement(Tag, { className: 'size-3.5' }),
97
98
  help: 'T',
98
99
  disabled: !available.tags,
@@ -102,3 +103,4 @@ export function useItemPane({
102
103
  );
103
104
  return { pane, select, options };
104
105
  }
106
+
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { startTransition, useCallback, useEffect, useRef } from 'react';
4
5
  import {
@@ -18,7 +19,7 @@ export function useItemPress({
18
19
  nav,
19
20
  isCalc,
20
21
  }: {
21
- dataset: string;
22
+ dataset: DatasetRef;
22
23
  lang: Lang;
23
24
  nav: RecipeHrefOpts;
24
25
  isCalc: boolean;
@@ -30,35 +31,37 @@ export function useItemPress({
30
31
  pressRef.current = (id: string, ref?: 'item' | 'fluid') => {
31
32
  startTransition(() => {
32
33
  if (isCalc) {
33
-
34
34
  const key = calcStorageKey('recipe', dataset);
35
35
  const parsed = readCalcStorage(key) ?? {
36
- target: null,
37
- selections: {},
36
+ cards: [],
37
+ wires: [],
38
38
  tagSelections: {},
39
39
  };
40
40
  const kind = ref === 'fluid' ? ('fluid' as const) : ('item' as const);
41
- const same =
42
- parsed.target?.id === id && parsed.target.kind === kind;
43
-
44
- const keepTree = same;
41
+ const same = parsed.cards.findIndex(
42
+ (row) => row.materialId === id && row.kind === kind,
43
+ );
44
+ const cards =
45
+ same >= 0
46
+ ? parsed.cards.map((row, index) =>
47
+ index === same ? { ...row, crafts: row.crafts + 1 } : row,
48
+ )
49
+ : [
50
+ ...parsed.cards,
51
+ {
52
+ id: `c${Date.now().toString(36)}${Math.random()
53
+ .toString(36)
54
+ .slice(2, 4)}`,
55
+ materialId: id,
56
+ kind,
57
+ recipeId: null,
58
+ crafts: 1,
59
+ },
60
+ ];
45
61
  writeCalcStorage(key, {
46
- target: same
47
- ? { ...parsed.target!, amount: parsed.target!.amount + 1 }
48
- : { id, kind, amount: 1 },
49
- ...(parsed.inventory ? { inventory: parsed.inventory } : {}),
50
- selections: keepTree
51
- ? parsed.selections
52
- : Object.fromEntries(
53
- Object.entries(parsed.selections).filter(
54
- ([path]) => !/^g\d/.test(path),
55
- ),
56
- ),
62
+ cards,
63
+ wires: parsed.wires,
57
64
  tagSelections: parsed.tagSelections,
58
- ...(keepTree && parsed.collapsed
59
- ? { collapsed: parsed.collapsed }
60
- : {}),
61
- ...(keepTree && parsed.auto ? { auto: parsed.auto } : {}),
62
65
  });
63
66
  requestCalcFocus(id);
64
67
  return;
@@ -83,3 +86,4 @@ export function useItemPress({
83
86
  [],
84
87
  );
85
88
  }
89
+
@@ -1,10 +1,11 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { useMemo } from 'react';
4
5
  import { useAsyncValue } from '@terrafirmagreg-team/ui/hooks/use-async-value';
5
6
  import { loadTagSlice } from '@terrafirmagreg-team/recipe/lib/client-index';
6
7
  import { registryId } from '@terrafirmagreg-team/recipe/lib/lookup';
7
- import { splitDumpId } from '@terrafirmagreg-team/game/lib/dump/paths';
8
+ import { splitDumpId } from '@terrafirmagreg-team/dump/lib/dump/paths';
8
9
  import type { RecipeCompactIndex } from '@terrafirmagreg-team/recipe/types';
9
10
 
10
11
  const EMPTY_TAGS: { id: string; kind: string }[] = [];
@@ -15,7 +16,7 @@ export function useItemTagGroups({
15
16
  item,
16
17
  kind,
17
18
  }: {
18
- dataset: string;
19
+ dataset: DatasetRef;
19
20
  index: RecipeCompactIndex | null;
20
21
  item: string;
21
22
  kind: 'item' | 'fluid';
@@ -50,3 +51,4 @@ export function useItemTagGroups({
50
51
  }, [ids]);
51
52
  return { ids, groups };
52
53
  }
54
+
@@ -1,11 +1,12 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { useAsyncValue } from '@terrafirmagreg-team/ui/hooks/use-async-value';
4
5
  import { loadItemTooltips } from '@terrafirmagreg-team/recipe/lib/client-index';
5
6
  import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
6
7
 
7
8
  export function useItemTooltips(
8
- dataset: string,
9
+ dataset: DatasetRef,
9
10
  lang: Lang,
10
11
  enabled: boolean,
11
12
  ): Record<string, string[]> | undefined {
@@ -14,3 +15,4 @@ export function useItemTooltips(
14
15
  [dataset, lang, enabled],
15
16
  );
16
17
  }
18
+
@@ -1,18 +1,25 @@
1
1
  'use client';
2
2
  import { usePathname } from 'next/navigation';
3
+ import { useMemo } from 'react';
3
4
  import type * as PageTree from 'fumadocs-core/page-tree';
5
+ import { useLocationSearchParams } from '@terrafirmagreg-team/ui/hooks/page/use-location-search';
4
6
  import { useResolvedLang } from '@terrafirmagreg-team/translate/hooks/use-resolved-lang';
5
7
  import { useLastValue } from '@terrafirmagreg-team/ui/hooks/use-last-value';
6
8
  import { useRecipeModNames } from '@terrafirmagreg-team/recipe/hooks/use-mod-names';
7
9
  import { useSidebarGroup } from '@terrafirmagreg-team/recipe/hooks/use-sidebar-group';
8
10
  import { groupRecipeSidebarByMod } from '@terrafirmagreg-team/recipe/lib/sidebar';
9
- import { parseSectionLocation } from '@terrafirmagreg-team/kit/lib/location';
11
+ import { parseSectionLocation } from '@terrafirmagreg-team/sites/location';
10
12
 
11
13
  export function useRecipeLeftSidebarTree(tree: PageTree.Root): PageTree.Root {
12
- const { dataset } = parseSectionLocation(usePathname());
14
+ const { dataset: datasetId } = parseSectionLocation(usePathname());
13
15
  const lang = useResolvedLang();
14
16
  const { sidebarGroup } = useSidebarGroup();
15
- const scopeKey = dataset ?? '';
17
+ const scopeKey = datasetId ?? '';
18
+ const version = useLocationSearchParams().get('version') ?? undefined;
19
+ const dataset = useMemo(
20
+ () => (datasetId ? { id: datasetId, version } : undefined),
21
+ [datasetId, version],
22
+ );
16
23
  const modNames = useLastValue(
17
24
  useRecipeModNames(
18
25
  sidebarGroup === 'mod' ? dataset : undefined,
@@ -24,3 +31,4 @@ export function useRecipeLeftSidebarTree(tree: PageTree.Root): PageTree.Root {
24
31
  ? groupRecipeSidebarByMod(tree, modNames)
25
32
  : tree;
26
33
  }
34
+
@@ -1,11 +1,12 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { useAsyncValue } from '@terrafirmagreg-team/ui/hooks/use-async-value';
4
- import { loadClientModNames } from '@terrafirmagreg-team/game/lib/dump/client/mods';
5
+ import { loadClientModNames } from '@terrafirmagreg-team/dump/lib/dump/client/mods';
5
6
  import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
6
7
 
7
8
  export function useRecipeModNames(
8
- dataset: string | undefined,
9
+ dataset: DatasetRef | undefined,
9
10
  lang: Lang,
10
11
  ): Record<string, string> | undefined {
11
12
  return useAsyncValue(
@@ -13,3 +14,4 @@ export function useRecipeModNames(
13
14
  [dataset, lang],
14
15
  );
15
16
  }
17
+
@@ -19,3 +19,4 @@ export function useMountLimit(length: number, resetKey: unknown): number {
19
19
  }, [length, limit]);
20
20
  return limit;
21
21
  }
22
+
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { useDeferredValue, useMemo } from 'react';
4
5
  import { useItemTooltips } from '@terrafirmagreg-team/recipe/hooks/use-item-tooltips';
@@ -33,7 +34,7 @@ export function usePanelCells({
33
34
  favorites,
34
35
  expandedGroups,
35
36
  }: {
36
- dataset: string;
37
+ dataset: DatasetRef;
37
38
  index: RecipeCompactIndex | null;
38
39
  names: Record<string, Record<string, string>>;
39
40
  lang: Lang;
@@ -113,3 +114,4 @@ export function usePanelCells({
113
114
 
114
115
  return { cells, filtered: deferredFiltered, favoriteIds, dIndex, dNames, dLang };
115
116
  }
117
+
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { useCallback } from 'react';
4
5
  import {
@@ -32,20 +33,22 @@ const useStoredPanelPages = createStoredValue(
32
33
  codec,
33
34
  );
34
35
 
35
- export function usePanelPage(dataset: string) {
36
+ export function usePanelPage(dataset: DatasetRef) {
36
37
  const [pages, setPages] = useStoredPanelPages();
38
+ const refKey = `${dataset.id}/${dataset.version ?? ''}`;
37
39
  const setPage = useCallback(
38
40
  (page: number) => {
39
41
  setPages((prev) => {
40
42
  const next = page > 0 ? page : 0;
41
- if ((prev[dataset] ?? 0) === next) return prev;
43
+ if ((prev[refKey] ?? 0) === next) return prev;
42
44
  const copy = { ...prev };
43
- if (next > 0) copy[dataset] = next;
44
- else delete copy[dataset];
45
+ if (next > 0) copy[refKey] = next;
46
+ else delete copy[refKey];
45
47
  return copy;
46
48
  });
47
49
  },
48
- [dataset, setPages],
50
+ [refKey, setPages],
49
51
  );
50
- return { page: pages[dataset] ?? 0, setPage };
52
+ return { page: pages[refKey] ?? 0, setPage };
51
53
  }
54
+
@@ -23,7 +23,7 @@ export function usePanelTabs(
23
23
  size: 'lg',
24
24
  href: false,
25
25
  }),
26
- help: t('recipe.all_items'),
26
+ help: t('All items', { note: 'recipe.all_items' }),
27
27
  },
28
28
  ...panelTabRows(index, names, lang).map((row) => ({
29
29
  id: row.id,
@@ -40,3 +40,4 @@ export function usePanelTabs(
40
40
  [index, lang, names, t],
41
41
  );
42
42
  }
43
+
@@ -1,31 +1,32 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { useEffect, useState } from 'react';
4
- import { useSearchParams } from 'next/navigation';
5
+ import { useLocationSearchParams } from '@terrafirmagreg-team/ui/hooks/page/use-location-search';
5
6
  import { replaceUrlState } from '@terrafirmagreg-team/ui/lib/utils/history';
6
7
  import {
7
8
  recipeCalcHref,
8
9
  recipeHref,
9
10
  recipePlaceFromParams,
10
11
  } from '@terrafirmagreg-team/recipe/lib/links';
11
- import { canonicalStackId } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
12
+ import { canonicalStackId } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
12
13
  import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
13
14
  import type { RecipeRelation } from '@terrafirmagreg-team/recipe/types';
14
15
 
15
- function urlPage(params: ReturnType<typeof useSearchParams>, key: string) {
16
+ function urlPage(params: URLSearchParams, key: string) {
16
17
  const raw = Number.parseInt(params.get(key) || '1', 10);
17
18
  return Number.isFinite(raw) && raw > 0 ? raw - 1 : 0;
18
19
  }
19
20
 
20
21
  function urlRelation(
21
- params: ReturnType<typeof useSearchParams>,
22
+ params: URLSearchParams,
22
23
  ): RecipeRelation | undefined {
23
24
  const value = params.get('relation');
24
25
  return value === 'uses' || value === 'tags' ? value : undefined;
25
26
  }
26
27
 
27
- export function useRecipeQuery(dataset: string, lang: Lang) {
28
- const params = useSearchParams();
28
+ export function useRecipeQuery(dataset: DatasetRef, lang: Lang) {
29
+ const params = useLocationSearchParams();
29
30
 
30
31
  const place = recipePlaceFromParams(params);
31
32
  const isCalc = place.kind === 'calc';
@@ -182,3 +183,4 @@ export function useRecipeQuery(dataset: string, lang: Lang) {
182
183
  rpage: rpageValue,
183
184
  };
184
185
  }
186
+
@@ -1,10 +1,11 @@
1
1
  'use client';
2
2
 
3
3
  import { useEffect, useMemo } from 'react';
4
+ import { useVersionedDataset } from '@terrafirmagreg-team/kit/hooks/use-versioned-dataset';
4
5
  import { useRecipeIndex } from '@terrafirmagreg-team/recipe/hooks/use-index';
5
6
  import { useRecipeQuery } from '@terrafirmagreg-team/recipe/hooks/use-query';
6
7
  import { useAsyncValue } from '@terrafirmagreg-team/ui/hooks/use-async-value';
7
- import { clientDumpLang } from '@terrafirmagreg-team/game/lib/dump/client/dump';
8
+ import { clientDumpLang } from '@terrafirmagreg-team/dump/lib/dump/client/dump';
8
9
  import { useRecipeIndexNames } from '@terrafirmagreg-team/recipe/lib/index-store';
9
10
  import {
10
11
  RECIPE_ALL_TYPE,
@@ -20,7 +21,9 @@ import type {
20
21
  } from '@terrafirmagreg-team/recipe/types';
21
22
 
22
23
  export function useRecipeSession(page: RecipePageData): RecipeSession {
23
- const { dataset, lang } = page;
24
+ const { dataset: id, lang } = page;
25
+ const dataset =
26
+ useVersionedDataset(id.id, { versions: page.versions }) ?? id;
24
27
  const query = useRecipeQuery(dataset, lang);
25
28
  const { index, indexLoading } = useRecipeIndex(dataset, lang);
26
29
  const placeOpts = recipePlaceOpts(query.place);
@@ -82,3 +85,4 @@ export function useRecipeSession(page: RecipePageData): RecipeSession {
82
85
  used,
83
86
  };
84
87
  }
88
+
@@ -1,20 +1,16 @@
1
1
  import {
2
2
  createStoredValue,
3
- type StoredCodec,
3
+ literalCodec,
4
4
  } from '@terrafirmagreg-team/ui/lib/utils/stored-value';
5
5
  import type { RecipeSidebarGroup } from '@terrafirmagreg-team/recipe/types';
6
6
 
7
- const codec: StoredCodec<RecipeSidebarGroup> = {
8
- read: (raw) => (raw === 'mod' ? 'mod' : 'flat'),
9
- write: (value) => value,
10
- };
11
-
12
7
  const useStoredSidebarGroup = createStoredValue(
13
8
  'wiki.recipe.sidebarGroup',
14
- codec,
9
+ literalCodec<RecipeSidebarGroup>(['flat', 'mod'], 'mod'),
15
10
  );
16
11
 
17
12
  export function useSidebarGroup() {
18
13
  const [sidebarGroup, setSidebarGroup] = useStoredSidebarGroup();
19
14
  return { sidebarGroup, setSidebarGroup };
20
15
  }
16
+
@@ -1,10 +1,11 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { useAsyncValue } from '@terrafirmagreg-team/ui/hooks/use-async-value';
4
5
  import { loadAllTagMembers } from '@terrafirmagreg-team/recipe/lib/client-index';
5
6
 
6
7
  export function useTagMembers(
7
- dataset: string,
8
+ dataset: DatasetRef,
8
9
  enabled: boolean,
9
10
  ): Record<string, string[]> | undefined {
10
11
  return useAsyncValue(
@@ -12,3 +13,4 @@ export function useTagMembers(
12
13
  [dataset, enabled],
13
14
  );
14
15
  }
16
+
@@ -23,3 +23,4 @@ export function useToolRecipes<T>(
23
23
  [dataset, index, category, parse],
24
24
  );
25
25
  }
26
+
@@ -70,3 +70,4 @@ export function useTypeFilter(
70
70
  rows: activeRows,
71
71
  };
72
72
  }
73
+
@@ -207,3 +207,4 @@ export function minimalAdditions(
207
207
  }
208
208
  return null;
209
209
  }
210
+
@@ -34,3 +34,4 @@ export function toAlloyRecipe(recipe: Recipe): AlloyRecipe {
34
34
  }));
35
35
  return { recipe, components };
36
36
  }
37
+
@@ -1,9 +1,10 @@
1
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
1
2
  const KEY_PREFIX = 'calc:recipe-defaults:';
2
3
 
3
- export function readRecipeDefaults(dataset: string): Record<string, string> {
4
+ export function readRecipeDefaults(dataset: DatasetRef): Record<string, string> {
4
5
  if (typeof window === 'undefined') return {};
5
6
  try {
6
- const raw = window.localStorage.getItem(`${KEY_PREFIX}${dataset}`);
7
+ const raw = window.localStorage.getItem(`${KEY_PREFIX}${dataset.id}/${dataset.version ?? ''}`);
7
8
  if (!raw) return {};
8
9
  const parsed = JSON.parse(raw);
9
10
  if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
@@ -28,7 +29,7 @@ export function subscribeRecipeDefaults(listener: () => void): () => void {
28
29
  }
29
30
 
30
31
  export function writeRecipeDefault(
31
- dataset: string,
32
+ dataset: DatasetRef,
32
33
  materialId: string,
33
34
  recipeId: string | null,
34
35
  ): Record<string, string> {
@@ -36,10 +37,11 @@ export function writeRecipeDefault(
36
37
  if (recipeId === null) delete next[materialId];
37
38
  else next[materialId] = recipeId;
38
39
  try {
39
- window.localStorage.setItem(`${KEY_PREFIX}${dataset}`, JSON.stringify(next));
40
+ window.localStorage.setItem(`${KEY_PREFIX}${dataset.id}/${dataset.version ?? ''}`, JSON.stringify(next));
40
41
  } catch {
41
42
 
42
43
  }
43
44
  window.dispatchEvent(new Event(EVENT));
44
45
  return next;
45
46
  }
47
+