@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
@@ -6,37 +6,26 @@ import { EmptyState } from '@terrafirmagreg-team/ui/components/blocks/page/empty
6
6
  import { LeftSidebarPortal } from '@terrafirmagreg-team/ui/components/layouts/left-sidebar';
7
7
  import { CalcFlow } from '@terrafirmagreg-team/recipe/components/views/calc/flow';
8
8
  import { CalcSidebar } from '@terrafirmagreg-team/recipe/components/views/calc/sidebar';
9
- import { CalcTarget } from '@terrafirmagreg-team/recipe/components/views/calc/target';
10
9
  import { CalcHistoryList } from '@terrafirmagreg-team/recipe/components/views/calc/trees';
11
10
  import { CalcActions } from '@terrafirmagreg-team/recipe/components/views/calc/calculator/actions';
12
11
  import { useCalcFocus } from '@terrafirmagreg-team/recipe/hooks/calc/use-focus';
13
12
  import { useCalcReset } from '@terrafirmagreg-team/recipe/hooks/calc/use-reset';
14
13
  import { useCalcAmounts } from '@terrafirmagreg-team/recipe/hooks/calc/use-amounts';
15
- import { useCalcFill } from '@terrafirmagreg-team/recipe/hooks/calc/use-calc-fill';
16
14
  import { useCalcActions } from '@terrafirmagreg-team/recipe/hooks/calc/use-actions';
17
15
  import type { RecipeSession } from '@terrafirmagreg-team/recipe/types';
18
16
  import { useCalcData } from '@terrafirmagreg-team/recipe/hooks/calc/use-data';
19
17
  import { useCalcViewMode } from '@terrafirmagreg-team/recipe/hooks/calc/use-view-mode';
20
- import { useCalcDirection } from '@terrafirmagreg-team/recipe/hooks/calc/use-direction';
21
- import { useCalcEdgeStyle } from '@terrafirmagreg-team/recipe/hooks/calc/use-edge-style';
22
18
  import { useCalcFarMode } from '@terrafirmagreg-team/recipe/hooks/calc/use-far-mode';
23
- import { useCalcDensity } from '@terrafirmagreg-team/recipe/hooks/calc/use-density';
24
19
 
