@terrafirmagreg-team/recipe 0.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -1,10 +1,11 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
4
5
  import { EmptyState } from '@terrafirmagreg-team/ui/components/blocks/page/empty-state';
5
6
  import { LinkPreview } from '@terrafirmagreg-team/ui/components/blocks/link/link-preview';
6
7
  import { recipeItemName } from '@terrafirmagreg-team/recipe/lib/client-index';
7
- import { tagPseudoId } from '@terrafirmagreg-team/game/lib/dump/paths';
8
+ import { tagPseudoId } from '@terrafirmagreg-team/dump/lib/dump/paths';
8
9
  import { recipeHref } from '@terrafirmagreg-team/recipe/lib/links';
9
10
  import { replaceUrlState } from '@terrafirmagreg-team/ui/lib/utils/history';
10
11
  import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
@@ -19,7 +20,7 @@ export function ItemTagGroups({
19
20
  nav,
20
21
  }: {
21
22
  groups: [string, string[]][];
22
- dataset: string;
23
+ dataset: DatasetRef;
23
24
  lang: Lang;
24
25
  names: Record<string, Record<string, string>>;
25
26
  query: string;
@@ -27,7 +28,7 @@ export function ItemTagGroups({
27
28
  }) {
28
29
  const t = useTranslations();
29
30
  if (!groups.length) {
30
- return <EmptyState variant="empty">{t('recipe.empty_tags')}</EmptyState>;
31
+ return <EmptyState variant="empty">{t('No tags for this item.', { note: 'recipe.empty_tags' })}</EmptyState>;
31
32
  }
32
33
  return (
33
34
  <div className="flex flex-col gap-3 pt-1">
@@ -36,8 +37,8 @@ export function ItemTagGroups({
36
37
  {groups.length > 1 && (
37
38
  <p className="text-xs font-medium text-muted-foreground">
38
39
  {tagKind === 'block'
39
- ? t('recipe.tags_block')
40
- : t('recipe.tags_item')}
40
+ ? t('Blocks', { note: 'recipe.tags_block' })
41
+ : t('Items', { note: 'recipe.tags_item' })}
41
42
  </p>
42
43
  )}
43
44
  <div className="flex flex-wrap gap-1">
@@ -81,3 +82,4 @@ export function ItemTagGroups({
81
82
  </div>
82
83
  );
83
84
  }
85
+
@@ -15,9 +15,9 @@ import {
15
15
  PopoverTitle,
16
16
  PopoverTrigger,
17
17
  } from '@terrafirmagreg-team/ui/components/ui/popover';
18
- import { formatNbt, formatNbtPretty } from '@terrafirmagreg-team/game/lib/snbt';
19
- import type { ParsedStackId } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
20
- import type { ReferenceKind } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
18
+ import { formatNbt, formatNbtPretty } from '@terrafirmagreg-team/dump/lib/snbt';
19
+ import type { ParsedStackId } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
20
+ import type { ReferenceKind } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
21
21
 
22
22
  export function ItemViewHeader({
23
23
  item,
@@ -42,7 +42,7 @@ export function ItemViewHeader({
42
42
  const pretty =
43
43
  parsed.nbt !== undefined ? formatNbtPretty(parsed.nbt) : undefined;
44
44
  const copyId = held.shift && nbt ? `${parsed.id}${nbt}` : parsed.id;
45
- const copyTitle = held.shift ? t('recipe.copy_stack') : t('recipe.copy');
45
+ const copyTitle = held.shift ? t('Copy stack id', { note: 'recipe.copy_stack' }) : t('Copy id', { note: 'recipe.copy' });
46
46
  return (
47
47
  <>
48
48
  <ItemRow
@@ -82,7 +82,7 @@ export function ItemViewHeader({
82
82
  <IconButton
83
83
  variant="ghost"
84
84
  tooltip={false}
85
- label={t('recipe.nbt')}
85
+ label={t('NBT', { note: 'recipe.nbt' })}
86
86
  className="size-7 rounded-lg text-muted-foreground hover:text-foreground [&_svg]:size-3.5"
87
87
  >
88
88
  <Braces />
@@ -95,12 +95,12 @@ export function ItemViewHeader({
95
95
  >
96
96
  <div className="flex items-center justify-between">
97
97
  <PopoverTitle className="text-xs font-medium tracking-wider text-muted-foreground uppercase">
98
- {t('recipe.nbt')}
98
+ {t('NBT', { note: 'recipe.nbt' })}
99
99
  </PopoverTitle>
100
100
  <ButtonCopy
101
101
  value={nbt}
102
- title={t('recipe.copy_nbt')}
103
- aria-label={t('recipe.copy_nbt')}
102
+ title={t('Copy NBT', { note: 'recipe.copy_nbt' })}
103
+ aria-label={t('Copy NBT', { note: 'recipe.copy_nbt' })}
104
104
  copy={<Copy className="size-3" />}
105
105
  copied={<Check className="size-3" />}
106
106
  />
@@ -114,7 +114,7 @@ export function ItemViewHeader({
114
114
  {onAddToCalc ? (
115
115
  <IconButton
116
116
  variant="ghost"
117
- label={t('recipe.calc.add.title')}
117
+ label={t('Add to calculator', { note: 'recipe.calc.add.title' })}
118
118
  className="size-7 rounded-lg text-muted-foreground hover:text-foreground [&_svg]:size-3.5"
119
119
  onClick={onAddToCalc}
120
120
  >
@@ -128,3 +128,4 @@ export function ItemViewHeader({
128
128
  </>
129
129
  );
130
130
  }
131
+
@@ -58,3 +58,4 @@ export function PaneTabs<T extends string>({
58
58
  </Tabs>
59
59
  );
60
60
  }
61
+
@@ -25,7 +25,7 @@ export function PlaceHeader({
25
25
  ? index?.categories?.find((row) => row.id === placeId)
26
26
  : undefined;
27
27
  const title = isCalc
28
- ? t('recipe.calculator')
28
+ ? t('Calculator', { note: 'recipe.calculator' })
29
29
  : placeId
30
30
  ? recipeItemName(names, lang, placeId)
31
31
  : '';
@@ -58,8 +58,8 @@ export function PlaceHeader({
58
58
  <span className="truncate">{placeId}</span>
59
59
  <ButtonCopy
60
60
  value={placeId}
61
- title={t('recipe.copy')}
62
- aria-label={t('recipe.copy')}
61
+ title={t('Copy id', { note: 'recipe.copy' })}
62
+ aria-label={t('Copy id', { note: 'recipe.copy' })}
63
63
  copy={<Copy className="size-3" />}
64
64
  copied={<Check className="size-3" />}
65
65
  />
@@ -69,3 +69,4 @@ export function PlaceHeader({
69
69
  />
70
70
  );
71
71
  }
72
+
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import type { ReactNode } from 'react';
4
5
  import { Search } from 'lucide-react';
@@ -15,7 +16,7 @@ import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
15
16
  import type { RecipeHrefOpts } from '@terrafirmagreg-team/recipe/types';
16
17
 
17
18
  type QueryNav = {
18
- dataset: string;
19
+ dataset: DatasetRef;
19
20
  lang: Lang;
20
21
  params?: RecipeHrefOpts;
21
22
  };
@@ -101,3 +102,4 @@ export function QueryPager({
101
102
  />
102
103
  );
103
104
  }
105
+
@@ -7,8 +7,8 @@ import { Entry } from '@terrafirmagreg-team/game/components/entry/entry';
7
7
  import { CopyIdMenu } from '@terrafirmagreg-team/game/components/entry/copy-id-menu';
8
8
  import { ButtonCopy } from '@terrafirmagreg-team/recipe/components/button-copy';
9
9
  import { recipeItemName } from '@terrafirmagreg-team/recipe/lib/client-index';
10
- import { parseStackId } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
11
- import { formatNbt } from '@terrafirmagreg-team/game/lib/snbt';
10
+ import { parseStackId } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
11
+ import { formatNbt } from '@terrafirmagreg-team/dump/lib/snbt';
12
12
  import type { Recipe } from '@terrafirmagreg-team/recipe/lib/data/dto';
13
13
  import type { RecipeSession } from '@terrafirmagreg-team/recipe/types';
14
14
 
@@ -47,8 +47,8 @@ export function RecipeItemHeader({
47
47
  <span className="truncate">{recipe.id}</span>
48
48
  <ButtonCopy
49
49
  value={recipe.id}
50
- title={t('recipe.copy_recipe_id')}
51
- aria-label={t('recipe.copy_recipe_id')}
50
+ title={t('Copy recipe id', { note: 'recipe.copy_recipe_id' })}
51
+ aria-label={t('Copy recipe id', { note: 'recipe.copy_recipe_id' })}
52
52
  copy={<Copy className="size-3" />}
53
53
  copied={<Check className="size-3" />}
54
54
  />
@@ -57,3 +57,4 @@ export function RecipeItemHeader({
57
57
  />
58
58
  );
59
59
  }
60
+
@@ -25,7 +25,7 @@ export function RecipeSettings() {
25
25
  <Button
26
26
  variant="outline"
27
27
  size="icon-sm"
28
- aria-label={t('recipe.settings')}
28
+ aria-label={t('Settings', { note: 'recipe.settings' })}
29
29
  className="rounded-lg"
30
30
  />
31
31
  }
@@ -35,7 +35,7 @@ export function RecipeSettings() {
35
35
  <PopoverContent align="start" className="flex w-56 flex-col gap-2">
36
36
  <Label className="gap-1.5 text-sm font-normal">
37
37
  <Switch size="sm" checked={hideUnused} onCheckedChange={setHideUnused} />
38
- {t('recipe.hide_unused')}
38
+ {t('Hide unused', { note: 'recipe.hide_unused' })}
39
39
  </Label>
40
40
  <Label className="gap-1.5 text-sm font-normal">
41
41
  <Switch
@@ -43,9 +43,10 @@ export function RecipeSettings() {
43
43
  checked={sidebarGroup === 'mod'}
44
44
  onCheckedChange={(value) => setSidebarGroup(value ? 'mod' : 'flat')}
45
45
  />
46
- {t('recipe.sidebar_group_mod')}
46
+ {t('Group sidebar by mod', { note: 'recipe.sidebar_group_mod' })}
47
47
  </Label>
48
48
  </PopoverContent>
49
49
  </Popover>
50
50
  );
51
51
  }
52
+
@@ -26,12 +26,12 @@ export function RecipeToolbar({ session }: { session: RecipeSession }) {
26
26
  onClick={() => setItemsOpen(true)}
27
27
  >
28
28
  <LayoutGrid className="size-3.5" />
29
- {t('recipe.items')}
29
+ {t('Items', { note: 'recipe.items' })}
30
30
  </Button>
31
31
  <Sheet open={itemsOpen} onOpenChange={setItemsOpen}>
32
32
  <SheetContent side="right" className="w-full gap-3 sm:max-w-md">
33
33
  <SheetHeader>
34
- <SheetTitle>{t('recipe.items')}</SheetTitle>
34
+ <SheetTitle>{t('Items', { note: 'recipe.items' })}</SheetTitle>
35
35
  </SheetHeader>
36
36
  <ItemPanel
37
37
  dataset={session.dataset}
@@ -51,3 +51,4 @@ export function RecipeToolbar({ session }: { session: RecipeSession }) {
51
51
  </div>
52
52
  );
53
53
  }
54
+
@@ -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 { Shapes } from 'lucide-react';
5
6
  import { Entry } from '@terrafirmagreg-team/game/components/entry/entry';
6
7
  import { recipeItemName } from '@terrafirmagreg-team/recipe/lib/client-index';
7
- import { IconTabs } from '@terrafirmagreg-team/game/components/entry/icon/tabs';
8
+ import { IconPager } from '@terrafirmagreg-team/ui/components/blocks/page/pager';
8
9
  import type { RecipeCategory, RecipeSession } from '@terrafirmagreg-team/recipe/types';
9
10
 
10
11
  function CategoryGlyph({
@@ -12,7 +13,7 @@ function CategoryGlyph({
12
13
  dataset,
13
14
  }: {
14
15
  category?: RecipeCategory;
15
- dataset: string;
16
+ dataset: DatasetRef;
16
17
  }) {
17
18
  return category?.icon ? (
18
19
  <Entry
@@ -59,7 +60,7 @@ export function RecipeTypeTabs({
59
60
  }, [index, ids, lang, names, dataset]);
60
61
 
61
62
  return (
62
- <IconTabs
63
+ <IconPager
63
64
  value={value}
64
65
  options={options}
65
66
  onChange={onChange ?? (() => {})}
@@ -67,3 +68,4 @@ export function RecipeTypeTabs({
67
68
  />
68
69
  );
69
70
  }
71
+
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useEffect, useMemo, useRef } from 'react';
3
+ import { useEffect, useRef } from 'react';
4
4
  import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
5
5
  import { useAsyncResult, useAsyncValue } from '@terrafirmagreg-team/ui/hooks/use-async-value';
6
6
  import { RecipeFrame } from '@terrafirmagreg-team/recipe/components/views/browser/frame';
@@ -9,6 +9,7 @@ import {
9
9
  recipeCardDim,
10
10
  } from '@terrafirmagreg-team/recipe/components/views/browser/card-grid';
11
11
  import { PlaceHeader } from '@terrafirmagreg-team/recipe/components/views/browser/place-header';
12
+ import { useCategoriesById } from '@terrafirmagreg-team/recipe/hooks/use-categories-by-id';
12
13
  import { useFitPages } from '@terrafirmagreg-team/ui/hooks/page/use-fit-pages';
13
14
  import {
14
15
  loadTypeRows,
@@ -20,7 +21,6 @@ import { RecipeTypeTabs } from '@terrafirmagreg-team/recipe/components/views/bro
20
21
  import { WorkstationRail } from '@terrafirmagreg-team/recipe/components/views/browser/workstation-rail';
21
22
  import { replaceUrlState } from '@terrafirmagreg-team/ui/lib/utils/history';
22
23
  import type {
23
- RecipeCategory,
24
24
  RecipeCompactRow,
25
25
  RecipeSession,
26
26
  } from '@terrafirmagreg-team/recipe/types';
@@ -43,13 +43,7 @@ export function TypeView({ session }: { session: RecipeSession }) {
43
43
 
44
44
  const placeId = place?.kind === 'type' ? place.id : RECIPE_ALL_TYPE;
45
45
 
46
- const categoriesById = useMemo(
47
- () =>
48
- new Map<string, RecipeCategory>(
49
- (index?.categories ?? []).map((row) => [row.id, row]),
50
- ),
51
- [index],
52
- );
46
+ const categoriesById = useCategoriesById(index);
53
47
  const nonEmptyIds = nonEmptyCategoryIds(index);
54
48
 
55
49
  const shown =
@@ -133,7 +127,7 @@ export function TypeView({ session }: { session: RecipeSession }) {
133
127
  session={session}
134
128
  rows={EMPTY_ROWS}
135
129
  boxRef={boxRef}
136
- empty={t('recipe.no_dump')}
130
+ empty={t('No recipe dump for this dataset. Run export to generate data.', { note: 'recipe.no_dump' })}
137
131
  />
138
132
  ) : (
139
133
  <RecipeCardGrid
@@ -143,7 +137,7 @@ export function TypeView({ session }: { session: RecipeSession }) {
143
137
  maxH={list.box.h}
144
138
  maxW={list.box.w}
145
139
  loading={index == null || (shown != null && rows === undefined)}
146
- empty={t('recipe.empty_type')}
140
+ empty={t('No recipes of this type.', { note: 'recipe.empty_type' })}
147
141
  onSwipe={onSwipe}
148
142
  />
149
143
  );
@@ -172,7 +166,7 @@ export function TypeView({ session }: { session: RecipeSession }) {
172
166
  />
173
167
  }
174
168
  header={
175
- <PlaceHeader session={session} overline={t('recipe.axis_type')} />
169
+ <PlaceHeader session={session} overline={t('Categories', { note: 'recipe.axis_type' })} />
176
170
  }
177
171
  rail={
178
172
  <WorkstationRail
@@ -187,3 +181,4 @@ export function TypeView({ session }: { session: RecipeSession }) {
187
181
  </RecipeFrame>
188
182
  );
189
183
  }
184
+
@@ -3,8 +3,8 @@
3
3
  import { Factory } from 'lucide-react';
4
4
  import { Entry } from '@terrafirmagreg-team/game/components/entry/entry';
5
5
  import { recipeItemName } from '@terrafirmagreg-team/recipe/lib/client-index';
6
- import { stackIdFromRef } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
7
- import type { TaggedReference } from '@terrafirmagreg-team/game/lib/dump/contract/ref';
6
+ import { stackIdFromRef } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
7
+ import type { TaggedReference } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
8
8
  import type { RecipeSession } from '@terrafirmagreg-team/recipe/types';
9
9
 
10
10
  export function WorkstationRail({
@@ -17,7 +17,7 @@ export function WorkstationRail({
17
17
  if (!workstations?.length) return null;
18
18
  const { lang, names, nav } = session;
19
19
  return (
20
- <div className="flex max-h-full self-start flex-col flex-wrap content-start items-center gap-1 rounded-lg border border-border bg-muted/60 p-1">
20
+ <div className="scrollbar-thin flex max-h-full flex-col items-center gap-1 self-start overflow-y-auto overflow-x-hidden rounded-lg border border-border bg-muted/60 p-1">
21
21
  <Factory
22
22
  className="m-1 size-3.5 shrink-0 text-muted-foreground"
23
23
  aria-hidden
@@ -40,3 +40,4 @@ export function WorkstationRail({
40
40
  </div>
41
41
  );
42
42
  }
43
+
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { Play, Share2, Trash2 } from 'lucide-react';
3
+ import { Share2, Trash2 } from 'lucide-react';
4
4
  import { IconButton } from '@terrafirmagreg-team/ui/components/blocks/button/icon-button';
5
5
  import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
6
6
  import { RecipeToolbar } from '@terrafirmagreg-team/recipe/components/views/browser/toolbar';
@@ -12,14 +12,12 @@ export function CalcActions({
12
12
  shared,
13
13
  onShare,
14
14
  hasTargets,
15
- onAutoBuild,
16
15
  onClear,
17
16
  }: {
18
17
  session: RecipeSession;
19
18
  shared: boolean;
20
19
  onShare: () => void;
21
20
  hasTargets: boolean;
22
- onAutoBuild: () => void;
23
21
  onClear: () => void;
24
22
  }) {
25
23
  const t = useTranslations();
@@ -28,7 +26,7 @@ export function CalcActions({
28
26
  <RecipeToolbar session={session} />
29
27
  <IconButton
30
28
  overlay
31
- label={shared ? t('recipe.copied') : t('recipe.calc.share')}
29
+ label={shared ? t('Copied', { note: 'recipe.copied' }) : t('Share', { note: 'recipe.calc.share' })}
32
30
  onClick={onShare}
33
31
  >
34
32
  <Share2 className="size-3.5" />
@@ -37,16 +35,7 @@ export function CalcActions({
37
35
  {hasTargets ? (
38
36
  <IconButton
39
37
  overlay
40
- label={t('recipe.calc.auto.build')}
41
- onClick={onAutoBuild}
42
- >
43
- <Play className="size-3.5" />
44
- </IconButton>
45
- ) : null}
46
- {hasTargets ? (
47
- <IconButton
48
- overlay
49
- label={t('recipe.calc.clear.title')}
38
+ label={t('Clear', { note: 'recipe.calc.clear.title' })}
50
39
  onClick={onClear}
51
40
  >
52
41
  <Trash2 className="size-3.5" />
@@ -55,3 +44,4 @@ export function CalcActions({
55
44
  </>
56
45
  );
57
46
  }
47
+
@@ -2,10 +2,13 @@
2
2
 
3
3
  import {
4
4
  ArrowUp,
5
+ ChevronUp,
5
6
  ChevronsUpDown,
6
7
  Eye,
7
8
  Info,
9
+ Mountain,
8
10
  Move,
11
+ Scissors,
9
12
  Settings2,
10
13
  SquareMousePointer,
11
14
  } from 'lucide-react';
@@ -22,39 +25,33 @@ export function CalcHelp() {
22
25
  return (
23
26
  <Popover>
24
27
  <PopoverTrigger
25
- render={<IconButton overlay label={t('recipe.calc.help.title')} />}
28
+ render={<IconButton overlay label={t('Calculator help', { note: 'recipe.calc.help.title' })} />}
26
29
  >
27
30
  <Info className="size-3.5" />
28
31
  </PopoverTrigger>
29
32
  <PopoverContent side="bottom" align="end" className="w-80 space-y-3 p-3">
30
33
  <section className="space-y-1.5">
31
34
  <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/70">
32
- {t('recipe.calc.help.sec_nodes')}
35
+ {t('Nodes', { note: 'recipe.calc.help.sec_nodes' })}
33
36
  </h4>
34
37
  <ul className="space-y-1.5 text-xs text-muted-foreground">
35
38
  <li className="flex items-center gap-2">
36
39
  <span className="flex w-5 shrink-0 items-center justify-center">
37
40
  <span className="size-4 rounded border border-primary/60" />
38
41
  </span>
39
- {t('recipe.calc.help.recipe')}
40
- </li>
41
- <li className="flex items-center gap-2">
42
- <span className="flex w-5 shrink-0 items-center justify-center">
43
- <span className="size-4 rounded border border-dashed border-muted-foreground/50" />
44
- </span>
45
- {t('recipe.calc.help.catalyst')}
42
+ {t('Resolved with a recipe', { note: 'recipe.calc.help.recipe' })}
46
43
  </li>
47
44
  <li className="flex items-center gap-2">
48
45
  <span className="flex w-5 shrink-0 items-center justify-center">
49
46
  <span className="size-4 rounded border border-red-500/70" />
50
47
  </span>
51
- {t('recipe.calc.help.cycle')}
48
+ {t('Recipe cycle', { note: 'recipe.calc.help.cycle' })}
52
49
  </li>
53
50
  <li className="flex items-center gap-2">
54
51
  <span className="flex w-5 shrink-0 items-center justify-center">
55
52
  <span className="size-3.5 rounded ring-2 ring-primary/70" />
56
53
  </span>
57
- {t('recipe.calc.help.focus')}
54
+ {t('Focused node', { note: 'recipe.calc.help.focus' })}
58
55
  </li>
59
56
  <li className="flex items-center gap-2">
60
57
  <span className="flex w-5 shrink-0 items-center justify-center">
@@ -62,83 +59,126 @@ export function CalcHelp() {
62
59
  +N
63
60
  </span>
64
61
  </span>
65
- {t('recipe.calc.help.satisfied')}
62
+ {t('Covered by another card output', { note: 'recipe.calc.help.satisfied' })}
66
63
  </li>
67
64
  <li className="flex items-center gap-2">
68
65
  <span className="flex w-5 shrink-0 items-center justify-center">
69
- <span className="rounded border border-border bg-background px-0.5 text-[10px] leading-3">
70
- S
71
- </span>
66
+ <Mountain className="size-3.5 text-muted-foreground" />
72
67
  </span>
73
- {t('recipe.calc.help.source')}
68
+ {t('No recipe; raw material', { note: 'recipe.calc.help.source' })}
74
69
  </li>
75
70
  </ul>
76
71
  </section>
77
72
  <section className="space-y-1.5">
78
73
  <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/70">
79
- {t('recipe.calc.help.sec_edges')}
74
+ {t('Ports', { note: 'recipe.calc.help.sec_ports' })}
75
+ </h4>
76
+ <ul className="space-y-1.5 text-xs text-muted-foreground">
77
+ <li className="flex items-center gap-2">
78
+ <span className="flex w-5 shrink-0 items-center justify-center">
79
+ <span className="size-3.5 rounded-sm border border-emerald-500" />
80
+ </span>
81
+ {t('Input port: click to pick a supplier or drag a wire', { note: 'recipe.calc.help.port_in' })}
82
+ </li>
83
+ <li className="flex items-center gap-2">
84
+ <span className="flex w-5 shrink-0 items-center justify-center">
85
+ <span className="size-3.5 rounded-sm border border-red-400" />
86
+ </span>
87
+ {t('Output port: click to pick a recipe or drag a wire', { note: 'recipe.calc.help.port_out' })}
88
+ </li>
89
+ <li className="flex items-center gap-2">
90
+ <span className="flex w-5 shrink-0 items-center justify-center">
91
+ <span className="size-3.5 rounded-sm border border-dotted border-emerald-500" />
92
+ </span>
93
+ {t('Catalyst: needed but not consumed; wire it or click to pick a supplier', { note: 'recipe.calc.help.port_cat' })}
94
+ </li>
95
+ <li className="flex items-center gap-2">
96
+ <span className="flex w-5 shrink-0 items-center justify-center">
97
+ <ChevronUp className="size-3.5 text-muted-foreground" />
98
+ </span>
99
+ {t('Hidden ports - scroll the rail or click the arrow', { note: 'recipe.calc.help.port_more' })}
100
+ </li>
101
+ </ul>
102
+ </section>
103
+ <section className="space-y-1.5">
104
+ <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/70">
105
+ {t('Edges', { note: 'recipe.calc.help.sec_edges' })}
80
106
  </h4>
81
107
  <ul className="space-y-1.5 text-xs text-muted-foreground">
82
108
  <li className="flex items-center gap-2">
83
109
  <span className="flex w-5 shrink-0 items-center justify-center">
84
110
  <ArrowUp className="size-3.5 text-muted-foreground" />
85
111
  </span>
86
- {t('recipe.calc.help.edges')}
112
+ {t('Arrows flow from ingredient to product', { note: 'recipe.calc.help.edges' })}
87
113
  </li>
88
114
  <li className="flex items-center gap-2">
89
115
  <span className="flex w-5 shrink-0 items-center justify-center">
90
116
  <span className="h-0.5 w-4 rounded bg-teal-500" />
91
117
  </span>
92
- {t('recipe.calc.help.edge_out')}
118
+ {t('Outgoing edges', { note: 'recipe.calc.help.edge_out' })}
93
119
  </li>
94
120
  <li className="flex items-center gap-2">
95
121
  <span className="flex w-5 shrink-0 items-center justify-center">
96
122
  <span className="h-0.5 w-4 rounded bg-yellow-500" />
97
123
  </span>
98
- {t('recipe.calc.help.edge_in')}
124
+ {t('Incoming edges', { note: 'recipe.calc.help.edge_in' })}
125
+ </li>
126
+ <li className="flex items-center gap-2">
127
+ <span className="flex w-5 shrink-0 items-center justify-center">
128
+ <span className="h-0 w-4 border-t border-dashed border-primary/70" />
129
+ </span>
130
+ {t('Drag port to port to link; drag a wire end to retarget', { note: 'recipe.calc.help.edge_link' })}
99
131
  </li>
100
132
  <li className="flex items-center gap-2">
101
133
  <span className="flex w-5 shrink-0 items-center justify-center">
102
- <span className="h-0 w-4 border-t border-dashed border-muted-foreground/60" />
134
+ <span className="rounded border border-border bg-background px-0.5 text-[9px] leading-3 tabular-nums">
135
+ 16
136
+ </span>
103
137
  </span>
104
- {t('recipe.calc.help.edge_catalyst')}
138
+ {t('Flow on a wire - shows on hover; click to cap the amount', { note: 'recipe.calc.help.edge_flow' })}
139
+ </li>
140
+ <li className="flex items-center gap-2">
141
+ <span className="flex w-5 shrink-0 items-center justify-center">
142
+ <Scissors className="size-3.5 text-muted-foreground" />
143
+ </span>
144
+ {t('Shift + hover a wire shows x - click to delete it', { note: 'recipe.calc.help.edge_cut' })}
105
145
  </li>
106
146
  </ul>
107
147
  </section>
108
148
  <section className="space-y-1.5">
109
149
  <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/70">
110
- {t('recipe.calc.help.sec_actions')}
150
+ {t('Actions', { note: 'recipe.calc.help.sec_actions' })}
111
151
  </h4>
112
152
  <ul className="space-y-1.5 text-xs text-muted-foreground">
113
153
  <li className="flex items-center gap-2">
114
154
  <span className="flex w-5 shrink-0 items-center justify-center">
115
155
  <ChevronsUpDown className="size-3.5 text-muted-foreground" />
116
156
  </span>
117
- {t('recipe.calc.help.chip')}
157
+ {t('Recipe/tag member: open picker', { note: 'recipe.calc.help.chip' })}
118
158
  </li>
119
159
  <li className="flex items-center gap-2">
120
160
  <span className="flex w-5 shrink-0 items-center justify-center">
121
161
  <Eye className="size-3.5 text-muted-foreground" />
122
162
  </span>
123
- {t('recipe.calc.help.hover')}
163
+ {t('Recipe preview', { note: 'recipe.calc.help.hover' })}
124
164
  </li>
125
165
  <li className="flex items-center gap-2">
126
166
  <span className="flex w-5 shrink-0 items-center justify-center">
127
167
  <SquareMousePointer className="size-3.5 text-muted-foreground" />
128
168
  </span>
129
- {t('recipe.calc.help.ctx')}
169
+ {t('Node context menu', { note: 'recipe.calc.help.ctx' })}
130
170
  </li>
131
171
  <li className="flex items-center gap-2">
132
172
  <span className="flex w-5 shrink-0 items-center justify-center">
133
173
  <Settings2 className="size-3.5 text-muted-foreground" />
134
174
  </span>
135
- {t('recipe.calc.help.modes')}
175
+ {t('View modes', { note: 'recipe.calc.help.modes' })}
136
176
  </li>
137
177
  <li className="flex items-center gap-2">
138
178
  <span className="flex w-5 shrink-0 items-center justify-center">
139
179
  <Move className="size-3.5 text-muted-foreground" />
140
180
  </span>
141
- {t('recipe.calc.help.nav')}
181
+ {t('Pan and zoom', { note: 'recipe.calc.help.nav' })}
142
182
  </li>
143
183
  </ul>
144
184
  </section>
@@ -146,3 +186,4 @@ export function CalcHelp() {
146
186
  </Popover>
147
187
  );
148
188
  }
189
+