@@ -58,6 +58,7 @@ import {
5858 Gamepad2 ,
5959 Grid2x2 ,
6060 GripVertical ,
61+ Hammer ,
6162 Lock ,
6263 MapPin ,
6364 Pencil ,
@@ -71,6 +72,13 @@ import {
7172import { ItemHover , RecipeHover , TechLine } from "../lib/recipe-card" ;
7273import { Badge } from "#/components/ui/badge.tsx" ;
7374import { Card , CardContent , CardHeader , CardTitle } from "#/components/ui/card.tsx" ;
75+ import {
76+ Sheet ,
77+ SheetContent ,
78+ SheetHeader ,
79+ SheetTitle ,
80+ SheetTrigger ,
81+ } from "#/components/ui/sheet.tsx" ;
7482import { HelpButton } from "#/components/help-drawer.tsx" ;
7583import { Input } from "#/components/ui/input.tsx" ;
7684
@@ -1021,6 +1029,61 @@ function Block({ blockId }: { blockId: number }) {
10211029 >
10221030 < Gamepad2 className = { `size-4 ${ showInGame . isPending ? "animate-pulse" : "" } ` } />
10231031 </ button >
1032+ { res ?. buildCost && res . buildCost . buildings . length > 0 && (
1033+ < Sheet >
1034+ < SheetTrigger asChild >
1035+ < button
1036+ title = "Building summary — the buildings + one-time materials to construct this block"
1037+ className = "flex size-7 items-center justify-center rounded border border-border text-muted-foreground hover:bg-muted hover:text-foreground"
1038+ >
1039+ < Hammer className = "size-4" />
1040+ </ button >
1041+ </ SheetTrigger >
1042+ < SheetContent side = "right" className = "w-96 max-w-[92vw] font-mono" >
1043+ < SheetHeader >
1044+ < SheetTitle > Building summary</ SheetTitle >
1045+ </ SheetHeader >
1046+ < div className = "min-h-0 flex-1 space-y-4 overflow-auto p-3" >
1047+ < p className = "text-xs text-muted-foreground" >
1048+ The buildings to construct this block, and the one-time materials to build them —
1049+ a shopping list, separate from the per-second flows.
1050+ </ p >
1051+ < div >
1052+ < div className = "mb-1.5 text-xs font-semibold text-muted-foreground" >
1053+ Buildings
1054+ </ div >
1055+ < div className = "flex flex-wrap gap-2" >
1056+ { res . buildCost . buildings . map ( ( b ) => (
1057+ < span key = { b . name } className = { cellChip } title = { b . display } >
1058+ < Icon kind = "item" name = { b . name } size = "sm" />
1059+ < span className = "tabular-nums" > ×{ b . count } </ span >
1060+ </ span >
1061+ ) ) }
1062+ </ div >
1063+ </ div >
1064+ < div >
1065+ < div className = "mb-1.5 text-xs font-semibold text-muted-foreground" >
1066+ Materials to build them
1067+ </ div >
1068+ { res . buildCost . materials . length === 0 ? (
1069+ < span className = "text-sm text-muted-foreground" >
1070+ — (no build recipe found for these buildings)
1071+ </ span >
1072+ ) : (
1073+ < div className = "flex flex-wrap gap-2" >
1074+ { res . buildCost . materials . map ( ( m ) => (
1075+ < span key = { m . name } className = { cellChip } title = { m . display } >
1076+ < Icon kind = { m . kind as "item" | "fluid" } name = { m . name } size = "sm" />
1077+ < span className = "tabular-nums" > { fmtAmt ( m . amount ) } </ span >
1078+ </ span >
1079+ ) ) }
1080+ </ div >
1081+ ) }
1082+ </ div >
1083+ </ div >
1084+ </ SheetContent >
1085+ </ Sheet >
1086+ ) }
10241087 { showInGame . data && ! showInGame . data . sent && (
10251088 < span className = "text-xs text-amber-300" > game not connected</ span >
10261089 ) }
@@ -1557,60 +1620,6 @@ function Block({ blockId }: { blockId: number }) {
15571620 </ Card >
15581621 </ div >
15591622
1560- { /* One-time build cost: the materials to construct this block's buildings (#38)
1561- — why e.g. steel is needed even when no recipe in the chain consumes it. */ }
1562- { res ?. buildCost && res . buildCost . buildings . length > 0 && (
1563- < Card className = "mb-4" >
1564- < CardHeader className = "justify-between" >
1565- < CardTitle > Build cost (one-time)</ CardTitle >
1566- < HelpButton title = "Build cost" >
1567- < p >
1568- The materials needed to{ " " }
1569- < span className = "text-foreground" > construct this block's buildings</ span > — the
1570- "build the stuff to build the stuff" requirement. It's a one-time
1571- cost (not a per-second rate), and it's why something like steel is required
1572- even when no recipe in the chain consumes it.
1573- </ p >
1574- < p >
1575- Direct ingredients of each building's own recipe, summed over the building
1576- counts (rounded up to whole machines). Producing those materials' own sub-chain
1577- is the factory ledger's job.
1578- </ p >
1579- </ HelpButton >
1580- </ CardHeader >
1581- < CardContent className = "space-y-3" >
1582- < div >
1583- < div className = "mb-1 text-xs text-muted-foreground" > Buildings</ div >
1584- < div className = "flex flex-wrap gap-2" >
1585- { res . buildCost . buildings . map ( ( b ) => (
1586- < span key = { b . name } className = { cellChip } title = { b . display } >
1587- < Icon kind = "item" name = { b . name } size = "sm" />
1588- < span className = "tabular-nums" > ×{ b . count } </ span >
1589- </ span >
1590- ) ) }
1591- </ div >
1592- </ div >
1593- < div >
1594- < div className = "mb-1 text-xs text-muted-foreground" > Materials to build them</ div >
1595- { res . buildCost . materials . length === 0 ? (
1596- < span className = "text-sm text-muted-foreground" >
1597- — (no build recipe found for these buildings)
1598- </ span >
1599- ) : (
1600- < div className = "flex flex-wrap gap-2" >
1601- { res . buildCost . materials . map ( ( m ) => (
1602- < span key = { m . name } className = { cellChip } title = { m . display } >
1603- < Icon kind = { m . kind as "item" | "fluid" } name = { m . name } size = "sm" />
1604- < span className = "tabular-nums" > { fmtAmt ( m . amount ) } </ span >
1605- </ span >
1606- ) ) }
1607- </ div >
1608- ) }
1609- </ div >
1610- </ CardContent >
1611- </ Card >
1612- ) }
1613-
16141623 < BlockTasks blockId = { blockId } />
16151624
16161625 { /* Recipe grid: each row's I/O at the solved rate. Click any item to add a
0 commit comments