@@ -2,7 +2,7 @@ import { Flame, Timer } from "lucide-react";
22import type { ReactNode } from "react" ;
33import { ItemHover } from "../../lib/recipe-card" ;
44import { fmtSpoilTime , Icon } from "../../lib/icons" ;
5- import { quantityLabel , rateLabel } from "./format.ts" ;
5+ import { num , quantityLabel , rateLabel } from "./format.ts" ;
66
77/** A block item's role under the current solve — drives the chip colour so it's
88 * obvious which flows are linked internally vs. need a recipe vs. spill out. */
@@ -27,6 +27,10 @@ export function ItemChip({
2727 display,
2828 rate,
2929 total,
30+ probability,
31+ amountExpected,
32+ amountMin,
33+ amountMax,
3034 rateMin,
3135 rateMax,
3236 temp,
@@ -48,6 +52,11 @@ export function ItemChip({
4852 /** Finite campaign amount. Replaces the throughput rate as the chip's
4953 * primary value; the caller may show the derived average separately. */
5054 total ?: number ;
55+ /** Per-craft chance and yield context for a recipe product. */
56+ probability ?: number ;
57+ amountExpected ?: number ;
58+ amountMin ?: number ;
59+ amountMax ?: number ;
5160 /** Variable energy production range; `rate` is the planner average. */
5261 rateMin ?: number ;
5362 rateMax ?: number ;
@@ -96,11 +105,28 @@ export function ItemChip({
96105 rate == null ? "" : variableRate ? displayedRate : rateLabel ( name , rate , { perSec : true } ) ;
97106 const displayedTotal = total == null ? "" : `${ quantityLabel ( name , total ) } total` ;
98107 const totalRate = total != null && rate != null ? rateLabel ( name , rate , { perSec : true } ) : "" ;
108+ const chance = probability != null && probability < 1 ? `${ num ( probability * 100 ) } %` : "" ;
109+ const chanceContext = chance ? (
110+ < div >
111+ < div className = "text-warning" > { chance } chance per craft</ div >
112+ { amountExpected != null && (
113+ < div className = "text-muted-foreground" >
114+ { amountMin != null && amountMax != null
115+ ? amountMin === amountMax
116+ ? `${ num ( amountMin ) } on success · `
117+ : `${ num ( amountMin ) } –${ num ( amountMax ) } on success · `
118+ : "" }
119+ { num ( amountExpected ) } expected per craft
120+ </ div >
121+ ) }
122+ </ div >
123+ ) : undefined ;
99124 return (
100125 < span className = { `inline-flex items-center gap-1 ${ cls } ` } >
101126 < ItemHover
102127 name = { name }
103128 kind = { kind as "item" | "fluid" }
129+ extraText = { chanceContext }
104130 className = "inline-flex"
105131 // the rich card (cost, produced-by / used-in) replaces the old native title;
106132 // role is the chip colour, rate is shown on the chip, actions are on right-click
@@ -112,7 +138,7 @@ export function ItemChip({
112138 e . preventDefault ( ) ;
113139 onContext ( e ) ;
114140 } }
115- aria-label = { `${ display ?? name } ${ displayedTotal ? ` ${ displayedTotal } ${ totalRate ? ` · ${ totalRate } average` : "" } ` : accessibleRate ? ` ${ accessibleRate } ` : "" } ${ spoilTime ? ` · spoils in ${ spoilTime } ` : "" } ${ incidental ? " · includes estimated incidental spoilage" : "" } ${ indicatorLabel ? ` · ${ indicatorLabel } ` : "" } · ${ why } ` }
141+ aria-label = { `${ display ?? name } ${ displayedTotal ? ` ${ displayedTotal } ${ totalRate ? ` · ${ totalRate } average` : "" } ` : accessibleRate ? ` ${ accessibleRate } ` : "" } ${ chance ? ` · ${ chance } chance` : "" } ${ spoilTime ? ` · spoils in ${ spoilTime } ` : "" } ${ incidental ? " · includes estimated incidental spoilage" : "" } ${ indicatorLabel ? ` · ${ indicatorLabel } ` : "" } · ${ why } ` }
116142 className = "flex items-center gap-1 px-1.5 py-1 text-sm hover:brightness-95"
117143 >
118144 < span className = "relative flex" >
@@ -137,7 +163,16 @@ export function ItemChip({
137163 ) }
138164 </ span >
139165 ) : rate != null ? (
140- < span data-rate-range = { variableRate ? "variable" : undefined } > { displayedRate } </ span >
166+ chance ? (
167+ < span className = "flex flex-col items-start leading-tight tabular-nums" >
168+ < span data-rate-range = { variableRate ? "variable" : undefined } > { displayedRate } </ span >
169+ < span data-product-probability className = "text-xs text-warning" >
170+ { chance }
171+ </ span >
172+ </ span >
173+ ) : (
174+ < span data-rate-range = { variableRate ? "variable" : undefined } > { displayedRate } </ span >
175+ )
141176 ) : null }
142177 { incidental && (
143178 < span
0 commit comments