@terrafirmagreg-team/recipe 0.0.0-stage → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +62 -3
- package/src/components/button-copy.tsx +43 -0
- package/src/components/cards/card/index.tsx +214 -0
- package/src/components/cards/card/scene.tsx +107 -0
- package/src/components/cards/card/strip.tsx +198 -0
- package/src/components/cards/card/text.tsx +46 -0
- package/src/components/cards/card/tips.tsx +63 -0
- package/src/components/cards/default-item.tsx +47 -0
- package/src/components/cards/glare-filter.tsx +29 -0
- package/src/components/cards/paged-card.tsx +222 -0
- package/src/components/cards/slot-hitbox.tsx +213 -0
- package/src/components/cards/strip-card/buttons.tsx +106 -0
- package/src/components/cards/strip-card/index.tsx +214 -0
- package/src/components/cards/strip-card/member-cell.tsx +167 -0
- package/src/components/cards/strip-card/sprite.tsx +36 -0
- package/src/components/embed-card.tsx +44 -0
- package/src/components/mdx/recipe.tsx +129 -0
- package/src/components/tool-list.tsx +49 -0
- package/src/components/tree.tsx +98 -0
- package/src/components/views/alloy/index.tsx +112 -0
- package/src/components/views/alloy/list.tsx +85 -0
- package/src/components/views/alloy/mix/index.tsx +35 -0
- package/src/components/views/alloy/mix/ratio.tsx +94 -0
- package/src/components/views/alloy/mix/rows.tsx +145 -0
- package/src/components/views/alloy/mix/status.tsx +122 -0
- package/src/components/views/alloy/mix/total.tsx +132 -0
- package/src/components/views/alloy/range-bar.tsx +65 -0
- package/src/components/views/alloy/stack-bar.tsx +88 -0
- package/src/components/views/browser/axis-switcher.tsx +54 -0
- package/src/components/views/browser/card-grid.tsx +275 -0
- package/src/components/views/browser/favorite-button.tsx +46 -0
- package/src/components/views/browser/frame.tsx +112 -0
- package/src/components/views/browser/index.tsx +114 -0
- package/src/components/views/browser/item-panel/index.tsx +229 -0
- package/src/components/views/browser/item-panel/skeleton.tsx +26 -0
- package/src/components/views/browser/item-panel-grid/cell.tsx +178 -0
- package/src/components/views/browser/item-panel-grid/favorite-cell.tsx +129 -0
- package/src/components/views/browser/item-panel-grid/index.tsx +105 -0
- package/src/components/views/browser/item-view/index.tsx +186 -0
- package/src/components/views/browser/item-view/tags.tsx +85 -0
- package/src/components/views/browser/item-view-header.tsx +131 -0
- package/src/components/views/browser/pane-tabs.tsx +61 -0
- package/src/components/views/browser/place-header.tsx +72 -0
- package/src/components/views/browser/query-controls.tsx +105 -0
- package/src/components/views/browser/recipe-item-header.tsx +60 -0
- package/src/components/views/browser/settings.tsx +52 -0
- package/src/components/views/browser/toolbar.tsx +54 -0
- package/src/components/views/browser/type-tabs.tsx +71 -0
- package/src/components/views/browser/type-view.tsx +184 -0
- package/src/components/views/browser/workstation-rail.tsx +43 -0
- package/src/components/views/calc/calculator/actions.tsx +47 -0
- package/src/components/views/calc/calculator/help.tsx +189 -0
- package/src/components/views/calc/calculator/index.tsx +172 -0
- package/src/components/views/calc/dot-overlay.tsx +80 -0
- package/src/components/views/calc/edge.tsx +152 -0
- package/src/components/views/calc/flow/index.tsx +305 -0
- package/src/components/views/calc/flow/picker-dialogs.tsx +146 -0
- package/src/components/views/calc/flow/toolbar.tsx +60 -0
- package/src/components/views/calc/flow/view-menu.tsx +117 -0
- package/src/components/views/calc/member-picker.tsx +120 -0
- package/src/components/views/calc/node-card/actions.tsx +90 -0
- package/src/components/views/calc/node-card/badges.tsx +59 -0
- package/src/components/views/calc/node-card/chips.tsx +187 -0
- package/src/components/views/calc/node-card/far.tsx +123 -0
- package/src/components/views/calc/node-card/full.tsx +332 -0
- package/src/components/views/calc/node-card/index.tsx +27 -0
- package/src/components/views/calc/node-card/menu.tsx +282 -0
- 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 +207 -0
- package/src/components/views/calc/recipe-picker/footer.tsx +131 -0
- package/src/components/views/calc/recipe-picker/index.tsx +52 -0
- package/src/components/views/calc/sidebar.tsx +171 -0
- package/src/components/views/calc/trees/icon-picker.tsx +97 -0
- package/src/components/views/calc/trees/index.tsx +134 -0
- package/src/components/views/calc/trees/label.tsx +67 -0
- package/src/components/views/calc/trees/rows.tsx +140 -0
- package/src/components/views/forge/gui.tsx +131 -0
- package/src/components/views/forge/index.tsx +112 -0
- package/src/components/views/forge/list.tsx +113 -0
- package/src/components/views/forge/workbench/controls.tsx +103 -0
- package/src/components/views/forge/workbench/index.tsx +85 -0
- package/src/components/views/forge/workbench/panel.tsx +228 -0
- package/src/components/views/forge/workbench/ruler.tsx +133 -0
- package/src/components/views/forge/workbench/status.tsx +55 -0
- package/src/components/views/forge/workbench/tape.tsx +70 -0
- package/src/hooks/alloy/use-fluid-colors.ts +125 -0
- package/src/hooks/alloy/use-mix-state.ts +240 -0
- 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/use-mount.ts +97 -0
- package/src/hooks/calc/flow/use-nodes.ts +260 -0
- package/src/hooks/calc/flow/use-pickers.ts +57 -0
- package/src/hooks/calc/flow/use-viewport.ts +141 -0
- package/src/hooks/calc/flow/use-window.ts +87 -0
- package/src/hooks/calc/use-actions.ts +355 -0
- package/src/hooks/calc/use-amounts.ts +64 -0
- package/src/hooks/calc/use-data.ts +203 -0
- package/src/hooks/calc/use-defaults.ts +40 -0
- package/src/hooks/calc/use-far-mode.ts +37 -0
- package/src/hooks/calc/use-focus.ts +31 -0
- package/src/hooks/calc/use-maps.ts +76 -0
- package/src/hooks/calc/use-reset.ts +15 -0
- package/src/hooks/calc/use-resolver.ts +70 -0
- package/src/hooks/calc/use-session.ts +13 -0
- package/src/hooks/calc/use-share.ts +64 -0
- package/src/hooks/calc/use-storage.ts +126 -0
- package/src/hooks/calc/use-tier.ts +13 -0
- package/src/hooks/calc/use-view-mode.ts +24 -0
- package/src/hooks/calc/use-worker.ts +204 -0
- package/src/hooks/forge/use-anvil-gui.ts +16 -0
- package/src/hooks/forge/use-forge-state.ts +135 -0
- package/src/hooks/forge/use-ruler.ts +100 -0
- package/src/hooks/use-add-to-calc.ts +51 -0
- package/src/hooks/use-categories-by-id.ts +18 -0
- package/src/hooks/use-favorite-hotkey.ts +39 -0
- package/src/hooks/use-favorites.ts +135 -0
- package/src/hooks/use-group-outlines.ts +65 -0
- package/src/hooks/use-hide-unused.ts +12 -0
- package/src/hooks/use-index.ts +72 -0
- package/src/hooks/use-item-feeds.ts +46 -0
- package/src/hooks/use-item-pane.ts +106 -0
- package/src/hooks/use-item-press.ts +89 -0
- package/src/hooks/use-item-tag-groups.ts +54 -0
- package/src/hooks/use-item-tooltips.ts +18 -0
- package/src/hooks/use-left-sidebar-tree.ts +34 -0
- package/src/hooks/use-mod-names.ts +17 -0
- package/src/hooks/use-mount-limit.ts +22 -0
- package/src/hooks/use-panel-cells.ts +117 -0
- package/src/hooks/use-panel-page.ts +54 -0
- package/src/hooks/use-panel-tabs.ts +43 -0
- package/src/hooks/use-query.ts +186 -0
- package/src/hooks/use-session.ts +88 -0
- package/src/hooks/use-sidebar-group.ts +16 -0
- package/src/hooks/use-tag-members.ts +16 -0
- package/src/hooks/use-tool-recipes.ts +26 -0
- package/src/hooks/use-type-filter.ts +73 -0
- package/src/lib/alloy/mix.ts +210 -0
- package/src/lib/alloy/parse.ts +37 -0
- package/src/lib/calc/defaults.ts +47 -0
- package/src/lib/calc/flow.ts +315 -0
- package/src/lib/calc/focus.ts +15 -0
- package/src/lib/calc/meta.ts +17 -0
- package/src/lib/calc/pack.ts +77 -0
- package/src/lib/calc/ports.ts +43 -0
- package/src/lib/calc/resolve.ts +198 -0
- package/src/lib/calc/route/buckets.ts +49 -0
- package/src/lib/calc/route/geometry.ts +51 -0
- package/src/lib/calc/route/index.ts +3 -0
- package/src/lib/calc/route/polyline.ts +103 -0
- package/src/lib/calc/route/router.ts +394 -0
- package/src/lib/calc/scheme.ts +226 -0
- package/src/lib/calc/storage/entry.ts +97 -0
- package/src/lib/calc/storage/index.ts +20 -0
- package/src/lib/calc/storage/keys.ts +38 -0
- package/src/lib/calc/storage/store.ts +198 -0
- package/src/lib/calc/tree/index.ts +4 -0
- package/src/lib/calc/tree/materials.ts +29 -0
- package/src/lib/calc/tree/recipe.ts +104 -0
- package/src/lib/calc/tree/share.ts +135 -0
- package/src/lib/calc/worker/api.ts +25 -0
- package/src/lib/calc/worker/fetch.ts +66 -0
- package/src/lib/calc/worker/flush.ts +39 -0
- package/src/lib/calc/worker/index.ts +8 -0
- package/src/lib/calc/worker/protocol.ts +35 -0
- package/src/lib/calc/worker/resolve.ts +49 -0
- package/src/lib/calc/worker/state.ts +48 -0
- package/src/lib/card-model.ts +65 -0
- package/src/lib/client/recipe-categories.ts +14 -0
- package/src/lib/client/recipe.ts +85 -0
- package/src/lib/client-index/catalog.ts +91 -0
- package/src/lib/client-index/chrome.ts +180 -0
- package/src/lib/client-index/index.ts +20 -0
- package/src/lib/client-index/registry.ts +121 -0
- package/src/lib/client-index/rows.ts +260 -0
- package/src/lib/client-index/tooltips.ts +74 -0
- package/src/lib/config.ts +18 -0
- package/src/lib/data/category/dto.ts +25 -0
- package/src/lib/data/category/load.ts +47 -0
- package/src/lib/data/category/parse.ts +71 -0
- package/src/lib/data/dto.ts +152 -0
- package/src/lib/data/grid.ts +40 -0
- package/src/lib/data/parse/entry.ts +85 -0
- package/src/lib/data/parse/index.ts +3 -0
- package/src/lib/data/parse/layout.ts +138 -0
- package/src/lib/data/parse/slots.ts +85 -0
- package/src/lib/data/parse/widgets.ts +81 -0
- package/src/lib/data/scene.ts +103 -0
- package/src/lib/data/slot-io.ts +44 -0
- package/src/lib/dump.ts +6 -0
- package/src/lib/embed.ts +22 -0
- package/src/lib/favorite-hover.ts +13 -0
- package/src/lib/fluid-outputs.ts +70 -0
- package/src/lib/forge/actions.ts +59 -0
- package/src/lib/forge/data.ts +51 -0
- package/src/lib/forge/solve.ts +80 -0
- package/src/lib/group.ts +23 -0
- package/src/lib/index-store.ts +70 -0
- package/src/lib/links.ts +131 -0
- package/src/lib/lookup.ts +119 -0
- package/src/lib/panel-outline.ts +86 -0
- package/src/lib/panel.ts +105 -0
- package/src/lib/preview.ts +32 -0
- package/src/lib/recipe-slots.ts +8 -0
- package/src/lib/search.ts +139 -0
- package/src/lib/sidebar.ts +47 -0
- package/src/lib/used-items.ts +25 -0
- package/src/locales/en_us.json +172 -0
- package/src/locales/ru_ru.json +172 -0
- package/src/locales.ts +6 -0
- package/src/types/alloy.ts +20 -0
- package/src/types/calc-flow-props.ts +44 -0
- package/src/types/calc.ts +92 -0
- package/src/types/forge.ts +21 -0
- package/src/types/index.ts +136 -0
- package/styles/calc.css +308 -0
- package/styles/strip.css +7 -0
- package/styles.css +2 -0
- package/README.md +0 -3
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
|
+
import type { Recipe } from '@terrafirmagreg-team/recipe/lib/data/dto';
|
|
3
|
+
import type { RecipeCompactRow } from '@terrafirmagreg-team/recipe/types';
|
|
4
|
+
import type {
|
|
5
|
+
CalcRecipe,
|
|
6
|
+
TagResolution,
|
|
7
|
+
} from '@terrafirmagreg-team/recipe/types/calc';
|
|
8
|
+
|
|
9
|
+
export type ResolveInput = {
|
|
10
|
+
materials?: string[];
|
|
11
|
+
recipes?: string[];
|
|
12
|
+
tags?: string[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type CalcWorkerFlush = {
|
|
16
|
+
epoch: number;
|
|
17
|
+
out: [string, RecipeCompactRow[]][];
|
|
18
|
+
tag: [string, TagResolution][];
|
|
19
|
+
recipes: [string, CalcRecipe][];
|
|
20
|
+
bodies: [string, Recipe][];
|
|
21
|
+
dead: string[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type FlushHandler = (msg: CalcWorkerFlush) => void;
|
|
25
|
+
|
|
26
|
+
export type CalcWorkerApi = {
|
|
27
|
+
init(
|
|
28
|
+
dataset: DatasetRef,
|
|
29
|
+
catalog: unknown,
|
|
30
|
+
flushHandler: FlushHandler,
|
|
31
|
+
): Promise<void>;
|
|
32
|
+
resolve(input: ResolveInput): Promise<void>;
|
|
33
|
+
invalidate(): Promise<void>;
|
|
34
|
+
};
|
|
35
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { fetchWanted } from './fetch';
|
|
2
|
+
import { createFlush, postFlush } from './flush';
|
|
3
|
+
import type { ResolveInput } from './protocol';
|
|
4
|
+
import { state } from './state';
|
|
5
|
+
|
|
6
|
+
export async function runResolve(input: ResolveInput) {
|
|
7
|
+
const gen = state.resolveGen;
|
|
8
|
+
const pending = createFlush();
|
|
9
|
+
const wantedRecipes = new Set(
|
|
10
|
+
(input.recipes ?? []).filter(
|
|
11
|
+
(id) => id && !state.recipeMap.has(id) && !state.deadIds.has(id),
|
|
12
|
+
),
|
|
13
|
+
);
|
|
14
|
+
const wantedOut = new Set(
|
|
15
|
+
(input.materials ?? []).filter((id) => id && !state.outMap.has(id)),
|
|
16
|
+
);
|
|
17
|
+
const wantedTag = new Set(
|
|
18
|
+
(input.tags ?? []).filter((id) => id && !state.tagMap.has(id)),
|
|
19
|
+
);
|
|
20
|
+
for (let iter = 0; iter < 50; iter += 1) {
|
|
21
|
+
if (gen !== state.resolveGen) return;
|
|
22
|
+
if (!wantedRecipes.size && !wantedOut.size && !wantedTag.size) break;
|
|
23
|
+
const progressed = await fetchWanted(
|
|
24
|
+
pending,
|
|
25
|
+
wantedRecipes,
|
|
26
|
+
wantedOut,
|
|
27
|
+
wantedTag,
|
|
28
|
+
state.recipeMap,
|
|
29
|
+
);
|
|
30
|
+
if (gen !== state.resolveGen) return;
|
|
31
|
+
if (!progressed) break;
|
|
32
|
+
for (const id of input.tags ?? []) {
|
|
33
|
+
if (id && !state.tagMap.has(id)) wantedTag.add(id);
|
|
34
|
+
}
|
|
35
|
+
wantedRecipes.forEach((id) => {
|
|
36
|
+
if (state.recipeMap.has(id) || state.deadIds.has(id)) {
|
|
37
|
+
wantedRecipes.delete(id);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
wantedOut.forEach((id) => {
|
|
41
|
+
if (state.outMap.has(id)) wantedOut.delete(id);
|
|
42
|
+
});
|
|
43
|
+
wantedTag.forEach((id) => {
|
|
44
|
+
if (state.tagMap.has(id)) wantedTag.delete(id);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
postFlush(pending);
|
|
48
|
+
}
|
|
49
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
|
+
import type { Recipe } from '@terrafirmagreg-team/recipe/lib/data/dto';
|
|
3
|
+
import type { RecipeCompactIndex, RecipeCompactRow } from '@terrafirmagreg-team/recipe/types';
|
|
4
|
+
import type { CalcRecipe, TagResolution } from '@terrafirmagreg-team/recipe/types/calc';
|
|
5
|
+
import type { FlushHandler } from './protocol';
|
|
6
|
+
|
|
7
|
+
export type CalcWorkerState = {
|
|
8
|
+
dataset: DatasetRef;
|
|
9
|
+
catalog: RecipeCompactIndex | null;
|
|
10
|
+
onFlush: FlushHandler;
|
|
11
|
+
outMap: Map<string, RecipeCompactRow[]>;
|
|
12
|
+
tagMap: Map<string, TagResolution>;
|
|
13
|
+
recipeMap: Map<string, CalcRecipe>;
|
|
14
|
+
bodyMap: Map<string, Recipe>;
|
|
15
|
+
deadIds: Set<string>;
|
|
16
|
+
|
|
17
|
+
epoch: number;
|
|
18
|
+
resolveGen: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const state: CalcWorkerState = {
|
|
22
|
+
dataset: { id: '' },
|
|
23
|
+
catalog: null,
|
|
24
|
+
onFlush: () => {},
|
|
25
|
+
outMap: new Map(),
|
|
26
|
+
tagMap: new Map(),
|
|
27
|
+
recipeMap: new Map(),
|
|
28
|
+
bodyMap: new Map(),
|
|
29
|
+
deadIds: new Set(),
|
|
30
|
+
epoch: 0,
|
|
31
|
+
resolveGen: 0,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export function resetWorkerState(
|
|
35
|
+
nextDataset: DatasetRef,
|
|
36
|
+
nextCatalog: RecipeCompactIndex,
|
|
37
|
+
) {
|
|
38
|
+
state.dataset = nextDataset;
|
|
39
|
+
state.catalog = nextCatalog;
|
|
40
|
+
state.resolveGen += 1;
|
|
41
|
+
state.epoch += 1;
|
|
42
|
+
state.outMap.clear();
|
|
43
|
+
state.tagMap.clear();
|
|
44
|
+
state.recipeMap.clear();
|
|
45
|
+
state.bodyMap.clear();
|
|
46
|
+
state.deadIds.clear();
|
|
47
|
+
}
|
|
48
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RecipeSlotWidget,
|
|
3
|
+
RecipeTextWidget,
|
|
4
|
+
} from '@terrafirmagreg-team/recipe/lib/data/dto';
|
|
5
|
+
|
|
6
|
+
export type CardStripGeo = {
|
|
7
|
+
slots: RecipeSlotWidget[];
|
|
8
|
+
top: number;
|
|
9
|
+
bottom: number;
|
|
10
|
+
right: number;
|
|
11
|
+
overflow: boolean;
|
|
12
|
+
patchBottom: number;
|
|
13
|
+
barTop: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function cardStripGeo(
|
|
17
|
+
slots: RecipeSlotWidget[],
|
|
18
|
+
contentGuiW: number,
|
|
19
|
+
innerBottomGui: number,
|
|
20
|
+
margin: number,
|
|
21
|
+
): CardStripGeo {
|
|
22
|
+
const stripSlots = slots.filter((slot) => slot.role === 'renderOnly');
|
|
23
|
+
const top = stripSlots.length
|
|
24
|
+
? Math.min(...stripSlots.map((slot) => slot.y))
|
|
25
|
+
: 0;
|
|
26
|
+
const bottom = stripSlots.length
|
|
27
|
+
? Math.max(...stripSlots.map((slot) => slot.y + slot.h))
|
|
28
|
+
: 0;
|
|
29
|
+
const right = stripSlots.length
|
|
30
|
+
? Math.max(...stripSlots.map((slot) => slot.x + slot.w))
|
|
31
|
+
: 0;
|
|
32
|
+
const overflow = right > contentGuiW + 1;
|
|
33
|
+
const patchBottom = Math.min(
|
|
34
|
+
bottom + margin + 16,
|
|
35
|
+
Math.max(innerBottomGui - 1, 0),
|
|
36
|
+
);
|
|
37
|
+
const barTop = Math.min(bottom + margin + 1, Math.max(innerBottomGui - 5, 0));
|
|
38
|
+
return { slots: stripSlots, top, bottom, right, overflow, patchBottom, barTop };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function countCoveredText(
|
|
42
|
+
widget: RecipeTextWidget,
|
|
43
|
+
stripSlots: RecipeSlotWidget[],
|
|
44
|
+
): boolean {
|
|
45
|
+
return stripSlots.some(
|
|
46
|
+
(slot) =>
|
|
47
|
+
slot.count !== undefined &&
|
|
48
|
+
String(slot.count) === widget.text &&
|
|
49
|
+
widget.x + widget.w > slot.x &&
|
|
50
|
+
widget.x < slot.x + slot.w,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function textInStrip(
|
|
55
|
+
widget: RecipeTextWidget,
|
|
56
|
+
geo: CardStripGeo,
|
|
57
|
+
): boolean {
|
|
58
|
+
return (
|
|
59
|
+
geo.overflow &&
|
|
60
|
+
widget.y + widget.h > geo.top &&
|
|
61
|
+
widget.y < geo.bottom &&
|
|
62
|
+
widget.x < geo.right
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
|
+
import { loadDatasetRecipeCategories } from '@terrafirmagreg-team/recipe/lib/data/category/load';
|
|
3
|
+
import type { DumpRecipeLayout } from '@terrafirmagreg-team/recipe/lib/data/category/dto';
|
|
4
|
+
|
|
5
|
+
export function clientRecipeLayout(
|
|
6
|
+
dataset: DatasetRef,
|
|
7
|
+
categoryId: string,
|
|
8
|
+
): Promise<DumpRecipeLayout | undefined> {
|
|
9
|
+
return loadDatasetRecipeCategories(dataset).then(
|
|
10
|
+
(categories) =>
|
|
11
|
+
categories.find((cat) => cat.id === categoryId)?.layout,
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
|
+
import { asRecord, asString } from '@terrafirmagreg-team/dump/lib/dump/contract/read';
|
|
3
|
+
import { parseRecipe } from '@terrafirmagreg-team/recipe/lib/data/parse';
|
|
4
|
+
import type { Recipe } from '@terrafirmagreg-team/recipe/lib/data/dto';
|
|
5
|
+
import { dumpRelPublicUrl } from '@terrafirmagreg-team/dump/lib/dump/paths';
|
|
6
|
+
import {
|
|
7
|
+
clientDumpJson,
|
|
8
|
+
loadClientManifest,
|
|
9
|
+
} from '@terrafirmagreg-team/dump/lib/dump/client/dump';
|
|
10
|
+
import { clientRecipeLayout } from '@terrafirmagreg-team/recipe/lib/client/recipe-categories';
|
|
11
|
+
import { createDatasetCache } from '@terrafirmagreg-team/dump/lib/dump/reader';
|
|
12
|
+
|
|
13
|
+
const recipeIndex = createDatasetCache(async (dataset: DatasetRef, ns: string) => {
|
|
14
|
+
const manifest = await loadClientManifest(dataset);
|
|
15
|
+
const listed = manifest?.namespaces[ns];
|
|
16
|
+
if (!listed || !listed.registry.includes('recipes')) return undefined;
|
|
17
|
+
return asRecord(
|
|
18
|
+
await clientDumpJson(dataset, 'registry/recipes.json', 'high'),
|
|
19
|
+
) as Record<string, string | string[]> | undefined;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export async function clientRecipeFileRels(
|
|
23
|
+
dataset: DatasetRef,
|
|
24
|
+
recipeId: string,
|
|
25
|
+
): Promise<string[]> {
|
|
26
|
+
const ns = recipeId.split(':', 1)[0];
|
|
27
|
+
if (!ns) return [];
|
|
28
|
+
const index = await recipeIndex(dataset, ns);
|
|
29
|
+
const value = index?.[recipeId];
|
|
30
|
+
const categories = Array.isArray(value)
|
|
31
|
+
? value
|
|
32
|
+
: typeof value === 'string'
|
|
33
|
+
? [value]
|
|
34
|
+
: [];
|
|
35
|
+
return categories
|
|
36
|
+
.filter(
|
|
37
|
+
(category): category is string =>
|
|
38
|
+
typeof category === 'string' && !!category,
|
|
39
|
+
)
|
|
40
|
+
.map((category) =>
|
|
41
|
+
category.includes(':')
|
|
42
|
+
? category.slice(category.indexOf(':') + 1)
|
|
43
|
+
: category,
|
|
44
|
+
)
|
|
45
|
+
.filter((path) => !!path)
|
|
46
|
+
.map((path) => `data/${ns}/recipes/${path}.json`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function clientRecipeFileRel(
|
|
50
|
+
dataset: DatasetRef,
|
|
51
|
+
recipeId: string,
|
|
52
|
+
): Promise<string | undefined> {
|
|
53
|
+
return (await clientRecipeFileRels(dataset, recipeId))[0];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const loadClientRecipe: (
|
|
57
|
+
dataset: DatasetRef,
|
|
58
|
+
id: string,
|
|
59
|
+
bucket?: string,
|
|
60
|
+
) => Promise<Recipe | undefined> = createDatasetCache(
|
|
61
|
+
async (
|
|
62
|
+
dataset: DatasetRef,
|
|
63
|
+
id: string,
|
|
64
|
+
bucket?: string,
|
|
65
|
+
): Promise<Recipe | undefined> => {
|
|
66
|
+
const rel = bucket ?? (await clientRecipeFileRel(dataset, id));
|
|
67
|
+
if (!rel) return undefined;
|
|
68
|
+
const doc = asRecord(await clientDumpJson(dataset, rel, 'high'));
|
|
69
|
+
const raw = doc?.[id];
|
|
70
|
+
if (raw === undefined) return undefined;
|
|
71
|
+
const ns = rel.split('/')[1] ?? '';
|
|
72
|
+
const category = asString(asRecord(raw)?.category);
|
|
73
|
+
const layout = category
|
|
74
|
+
? await clientRecipeLayout(dataset, category)
|
|
75
|
+
: undefined;
|
|
76
|
+
return parseRecipe(raw, {
|
|
77
|
+
modId: ns,
|
|
78
|
+
bodyUrl: dumpRelPublicUrl(dataset, rel) ?? `/${dataset.id}`,
|
|
79
|
+
id,
|
|
80
|
+
...(layout ? { layout } : {}),
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
(dataset, id, bucket) => JSON.stringify([dataset, id, bucket ?? '']),
|
|
84
|
+
);
|
|
85
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
|
+
import type { RecipeChrome, RecipeCompactIndex } from '@terrafirmagreg-team/recipe/types';
|
|
3
|
+
import { loadDatasetRecipeCategories } from '@terrafirmagreg-team/recipe/lib/data/category/load';
|
|
4
|
+
import { stackDisplayName } from '@terrafirmagreg-team/game/lib/entry/display';
|
|
5
|
+
import { createDatasetCache } from '@terrafirmagreg-team/dump/lib/dump/reader';
|
|
6
|
+
import { recipeTypeRows } from './rows';
|
|
7
|
+
|
|
8
|
+
const emptyRecipeIndex = (): RecipeCompactIndex => ({
|
|
9
|
+
types: [],
|
|
10
|
+
items: [],
|
|
11
|
+
names: {},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
async function loadRecipeStructureFromDump(
|
|
15
|
+
dataset: DatasetRef,
|
|
16
|
+
chrome: RecipeChrome,
|
|
17
|
+
): Promise<RecipeCompactIndex> {
|
|
18
|
+
const rows = await recipeTypeRows(dataset);
|
|
19
|
+
const typeCounts: Record<string, number> = {};
|
|
20
|
+
for (const row of rows) {
|
|
21
|
+
if (row.id) typeCounts[row.id] = row.count ?? 0;
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
types: rows.map((row) => row.id),
|
|
25
|
+
typeCounts,
|
|
26
|
+
items: chrome.items,
|
|
27
|
+
names: chrome.names,
|
|
28
|
+
...(chrome.fluids?.length ? { fluids: chrome.fluids } : {}),
|
|
29
|
+
...(chrome.usedIds?.length ? { usedIds: chrome.usedIds } : {}),
|
|
30
|
+
...(chrome.tabs?.length ? { tabs: chrome.tabs } : {}),
|
|
31
|
+
...(chrome.tabItems ? { tabItems: chrome.tabItems } : {}),
|
|
32
|
+
...(chrome.categories?.length ? { categories: chrome.categories } : {}),
|
|
33
|
+
...(chrome.mods ? { mods: chrome.mods } : {}),
|
|
34
|
+
...(chrome.groups?.length ? { groups: chrome.groups } : {}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const indexCache = createDatasetCache(
|
|
39
|
+
async (
|
|
40
|
+
dataset: DatasetRef,
|
|
41
|
+
chrome?: RecipeChrome,
|
|
42
|
+
): Promise<RecipeCompactIndex> => {
|
|
43
|
+
if (!chrome) return emptyRecipeIndex();
|
|
44
|
+
return loadRecipeStructureFromDump(dataset, chrome);
|
|
45
|
+
},
|
|
46
|
+
(dataset, chrome) =>
|
|
47
|
+
`${dataset.id}/${dataset.version ?? ''} ${chrome ? (Object.keys(chrome.names)[0] ?? '') : ''}`,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const skeletonCache = createDatasetCache(
|
|
51
|
+
async (dataset: DatasetRef): Promise<RecipeCompactIndex> =>
|
|
52
|
+
loadDatasetRecipeCategories(dataset).then((categories) =>
|
|
53
|
+
loadRecipeStructureFromDump(dataset, {
|
|
54
|
+
items: [],
|
|
55
|
+
names: {},
|
|
56
|
+
...(categories.length ? { categories } : {}),
|
|
57
|
+
}),
|
|
58
|
+
),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
export function loadRecipeCompactIndex(
|
|
62
|
+
dataset: DatasetRef,
|
|
63
|
+
chrome?: RecipeChrome,
|
|
64
|
+
): Promise<RecipeCompactIndex> {
|
|
65
|
+
return indexCache(dataset, chrome);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function loadRecipeSkeletonIndex(
|
|
69
|
+
dataset: DatasetRef,
|
|
70
|
+
): Promise<RecipeCompactIndex> {
|
|
71
|
+
return skeletonCache(dataset);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function nonEmptyCategoryIds(
|
|
75
|
+
index: RecipeCompactIndex | null,
|
|
76
|
+
): string[] {
|
|
77
|
+
const counts = index?.typeCounts;
|
|
78
|
+
if (!counts) return [];
|
|
79
|
+
return (index?.categories ?? [])
|
|
80
|
+
.map((row) => row.id)
|
|
81
|
+
.filter((id) => (counts[id] ?? 0) > 0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function recipeItemName(
|
|
85
|
+
names: Record<string, Record<string, string>>,
|
|
86
|
+
lang: string,
|
|
87
|
+
id: string,
|
|
88
|
+
): string {
|
|
89
|
+
return stackDisplayName(names[lang] ?? names.en_us, id);
|
|
90
|
+
}
|
|
91
|
+
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
|
+
import type {
|
|
3
|
+
RecipeCategory,
|
|
4
|
+
RecipeChrome,
|
|
5
|
+
RecipeStackGroup,
|
|
6
|
+
} from '@terrafirmagreg-team/recipe/types';
|
|
7
|
+
import { clientDumpLang, loadClientManifest } from '@terrafirmagreg-team/dump/lib/dump/client/dump';
|
|
8
|
+
import {
|
|
9
|
+
creativeTabName,
|
|
10
|
+
dumpTextString,
|
|
11
|
+
} from '@terrafirmagreg-team/dump/lib/dump/assets/lang/names';
|
|
12
|
+
import { stackDisplayName } from '@terrafirmagreg-team/game/lib/entry/display';
|
|
13
|
+
import {
|
|
14
|
+
loadClientRegistry,
|
|
15
|
+
loadClientRegistryOrder,
|
|
16
|
+
resolveClientStackDisplay,
|
|
17
|
+
} from '@terrafirmagreg-team/dump/lib/dump/client/registry';
|
|
18
|
+
import { loadClientTagMembers } from '@terrafirmagreg-team/dump/lib/dump/client/tags';
|
|
19
|
+
import { loadClientTabs } from '@terrafirmagreg-team/dump/lib/dump/client/tabs';
|
|
20
|
+
import { loadDatasetRecipeCategories } from '@terrafirmagreg-team/recipe/lib/data/category/load';
|
|
21
|
+
import { loadClientRegistryGroups } from '@terrafirmagreg-team/dump/lib/dump/client/groups';
|
|
22
|
+
import { loadClientModNames } from '@terrafirmagreg-team/dump/lib/dump/client/mods';
|
|
23
|
+
import { loadClientModAtlases } from '@terrafirmagreg-team/dump/lib/dump/client/atlases';
|
|
24
|
+
import { lookupSpriteInBundle } from '@terrafirmagreg-team/dump/lib/dump/assets/atlas/lookup';
|
|
25
|
+
import { stackIdBase, stackIdFromRef } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
|
|
26
|
+
import type { AtlasBundle } from '@terrafirmagreg-team/dump/lib/dump/assets/atlas/dto';
|
|
27
|
+
import { createDatasetCache } from '@terrafirmagreg-team/dump/lib/dump/reader';
|
|
28
|
+
|
|
29
|
+
async function clientEntityNames(
|
|
30
|
+
dataset: DatasetRef,
|
|
31
|
+
lang: string,
|
|
32
|
+
ids: Iterable<string>,
|
|
33
|
+
): Promise<Record<string, string>> {
|
|
34
|
+
const [map, items, fluids] = await Promise.all([
|
|
35
|
+
clientDumpLang(dataset, lang),
|
|
36
|
+
loadClientRegistry(dataset, 'items'),
|
|
37
|
+
loadClientRegistry(dataset, 'fluids'),
|
|
38
|
+
]);
|
|
39
|
+
const all = [...new Set([...items.keys(), ...fluids.keys(), ...ids])];
|
|
40
|
+
const info = new Map<string, { baseId: string; langKey?: string }>();
|
|
41
|
+
const mods = new Set<string>();
|
|
42
|
+
for (const id of all) {
|
|
43
|
+
const baseId = stackIdBase(id);
|
|
44
|
+
const langKey = resolveClientStackDisplay(id, items, fluids)?.name;
|
|
45
|
+
info.set(id, { baseId, ...(langKey ? { langKey } : {}) });
|
|
46
|
+
if (!langKey && baseId.includes(':') && !baseId.startsWith('#')) {
|
|
47
|
+
const mod = baseId.split(':', 2)[0];
|
|
48
|
+
if (mod) mods.add(mod);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const atlases = new Map<string, AtlasBundle>();
|
|
52
|
+
await Promise.all(
|
|
53
|
+
[...mods].map(async (mod) => {
|
|
54
|
+
atlases.set(mod, await loadClientModAtlases(dataset, mod));
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
const names: Record<string, string> = { ...map };
|
|
58
|
+
let processed = 0;
|
|
59
|
+
for (const [id, entry] of info) {
|
|
60
|
+
if (++processed % 2000 === 0) {
|
|
61
|
+
await new Promise((resolve) => setTimeout(resolve));
|
|
62
|
+
}
|
|
63
|
+
const langKey =
|
|
64
|
+
entry.langKey ??
|
|
65
|
+
lookupSpriteInBundle(
|
|
66
|
+
atlases.get(entry.baseId.split(':', 2)[0] ?? ''),
|
|
67
|
+
entry.baseId,
|
|
68
|
+
)?.sprite.langKey;
|
|
69
|
+
const kind = entry.baseId.startsWith('#')
|
|
70
|
+
? undefined
|
|
71
|
+
: fluids.has(entry.baseId)
|
|
72
|
+
? 'fluid'
|
|
73
|
+
: 'item';
|
|
74
|
+
names[id] = stackDisplayName(map, id, kind, langKey);
|
|
75
|
+
}
|
|
76
|
+
return names;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function clientNamedCategories(
|
|
80
|
+
dataset: DatasetRef,
|
|
81
|
+
lang: string,
|
|
82
|
+
types: string[],
|
|
83
|
+
categories: RecipeCategory[],
|
|
84
|
+
): Promise<Record<string, string>> {
|
|
85
|
+
const map = await clientDumpLang(dataset, lang);
|
|
86
|
+
const byId = new Map(categories.map((row) => [row.id, row]));
|
|
87
|
+
const names: Record<string, string> = {};
|
|
88
|
+
for (const id of types) {
|
|
89
|
+
names[id] = creativeTabName(map, byId.get(id)?.display?.name ?? id);
|
|
90
|
+
}
|
|
91
|
+
return names;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function clientUsedIds(dataset: DatasetRef): Promise<string[]> {
|
|
95
|
+
const manifest = await loadClientManifest(dataset);
|
|
96
|
+
return manifest?.indexes.used ?? [];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function clientRecipeGroups(
|
|
100
|
+
dataset: DatasetRef,
|
|
101
|
+
lang: string,
|
|
102
|
+
): Promise<RecipeStackGroup[]> {
|
|
103
|
+
const rows = await loadClientRegistryGroups(dataset);
|
|
104
|
+
if (!rows.length) return [];
|
|
105
|
+
const map = await clientDumpLang(dataset, lang);
|
|
106
|
+
return rows.map((row) => ({
|
|
107
|
+
id: row.id,
|
|
108
|
+
...(row.name ? { name: dumpTextString(map, row.name) } : {}),
|
|
109
|
+
members: row.members,
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const loadChrome = createDatasetCache(
|
|
114
|
+
async (dataset: DatasetRef, lang: string): Promise<RecipeChrome> => {
|
|
115
|
+
const [items, fluids, creative, categories, usedIds, itemTags, blockTags] =
|
|
116
|
+
await Promise.all([
|
|
117
|
+
loadClientRegistryOrder(dataset, 'item'),
|
|
118
|
+
loadClientRegistryOrder(dataset, 'fluid'),
|
|
119
|
+
loadClientTabs(dataset),
|
|
120
|
+
loadDatasetRecipeCategories(dataset),
|
|
121
|
+
clientUsedIds(dataset),
|
|
122
|
+
loadClientTagMembers(dataset, 'item'),
|
|
123
|
+
loadClientTagMembers(dataset, 'block'),
|
|
124
|
+
]);
|
|
125
|
+
const tabs = creative.tabs;
|
|
126
|
+
const tabItems = Object.keys(creative.members).length
|
|
127
|
+
? creative.members
|
|
128
|
+
: undefined;
|
|
129
|
+
const types = categories.map((row) => row.id);
|
|
130
|
+
const tagIds = new Set([
|
|
131
|
+
...Object.keys(itemTags ?? {}),
|
|
132
|
+
...Object.keys(blockTags ?? {}),
|
|
133
|
+
]);
|
|
134
|
+
const nameIds = [
|
|
135
|
+
...items,
|
|
136
|
+
...fluids,
|
|
137
|
+
...tagIds,
|
|
138
|
+
...(usedIds ?? []),
|
|
139
|
+
...Object.values(tabItems ?? {}).flat(),
|
|
140
|
+
...tabs.flatMap((tab) =>
|
|
141
|
+
tab.icon ? [stackIdFromRef(tab.icon)] : [],
|
|
142
|
+
),
|
|
143
|
+
];
|
|
144
|
+
const [groups, mods, entityNames, categoryNames, tabNames] =
|
|
145
|
+
await Promise.all([
|
|
146
|
+
clientRecipeGroups(dataset, lang),
|
|
147
|
+
loadClientModNames(dataset, lang),
|
|
148
|
+
clientEntityNames(dataset, lang, nameIds),
|
|
149
|
+
clientNamedCategories(dataset, lang, types, categories),
|
|
150
|
+
clientDumpLang(dataset, lang).then((map) => {
|
|
151
|
+
const names: Record<string, string> = {};
|
|
152
|
+
for (const tab of tabs) {
|
|
153
|
+
names[tab.id] = creativeTabName(map, tab.display.name ?? tab.id);
|
|
154
|
+
}
|
|
155
|
+
return names;
|
|
156
|
+
}),
|
|
157
|
+
]);
|
|
158
|
+
return {
|
|
159
|
+
items,
|
|
160
|
+
...(usedIds.length ? { usedIds } : {}),
|
|
161
|
+
...(fluids.length ? { fluids } : {}),
|
|
162
|
+
...(tabs.length ? { tabs } : {}),
|
|
163
|
+
...(tabItems ? { tabItems } : {}),
|
|
164
|
+
...(categories.length ? { categories } : {}),
|
|
165
|
+
...(groups.length ? { groups } : {}),
|
|
166
|
+
mods,
|
|
167
|
+
names: {
|
|
168
|
+
[lang]: { ...tabNames, ...categoryNames, ...entityNames },
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
export function loadRecipeChromeClient(
|
|
175
|
+
dataset: DatasetRef,
|
|
176
|
+
lang: string,
|
|
177
|
+
): Promise<RecipeChrome> {
|
|
178
|
+
return loadChrome(dataset, lang);
|
|
179
|
+
}
|
|
180
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export {
|
|
2
|
+
loadAllTagMembers,
|
|
3
|
+
loadRegistryEntrySlice,
|
|
4
|
+
loadTagSlice,
|
|
5
|
+
} from './registry';
|
|
6
|
+
export { loadRecipeChromeClient } from './chrome';
|
|
7
|
+
export {
|
|
8
|
+
loadCategoryRecipes,
|
|
9
|
+
loadTypeRows,
|
|
10
|
+
resolveRecipeRows,
|
|
11
|
+
TAG_RECIPE_TYPE,
|
|
12
|
+
} from './rows';
|
|
13
|
+
export { loadItemTooltips } from './tooltips';
|
|
14
|
+
export {
|
|
15
|
+
loadRecipeCompactIndex,
|
|
16
|
+
loadRecipeSkeletonIndex,
|
|
17
|
+
nonEmptyCategoryIds,
|
|
18
|
+
recipeItemName,
|
|
19
|
+
} from './catalog';
|
|
20
|
+
|