@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,73 +1,37 @@
1
1
  'use client';
2
2
 
3
3
  import { useAsyncEffect } from '@terrafirmagreg-team/ui/hooks/use-async-value';
4
- import {
5
- AUTO_NODE_BUDGET,
6
- buildGoal,
7
- type BuildContext,
8
- } from '@terrafirmagreg-team/recipe/lib/calc/tree';
9
- import {
10
- createFrameBudget,
11
- createSummaries,
12
- fetchSlices,
13
- pickAutoRecipe,
14
- } from '@terrafirmagreg-team/recipe/lib/calc/resolve';
15
4
  import type { RecipeCompactIndex } from '@terrafirmagreg-team/recipe/types';
16
5
  import type {
17
- CalcMaterial,
18
- CalcNode,
19
- CalculatorTarget,
20
- TagResolution,
6
+ CalcCard,
7
+ CalcRecipe,
21
8
  } from '@terrafirmagreg-team/recipe/types/calc';
22
- import type { CalcMaps, CalcMapsSlice } from './use-maps';
23
9
  import type { useCalcWorker } from './use-worker';
24
10
 
25
11
  type WorkerSlice = ReturnType<typeof useCalcWorker>;
26
12
 
27
13
  export function useCalcResolver({
28
14
  catalog,
29
- dataset,
30
15
  inputKey,
31
- dataSettled,
32
- activeTabId,
33
- target,
34
- inventory,
35
- effectiveSelections,
36
- tagSelections,
37
- collapsed,
38
- collapsedSet,
39
- auto,
40
- maps,
41
- applyMaps,
16
+ cards,
17
+ recipeMap,
42
18
  worker,
43
19
  }: {
44
20
  catalog: RecipeCompactIndex | null;
45
- dataset: string;
46
21
  inputKey: string;
47
- dataSettled: boolean;
48
- activeTabId: string | null;
49
- target: CalculatorTarget | null;
50
- inventory: CalcMaterial[];
51
- effectiveSelections: Record<string, string>;
52
- tagSelections: Record<string, string>;
53
- collapsed: string[];
54
- collapsedSet: Set<string>;
55
- auto: string[];
56
- maps: CalcMaps;
57
- applyMaps: (src: CalcMapsSlice) => void;
22
+ cards: CalcCard[];
23
+ recipeMap: Map<string, CalcRecipe>;
58
24
  worker: WorkerSlice;
59
25
  }) {
60
- const { outMap, tagMap, recipeMap, bodyMap, deadIds } = maps;
61
26
  const {
62
27
  ref: workerRef,
63
28
  state: workerState,
29
+ generation,
64
30
  call,
65
31
  isResolved,
66
32
  markResolved,
67
- getCached,
68
- applyResolved,
69
33
  } = worker;
70
- useAsyncEffect(async (isCancelled) => {
34
+ useAsyncEffect(async () => {
71
35
  if (
72
36
  !catalog ||
73
37
  (!catalog.items.length && !catalog.fluids?.length) ||
@@ -75,160 +39,32 @@ export function useCalcResolver({
75
39
  ) {
76
40
  return;
77
41
  }
78
- if (workerRef.current) {
42
+ if (!workerRef.current) return;
43
+ if (isResolved(inputKey)) return;
44
+ markResolved(inputKey);
79
45
 
80
- if (isResolved(inputKey)) return;
81
- markResolved(inputKey);
82
-
83
- const hit = activeTabId ? getCached(activeTabId) : undefined;
84
- if (hit?.inputKey === inputKey) {
85
- if (isCancelled()) return;
86
- applyResolved(hit);
87
- return;
88
- }
89
- await call((api) =>
90
- api.resolve({
91
- target,
92
- inventory,
93
- selections: effectiveSelections,
94
- tagSelections,
95
- collapsed,
96
- auto,
97
- }),
98
- )
99
- .catch(() => {});
100
- return;
46
+ const materials = new Set<string>();
47
+ const recipes = new Set<string>();
48
+ const tags = new Set<string>();
49
+ for (const card of cards) {
50
+ materials.add(card.materialId);
51
+ if (card.recipeId) recipes.add(card.recipeId);
101
52
  }
102
-
103
- if (dataSettled) return;
104
- const localOut = new Map(outMap);
105
- const localTag = new Map(tagMap);
106
- const localRecipes = new Map(recipeMap);
107
- const localBodies = new Map(bodyMap);
108
- const deadLocal = new Set(deadIds);
109
- const { get: summaries, clear: clearSummaries } = createSummaries(
110
- catalog,
111
- localOut,
112
- localRecipes,
113
- );
114
- const ctx = (): Omit<BuildContext, 'pool' | 'used' | 'usedMaterials'> => ({
115
- recipeMap: localRecipes,
116
- getRecipesForItem: summaries,
117
- selections: effectiveSelections,
118
- tagSelections,
119
- tagMembers: (id: string) => localTag.get(id) as TagResolution | undefined,
120
- collapsed: collapsedSet,
121
- autoRoots: auto,
122
- autoBudget: { left: AUTO_NODE_BUDGET },
123
- recipesResolved: (id) => localOut.has(id),
124
- deadIds: deadLocal,
125
- });
126
- let progressed = false;
127
- const yieldToInput = createFrameBudget();
128
- for (let iter = 0; iter < 200; iter += 1) {
129
- await yieldToInput();
130
- if (isCancelled()) return;
131
- const wanted = new Set<string>();
132
- const wantedOut = new Set<string>();
133
- const wantedTag = new Set<string>();
134
- if (target) {
135
- const goalCtx = ctx();
136
- const goal = buildGoal(target, goalCtx, 'g0', inventory);
137
- const walk = (n: CalcNode, ancestors: Set<string>): void => {
138
- const next =
139
- n.recipe && n.need > 0
140
- ? new Set(ancestors).add(`${n.kind}:${n.materialId}`)
141
- : ancestors;
142
- if (n.tagId && !localTag.has(n.tagId)) wantedTag.add(n.tagId);
143
- if (!localOut.has(n.materialId)) wantedOut.add(n.materialId);
144
- if (n.recipe) {
145
- for (const input of n.recipe.inputs) {
146
- if (input.tagId && !localTag.has(input.tagId)) {
147
- wantedTag.add(input.tagId);
148
- }
149
- }
150
- } else if (n.auto) {
151
- const autoPick = pickAutoRecipe(
152
- n,
153
- effectiveSelections,
154
- ancestors,
155
- goalCtx,
156
- );
157
- if (
158
- autoPick.pick &&
159
- !localRecipes.has(autoPick.pick) &&
160
- !deadLocal.has(autoPick.pick)
161
- ) {
162
- wanted.add(autoPick.pick);
163
- }
164
- for (const id of autoPick.wantRecipes) {
165
- if (!localRecipes.has(id) && !deadLocal.has(id)) wanted.add(id);
166
- }
167
- for (const id of autoPick.wantOut) {
168
- if (!localOut.has(id)) wantedOut.add(id);
169
- }
170
- }
171
- for (const child of n.children) walk(child, next);
172
- };
173
- walk(goal.node, new Set());
174
- }
175
- if (!wanted.size && !wantedOut.size && !wantedTag.size) break;
176
- const slices = await fetchSlices(dataset, catalog, {
177
- recipes: wanted,
178
- outs: wantedOut,
179
- tags: wantedTag,
180
- });
181
- if (isCancelled()) return;
182
- let step = false;
183
- for (const [id, entry] of slices.recipes) {
184
- localRecipes.set(id, entry.calc);
185
- localBodies.set(id, entry.body);
186
- step = true;
187
- }
188
- for (const id of slices.dead) deadLocal.add(id);
189
- for (const [id, list] of slices.outs) {
190
- localOut.set(id, list);
191
- clearSummaries();
192
- step = true;
193
- }
194
- for (const [id, tag] of slices.tags) {
195
- localTag.set(id, tag);
196
- step = true;
197
- }
198
- progressed ||= step;
199
- if (!step) break;
200
- if (iter % 5 === 4) {
201
- applyMaps({
202
- out: localOut,
203
- tag: localTag,
204
- recipes: localRecipes,
205
- bodies: localBodies,
206
- dead: deadLocal,
207
- });
53
+ for (const card of cards) {
54
+ if (!card.recipeId) continue;
55
+ const recipe = recipeMap.get(card.recipeId);
56
+ if (!recipe) continue;
57
+ for (const input of recipe.inputs) {
58
+ if (input.tagId) tags.add(input.tagId);
208
59
  }
209
60
  }
210
- if (!progressed || isCancelled()) return;
211
- applyMaps({
212
- out: localOut,
213
- tag: localTag,
214
- recipes: localRecipes,
215
- bodies: localBodies,
216
- dead: deadLocal,
217
- });
218
- }, [
219
- catalog,
220
- dataSettled,
221
- target,
222
- inventory,
223
- effectiveSelections,
224
- tagSelections,
225
- collapsed,
226
- collapsedSet,
227
- auto,
228
- dataset,
229
- workerState,
230
- call,
231
- activeTabId,
232
- inputKey,
233
- ]);
61
+ await call((api) =>
62
+ api.resolve({
63
+ materials: [...materials],
64
+ recipes: [...recipes],
65
+ tags: [...tags],
66
+ }),
67
+ ).catch(() => {});
68
+ }, [catalog, cards, recipeMap, workerState, generation, call, inputKey]);
234
69
  }
70
+
@@ -10,3 +10,4 @@ export const CalcSessionProvider = CalcSessionContext.Provider;
10
10
  export function useCalcSession(): RecipeSession | null {
11
11
  return useContext(CalcSessionContext);
12
12
  }
13
+
@@ -9,6 +9,7 @@ import {
9
9
  } from '@terrafirmagreg-team/recipe/lib/calc/storage';
10
10
  import { recipeCalcHref } from '@terrafirmagreg-team/recipe/lib/links';
11
11
  import { replaceUrlState } from '@terrafirmagreg-team/ui/lib/utils/history';
12
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
12
13
 
13
14
  export function useCalcShare({
14
15
  storageKey,
@@ -17,7 +18,7 @@ export function useCalcShare({
17
18
  incoming,
18
19
  }: {
19
20
  storageKey: string;
20
- dataset: string;
21
+ dataset: DatasetRef;
21
22
  lang: string;
22
23
  incoming: string;
23
24
  }): boolean {
@@ -26,8 +27,9 @@ export function useCalcShare({
26
27
  useEffect(() => {
27
28
  if (!isShareId) return;
28
29
  let cancelled = false;
30
+ const { id: datasetId, version } = dataset;
29
31
  void fetch(
30
- `/api/share/recipe/${encodeURIComponent(dataset)}/${encodeURIComponent(incoming.slice(1))}`,
32
+ `/api/share/recipe/${encodeURIComponent(datasetId)}/${encodeURIComponent(incoming.slice(1))}${version ? `?version=${encodeURIComponent(version)}` : ''}`,
31
33
  )
32
34
  .then((res) => (res.ok ? res.json() : null))
33
35
  .then((body: { tree?: unknown } | null) => {
@@ -59,3 +61,4 @@ export function useCalcShare({
59
61
 
60
62
  return isShareId && sharedTree === null;
61
63
  }
64
+
@@ -4,6 +4,7 @@ import { useCallback, useSyncExternalStore } from 'react';
4
4
  import {
5
5
  newCalcEntry,
6
6
  readCalcActive,
7
+ resolveCalcActive,
7
8
  readCalcHistory,
8
9
  readCalcStorage,
9
10
  readCalcTabs,
@@ -19,9 +20,8 @@ import type { CalculatorState } from '@terrafirmagreg-team/recipe/types/calc';
19
20
 
20
21
  const EMPTY_TABS: CalcTreeEntry[] = [];
21
22
  const EMPTY_CALC: CalculatorState = {
22
- target: null,
23
- inventory: [],
24
- selections: {},
23
+ cards: [],
24
+ wires: [],
25
25
  tagSelections: {},
26
26
  };
27
27
 
@@ -42,7 +42,7 @@ export function useCalcStorage(storageKey: string) {
42
42
  );
43
43
  const activeTabId = useSyncExternalStore(
44
44
  subscribeCalcStorage,
45
- () => readCalcActive(storageKey),
45
+ () => resolveCalcActive(readCalcTabs(storageKey), readCalcActive(storageKey)),
46
46
  () => null,
47
47
  );
48
48
  const history = useSyncExternalStore(
@@ -123,3 +123,4 @@ export function useCalcStorage(storageKey: string) {
123
123
  removeHistory,
124
124
  };
125
125
  }
126
+
@@ -10,3 +10,4 @@ export const CalcTierProvider = CalcTierContext.Provider;
10
10
  export function useCalcTier(): CalcTier {
11
11
  return useContext(CalcTierContext);
12
12
  }
13
+
@@ -21,3 +21,4 @@ export function useCalcViewMode() {
21
21
  );
22
22
  return [mode, setMode] as const;
23
23
  }
24
+
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
 
3
4
  import {
4
5
  startTransition,
@@ -12,64 +13,44 @@ import type {
12
13
  CalcWorkerApi,
13
14
  CalcWorkerFlush,
14
15
  } from '@terrafirmagreg-team/recipe/lib/calc/worker';
15
- import type { CalcGoal } from '@terrafirmagreg-team/recipe/lib/calc/tree';
16
+ import { api as localCalcApi } from '@terrafirmagreg-team/recipe/lib/calc/worker/api';
16
17
  import type { RecipeCompactIndex } from '@terrafirmagreg-team/recipe/types';
17
- import type { CalcMaterial } from '@terrafirmagreg-team/recipe/types/calc';
18
18
  import type { CalcMapsSlice } from './use-maps';
19
19
 
20
20
  type CalcWorkerHandle = {
21
- worker: Worker;
21
+ worker: Worker | null;
22
22
  api: Remote<CalcWorkerApi>;
23
23
  inFlight: number;
24
24
  rejecters: Set<(error: Error) => void>;
25
25
  };
26
26
 
27
- export type ResolvedCacheEntry = {
28
- inputKey: string;
29
- goals: CalcGoal[];
30
- summary: { raw: CalcMaterial[]; catalysts: CalcMaterial[] } | null;
31
- };
32
-
33
27
  export function useCalcWorker({
34
28
  dataset,
35
29
  catalog,
36
30
  activeTabId,
37
- inputKey,
38
31
  applyMaps,
39
32
  resetMaps,
40
33
  }: {
41
- dataset: string;
34
+ dataset: DatasetRef;
42
35
  catalog: RecipeCompactIndex | null;
43
36
  activeTabId: string | null;
44
- inputKey: string;
45
37
  applyMaps: (src: CalcMapsSlice, extra?: () => void) => void;
46
38
  resetMaps: () => void;
47
39
  }) {
48
40
  const workerRef = useRef<CalcWorkerHandle | null>(null);
49
- const [workerState, setWorkerState] = useState<
50
- 'pending' | 'ready' | 'failed'
51
- >('pending');
52
-
53
- const [workerGoals, setWorkerGoals] = useState<CalcGoal[] | null>(null);
54
- const [workerSummary, setWorkerSummary] = useState<{
55
- raw: CalcMaterial[];
56
- catalysts: CalcMaterial[];
57
- } | null>(null);
41
+ const [workerState, setWorkerState] = useState<'pending' | 'ready'>(
42
+ 'pending',
43
+ );
44
+ const [generation, setGeneration] = useState(0);
58
45
  const [workerSettled, setWorkerSettled] = useState(false);
59
-
60
46
  const resolvedKeyRef = useRef<string | null>(null);
61
47
 
62
- const resolvedCacheRef = useRef(new Map<string, ResolvedCacheEntry>());
63
-
64
- const [prevActiveTab, setPrevActiveTab] = useState(activeTabId);
65
- if (prevActiveTab !== activeTabId) {
66
- setPrevActiveTab(activeTabId);
67
- setWorkerGoals(null);
68
- setWorkerSummary(null);
48
+ const minFlushEpochRef = useRef(0);
49
+ const [prevTab, setPrevTab] = useState(activeTabId);
50
+ if (prevTab !== activeTabId) {
51
+ setPrevTab(activeTabId);
69
52
  setWorkerSettled(false);
70
53
  }
71
-
72
- const minFlushEpochRef = useRef(0);
73
54
  useEffect(() => {
74
55
  resolvedKeyRef.current = null;
75
56
  minFlushEpochRef.current += 1;
@@ -77,35 +58,16 @@ export function useCalcWorker({
77
58
  if (handle) void handle.api.invalidate().catch(() => {});
78
59
  }, [activeTabId]);
79
60
 
80
- useEffect(() => {
81
- if (!workerSettled || !activeTabId) return;
82
- if (resolvedKeyRef.current !== inputKey) return;
83
- resolvedCacheRef.current.set(activeTabId, {
84
- inputKey,
85
- goals: workerGoals ?? [],
86
- summary: workerSummary,
87
- });
88
- }, [workerSettled, workerGoals, workerSummary, activeTabId, inputKey]);
89
-
90
61
  const applyWorkerFlush = useCallback(
91
62
  (msg: CalcWorkerFlush) => {
92
-
93
- const stale = msg.epoch < minFlushEpochRef.current;
94
- applyMaps(
95
- {
96
- out: new Map(msg.out),
97
- tag: new Map(msg.tag),
98
- recipes: new Map(msg.recipes),
99
- bodies: new Map(msg.bodies),
100
- dead: new Set(msg.dead),
101
- },
102
- () => {
103
- if (!stale) {
104
- if (msg.goals) setWorkerGoals(msg.goals);
105
- if (msg.summary) setWorkerSummary(msg.summary);
106
- }
107
- },
108
- );
63
+ if (msg.epoch < minFlushEpochRef.current) return;
64
+ applyMaps({
65
+ out: new Map(msg.out),
66
+ tag: new Map(msg.tag),
67
+ recipes: new Map(msg.recipes),
68
+ bodies: new Map(msg.bodies),
69
+ dead: new Set(msg.dead),
70
+ });
109
71
  },
110
72
  [applyMaps],
111
73
  );
@@ -128,20 +90,7 @@ export function useCalcWorker({
128
90
  recipes: [...prev.recipes, ...msg.recipes],
129
91
  bodies: [...prev.bodies, ...msg.bodies],
130
92
  dead: [...prev.dead, ...msg.dead],
131
-
132
93
  epoch: Math.max(prev.epoch, msg.epoch),
133
- goals:
134
- msg.epoch > prev.epoch
135
- ? msg.goals
136
- : msg.epoch === prev.epoch
137
- ? (msg.goals ?? prev.goals)
138
- : prev.goals,
139
- summary:
140
- msg.epoch > prev.epoch
141
- ? msg.summary
142
- : msg.epoch === prev.epoch
143
- ? (msg.summary ?? prev.summary)
144
- : prev.summary,
145
94
  }
146
95
  : msg;
147
96
  if (flushTimer.current) return;
@@ -152,54 +101,53 @@ export function useCalcWorker({
152
101
 
153
102
  useEffect(() => {
154
103
  if (!catalog) return;
155
- if (typeof Worker === 'undefined') {
156
- queueMicrotask(() => setWorkerState('failed'));
157
- return;
158
- }
159
- let worker: Worker;
160
- try {
161
- worker = new Worker(
162
- new URL('../../lib/calc/worker/index.ts', import.meta.url),
163
- { type: 'module' },
164
- );
165
- } catch {
166
- queueMicrotask(() => setWorkerState('failed'));
167
- return;
168
- }
169
- const fail = () => {
170
- workerRef.current = null;
171
- worker.terminate();
172
- setWorkerState('failed');
173
- setWorkerGoals(null);
174
- setWorkerSummary(null);
175
- setWorkerSettled(false);
176
- };
177
104
  const handle: CalcWorkerHandle = {
178
- worker,
179
- api: wrap<CalcWorkerApi>(worker),
105
+ worker: null,
106
+ api: localCalcApi as Remote<CalcWorkerApi>,
180
107
  inFlight: 0,
181
108
  rejecters: new Set(),
182
109
  };
183
- worker.onerror = () => {
184
- fail();
110
+ const swapToLocal = () => {
111
+ handle.worker = null;
112
+ handle.api = localCalcApi as Remote<CalcWorkerApi>;
185
113
  const error = new Error('calc worker failed');
186
114
  for (const reject of handle.rejecters) reject(error);
187
115
  handle.rejecters.clear();
116
+ bufferedFlush.current = null;
117
+ if (flushTimer.current) {
118
+ clearTimeout(flushTimer.current);
119
+ flushTimer.current = null;
120
+ }
121
+ resolvedKeyRef.current = null;
122
+ minFlushEpochRef.current = 0;
123
+ setGeneration((g) => g + 1);
124
+ void handle.api.init(dataset, catalog, scheduleWorkerFlush).catch(() => {});
188
125
  };
126
+ if (typeof Worker !== 'undefined') {
127
+ try {
128
+ const worker = new Worker(
129
+ new URL('../../lib/calc/worker/index.ts', import.meta.url),
130
+ { type: 'module' },
131
+ );
132
+ worker.onerror = () => swapToLocal();
133
+ handle.worker = worker;
134
+ handle.api = wrap<CalcWorkerApi>(worker);
135
+ } catch {
136
+ }
137
+ }
189
138
  handle.api
190
- .init(dataset, catalog, proxy(scheduleWorkerFlush))
139
+ .init(
140
+ dataset,
141
+ catalog,
142
+ handle.worker ? proxy(scheduleWorkerFlush) : scheduleWorkerFlush,
143
+ )
191
144
  .then(() => setWorkerState('ready'))
192
- .catch(() => fail());
145
+ .catch(() => {});
193
146
  workerRef.current = handle;
194
147
  resolvedKeyRef.current = null;
195
- resolvedCacheRef.current.clear();
196
148
  minFlushEpochRef.current = 0;
197
149
  resetMaps();
198
- startTransition(() => {
199
- setWorkerGoals(null);
200
- setWorkerSummary(null);
201
- setWorkerSettled(false);
202
- });
150
+ startTransition(() => setWorkerSettled(false));
203
151
  return () => {
204
152
  if (flushTimer.current) {
205
153
  clearTimeout(flushTimer.current);
@@ -207,7 +155,7 @@ export function useCalcWorker({
207
155
  }
208
156
  bufferedFlush.current = null;
209
157
  workerRef.current = null;
210
- worker.terminate();
158
+ handle.worker?.terminate();
211
159
  };
212
160
  }, [dataset, catalog, scheduleWorkerFlush, resetMaps]);
213
161
 
@@ -241,29 +189,16 @@ export function useCalcWorker({
241
189
  [drainFlush],
242
190
  );
243
191
 
244
- const applyResolved = useCallback((entry: ResolvedCacheEntry) => {
245
- startTransition(() => {
246
- setWorkerGoals(entry.goals);
247
- setWorkerSummary(entry.summary);
248
- setWorkerSettled(true);
249
- });
250
- }, []);
251
-
252
192
  return {
253
193
  ref: workerRef,
254
194
  state: workerState,
255
- goals: workerGoals,
256
- summary: workerSummary,
195
+ generation,
257
196
  settled: workerSettled,
258
197
  call,
259
198
  isResolved: useCallback((key: string) => resolvedKeyRef.current === key, []),
260
199
  markResolved: useCallback((key: string) => {
261
200
  resolvedKeyRef.current = key;
262
201
  }, []),
263
- getCached: useCallback(
264
- (tabId: string) => resolvedCacheRef.current.get(tabId),
265
- [],
266
- ),
267
- applyResolved,
268
202
  };
269
203
  }
204
+
@@ -1,14 +1,16 @@
1
1
  'use client';
2
+ import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
2
3
  import { useSyncExternalStore } from 'react';
3
4
  import {
4
5
  dumpLocation,
5
6
  dumpPublicUrl,
6
7
  subscribeDumpLocations,
7
- } from '@terrafirmagreg-team/game/lib/dump/paths';
8
+ } from '@terrafirmagreg-team/dump/lib/dump/paths';
8
9
 
9
10
  export const ANVIL_GUI_REL = 'assets/tfc/textures/gui/anvil.webp';
10
11
 
11
- export function useAnvilGui(dataset: string): string | undefined {
12
+ export function useAnvilGui(dataset: DatasetRef): string | undefined {
12
13
  useSyncExternalStore(subscribeDumpLocations, () => dumpLocation(dataset));
13
14
  return dumpPublicUrl(dataset, ANVIL_GUI_REL);
14
15
  }
16
+
@@ -132,3 +132,4 @@ export function useForgeState(recipe: ForgeRecipe) {
132
132
  }
133
133
 
134
134
  export type ForgeState = ReturnType<typeof useForgeState>;
135
+
@@ -97,3 +97,4 @@ export function useRuler({
97
97
  onHandleKey,
98
98
  };
99
99
  }
100
+