@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.
- package/package.json +16 -16
- package/src/components/button-copy.tsx +1 -0
- package/src/components/cards/card/index.tsx +5 -3
- package/src/components/cards/card/scene.tsx +4 -2
- package/src/components/cards/card/strip.tsx +4 -2
- package/src/components/cards/card/text.tsx +2 -1
- package/src/components/cards/card/tips.tsx +1 -0
- package/src/components/cards/default-item.tsx +6 -2
- package/src/components/cards/glare-filter.tsx +1 -0
- package/src/components/cards/paged-card.tsx +1 -0
- package/src/components/cards/slot-hitbox.tsx +4 -2
- package/src/components/cards/strip-card/buttons.tsx +7 -2
- package/src/components/cards/strip-card/index.tsx +3 -1
- package/src/components/cards/strip-card/member-cell.tsx +4 -2
- package/src/components/cards/strip-card/sprite.tsx +1 -0
- package/src/components/embed-card.tsx +44 -0
- package/src/components/mdx/recipe.tsx +34 -16
- package/src/components/tool-list.tsx +1 -0
- package/src/{lib/tree.ts → components/tree.tsx} +20 -12
- package/src/components/views/alloy/index.tsx +14 -7
- package/src/components/views/alloy/list.tsx +3 -2
- package/src/components/views/alloy/mix/index.tsx +1 -0
- package/src/components/views/alloy/mix/ratio.tsx +7 -6
- package/src/components/views/alloy/mix/rows.tsx +6 -7
- package/src/components/views/alloy/mix/status.tsx +8 -7
- package/src/components/views/alloy/mix/total.tsx +13 -12
- package/src/components/views/alloy/range-bar.tsx +2 -1
- package/src/components/views/alloy/stack-bar.tsx +4 -2
- package/src/components/views/browser/axis-switcher.tsx +13 -12
- package/src/components/views/browser/card-grid.tsx +2 -1
- package/src/components/views/browser/favorite-button.tsx +2 -1
- package/src/components/views/browser/frame.tsx +3 -2
- package/src/components/views/browser/index.tsx +20 -2
- package/src/components/views/browser/item-panel/index.tsx +11 -9
- package/src/components/views/browser/item-panel/skeleton.tsx +1 -0
- package/src/components/views/browser/item-panel-grid/cell.tsx +5 -3
- package/src/components/views/browser/item-panel-grid/favorite-cell.tsx +4 -2
- package/src/components/views/browser/item-panel-grid/index.tsx +4 -2
- package/src/components/views/browser/item-view/index.tsx +7 -12
- package/src/components/views/browser/item-view/tags.tsx +7 -5
- package/src/components/views/browser/item-view-header.tsx +10 -9
- package/src/components/views/browser/pane-tabs.tsx +1 -0
- package/src/components/views/browser/place-header.tsx +4 -3
- package/src/components/views/browser/query-controls.tsx +3 -1
- package/src/components/views/browser/recipe-item-header.tsx +5 -4
- package/src/components/views/browser/settings.tsx +4 -3
- package/src/components/views/browser/toolbar.tsx +3 -2
- package/src/components/views/browser/type-tabs.tsx +5 -3
- package/src/components/views/browser/type-view.tsx +7 -12
- package/src/components/views/browser/workstation-rail.tsx +4 -3
- package/src/components/views/calc/calculator/actions.tsx +4 -14
- package/src/components/views/calc/calculator/help.tsx +69 -28
- package/src/components/views/calc/calculator/index.tsx +34 -82
- package/src/components/views/calc/dot-overlay.tsx +1 -0
- package/src/components/views/calc/edge.tsx +134 -32
- package/src/components/views/calc/flow/index.tsx +92 -54
- package/src/components/views/calc/flow/picker-dialogs.tsx +84 -6
- package/src/components/views/calc/flow/toolbar.tsx +9 -21
- package/src/components/views/calc/flow/view-menu.tsx +16 -120
- package/src/components/views/calc/member-picker.tsx +8 -6
- package/src/components/views/calc/node-card/actions.tsx +28 -106
- package/src/components/views/calc/node-card/badges.tsx +10 -28
- package/src/components/views/calc/node-card/chips.tsx +8 -40
- package/src/components/views/calc/node-card/far.tsx +13 -65
- package/src/components/views/calc/node-card/full.tsx +133 -83
- package/src/components/views/calc/node-card/index.tsx +1 -0
- package/src/components/views/calc/node-card/menu.tsx +93 -22
- package/src/components/views/calc/node-card/port-cells.tsx +186 -0
- package/src/components/views/calc/node-card/port-rails.tsx +251 -0
- package/src/components/views/calc/node-card/ports.tsx +86 -0
- package/src/components/views/calc/recipe-picker/body.tsx +17 -14
- package/src/components/views/calc/recipe-picker/footer.tsx +5 -3
- package/src/components/views/calc/recipe-picker/index.tsx +5 -0
- package/src/components/views/calc/sidebar.tsx +19 -10
- package/src/components/views/calc/trees/icon-picker.tsx +3 -2
- package/src/components/views/calc/trees/index.tsx +5 -4
- package/src/components/views/calc/trees/label.tsx +6 -4
- package/src/components/views/calc/trees/rows.tsx +6 -5
- package/src/components/views/forge/gui.tsx +2 -1
- package/src/components/views/forge/index.tsx +14 -7
- package/src/components/views/forge/list.tsx +3 -2
- package/src/components/views/forge/workbench/controls.tsx +13 -6
- package/src/components/views/forge/workbench/index.tsx +3 -2
- package/src/components/views/forge/workbench/panel.tsx +1 -0
- package/src/components/views/forge/workbench/ruler.tsx +5 -4
- package/src/components/views/forge/workbench/status.tsx +6 -5
- package/src/components/views/forge/workbench/tape.tsx +5 -4
- package/src/hooks/alloy/use-fluid-colors.ts +12 -9
- package/src/hooks/alloy/use-mix-state.ts +3 -2
- package/src/hooks/calc/flow/use-engage.ts +129 -0
- package/src/hooks/calc/flow/use-handlers.ts +192 -0
- package/src/hooks/calc/flow/use-layout.ts +105 -0
- package/src/hooks/calc/flow/{mount.ts → use-mount.ts} +1 -0
- package/src/hooks/calc/flow/{nodes.ts → use-nodes.ts} +46 -37
- package/src/hooks/calc/flow/use-pickers.ts +57 -0
- package/src/hooks/calc/flow/{viewport.ts → use-viewport.ts} +13 -26
- package/src/hooks/calc/flow/{window.ts → use-window.ts} +1 -0
- package/src/hooks/calc/use-actions.ts +208 -229
- package/src/hooks/calc/use-amounts.ts +23 -60
- package/src/hooks/calc/use-data.ts +106 -192
- package/src/hooks/calc/use-defaults.ts +4 -2
- package/src/hooks/calc/use-far-mode.ts +1 -0
- package/src/hooks/calc/use-focus.ts +8 -7
- package/src/hooks/calc/use-maps.ts +1 -0
- package/src/hooks/calc/use-reset.ts +1 -0
- package/src/hooks/calc/use-resolver.ts +32 -196
- package/src/hooks/calc/use-session.ts +1 -0
- package/src/hooks/calc/use-share.ts +5 -2
- package/src/hooks/calc/use-storage.ts +5 -4
- package/src/hooks/calc/use-tier.ts +1 -0
- package/src/hooks/calc/use-view-mode.ts +1 -0
- package/src/hooks/calc/use-worker.ts +56 -121
- package/src/hooks/forge/use-anvil-gui.ts +4 -2
- package/src/hooks/forge/use-forge-state.ts +1 -0
- package/src/hooks/forge/use-ruler.ts +1 -0
- package/src/hooks/use-add-to-calc.ts +23 -19
- package/src/hooks/use-categories-by-id.ts +18 -0
- package/src/hooks/use-favorite-hotkey.ts +1 -0
- package/src/hooks/use-favorites.ts +1 -0
- package/src/hooks/use-group-outlines.ts +1 -0
- package/src/hooks/use-hide-unused.ts +2 -1
- package/src/hooks/use-index.ts +6 -2
- package/src/hooks/use-item-feeds.ts +3 -1
- package/src/hooks/use-item-pane.ts +6 -4
- package/src/hooks/use-item-press.ts +27 -23
- package/src/hooks/use-item-tag-groups.ts +4 -2
- package/src/hooks/use-item-tooltips.ts +3 -1
- package/src/hooks/use-left-sidebar-tree.ts +11 -3
- package/src/hooks/use-mod-names.ts +4 -2
- package/src/hooks/use-mount-limit.ts +1 -0
- package/src/hooks/use-panel-cells.ts +3 -1
- package/src/hooks/use-panel-page.ts +9 -6
- package/src/hooks/use-panel-tabs.ts +2 -1
- package/src/hooks/use-query.ts +8 -6
- package/src/hooks/use-session.ts +6 -2
- package/src/hooks/use-sidebar-group.ts +3 -7
- package/src/hooks/use-tag-members.ts +3 -1
- package/src/hooks/use-tool-recipes.ts +1 -0
- package/src/hooks/use-type-filter.ts +1 -0
- package/src/lib/alloy/mix.ts +1 -0
- package/src/lib/alloy/parse.ts +1 -0
- package/src/lib/calc/defaults.ts +6 -4
- package/src/lib/calc/flow.ts +143 -107
- package/src/lib/calc/focus.ts +1 -0
- package/src/lib/calc/meta.ts +3 -1
- package/src/lib/calc/pack.ts +47 -154
- package/src/lib/calc/ports.ts +43 -0
- package/src/lib/calc/resolve.ts +6 -38
- package/src/lib/calc/route/buckets.ts +1 -0
- package/src/lib/calc/route/geometry.ts +13 -24
- package/src/lib/calc/route/index.ts +1 -0
- package/src/lib/calc/route/polyline.ts +3 -3
- package/src/lib/calc/route/router.ts +181 -57
- package/src/lib/calc/scheme.ts +226 -0
- package/src/lib/calc/storage/entry.ts +27 -19
- package/src/lib/calc/storage/index.ts +2 -0
- package/src/lib/calc/storage/keys.ts +4 -8
- package/src/lib/calc/storage/store.ts +11 -36
- package/src/lib/calc/tree/index.ts +2 -16
- package/src/lib/calc/tree/materials.ts +1 -0
- package/src/lib/calc/tree/recipe.ts +1 -0
- package/src/lib/calc/tree/share.ts +86 -149
- package/src/lib/calc/worker/api.ts +3 -11
- package/src/lib/calc/worker/fetch.ts +1 -0
- package/src/lib/calc/worker/flush.ts +7 -20
- package/src/lib/calc/worker/index.ts +1 -1
- package/src/lib/calc/worker/protocol.ts +16 -13
- package/src/lib/calc/worker/resolve.ts +32 -84
- package/src/lib/calc/worker/state.ts +5 -6
- package/src/{components/cards/card/model.ts → lib/card-model.ts} +1 -0
- package/src/lib/client/recipe-categories.ts +11 -93
- package/src/lib/client/recipe.ts +13 -11
- package/src/lib/client-index/catalog.ts +33 -27
- package/src/lib/client-index/chrome.ts +30 -30
- package/src/lib/client-index/index.ts +1 -0
- package/src/lib/client-index/registry.ts +70 -59
- package/src/lib/client-index/rows.ts +63 -57
- package/src/lib/client-index/tooltips.ts +28 -34
- package/src/lib/config.ts +2 -1
- package/src/lib/data/category/dto.ts +4 -3
- package/src/lib/data/category/load.ts +9 -8
- package/src/lib/data/category/parse.ts +4 -3
- package/src/lib/data/dto.ts +4 -3
- package/src/lib/data/grid.ts +2 -1
- package/src/lib/data/parse/entry.ts +3 -2
- package/src/lib/data/parse/index.ts +1 -0
- package/src/lib/data/parse/layout.ts +5 -2
- package/src/lib/data/parse/slots.ts +3 -2
- package/src/lib/data/parse/widgets.ts +3 -2
- package/src/lib/data/scene.ts +1 -1
- package/src/lib/data/slot-io.ts +1 -0
- package/src/lib/dump.ts +4 -3
- package/src/lib/embed.ts +22 -0
- package/src/lib/favorite-hover.ts +1 -0
- package/src/lib/fluid-outputs.ts +10 -12
- package/src/lib/forge/actions.ts +2 -1
- package/src/lib/forge/data.ts +2 -1
- package/src/lib/forge/solve.ts +1 -0
- package/src/lib/group.ts +1 -0
- package/src/lib/index-store.ts +15 -9
- package/src/lib/links.ts +15 -9
- package/src/lib/lookup.ts +10 -8
- package/src/lib/panel-outline.ts +1 -0
- package/src/lib/panel.ts +3 -2
- package/src/lib/preview.ts +12 -8
- package/src/lib/recipe-slots.ts +1 -0
- package/src/lib/search.ts +1 -0
- package/src/lib/sidebar.ts +1 -0
- package/src/lib/used-items.ts +1 -0
- package/src/locales/en_us.json +170 -226
- package/src/locales/ru_ru.json +170 -226
- package/src/locales.ts +1 -0
- package/src/types/alloy.ts +1 -0
- package/src/{hooks/calc/flow/props.ts → types/calc-flow-props.ts} +18 -14
- package/src/types/calc.ts +41 -44
- package/src/types/forge.ts +1 -0
- package/src/types/index.ts +4 -2
- package/styles/calc.css +308 -47
- package/src/components/cards/fit-card.tsx +0 -26
- package/src/components/views/calc/target.tsx +0 -271
- package/src/hooks/calc/flow/engage.ts +0 -89
- package/src/hooks/calc/flow/handlers.ts +0 -115
- package/src/hooks/calc/flow/layout.ts +0 -149
- package/src/hooks/calc/flow/pickers.ts +0 -50
- package/src/hooks/calc/use-calc-fill.ts +0 -91
- package/src/hooks/calc/use-density.ts +0 -28
- package/src/hooks/calc/use-derived.ts +0 -258
- package/src/hooks/calc/use-direction.ts +0 -23
- package/src/hooks/calc/use-edge-style.ts +0 -40
- package/src/hooks/calc/use-fill.ts +0 -213
- package/src/lib/alloy/alloy.test.ts +0 -186
- package/src/lib/calc/tree/build.ts +0 -196
- package/src/lib/calc/tree/context.ts +0 -42
- package/src/lib/calc/tree/goal.ts +0 -64
- package/src/lib/calc/tree/ideal.ts +0 -36
- package/src/lib/calc/tree/pick.ts +0 -68
- package/src/lib/calc/tree/tree.test.ts +0 -169
- package/src/lib/calc/worker/fill.ts +0 -123
- package/src/lib/client-index/cache.ts +0 -13
- package/src/lib/forge/forge.test.ts +0 -123
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
3
|
|
|
3
4
|
import { useEffect, useMemo, useRef } from 'react';
|
|
4
5
|
import type * as PageTree from 'fumadocs-core/page-tree';
|
|
@@ -20,19 +21,18 @@ export function CalcSidebar({
|
|
|
20
21
|
lang,
|
|
21
22
|
langMap,
|
|
22
23
|
summary,
|
|
23
|
-
target = null,
|
|
24
24
|
onFocus,
|
|
25
25
|
trees,
|
|
26
26
|
}: {
|
|
27
|
-
dataset:
|
|
27
|
+
dataset: DatasetRef;
|
|
28
28
|
lang: string;
|
|
29
29
|
langMap?: Record<string, string>;
|
|
30
30
|
summary: {
|
|
31
31
|
raw: CalcMaterial[];
|
|
32
32
|
remainders: CalcMaterial[];
|
|
33
33
|
catalysts: CalcMaterial[];
|
|
34
|
+
products: CalcMaterial[];
|
|
34
35
|
};
|
|
35
|
-
target?: CalcMaterial | null;
|
|
36
36
|
onFocus?: (id: string) => void;
|
|
37
37
|
trees?: {
|
|
38
38
|
tabs: CalcTreeEntry[];
|
|
@@ -53,21 +53,26 @@ export function CalcSidebar({
|
|
|
53
53
|
const focusByUrl = new Map<string, string>();
|
|
54
54
|
const children: PageTree.Node[] = [];
|
|
55
55
|
const sections = [
|
|
56
|
-
['inputs', summary.raw],
|
|
56
|
+
['inputs', summary.raw, t('Inputs', { note: 'recipe.calc.inputs' })],
|
|
57
57
|
[
|
|
58
58
|
'outputs',
|
|
59
|
-
[...
|
|
59
|
+
[...summary.products, ...summary.remainders],
|
|
60
|
+
t('Outputs', { note: 'recipe.calc.outputs' }),
|
|
61
|
+
],
|
|
62
|
+
[
|
|
63
|
+
'catalysts',
|
|
64
|
+
summary.catalysts,
|
|
65
|
+
t('Catalysts', { note: 'recipe.calc.catalysts' }),
|
|
60
66
|
],
|
|
61
|
-
['catalysts', summary.catalysts],
|
|
62
67
|
] as const;
|
|
63
|
-
for (const [key, rows] of sections) {
|
|
68
|
+
for (const [key, rows, label] of sections) {
|
|
64
69
|
if (!rows.length) continue;
|
|
65
70
|
children.push({
|
|
66
71
|
type: 'folder',
|
|
67
72
|
$id: `calc-${key}`,
|
|
68
73
|
name: (
|
|
69
74
|
<span className="-me-3 flex min-w-0 flex-1 items-center gap-1.5">
|
|
70
|
-
<span className="truncate">{
|
|
75
|
+
<span className="truncate">{label}</span>
|
|
71
76
|
<span className="shrink-0 tabular-nums text-xs text-muted-foreground/70">
|
|
72
77
|
×{rows.length}
|
|
73
78
|
</span>
|
|
@@ -97,7 +102,10 @@ export function CalcSidebar({
|
|
|
97
102
|
tooltip={false}
|
|
98
103
|
className="min-w-0 flex-1 justify-start truncate"
|
|
99
104
|
/>
|
|
100
|
-
<span
|
|
105
|
+
<span
|
|
106
|
+
className="ms-auto shrink-0 tabular-nums text-xs text-muted-foreground"
|
|
107
|
+
title={row.amount.toLocaleString()}
|
|
108
|
+
>
|
|
101
109
|
×{formatCount(row.amount)}
|
|
102
110
|
</span>
|
|
103
111
|
</>
|
|
@@ -115,7 +123,7 @@ export function CalcSidebar({
|
|
|
115
123
|
tree: { name: 'calc', $id: `calc:${stamp}`, children } as PageTree.Root,
|
|
116
124
|
focusByUrl,
|
|
117
125
|
};
|
|
118
|
-
}, [dataset, lang, langMap, summary,
|
|
126
|
+
}, [dataset, lang, langMap, summary, t]);
|
|
119
127
|
|
|
120
128
|
const CalcItem = useMemo(() => {
|
|
121
129
|
const focus = focusByUrl;
|
|
@@ -160,3 +168,4 @@ export function CalcSidebar({
|
|
|
160
168
|
</div>
|
|
161
169
|
);
|
|
162
170
|
}
|
|
171
|
+
|
|
@@ -47,7 +47,7 @@ export function IconPicker({
|
|
|
47
47
|
<PopoverTrigger
|
|
48
48
|
render={
|
|
49
49
|
<IconButton
|
|
50
|
-
label={label ?? t('recipe.calc.pick.icon')}
|
|
50
|
+
label={label ?? t('Pick icon', { note: 'recipe.calc.pick.icon' })}
|
|
51
51
|
className={
|
|
52
52
|
alwaysVisible
|
|
53
53
|
? undefined
|
|
@@ -63,7 +63,7 @@ export function IconPicker({
|
|
|
63
63
|
autoFocus
|
|
64
64
|
value={query}
|
|
65
65
|
onChange={(event) => setQuery(event.target.value)}
|
|
66
|
-
placeholder={t('recipe.search')}
|
|
66
|
+
placeholder={t('Search items', { note: 'recipe.search' })}
|
|
67
67
|
className="h-7 px-2 text-xs"
|
|
68
68
|
/>
|
|
69
69
|
<div className="scrollbar-thin mt-2 grid max-h-60 grid-cols-6 gap-1 overflow-y-auto">
|
|
@@ -94,3 +94,4 @@ export function IconPicker({
|
|
|
94
94
|
</Popover>
|
|
95
95
|
);
|
|
96
96
|
}
|
|
97
|
+
|
|
@@ -26,7 +26,7 @@ export function CalcHistoryList({
|
|
|
26
26
|
return (
|
|
27
27
|
<div className="flex w-64 max-w-full flex-col gap-1">
|
|
28
28
|
<p className="px-2 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/70">
|
|
29
|
-
{t('recipe.calc.history')}
|
|
29
|
+
{t('History', { note: 'recipe.calc.history' })}
|
|
30
30
|
</p>
|
|
31
31
|
<ul className="flex flex-col">
|
|
32
32
|
{history.slice(0, 5).map((entry) => (
|
|
@@ -75,7 +75,7 @@ export function CalcTrees({
|
|
|
75
75
|
<LeftSidebarFolder collapsible defaultOpen>
|
|
76
76
|
<LeftSidebarFolderTrigger className="gap-1.5 text-xs font-semibold uppercase tracking-wider text-muted-foreground/70">
|
|
77
77
|
<FolderTree className="size-3.5" />
|
|
78
|
-
{t('recipe.calc.trees')}
|
|
78
|
+
{t('Schemes', { note: 'recipe.calc.trees' })}
|
|
79
79
|
</LeftSidebarFolderTrigger>
|
|
80
80
|
<LeftSidebarFolderContent>
|
|
81
81
|
<ul className="flex flex-col">
|
|
@@ -99,7 +99,7 @@ export function CalcTrees({
|
|
|
99
99
|
>
|
|
100
100
|
<Plus className="size-4 shrink-0" />
|
|
101
101
|
<span className="min-w-0 flex-1 text-start">
|
|
102
|
-
{t('recipe.calc.new_tab')}
|
|
102
|
+
{t('New scheme', { note: 'recipe.calc.new_tab' })}
|
|
103
103
|
</span>
|
|
104
104
|
</button>
|
|
105
105
|
</li>
|
|
@@ -111,7 +111,7 @@ export function CalcTrees({
|
|
|
111
111
|
<LeftSidebarFolder collapsible defaultOpen>
|
|
112
112
|
<LeftSidebarFolderTrigger className="gap-1.5 text-xs font-semibold uppercase tracking-wider text-muted-foreground/70">
|
|
113
113
|
<History className="size-3.5" />
|
|
114
|
-
{t('recipe.calc.history')}
|
|
114
|
+
{t('History', { note: 'recipe.calc.history' })}
|
|
115
115
|
</LeftSidebarFolderTrigger>
|
|
116
116
|
<LeftSidebarFolderContent>
|
|
117
117
|
<ul className="flex flex-col">
|
|
@@ -131,3 +131,4 @@ export function CalcTrees({
|
|
|
131
131
|
</div>
|
|
132
132
|
);
|
|
133
133
|
}
|
|
134
|
+
|
|
@@ -12,7 +12,8 @@ export function TreeIcon({
|
|
|
12
12
|
lang,
|
|
13
13
|
langMap,
|
|
14
14
|
}: { entry: CalcTreeEntry } & EntryMeta) {
|
|
15
|
-
const
|
|
15
|
+
const card = entry.state.cards?.[0];
|
|
16
|
+
const ref = entry.icon ?? (card && { id: card.materialId, kind: card.kind });
|
|
16
17
|
if (!ref) return <Workflow className="size-4 shrink-0" />;
|
|
17
18
|
return (
|
|
18
19
|
<Entry
|
|
@@ -34,11 +35,11 @@ export function TreeLabel({
|
|
|
34
35
|
langMap,
|
|
35
36
|
}: { entry: CalcTreeEntry } & EntryMeta) {
|
|
36
37
|
const t = useTranslations();
|
|
37
|
-
const
|
|
38
|
+
const card = entry.state.cards?.[0];
|
|
38
39
|
const text =
|
|
39
40
|
entry.name ??
|
|
40
|
-
(
|
|
41
|
-
t('recipe.calc.tab.untitled');
|
|
41
|
+
(card ? langMap?.[card.materialId] || card.materialId : undefined) ??
|
|
42
|
+
t('Untitled scheme', { note: 'recipe.calc.tab.untitled' });
|
|
42
43
|
return (
|
|
43
44
|
<span className="min-w-0 flex-1 truncate text-start">
|
|
44
45
|
{text}
|
|
@@ -63,3 +64,4 @@ export function TreeDate({
|
|
|
63
64
|
</span>
|
|
64
65
|
);
|
|
65
66
|
}
|
|
67
|
+
|
|
@@ -50,9 +50,9 @@ export function TabRow({
|
|
|
50
50
|
}}
|
|
51
51
|
onBlur={commit}
|
|
52
52
|
className="h-6 px-1.5 text-xs"
|
|
53
|
-
placeholder={t('recipe.calc.rename')}
|
|
53
|
+
placeholder={t('Rename', { note: 'recipe.calc.rename' })}
|
|
54
54
|
/>
|
|
55
|
-
<IconButton label={t('recipe.calc.rename')} onClick={commit}>
|
|
55
|
+
<IconButton label={t('Rename', { note: 'recipe.calc.rename' })} onClick={commit}>
|
|
56
56
|
<Check className="size-3.5" />
|
|
57
57
|
</IconButton>
|
|
58
58
|
</span>
|
|
@@ -80,7 +80,7 @@ export function TabRow({
|
|
|
80
80
|
/>
|
|
81
81
|
) : null}
|
|
82
82
|
<IconButton
|
|
83
|
-
label={t('recipe.calc.rename')}
|
|
83
|
+
label={t('Rename', { note: 'recipe.calc.rename' })}
|
|
84
84
|
className="opacity-0 transition-opacity group-hover:opacity-100"
|
|
85
85
|
onClick={() => {
|
|
86
86
|
setName(entry.name ?? '');
|
|
@@ -90,7 +90,7 @@ export function TabRow({
|
|
|
90
90
|
<Pencil className="size-3.5" />
|
|
91
91
|
</IconButton>
|
|
92
92
|
<IconButton
|
|
93
|
-
label={t('recipe.calc.close_tab')}
|
|
93
|
+
label={t('Close scheme', { note: 'recipe.calc.close_tab' })}
|
|
94
94
|
className="opacity-0 transition-opacity group-hover:opacity-100"
|
|
95
95
|
onClick={() => onClose(entry.id)}
|
|
96
96
|
>
|
|
@@ -127,7 +127,7 @@ export function HistoryRow({
|
|
|
127
127
|
</button>
|
|
128
128
|
{onRemove && (
|
|
129
129
|
<IconButton
|
|
130
|
-
label={t('recipe.calc.remove.history')}
|
|
130
|
+
label={t('Remove from history', { note: 'recipe.calc.remove.history' })}
|
|
131
131
|
className="opacity-0 transition-opacity group-hover:opacity-100"
|
|
132
132
|
onClick={() => onRemove(entry.id)}
|
|
133
133
|
>
|
|
@@ -137,3 +137,4 @@ export function HistoryRow({
|
|
|
137
137
|
</li>
|
|
138
138
|
);
|
|
139
139
|
}
|
|
140
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import type { CSSProperties } from 'react';
|
|
4
|
-
import { cn } from 'cn';
|
|
4
|
+
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
5
5
|
import type { ForgeRule, ForgeRuleAction } from '@terrafirmagreg-team/recipe/types/forge';
|
|
6
6
|
|
|
7
7
|
const ATLAS_SIZE = 256;
|
|
@@ -128,3 +128,4 @@ export const MARK_TARGET: GuiRect = { x: 181, y: 0, w: 5, h: 5 };
|
|
|
128
128
|
export function actionGlyphRect(action: string): GuiRect {
|
|
129
129
|
return { x: STEP_GLYPH_X[action] ?? 0, y: 224, w: 32, h: 32 };
|
|
130
130
|
}
|
|
131
|
+
|
|
@@ -9,7 +9,7 @@ import { RecipeFrame } from '@terrafirmagreg-team/recipe/components/views/browse
|
|
|
9
9
|
import { useToolRecipes } from '@terrafirmagreg-team/recipe/hooks/use-tool-recipes';
|
|
10
10
|
import { toForgeRecipe } from '@terrafirmagreg-team/recipe/lib/forge/data';
|
|
11
11
|
import { RecipeCard } from '@terrafirmagreg-team/recipe/components/cards/card';
|
|
12
|
-
import {
|
|
12
|
+
import { FitBox } from '@terrafirmagreg-team/ui/components/blocks/fit-box';
|
|
13
13
|
import {
|
|
14
14
|
recipeCardImageUrl,
|
|
15
15
|
recipeImageUrl,
|
|
@@ -67,16 +67,16 @@ export function ForgeCalculator({ session }: { session: RecipeSession }) {
|
|
|
67
67
|
active ? (
|
|
68
68
|
<RecipeItemHeader recipe={active.recipe} session={session} />
|
|
69
69
|
) : (
|
|
70
|
-
<ItemRow layout="header" title={t('recipe.forge.title')} />
|
|
70
|
+
<ItemRow layout="header" title={t('Forging', { note: 'recipe.forge.title' })} />
|
|
71
71
|
)
|
|
72
72
|
}
|
|
73
73
|
>
|
|
74
|
-
<div className="flex-1 space-y-5 overflow-y-auto p-2 sm:p-4">
|
|
74
|
+
<div className="scrollbar-thin flex-1 space-y-5 overflow-y-auto p-2 sm:p-4">
|
|
75
75
|
{loading ? (
|
|
76
76
|
<EmptyState variant="loading" />
|
|
77
77
|
) : value === undefined ? (
|
|
78
78
|
<EmptyState variant="missing">
|
|
79
|
-
{t('recipe.forge.load_error')}
|
|
79
|
+
{t('Could not load recipes. Reload the page to retry.', { note: 'recipe.forge.load_error' })}
|
|
80
80
|
</EmptyState>
|
|
81
81
|
) : active ? (
|
|
82
82
|
<>
|
|
@@ -85,17 +85,23 @@ export function ForgeCalculator({ session }: { session: RecipeSession }) {
|
|
|
85
85
|
recipe={active}
|
|
86
86
|
session={session}
|
|
87
87
|
/>
|
|
88
|
-
<
|
|
88
|
+
<FitBox w={active.recipe.w ?? 0}>
|
|
89
89
|
<ForgeRecipeCard
|
|
90
90
|
key={active.recipe.id}
|
|
91
91
|
recipe={active.recipe}
|
|
92
92
|
session={session}
|
|
93
93
|
/>
|
|
94
|
-
</
|
|
94
|
+
</FitBox>
|
|
95
95
|
</>
|
|
96
96
|
) : (
|
|
97
97
|
<EmptyState>
|
|
98
|
-
{
|
|
98
|
+
{activeId
|
|
99
|
+
? t('Recipe not found in this dump. Select another recipe.', {
|
|
100
|
+
note: 'recipe.forge.missing',
|
|
101
|
+
})
|
|
102
|
+
: t('No matching recipes in this dump.', {
|
|
103
|
+
note: 'recipe.forge.empty',
|
|
104
|
+
})}
|
|
99
105
|
</EmptyState>
|
|
100
106
|
)}
|
|
101
107
|
</div>
|
|
@@ -103,3 +109,4 @@ export function ForgeCalculator({ session }: { session: RecipeSession }) {
|
|
|
103
109
|
</>
|
|
104
110
|
);
|
|
105
111
|
}
|
|
112
|
+
|
|
@@ -105,8 +105,9 @@ export function ForgeList({ session }: { session: RecipeSession }) {
|
|
|
105
105
|
loaded={value !== undefined}
|
|
106
106
|
query={query}
|
|
107
107
|
onQuery={setQuery}
|
|
108
|
-
searchLabel={t('recipe.forge.search')}
|
|
109
|
-
emptyLabel={t('recipe.forge.empty')}
|
|
108
|
+
searchLabel={t('Search recipes', { note: 'recipe.forge.search' })}
|
|
109
|
+
emptyLabel={t('No matching recipes in this dump.', { note: 'recipe.forge.empty' })}
|
|
110
110
|
/>
|
|
111
111
|
);
|
|
112
112
|
}
|
|
113
|
+
|
|
@@ -34,7 +34,7 @@ export function WorkbenchControls({
|
|
|
34
34
|
return (
|
|
35
35
|
<div className="flex flex-wrap items-end gap-3">
|
|
36
36
|
<label className="flex w-24 flex-col gap-1 border-l-2 border-green-600 pl-2 text-xs text-green-700 dark:border-green-500 dark:text-green-400">
|
|
37
|
-
{t('recipe.forge.current')}
|
|
37
|
+
{t('Current work', { note: 'recipe.forge.current' })}
|
|
38
38
|
<Input
|
|
39
39
|
inputMode="numeric"
|
|
40
40
|
name="forge-start"
|
|
@@ -46,7 +46,7 @@ export function WorkbenchControls({
|
|
|
46
46
|
/>
|
|
47
47
|
</label>
|
|
48
48
|
<label className="flex w-24 flex-col gap-1 border-l-2 border-red-600 pl-2 text-xs text-red-700 dark:border-red-500 dark:text-red-400">
|
|
49
|
-
{t('recipe.forge.target')}
|
|
49
|
+
{t('Target', { note: 'recipe.forge.target' })}
|
|
50
50
|
<Input
|
|
51
51
|
inputMode="numeric"
|
|
52
52
|
name="forge-target"
|
|
@@ -62,25 +62,31 @@ export function WorkbenchControls({
|
|
|
62
62
|
disabled={target === null || !rulesSupported || solving}
|
|
63
63
|
>
|
|
64
64
|
<Wand2 className="size-4" />
|
|
65
|
-
{
|
|
65
|
+
{solving
|
|
66
|
+
? t('Solving…', { note: 'recipe.forge.solving' })
|
|
67
|
+
: t('Solve', { note: 'recipe.forge.solve' })}
|
|
66
68
|
</Button>
|
|
67
69
|
<span className="flex items-center gap-1.5">
|
|
68
70
|
<IconButton
|
|
69
|
-
label={t('recipe.forge.undo')}
|
|
71
|
+
label={t('Undo last action', { note: 'recipe.forge.undo' })}
|
|
70
72
|
disabled={!applied.length || solving}
|
|
71
73
|
onClick={() => state.setApplied((prev) => prev.slice(0, -1))}
|
|
72
74
|
>
|
|
73
75
|
<Undo2 />
|
|
74
76
|
</IconButton>
|
|
75
77
|
<IconButton
|
|
76
|
-
label={t('recipe.forge.reset')}
|
|
78
|
+
label={t('Reset the workpiece', { note: 'recipe.forge.reset' })}
|
|
77
79
|
disabled={!applied.length || solving}
|
|
78
80
|
onClick={() => state.setApplied([])}
|
|
79
81
|
>
|
|
80
82
|
<RotateCcw />
|
|
81
83
|
</IconButton>
|
|
82
84
|
<IconButton
|
|
83
|
-
label={
|
|
85
|
+
label={
|
|
86
|
+
copied
|
|
87
|
+
? t('Copied', { note: 'recipe.forge.copied' })
|
|
88
|
+
: t('Copy steps', { note: 'recipe.forge.copy' })
|
|
89
|
+
}
|
|
84
90
|
disabled={!steps.length}
|
|
85
91
|
onClick={copy}
|
|
86
92
|
>
|
|
@@ -94,3 +100,4 @@ export function WorkbenchControls({
|
|
|
94
100
|
</div>
|
|
95
101
|
);
|
|
96
102
|
}
|
|
103
|
+
|
|
@@ -11,7 +11,7 @@ import { AnvilPanel } from './panel';
|
|
|
11
11
|
import { RulerOverlay } from './ruler';
|
|
12
12
|
import { StatusPlate } from './status';
|
|
13
13
|
import { StepTape } from './tape';
|
|
14
|
-
import { cn } from 'cn';
|
|
14
|
+
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
15
15
|
import type { ForgeRecipe } from '@terrafirmagreg-team/recipe/types/forge';
|
|
16
16
|
import type { RecipeSession } from '@terrafirmagreg-team/recipe/types';
|
|
17
17
|
|
|
@@ -43,7 +43,7 @@ export function ForgeWorkbench({
|
|
|
43
43
|
<WorkbenchControls state={state} rulesSupported={recipe.rulesSupported} langMap={session.langMap} />
|
|
44
44
|
{!recipe.rulesSupported ? (
|
|
45
45
|
<p role="alert" className="text-sm text-destructive">
|
|
46
|
-
{t('recipe.forge.unsupported')}
|
|
46
|
+
{t('The dump contains missing or unrecognized forging rules. A safe solution cannot be calculated.', { note: 'recipe.forge.unsupported' })}
|
|
47
47
|
</p>
|
|
48
48
|
) : null}
|
|
49
49
|
|
|
@@ -82,3 +82,4 @@ export function ForgeWorkbench({
|
|
|
82
82
|
</section>
|
|
83
83
|
);
|
|
84
84
|
}
|
|
85
|
+
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
|
|
4
4
|
import { MARK_PROGRESS, MARK_TARGET, GuiSprite } from '@terrafirmagreg-team/recipe/components/views/forge/gui';
|
|
5
5
|
import { RULER, useRuler } from '@terrafirmagreg-team/recipe/hooks/forge/use-ruler';
|
|
6
|
-
import { cn } from 'cn';
|
|
6
|
+
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
7
7
|
import type { ForgeState } from '@terrafirmagreg-team/recipe/hooks/forge/use-forge-state';
|
|
8
8
|
|
|
9
9
|
export function RulerOverlay({
|
|
@@ -38,8 +38,8 @@ export function RulerOverlay({
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
const markerHandles = [
|
|
41
|
-
{ kind: 'target', value: target ?? 0, label: t('recipe.forge.target') },
|
|
42
|
-
{ kind: 'work', value: work, label: t('recipe.forge.current') },
|
|
41
|
+
{ kind: 'target', value: target ?? 0, label: t('Target', { note: 'recipe.forge.target' }) },
|
|
42
|
+
{ kind: 'work', value: work, label: t('Current work', { note: 'recipe.forge.current' }) },
|
|
43
43
|
] as const;
|
|
44
44
|
|
|
45
45
|
return (
|
|
@@ -66,7 +66,7 @@ export function RulerOverlay({
|
|
|
66
66
|
/>
|
|
67
67
|
<div
|
|
68
68
|
role="slider"
|
|
69
|
-
aria-label={t('recipe.forge.ruler')}
|
|
69
|
+
aria-label={t('Work ruler', { note: 'recipe.forge.ruler' })}
|
|
70
70
|
aria-valuemin={0}
|
|
71
71
|
aria-valuemax={149}
|
|
72
72
|
aria-valuenow={target ?? work}
|
|
@@ -130,3 +130,4 @@ export function RulerOverlay({
|
|
|
130
130
|
</>
|
|
131
131
|
);
|
|
132
132
|
}
|
|
133
|
+
|
|
@@ -22,20 +22,20 @@ export function StatusPlate({
|
|
|
22
22
|
style={{ width: PX(GUI_PANEL.w) }}
|
|
23
23
|
>
|
|
24
24
|
<span>
|
|
25
|
-
{t('recipe.forge.work')}:{' '}
|
|
25
|
+
{t('Work', { note: 'recipe.forge.work' })}:{' '}
|
|
26
26
|
<span className="font-medium text-green-700 dark:text-green-400">
|
|
27
27
|
{work}
|
|
28
28
|
</span>
|
|
29
29
|
</span>
|
|
30
30
|
<span>
|
|
31
|
-
{t('recipe.forge.target')}:{' '}
|
|
31
|
+
{t('Target', { note: 'recipe.forge.target' })}:{' '}
|
|
32
32
|
<span className="font-medium text-red-700 dark:text-red-400">
|
|
33
33
|
{target ?? 0}
|
|
34
34
|
</span>
|
|
35
35
|
</span>
|
|
36
36
|
{finished ? (
|
|
37
37
|
<span className="inline-flex items-center gap-1 text-green-600 animate-in fade-in-0">
|
|
38
|
-
<Check className="size-3" /> {t('recipe.forge.done')}
|
|
38
|
+
<Check className="size-3" /> {t('Done', { note: 'recipe.forge.done' })}
|
|
39
39
|
</span>
|
|
40
40
|
) : null}
|
|
41
41
|
{failed ? (
|
|
@@ -43,12 +43,13 @@ export function StatusPlate({
|
|
|
43
43
|
role="status"
|
|
44
44
|
className="inline-flex items-center gap-1 text-destructive"
|
|
45
45
|
>
|
|
46
|
-
<X className="size-3" /> {t('recipe.forge.no_solution')}
|
|
46
|
+
<X className="size-3" /> {t('No sequence reaches this target under these rules. Reset the workpiece and try another target.', { note: 'recipe.forge.no_solution' })}
|
|
47
47
|
</span>
|
|
48
48
|
) : null}
|
|
49
49
|
{target !== null && work > target && !failed ? (
|
|
50
|
-
<span>{t('recipe.forge.over_target')}</span>
|
|
50
|
+
<span>{t('Work is above the target, use Draw or lighter hits to come back.', { note: 'recipe.forge.over_target' })}</span>
|
|
51
51
|
) : null}
|
|
52
52
|
</div>
|
|
53
53
|
);
|
|
54
54
|
}
|
|
55
|
+
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
|
|
4
4
|
import { GuiSprite, actionGlyphRect } from '@terrafirmagreg-team/recipe/components/views/forge/gui';
|
|
5
|
-
import { cn } from 'cn';
|
|
5
|
+
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
6
6
|
import type { ForgeState } from '@terrafirmagreg-team/recipe/hooks/forge/use-forge-state';
|
|
7
7
|
|
|
8
8
|
const TAPE_MAX = 12;
|
|
@@ -26,8 +26,8 @@ export function StepTape({
|
|
|
26
26
|
<li>
|
|
27
27
|
<button
|
|
28
28
|
type="button"
|
|
29
|
-
title={t('recipe.forge.expand_steps')}
|
|
30
|
-
aria-label={t('recipe.forge.expand_steps')}
|
|
29
|
+
title={t('Show all steps', { note: 'recipe.forge.expand_steps' })}
|
|
30
|
+
aria-label={t('Show all steps', { note: 'recipe.forge.expand_steps' })}
|
|
31
31
|
onClick={() => setExpanded(true)}
|
|
32
32
|
className="flex items-center rounded border border-dashed px-1.5 py-0.5 text-xs tabular-nums text-muted-foreground hover:bg-accent"
|
|
33
33
|
>
|
|
@@ -41,7 +41,7 @@ export function StepTape({
|
|
|
41
41
|
<li key={i} className="animate-in fade-in-0 zoom-in-95 duration-150">
|
|
42
42
|
<button
|
|
43
43
|
type="button"
|
|
44
|
-
title={t('recipe.forge.trim_to')}
|
|
44
|
+
title={t('Trim to this step', { note: 'recipe.forge.trim_to' })}
|
|
45
45
|
onClick={() => setApplied((prev) => prev.slice(0, i + 1))}
|
|
46
46
|
className={cn(
|
|
47
47
|
'flex items-center gap-1.5 rounded border px-1.5 py-0.5 text-xs tabular-nums hover:bg-accent',
|
|
@@ -67,3 +67,4 @@ export function StepTape({
|
|
|
67
67
|
</ol>
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
|
+
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
3
|
|
|
3
4
|
import { useEffect, useMemo, useReducer } from 'react';
|
|
4
|
-
import { loadClientModAtlases } from '@terrafirmagreg-team/
|
|
5
|
-
import { lookupSpriteInBundle } from '@terrafirmagreg-team/
|
|
6
|
-
import { atlasPagePublicUrl } from '@terrafirmagreg-team/
|
|
7
|
-
import { splitDumpId } from '@terrafirmagreg-team/
|
|
8
|
-
import { createAsyncCache } from '@terrafirmagreg-team/
|
|
5
|
+
import { loadClientModAtlases } from '@terrafirmagreg-team/dump/lib/dump/client/atlases';
|
|
6
|
+
import { lookupSpriteInBundle } from '@terrafirmagreg-team/dump/lib/dump/assets/atlas/lookup';
|
|
7
|
+
import { atlasPagePublicUrl } from '@terrafirmagreg-team/dump/lib/dump/assets/urls';
|
|
8
|
+
import { splitDumpId } from '@terrafirmagreg-team/dump/lib/dump/paths';
|
|
9
|
+
import { createAsyncCache } from '@terrafirmagreg-team/data/lib/async-cache';
|
|
10
|
+
import { createDatasetCache } from '@terrafirmagreg-team/dump/lib/dump/reader';
|
|
9
11
|
import type { RecipeSlotFill } from '@terrafirmagreg-team/recipe/lib/data/dto';
|
|
10
12
|
|
|
11
13
|
const loadAtlasPage = createAsyncCache(
|
|
@@ -43,8 +45,8 @@ function displayColor(r: number, g: number, b: number): string {
|
|
|
43
45
|
return `hsl(${Math.round(h * 360)} ${Math.round(sat * 100)}% ${Math.round(light * 100)}%)`;
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
const loadFluidColor =
|
|
47
|
-
async (dataset:
|
|
48
|
+
const loadFluidColor = createDatasetCache(
|
|
49
|
+
async (dataset: DatasetRef, id: string): Promise<string | null> => {
|
|
48
50
|
const parts = splitDumpId(id);
|
|
49
51
|
if (!parts) return null;
|
|
50
52
|
const bundle = await loadClientModAtlases(dataset, parts.ns);
|
|
@@ -84,11 +86,11 @@ const loadFluidColor = createAsyncCache(
|
|
|
84
86
|
}
|
|
85
87
|
return n ? displayColor(r / n, g / n, b / n) : null;
|
|
86
88
|
},
|
|
87
|
-
(dataset, id) => `${dataset}|${id}`,
|
|
89
|
+
(dataset, id) => `${dataset.id}/${dataset.version ?? ''}|${id}`,
|
|
88
90
|
);
|
|
89
91
|
|
|
90
92
|
export function useFluidColors(
|
|
91
|
-
dataset:
|
|
93
|
+
dataset: DatasetRef,
|
|
92
94
|
slots: readonly (RecipeSlotFill | { id: string; kind?: string })[],
|
|
93
95
|
): (string | undefined)[] {
|
|
94
96
|
const ids = useMemo(
|
|
@@ -120,3 +122,4 @@ export function useFluidColors(
|
|
|
120
122
|
id ? (loadFluidColor.peek(dataset, id) ?? undefined) : undefined,
|
|
121
123
|
);
|
|
122
124
|
}
|
|
125
|
+
|
|
@@ -143,7 +143,7 @@ export function useMixState(recipe: AlloyRecipe, session: RecipeSession) {
|
|
|
143
143
|
);
|
|
144
144
|
const output = recipe.recipe.outputs[0];
|
|
145
145
|
const unitLabel =
|
|
146
|
-
unit === 'ingot' ? t('recipe.alloy.unit.ingot') : t('recipe.alloy.unit.mb');
|
|
146
|
+
unit === 'ingot' ? t('ing.', { note: 'recipe.alloy.unit.ingot' }) : t('mB', { note: 'recipe.alloy.unit.mb' });
|
|
147
147
|
const format = (value: number) =>
|
|
148
148
|
value.toLocaleString(undefined, { maximumFractionDigits: 2 });
|
|
149
149
|
const formatPercent = (value: number) =>
|
|
@@ -158,7 +158,7 @@ export function useMixState(recipe: AlloyRecipe, session: RecipeSession) {
|
|
|
158
158
|
.join(', ');
|
|
159
159
|
const copyText = `${recipe.components
|
|
160
160
|
.map(({ slot }, i) => `${name(slot.id)} ${format(amounts[i] || 0)}`)
|
|
161
|
-
.join(' + ')} → ${output ? name(output.id) : t('recipe.alloy.result')} ${format(
|
|
161
|
+
.join(' + ')} → ${output ? name(output.id) : t('Alloy', { note: 'recipe.alloy.result' })} ${format(
|
|
162
162
|
mix.output,
|
|
163
163
|
)} ${unitLabel}`;
|
|
164
164
|
const fill = (target: number) => {
|
|
@@ -237,3 +237,4 @@ export function useMixState(recipe: AlloyRecipe, session: RecipeSession) {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
export type MixState = ReturnType<typeof useMixState>;
|
|
240
|
+
|