/** * LANE-UI — the commission fax. The fax machine is sacred, do not ask why it works * (lore §6). Sits under THE SCREEN, top right. * * Progress comes from `snap.commissionProgress` (itemId -> count delivered), measured * against the active CommissionDef's `wants`. */ import type { CommissionDef, GameData, ItemDef, SimSnapshot } from '../contracts'; import { createChipRow, type ChipRow } from './chips'; import { cls, el, panel, text } from './dom'; import { COPY } from './voice'; export interface Fax { el: HTMLElement; update(snap: SimSnapshot): void; /** Play the stamp. Driven by the `commissionDone` event, not by progress math. */ stamp(): void; } const STAMP_MS = 1800; // must match the fk-stamp keyframes in style.ts export function createFax(data: GameData): Fax { const commissions = new Map(data.commissions.map((c) => [c.id, c])); const items = new Map(data.items.map((i) => [i.id, i])); const root = panel(); root.id = 'fk-fax'; const head = el('div', 'fk-h fk-fax-h', [COPY.faxHeader]); const sub = el('div', 'fk-fax-sub', [COPY.faxSubheader]); const flavor = el('div', 'fk-fax-flavor'); const chips: ChipRow = createChipRow(items); const reward = el('div', 'fk-fax-sub'); const stampEl = el('div', 'fk-fax-stamp', [el('span', undefined, [COPY.faxStamp])]); root.append(head, sub, flavor, el('div', 'fk-rule'), chips.el, reward, stampEl); let stampTimer: number | undefined; return { el: root, update(snap) { const c = snap.activeCommission ? commissions.get(snap.activeCommission) : null; cls(root, 'is-open', !!c); if (!c) return; text(flavor, c.flavor); chips.update( Object.entries(c.wants).map(([item, need]) => { const have = snap.commissionProgress[item] ?? 0; return { item, count: `${Math.min(have, need)}/${need}`, state: have >= need ? ('met' as const) : ('plain' as const), }; }), ); text(reward, `ON RECEIPT: +${c.rewardBandwidth} BANDWIDTH`); }, stamp() { // Restart cleanly if two commissions complete back to back. cls(stampEl, 'is-on', false); void stampEl.offsetWidth; // force reflow so the animation re-runs cls(stampEl, 'is-on', true); clearTimeout(stampTimer); stampTimer = setTimeout(() => cls(stampEl, 'is-on', false), STAMP_MS); }, }; }