diff --git a/frontend/js/ui/inspectorView.js b/frontend/js/ui/inspectorView.js
index c70396e..3a2b6df 100644
--- a/frontend/js/ui/inspectorView.js
+++ b/frontend/js/ui/inspectorView.js
@@ -1,8 +1,6 @@
-// Production - The Idle Game: Desktop Inspector & Supply Chain Analytics Panel
-// Fine-grained DOM updates without innerHTML teardowns
-
import { PRODUCTS, SECTORS, getProduct } from '../data/products.js';
import { fmtCurrency, fmtNum } from '../core/formatters.js';
+import { computeProductNetFlow } from './production/ratesCalculator.js';
let currentMountedProductId = null;
@@ -18,6 +16,7 @@ export function mountInspector(container, state, onAction) {
const totalLiquidationValue = (pState.stock || 0) * product.basePrice;
const halfLiquidationValue = totalLiquidationValue * 0.5;
+ const netFlow = computeProductNetFlow(product, state);
let html = `
@@ -43,12 +42,14 @@ export function mountInspector(container, state, onAction) {
${fmtCurrency(product.basePrice)}
- Production Lines:
- ${pState.lines} active
+ Net Supply Flow:
+
+ ${netFlow.netRate > 0.01 ? '+' : ''}${fmtNum(netFlow.netRate, 1)}/s
+
Logistics Fleets:
- ${pState.logistics || 0} active
+ ${pState.logistics || 0} active (${fmtNum((pState.logistics || 0) * 0.75, 2)}/s)
@@ -181,6 +182,20 @@ export function updateInspector(container, state) {
const btnAll = document.getElementById('insp-btn-all');
if (btnAll) btnAll.disabled = pState.stock < 1;
+ // Update Net Flow rate
+ const netFlow = computeProductNetFlow(product, state);
+ const elNetRate = document.getElementById('insp-net-rate');
+ if (elNetRate) {
+ const sign = netFlow.netRate > 0.01 ? '+' : '';
+ const netText = `${sign}${fmtNum(netFlow.netRate, 1)}/s`;
+ if (elNetRate.textContent !== netText) {
+ elNetRate.textContent = netText;
+ }
+ const isDeficit = netFlow.netRate < -0.05;
+ const isSurplus = netFlow.netRate > 0.05;
+ elNetRate.className = `metric-val font-mono ${isDeficit ? 'text-warning' : (isSurplus ? 'text-profit' : '')}`;
+ }
+
// Update input values
if (product.inputs) {
for (const inp of product.inputs) {
@@ -191,3 +206,4 @@ export function updateInspector(container, state) {
}
}
}
+
diff --git a/frontend/js/ui/production/facilityCard.js b/frontend/js/ui/production/facilityCard.js
index a07174f..5c9d4e8 100644
--- a/frontend/js/ui/production/facilityCard.js
+++ b/frontend/js/ui/production/facilityCard.js
@@ -1,7 +1,7 @@
// Production UI: Individual Facility Card (HTML Mount and Fine-grained DOM update)
import { getProduct, getNextMilestone, getUnitMilestoneMultiplier, SECTORS } from '../../data/products.js';
import { fmtCurrency, fmtNum } from '../../core/formatters.js';
-import { calculateMultiCost, getProductOutputRates } from './ratesCalculator.js';
+import { calculateMultiCost, getProductOutputRates, computeProductNetFlow } from './ratesCalculator.js';
export function renderFacilityCardHtml(p, pState, state, buyMode) {
const sector = SECTORS.find(s => s.id === p.sector);
@@ -113,6 +113,10 @@ export function renderFacilityCardHtml(p, pState, state, buyMode) {
${rates.isBottlenecked ? '0.0 (Paused)' : `${fmtNum(rates.prodRate, 1)}/s`}
+
+ Net Balance:
+ +0.0/s
+
Logistics Rev:
+$${fmtNum(rates.revRate, 2)}/s
@@ -125,10 +129,6 @@ export function renderFacilityCardHtml(p, pState, state, buyMode) {
Logistics Auto-Sell:
${pState.logistics || 0} fleets (${fmtNum((pState.logistics || 0) * 0.75, 2)}/s)
-
- Milestone Eff:
- ${milestoneMult}x Speed
-
${inputsHtml}
@@ -190,6 +190,20 @@ export function updateFacilityCardDom(p, pState, state, buyMode) {
elRateUnits.className = `stat-value font-mono ${rates.isBottlenecked ? 'text-warning' : 'text-profit'}`;
}
+ // Live Net Flow (Production minus Downstream Consumption minus Logistics Sales)
+ const netFlow = computeProductNetFlow(p, state);
+ const elNetFlow = document.getElementById(`net-flow-${p.id}`);
+ if (elNetFlow) {
+ const sign = netFlow.netRate > 0.01 ? '+' : '';
+ const netText = `${sign}${fmtNum(netFlow.netRate, 1)}/s`;
+ if (elNetFlow.textContent !== netText) {
+ elNetFlow.textContent = netText;
+ }
+ const isDeficit = netFlow.netRate < -0.05;
+ const isSurplus = netFlow.netRate > 0.05;
+ elNetFlow.className = `stat-value font-mono ${isDeficit ? 'text-warning' : (isSurplus ? 'text-profit' : '')}`;
+ }
+
const elRateRev = document.getElementById(`rate-rev-${p.id}`);
if (elRateRev) {
const revText = `+$${fmtNum(rates.revRate, 2)}/s`;
diff --git a/frontend/js/ui/production/ratesCalculator.js b/frontend/js/ui/production/ratesCalculator.js
index 7892ecc..3e05da4 100644
--- a/frontend/js/ui/production/ratesCalculator.js
+++ b/frontend/js/ui/production/ratesCalculator.js
@@ -1,5 +1,4 @@
-// Production UI: Cost & Rate Calculations
-import { getNextMilestone, getUnitMilestoneMultiplier } from '../../data/products.js';
+import { PRODUCTS, getNextMilestone, getUnitMilestoneMultiplier } from '../../data/products.js';
export function calculateMultiCost(baseCost, currentCount, multiplier) {
const growth = 1.15;
@@ -64,16 +63,32 @@ export function computeProductNetFlow(product, state) {
// Calculate how much active downstream factories consume per second
let consumedRate = 0;
- // Import PRODUCTS dynamically from caller or check through global/state if needed
- // In our architecture, caller passes or we compute from state.products and dependencies
+ for (const otherP of PRODUCTS) {
+ if (!otherP.inputs) continue;
+ const inpMatch = otherP.inputs.find(inp => inp.id === product.id);
+ if (inpMatch) {
+ const otherState = state.products[otherP.id];
+ if (otherState && otherState.unlocked && otherState.lines > 0 && !otherState.isBottlenecked) {
+ const otherRates = getProductOutputRates(otherP, otherState, state);
+ const cyclesPerSec = otherRates.prodRate / (otherP.outputAmount * otherState.lines);
+ consumedRate += inpMatch.amount * otherState.lines * cyclesPerSec;
+ }
+ }
+ }
+
const logisticsUnits = pState.logistics || 0;
const maxSellCapacity = logisticsUnits * 0.75;
- const soldRate = Math.min(maxSellCapacity, pState.stock > 0 ? maxSellCapacity : effectiveProd);
+ const availableForSale = Math.max(0, effectiveProd - consumedRate);
+ const soldRate = Math.min(maxSellCapacity, pState.stock > 0 ? maxSellCapacity : availableForSale);
+
+ const netRate = effectiveProd - consumedRate - soldRate;
return {
prodRate: effectiveProd,
+ consumedRate,
soldRate,
- netRate: effectiveProd - soldRate
+ netRate
};
}
+