@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,89 +0,0 @@
1
- 'use client';
2
-
3
- import {
4
- useCallback,
5
- useEffect,
6
- useMemo,
7
- useRef,
8
- type Dispatch,
9
- type RefObject,
10
- type SetStateAction,
11
- } from 'react';
12
- import type { Edge } from '@terrafirmagreg-team/ui/lib/canvas/xyflow';
13
- import type {
14
- AnyCalcFlowNode,
15
- goalsToFlow,
16
- } from '@terrafirmagreg-team/recipe/lib/calc/flow';
17
- import type { ChevronEdgeHighlight } from '@terrafirmagreg-team/ui/types/canvas';
18
- import type { MountedNodes } from './mount';
19
-
20
- function adjacencyList(
21
- map: Map<string, string[]>,
22
- key: string | null,
23
- ): string[] {
24
- return key === null ? [] : (map.get(key) ?? []);
25
- }
26
-
27
- export function useEdgeEngage({
28
- computed,
29
- setState,
30
- rawFlow,
31
- focusIdRef,
32
- }: {
33
- computed: { nodes: AnyCalcFlowNode[]; edges: Edge[] };
34
- setState: Dispatch<SetStateAction<MountedNodes>>;
35
- rawFlow: ReturnType<typeof goalsToFlow>;
36
- focusIdRef: RefObject<string | null>;
37
- }) {
38
- const engagedRef = useRef<string | null>(null);
39
- const edgeAdjacency = useMemo(() => {
40
- const map = new Map<string, string[]>();
41
- for (const edge of computed.edges) {
42
- const list = map.get(edge.source) ?? [];
43
- list.push(edge.id);
44
- map.set(edge.source, list);
45
- const back = map.get(edge.target) ?? [];
46
- back.push(edge.id);
47
- map.set(edge.target, back);
48
- }
49
- return map;
50
- }, [computed]);
51
- const applyEngaged = useCallback(
52
- (id: string | null) => {
53
- const prev = engagedRef.current;
54
- if (prev === id) return;
55
- engagedRef.current = id;
56
- const touched = new Set<string>();
57
- for (const edgeId of adjacencyList(edgeAdjacency, prev)) {
58
- touched.add(edgeId);
59
- }
60
- for (const edgeId of adjacencyList(edgeAdjacency, id)) {
61
- touched.add(edgeId);
62
- }
63
- if (!touched.size) return;
64
- setState((current) => {
65
- let next = null as typeof current.edges | null;
66
- current.edges.forEach((edge, index) => {
67
- if (!touched.has(edge.id)) return;
68
- const highlight = (edge.source === id
69
- ? 'outgoing'
70
- : edge.target === id
71
- ? 'incoming'
72
- : undefined) as ChevronEdgeHighlight;
73
- if ((edge.data?.highlight ?? undefined) === highlight) return;
74
- (next ??= current.edges.slice())[index] = {
75
- ...edge,
76
- data: { ...edge.data, highlight },
77
- };
78
- });
79
- return next ? { ...current, edges: next } : current;
80
- });
81
- },
82
- [edgeAdjacency, setState],
83
- );
84
- useEffect(() => {
85
- engagedRef.current = null;
86
- focusIdRef.current = null;
87
- }, [rawFlow, focusIdRef]);
88
- return applyEngaged;
89
- }
@@ -1,115 +0,0 @@
1
- 'use client';
2
-
3
- import {
4
- useCallback,
5
- type Dispatch,
6
- type DragEvent,
7
- type MouseEvent,
8
- type SetStateAction,
9
- } from 'react';
10
- import { useReactFlow } from '@terrafirmagreg-team/ui/lib/canvas/xyflow';
11
- import type {
12
- AnyCalcFlowNode,
13
- CalcFlowData,
14
- } from '@terrafirmagreg-team/recipe/lib/calc/flow';
15
- import type { CalcKind, CalcNode } from '@terrafirmagreg-team/recipe/types/calc';
16
-
17
- export function useFlowHandlers({
18
- data,
19
- applyFocus,
20
- applyEngaged,
21
- centerOnNode,
22
- setPickerNode,
23
- setMovedNodes,
24
- }: {
25
- data: Omit<
26
- CalcFlowData,
27
- 'node' | 'mode' | 'direction' | 'onRequestPicker' | 'onRequestMemberPicker'
28
- >;
29
- applyFocus: (id: string | null) => void;
30
- applyEngaged: (id: string | null) => void;
31
- centerOnNode: (node: AnyCalcFlowNode) => void;
32
- setPickerNode: (node: CalcNode | null) => void;
33
- setMovedNodes: Dispatch<
34
- SetStateAction<Map<string, { x: number; y: number }> | null>
35
- >;
36
- }) {
37
- const { screenToFlowPosition } = useReactFlow();
38
-
39
- const handleNodeEnter = useCallback(
40
- (_event: MouseEvent, node: AnyCalcFlowNode) => applyEngaged(node.id),
41
- [applyEngaged],
42
- );
43
- const handleNodeLeave = useCallback(
44
- () => applyEngaged(null),
45
- [applyEngaged],
46
- );
47
- const handleNodeClick = useCallback(
48
- (_event: MouseEvent, node: AnyCalcFlowNode) => {
49
- applyFocus(node.id);
50
- centerOnNode(node);
51
- },
52
- [applyFocus, centerOnNode],
53
- );
54
-
55
- const handleNodeDoubleClick = useCallback(
56
- (_event: MouseEvent, node: AnyCalcFlowNode) => {
57
- const target = (node.data as CalcFlowData).node;
58
- if (!target) return;
59
-
60
- if (target.recipe != null || target.availableRecipes.length > 0)
61
- setPickerNode(target);
62
- },
63
- [setPickerNode],
64
- );
65
- const handlePaneClick = useCallback(
66
- () => applyFocus(null),
67
- [applyFocus],
68
- );
69
- const handleDragOver = useCallback((event: DragEvent) => {
70
- if (event.dataTransfer.types.includes('application/x-tfg-calc')) {
71
- event.preventDefault();
72
- event.dataTransfer.dropEffect = 'copy';
73
- }
74
- }, []);
75
- const handleDrop = useCallback(
76
- (event: DragEvent) => {
77
- const raw = event.dataTransfer.getData('application/x-tfg-calc');
78
- if (!raw) return;
79
- event.preventDefault();
80
- try {
81
- const parsed = JSON.parse(raw) as { id?: string; kind?: CalcKind };
82
- if (parsed.id && (parsed.kind === 'item' || parsed.kind === 'fluid')) {
83
-
84
- const pos = screenToFlowPosition({
85
- x: event.clientX,
86
- y: event.clientY,
87
- });
88
- setMovedNodes((prev) => {
89
- const next = new Map(prev ?? []);
90
- next.set('g0', pos);
91
- return next;
92
- });
93
- data.onSetTarget(parsed.id, parsed.kind);
94
- }
95
- } catch {}
96
- },
97
- [screenToFlowPosition, data, setMovedNodes],
98
- );
99
-
100
- const fitRecipeNodes = useCallback(
101
- (node: AnyCalcFlowNode) => node.data.node?.recipe != null,
102
- [],
103
- );
104
-
105
- return {
106
- handleNodeEnter,
107
- handleNodeLeave,
108
- handleNodeClick,
109
- handleNodeDoubleClick,
110
- handlePaneClick,
111
- handleDragOver,
112
- handleDrop,
113
- fitRecipeNodes,
114
- };
115
- }
@@ -1,149 +0,0 @@
1
- 'use client';
2
-
3
- import {
4
- startTransition,
5
- useCallback,
6
- useEffect,
7
- useMemo,
8
- useState,
9
- } from 'react';
10
- import {
11
- densityGaps,
12
- goalsToFlow,
13
- type CalcDensity,
14
- type CalcDirection,
15
- type CalcEdgeStyle,
16
- type CalcFlowData,
17
- type CalcViewMode,
18
- } from '@terrafirmagreg-team/recipe/lib/calc/flow';
19
- import { packGoals } from '@terrafirmagreg-team/recipe/lib/calc/pack';
20
- import type { CalcGoal } from '@terrafirmagreg-team/recipe/lib/calc/tree';
21
- import type { CalcNode } from '@terrafirmagreg-team/recipe/types/calc';
22
-
23
- export function useFlowLayout({
24
- goals,
25
- data,
26
- mode,
27
- direction,
28
- density,
29
- edgeStyle,
30
- treeReady,
31
- onRequestPicker,
32
- onRequestMemberPicker,
33
- }: {
34
- goals: CalcGoal[];
35
- data: Omit<
36
- CalcFlowData,
37
- 'node' | 'mode' | 'direction' | 'onRequestPicker' | 'onRequestMemberPicker'
38
- >;
39
- mode: CalcViewMode;
40
- direction: CalcDirection;
41
- density: CalcDensity;
42
- edgeStyle: CalcEdgeStyle;
43
- treeReady: boolean;
44
- onRequestPicker: (node: CalcNode) => void;
45
- onRequestMemberPicker: (node: CalcNode) => void;
46
- }) {
47
- const layoutData = useMemo(
48
- () =>
49
- ({
50
- mode,
51
- direction,
52
- recipeBody: data.recipeBody,
53
- onRequestPicker,
54
- onRequestMemberPicker,
55
- }) as Omit<CalcFlowData, 'node'>,
56
- [
57
- mode,
58
- direction,
59
- data.recipeBody,
60
- onRequestPicker,
61
- onRequestMemberPicker,
62
- ],
63
- );
64
- const flowData = useMemo(
65
- () => ({ ...data, ...layoutData }) as Omit<CalcFlowData, 'node'>,
66
- [data, layoutData],
67
- );
68
- const rawFlow = useMemo(
69
- () => goalsToFlow(goals, flowData, edgeStyle),
70
- [goals, flowData, edgeStyle],
71
- );
72
-
73
- const layoutKey = useMemo(() => {
74
- const ns = rawFlow.nodes
75
- .map((n) => `${n.id}:${n.width}x${n.height}`)
76
- .join(',');
77
- const es = rawFlow.edges.map((e) => e.id).join(',');
78
- return `${direction}|${density}|${ns}|${es}`;
79
- }, [rawFlow, direction, density]);
80
-
81
- const [layout, setLayout] = useState<{ key: string } | null>(null);
82
-
83
- const [posCache, setPosCache] = useState<
84
- Map<string, { x: number; y: number }>
85
- >(() => new Map());
86
- useEffect(() => {
87
- if (layout?.key === layoutKey) return;
88
-
89
- const key = layoutKey;
90
- const positions = packGoals(goals, {
91
- mode,
92
- direction,
93
- gaps: densityGaps(density),
94
- recipeBody: data.recipeBody,
95
- });
96
- startTransition(() => {
97
- setPosCache(positions);
98
- setLayout({ key });
99
- });
100
- }, [
101
- layoutKey,
102
- layout,
103
- direction,
104
- density,
105
- goals,
106
- mode,
107
- data.recipeBody,
108
- ]);
109
-
110
- const positioned = useMemo(() => {
111
-
112
- const nodes = rawFlow.nodes.flatMap((node) => {
113
- const position = posCache.get(node.id);
114
- return position ? [{ ...node, position }] : [];
115
- });
116
- const present = new Set(nodes.map((node) => node.id));
117
- const edges = rawFlow.edges.filter(
118
- (edge) => present.has(edge.source) && present.has(edge.target),
119
- );
120
- return { nodes, edges };
121
- }, [rawFlow, posCache]);
122
-
123
- const [pin, setPin] = useState<{ x: number; y: number } | null>(null);
124
- const anchor = rawFlow.nodes.find((node) => node.id === 'g0');
125
- if (treeReady && pin) setPin(null);
126
- if (!treeReady && anchor && !pin) {
127
- setPin({ ...anchor.position });
128
- }
129
- const shift = useMemo(
130
- () =>
131
- pin && anchor
132
- ? { x: pin.x - anchor.position.x, y: pin.y - anchor.position.y }
133
- : null,
134
- [pin, anchor],
135
- );
136
-
137
- return {
138
- rawFlow,
139
- layout,
140
- layoutKey,
141
- positioned,
142
- shift,
143
- resetLayout: useCallback(() => {
144
- setPosCache(new Map());
145
- setPin(null);
146
- setLayout(null);
147
- }, []),
148
- };
149
- }
@@ -1,50 +0,0 @@
1
- 'use client';
2
-
3
- import { useCallback, useEffect, useState } from 'react';
4
- import type { CalcGoal } from '@terrafirmagreg-team/recipe/lib/calc/tree';
5
- import type { CalcNode } from '@terrafirmagreg-team/recipe/types/calc';
6
-
7
- export function useFlowPickers(goals: CalcGoal[]) {
8
- const [pickerNode, setPickerNode] = useState<CalcNode | null>(null);
9
- const [memberNode, setMemberNode] = useState<CalcNode | null>(null);
10
- const onRequestPicker = useCallback(
11
- (node: CalcNode) => setPickerNode(node),
12
- [],
13
- );
14
- const onRequestMemberPicker = useCallback(
15
- (node: CalcNode) => setMemberNode(node),
16
- [],
17
- );
18
-
19
- useEffect(() => {
20
- if (!pickerNode?.path && !memberNode?.path) return;
21
- const find = (n: CalcNode, want?: CalcNode): CalcNode | undefined => {
22
- if (n === want || (want?.path != null && n.path === want.path))
23
- return n;
24
- for (const child of n.children) {
25
- const hit = find(child, want);
26
- if (hit) return hit;
27
- }
28
- return undefined;
29
- };
30
- for (const goal of goals) {
31
- for (const [node, set] of [
32
- [pickerNode, setPickerNode],
33
- [memberNode, setMemberNode],
34
- ] as const) {
35
- if (!node) continue;
36
- const live = find(goal.node, node);
37
- if (live && live !== node) set(live);
38
- }
39
- }
40
- }, [goals, pickerNode, memberNode]);
41
-
42
- return {
43
- pickerNode,
44
- setPickerNode,
45
- memberNode,
46
- setMemberNode,
47
- onRequestPicker,
48
- onRequestMemberPicker,
49
- };
50
- }
@@ -1,91 +0,0 @@
1
- 'use client';
2
-
3
- import {
4
- useCallback,
5
- type Dispatch,
6
- type SetStateAction,
7
- } from 'react';
8
- import {
9
- calcStorageKey,
10
- readCalcActive,
11
- updateCalcTab,
12
- } from '@terrafirmagreg-team/recipe/lib/calc/storage';
13
-
14
- export function useCalcFill({
15
- dataset,
16
- hasTarget,
17
- fillTree,
18
- setResetGen,
19
- }: {
20
- dataset: string;
21
- hasTarget: boolean;
22
- fillTree: (roots: readonly string[]) => Promise<Record<string, string>>;
23
- setResetGen: Dispatch<SetStateAction<number>>;
24
- }) {
25
-
26
- const mergeFill = useCallback(
27
- (
28
- tabId: string | null,
29
- roots: readonly string[],
30
- picked: Record<string, string>,
31
- ) => {
32
- if (!tabId) return;
33
- const key = calcStorageKey('recipe', dataset);
34
- updateCalcTab(key, tabId, (tab) => {
35
- const current = tab.state;
36
- const underRoot = (key: string) =>
37
- roots.some((root) => key === root || key.startsWith(`${root}/`));
38
- return {
39
- ...tab,
40
- savedAt: Date.now(),
41
- state: {
42
- target: current.target,
43
- inventory: current.inventory,
44
-
45
- selections: {
46
- ...Object.fromEntries(
47
- Object.entries(current.selections).filter(
48
- ([key]) => !underRoot(key),
49
- ),
50
- ),
51
- ...picked,
52
- },
53
- tagSelections: current.tagSelections,
54
- collapsed: (current.collapsed ?? []).filter(
55
- (p) => !underRoot(p),
56
- ),
57
- auto: [
58
- ...new Set([
59
- ...(current.auto ?? []),
60
- ...roots.filter((path) => /^g0(\/|$)/.test(path)),
61
- ]),
62
- ],
63
- },
64
- };
65
- });
66
- },
67
- [dataset],
68
- );
69
-
70
- const onFillNode = useCallback(
71
- (path: string) => {
72
- const tabId = readCalcActive(calcStorageKey('recipe', dataset));
73
- void fillTree([path]).then((picked) => {
74
- setResetGen((gen) => gen + 1);
75
- mergeFill(tabId, [path], picked);
76
- });
77
- },
78
- [fillTree, mergeFill, dataset, setResetGen],
79
- );
80
-
81
- const onAutoBuild = useCallback(() => {
82
- const tabId = readCalcActive(calcStorageKey('recipe', dataset));
83
- const roots = hasTarget ? ['g0'] : [];
84
- void fillTree(roots).then((picked) => {
85
- setResetGen((gen) => gen + 1);
86
- mergeFill(tabId, roots, picked);
87
- });
88
- }, [hasTarget, fillTree, mergeFill, dataset, setResetGen]);
89
-
90
- return { onFillNode, onAutoBuild };
91
- }
@@ -1,28 +0,0 @@
1
- 'use client';
2
-
3
- import { startTransition, useCallback } from 'react';
4
- import {
5
- createStoredValue,
6
- type StoredCodec,
7
- } from '@terrafirmagreg-team/ui/lib/utils/stored-value';
8
- import type { CalcDensity } from '@terrafirmagreg-team/recipe/lib/calc/flow';
9
-
10
- const codec: StoredCodec<CalcDensity> = {
11
- read: (raw) => {
12
- const stored = Number(raw);
13
- return Number.isFinite(stored) && raw != null && stored >= 0 && stored <= 100
14
- ? stored / 100
15
- : 0;
16
- },
17
- write: (value) => String(Math.round(Math.min(1, Math.max(0, value)) * 100)),
18
- };
19
- const useStoredDensity = createStoredValue('calc:density', codec);
20
-
21
- export function useCalcDensity() {
22
- const [density, setStored] = useStoredDensity();
23
- const setDensity = useCallback(
24
- (next: CalcDensity) => startTransition(() => setStored(next)),
25
- [setStored],
26
- );
27
- return [density, setDensity] as const;
28
- }