25
20
  export function Calculator({ session }: { session: RecipeSession }) {
26
21
  const t = useTranslations();
27
22
  const { dataset, lang } = session;
28
23
  const [mode, setMode] = useCalcViewMode();
29
- const [direction, setDirection] = useCalcDirection();
30
- const [edgeStyle, setEdgeStyle] = useCalcEdgeStyle();
31
24
  const [farCards, setFarCards] = useCalcFarMode();
32
- const [density, setDensity] = useCalcDensity();
33
25
  const {
34
- target,
35
- inventory,
36
- selections,
26
+ cards,
27
+ wires,
37
28
  tagSelections,
38
- collapsed,
39
- auto,
40
29
  tabs,
41
30
  activeTabId,
42
31
  history,
@@ -49,59 +38,40 @@ export function Calculator({ session }: { session: RecipeSession }) {
49
38
  removeHistory,
50
39
  recipeDefaults,
51
40
  setRecipeDefault,
52
- goals,
41
+ getRecipesForItem,
42
+ ensureMaterial,
43
+ nodes,
44
+ flows,
53
45
  summary,
54
- ideal,
55
46
  persist,
56
- fillTree,
57
47
  encodeShare,
58
48
  recipeBody,
59
49
  settled,
60
50
  } = useCalcData(session);
61
- const deferredGoals = useDeferredValue(goals);
51
+ const deferredNodes = useDeferredValue(nodes);
52
+ const deferredFlows = useDeferredValue(flows);
62
53
  const deferredLangMap = useDeferredValue(session.langMap);
63
54
  const { focusNodeRef, onFocusNode, focusNodeId, setFocusNodeId } =
64
- useCalcFocus(target);
65
- const { resetGen, setResetGen } = useCalcReset(activeTabId);
66
- const {
67
- displayTarget,
68
- displayInventory,
69
- flushAmounts,
70
- onAmount,
71
- onStockAmount,
72
- } = useCalcAmounts({
73
- target,
74
- inventory,
75
- persist,
76
- selections,
55
+ useCalcFocus(cards);
56
+ const { resetGen } = useCalcReset(activeTabId);
57
+ const { displayCards, flushAmounts, onCrafts } = useCalcAmounts({
58
+ cards,
59
+ wires,
77
60
  tagSelections,
78
- collapsed,
79
- auto,
80
- });
81
- const { onFillNode, onAutoBuild } = useCalcFill({
82
- dataset,
83
- hasTarget: !!target,
84
- fillTree,
85
- setResetGen,
61
+ persist,
86
62
  });
87
63
  const {
88
64
  shared,
89
65
  onShare,
90
66
  flowData,
91
- onAddStock,
92
- onRemoveStock,
93
- onApplyIdeal,
94
- onRemoveTarget,
67
+ onMoveCards,
95
68
  onClear,
96
69
  } = useCalcActions({
97
70
  dataset,
98
71
  lang,
99
- selections,
72
+ cards: displayCards,
73
+ wires,
100
74
  tagSelections,
101
- collapsed,
102
- auto,
103
- goal: goals[0] ?? null,
104
- ideal,
105
75
  persist,
106
76
  recipeDefaults,
107
77
  setRecipeDefault,
@@ -109,7 +79,7 @@ export function Calculator({ session }: { session: RecipeSession }) {
109
79
  encodeShare,
110
80
  flushAmounts,
111
81
  setFocusNodeId,
112
- onFillNode,
82
+ nodes: deferredNodes,
113
83
  });
114
84
 
115
85
  const iconItems = useMemo(
@@ -124,6 +94,11 @@ export function Calculator({ session }: { session: RecipeSession }) {
124
94
  [session.index],
125
95
  );
126
96
 
97
+ const data = useMemo(
98
+ () => ({ ...flowData, onCrafts, getRecipesForItem, ensureMaterial }),
99
+ [flowData, onCrafts, getRecipesForItem, ensureMaterial],
100
+ );
101
+
127
102
  return (
128
103
  <>
129
104
  <LeftSidebarPortal>
@@ -132,7 +107,6 @@ export function Calculator({ session }: { session: RecipeSession }) {
132
107
  lang={lang}
133
108
  langMap={deferredLangMap}
134
109
  summary={summary}
135
- target={displayTarget}
136
110
  onFocus={onFocusNode}
137
111
  trees={{
138
112
  tabs,
@@ -150,61 +124,38 @@ export function Calculator({ session }: { session: RecipeSession }) {
150
124
  />
151
125
  </LeftSidebarPortal>
152
126
  <CalcFlow
153
- goals={deferredGoals}
127
+ nodes={deferredNodes}
128
+ wires={wires}
129
+ flows={deferredFlows}
130
+ cards={displayCards}
154
131
  dataset={dataset}
155
132
  session={session}
156
- data={flowData}
133
+ data={data}
157
134
  mode={mode}
158
135
  onModeChange={setMode}
159
- direction={direction}
160
- onDirectionChange={setDirection}
161
- edgeStyle={edgeStyle}
162
- onEdgeStyleChange={setEdgeStyle}
163
136
  farCards={farCards}
164
137
  onFarCardsChange={setFarCards}
165
- density={density}
166
- onDensityChange={setDensity}
167
-
168
- treeReady={settled && goals === deferredGoals}
138
+ treeReady={settled}
169
139
  resetGen={resetGen}
170
140
  canvasId={activeTabId ?? undefined}
141
+ onRelayout={onMoveCards}
171
142
  actions={
172
143
  <CalcActions
173
144
  session={session}
174
145
  shared={shared}
175
146
  onShare={onShare}
176
- hasTargets={!!target}
177
- onAutoBuild={onAutoBuild}
178
- onClear={onClear}
179
- />
180
- }
181
- panel={
182
- <CalcTarget
183
- dataset={dataset}
184
- lang={lang}
185
- langMap={deferredLangMap}
186
- target={displayTarget}
187
- inventory={displayInventory}
188
- iconItems={iconItems}
189
- ideal={ideal}
190
- onAmount={onAmount}
191
- onStockAmount={onStockAmount}
192
- onRemoveTarget={onRemoveTarget}
193
- onRemoveStock={onRemoveStock}
194
- onAddStock={onAddStock}
195
- onApplyIdeal={onApplyIdeal}
147
+ hasTargets={displayCards.length > 0}
196
148
  onClear={onClear}
197
- onFocus={onFocusNode}
198
149
  />
199
150
  }
200
151
  focusRef={focusNodeRef}
201
152
  focusNodeId={focusNodeId}
202
153
  onFocusedNode={() => setFocusNodeId(null)}
203
154
  empty={
204
- <div className="absolute inset-0 z-10 grid place-items-center overflow-auto p-4">
155
+ <div className="scrollbar-thin absolute inset-0 z-10 grid place-items-center overflow-auto p-4">
205
156
  <div className="flex flex-col items-center gap-4">
206
157
  <EmptyState variant="empty">
207
- {t('recipe.calc.empty')}
158
+ {t('Click an item in the catalog to add a card.', { note: 'recipe.calc.empty' })}
208
159
  </EmptyState>
209
160
  <CalcHistoryList
210
161
  history={history}
@@ -218,3 +169,4 @@ export function Calculator({ session }: { session: RecipeSession }) {
218
169
  </>
219
170
  );
220
171
  }
172
+
@@ -77,3 +77,4 @@ export const CalcDotOverlay = memo(function CalcDotOverlay({
77
77
  />
78
78
  );
79
79
  });
80
+
@@ -1,50 +1,152 @@
1
1
  'use client';
2
2
 
3
- import { createChevronEdge } from '@terrafirmagreg-team/ui/components/blocks/canvas/chevron-edge';
3
+ import { useState, type KeyboardEvent } from 'react';
4
4
  import {
5
- getBezierPath,
6
- getSmoothStepPath,
7
- } from '@terrafirmagreg-team/ui/lib/canvas/xyflow';
5
+ createChevronEdge,
6
+ type ChevronEdgePathContext,
7
+ } from '@terrafirmagreg-team/ui/components/blocks/canvas/chevron-edge';
8
+ import { getSmoothStepPath } from '@terrafirmagreg-team/ui/lib/canvas/xyflow';
8
9
  import {
9
- chamferedPolylinePath,
10
+ roundedPolylinePath,
10
11
  svgPathToPoints,
11
12
  } from '@terrafirmagreg-team/ui/lib/canvas/edge';
13
+ import { Entry } from '@terrafirmagreg-team/game/components/entry/entry';
14
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
15
+ import { useCalcSession } from '@terrafirmagreg-team/recipe/hooks/calc/use-session';
16
+ import { recipeItemName } from '@terrafirmagreg-team/recipe/lib/client-index';
17
+ import { formatCount } from '@terrafirmagreg-team/ui/lib/utils/format';
12
18
  import type { FlowPoint } from '@terrafirmagreg-team/ui/types/canvas';
13
- import type { CalcEdgeStyle } from '@terrafirmagreg-team/recipe/lib/calc/flow';
19
+ import type { CalcWire } from '@terrafirmagreg-team/recipe/types/calc';
20
+
21
+ function EdgeAmountLabel({ ctx }: { ctx: ChevronEdgePathContext }) {
22
+ const wire = ctx.data?.wire as CalcWire | undefined;
23
+ const flow = ctx.data?.flow as number | undefined;
24
+ const matId = ctx.data?.matId as string | undefined;
25
+ const matKind = ctx.data?.matKind as 'item' | 'fluid' | undefined;
26
+ const dataset = ctx.data?.dataset as DatasetRef | undefined;
27
+ const onWireAmount = ctx.data?.onWireAmount as
28
+ | ((from: string, to: string, input: number, amount: number | null) => void)
29
+ | undefined;
30
+ const onUnwire = ctx.data?.onUnwire as
31
+ | ((from: string, to: string, input: number, out?: number) => void)
32
+ | undefined;
33
+ const session = useCalcSession();
34
+ const [edit, setEdit] = useState<string | null>(null);
35
+ if (!wire || flow == null) return null;
36
+ const cap = wire.amount;
37
+
38
+ const commit = () => {
39
+ if (edit == null) return;
40
+ const raw = edit.trim();
41
+ const value = raw === '' ? null : Math.max(0, Number(raw));
42
+ onWireAmount?.(
43
+ wire.from,
44
+ wire.to,
45
+ wire.input,
46
+ value != null && Number.isFinite(value) ? value : null,
47
+ );
48
+ setEdit(null);
49
+ };
50
+ const onKey = (e: KeyboardEvent<HTMLInputElement>) => {
51
+ if (e.key === 'Enter') commit();
52
+ if (e.key === 'Escape') setEdit(null);
53
+ };
54
+
55
+ if (edit != null) {
56
+ return (
57
+ <input
58
+ autoFocus
59
+ className="calc-edge-label__input"
60
+ value={edit}
61
+ onChange={(e) => setEdit(e.target.value)}
62
+ onBlur={commit}
63
+ onKeyDown={onKey}
64
+ onClick={(e) => e.stopPropagation()}
65
+ />
66
+ );
67
+ }
68
+ const matName =
69
+ matId && session?.names
70
+ ? recipeItemName(session.names, session.lang, matId)
71
+ : matId;
72
+ return (
73
+ <button
74
+ type="button"
75
+ className="calc-edge-label"
76
+ title={`${matName ?? ''} ${flow}${cap != null ? ` / ${cap}` : ''}`}
77
+ onClick={(e) => {
78
+ e.stopPropagation();
79
+ if (e.shiftKey) {
80
+ onUnwire?.(wire.from, wire.to, wire.input, wire.out);
81
+ return;
82
+ }
83
+ setEdit(cap != null ? String(cap) : '');
84
+ }}
85
+ >
86
+ {matId && matKind && dataset ? (
87
+ <Entry
88
+ id={matId}
89
+ kind={matKind}
90
+ dataset={dataset}
91
+ size="sm"
92
+ href={false}
93
+ tooltip={false}
94
+ />
95
+ ) : null}
96
+ {matName ? (
97
+ <span className="calc-edge-label__name">{matName}</span>
98
+ ) : null}
99
+ <span className="calc-edge-label__amount">
100
+ ×{formatCount(flow, 1_000)}
101
+ {cap != null ? `/${formatCount(cap, 1_000)}` : ''}
102
+ </span>
103
+ <span className="calc-edge-label__cut" aria-hidden>
104
+ ×
105
+ </span>
106
+ </button>
107
+ );
108
+ }
14
109
 
15
110
  export const CalcEdge = createChevronEdge({
111
+ renderLabel: (ctx) => <EdgeAmountLabel ctx={ctx} />,
112
+ labelVisibility: 'hover',
16
113
  resolvePath: (ctx) => {
17
- const route = (ctx.data?.route as CalcEdgeStyle | undefined) ?? 'default';
18
- if (route === 'circuit') {
19
- const points = ctx.data?.points;
20
- if (Array.isArray(points) && points.length >= 2) {
21
-
22
- return svgPathToPoints(chamferedPolylinePath(points as FlowPoint[]));
114
+ const points = ctx.data?.points;
115
+ if (Array.isArray(points) && points.length >= 2) {
116
+ const pts = (points as FlowPoint[]).slice();
117
+ const oldA = pts[0]!;
118
+ const oldB = pts[pts.length - 1]!;
119
+ pts[0] = { x: ctx.sourceX, y: ctx.sourceY };
120
+ pts[pts.length - 1] = { x: ctx.targetX, y: ctx.targetY };
121
+ if (pts.length > 2) {
122
+ const s2 = pts[1]!;
123
+ const sL = pts[pts.length - 2]!;
124
+ if (s2.x === oldA.x) s2.x = ctx.sourceX;
125
+ if (s2.y === oldA.y) s2.y = ctx.sourceY;
126
+ if (sL.x === oldB.x) sL.x = ctx.targetX;
127
+ if (sL.y === oldB.y) sL.y = ctx.targetY;
23
128
  }
129
+ if (
130
+ pts.length === 2 &&
131
+ pts[0].x !== pts[1].x &&
132
+ pts[0].y !== pts[1].y
133
+ ) {
134
+ const mid = (pts[0].x + pts[1].x) / 2;
135
+ pts.splice(1, 0, { x: mid, y: pts[0].y }, { x: mid, y: pts[1].y });
136
+ }
137
+ return svgPathToPoints(roundedPolylinePath(pts, 12), 8);
24
138
  }
25
- const anchors = {
26
- sourceX: ctx.sourceX,
27
- sourceY: ctx.sourceY,
28
- sourcePosition: ctx.sourcePosition,
29
- targetX: ctx.targetX,
30
- targetY: ctx.targetY,
31
- targetPosition: ctx.targetPosition,
32
- };
33
- if (route === 'straight') {
34
- return [
35
- { x: ctx.sourceX, y: ctx.sourceY },
36
- { x: ctx.targetX, y: ctx.targetY },
37
- ];
38
- }
39
- if (route === 'default') {
40
- return svgPathToPoints(getBezierPath(anchors)[0]);
41
- }
42
-
43
139
  return svgPathToPoints(
44
140
  getSmoothStepPath({
45
- ...anchors,
46
- borderRadius: route === 'smoothstep' ? 10 : 0,
141
+ sourceX: ctx.sourceX,
142
+ sourceY: ctx.sourceY,
143
+ sourcePosition: ctx.sourcePosition,
144
+ targetX: ctx.targetX,
145
+ targetY: ctx.targetY,
146
+ targetPosition: ctx.targetPosition,
147
+ borderRadius: 10,
47
148
  })[0],
48
149
  );
49
150
  },
50
151
  });
152
+