feat(client): add Phase D UI integration (D.1-D.3)

- D.1: Inventory display in NPC info panel (two-column grid below stats)
- D.2: Activity labels for gather/craft/build/dropoff goals
- D.3: Gold color for invention events in feed, lightbulb icon in history

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-03-09 00:13:15 +00:00
parent 9e4d2583f5
commit c7bf7c9ef4
3 changed files with 80 additions and 3 deletions
+1 -1
View File
@@ -87,7 +87,7 @@ export class EventsFeed {
const el = document.createElement('div');
el.style.cssText = `
font-size: 11px;
color: ${event.isLlmGenerated ? '#d0d0f0' : '#b0b0d0'};
color: ${event.type === 'invention' ? '#f0d060' : event.isLlmGenerated ? '#d0d0f0' : '#b0b0d0'};
padding: 4px 0;
border-bottom: 1px solid #2a2a4e;
line-height: 1.5;
+77
View File
@@ -4,6 +4,10 @@ const ACTIVITY_LABELS: Record<string, string> = {
wander: 'Wandering',
eat: 'Eating',
rest: 'Resting',
gather: 'Gathering',
craft: 'Crafting',
build: 'Building',
dropoff: 'Dropping off',
};
// EarthBound-inspired color palette
@@ -40,6 +44,8 @@ export class NpcInfoPanel {
private backstoryEl: HTMLDivElement;
private statsContainer: HTMLDivElement;
private statElements: Map<string, HTMLDivElement> = new Map();
private inventorySeparator: HTMLDivElement;
private inventoryContainer: HTMLDivElement;
private statusTab!: HTMLDivElement;
private relationshipsTab!: HTMLDivElement;
private historyTab!: HTMLDivElement;
@@ -333,6 +339,30 @@ export class NpcInfoPanel {
`;
this.statusContent.appendChild(this.statsContainer);
// Inventory separator (hidden when empty)
this.inventorySeparator = document.createElement('div');
this.inventorySeparator.style.cssText = `
text-align: center;
font-size: 11px;
color: ${EB.textMuted};
padding: 4px 0;
letter-spacing: 6px;
user-select: none;
display: none;
`;
this.inventorySeparator.textContent = '\u25C6\u25C6\u25C6';
this.statusContent.appendChild(this.inventorySeparator);
// Inventory container - two columns like stats
this.inventoryContainer = document.createElement('div');
this.inventoryContainer.style.cssText = `
display: none;
grid-template-columns: 1fr 1fr;
gap: 3px 8px;
font-family: 'Press Start 2P', monospace;
`;
this.statusContent.appendChild(this.inventoryContainer);
contentWrapper.appendChild(this.statusContent);
this.relationshipsContent = document.createElement('div');
@@ -402,6 +432,8 @@ export class NpcInfoPanel {
if (entity.relationships) {
this.updateRelationships(entity.relationships);
}
this.updateInventory(entity.inventory);
}
updateNeeds(needs: { hunger: number; energy: number }): void {
@@ -409,6 +441,50 @@ export class NpcInfoPanel {
this.setNeedBar('Energy', needs.energy);
}
private updateInventory(inventory?: Record<string, number>): void {
const items = inventory ? Object.entries(inventory).filter(([, qty]) => qty > 0) : [];
if (items.length === 0) {
this.inventorySeparator.style.display = 'none';
this.inventoryContainer.style.display = 'none';
return;
}
this.inventorySeparator.style.display = '';
this.inventoryContainer.style.display = 'grid';
this.inventoryContainer.innerHTML = '';
// Section label spanning both columns
const label = document.createElement('div');
label.style.cssText = `
grid-column: 1 / -1;
font-size: 14px;
color: ${EB.textSecondary};
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 2px;
`;
label.textContent = 'Inventory';
this.inventoryContainer.appendChild(label);
for (const [itemId, qty] of items) {
const el = document.createElement('div');
el.style.cssText = `
display: flex;
justify-content: space-between;
font-size: 14px;
`;
const nameEl = document.createElement('span');
nameEl.style.cssText = `color: ${EB.textSecondary}; text-transform: capitalize;`;
nameEl.textContent = itemId.replace(/_/g, ' ');
const qtyEl = document.createElement('span');
qtyEl.style.cssText = `color: ${EB.textPrimary}; text-shadow: 1px 1px 0 rgba(0,0,0,0.5);`;
qtyEl.textContent = String(qty);
el.appendChild(nameEl);
el.appendChild(qtyEl);
this.inventoryContainer.appendChild(el);
}
}
updateRecentEvents(events: NarrationEvent[]): void {
if (events.length === 0) {
this.recentEventsEl.style.display = 'none';
@@ -878,6 +954,7 @@ export class NpcInfoPanel {
case 'bond_formed': return '\u{2764}';
case 'bond_dissolved': return '\u{1F525}';
case 'spawned': return '\u{1F331}';
case 'invention': return '\u{1F4A1}';
default: return '\u{25CF}';
}
}
@@ -283,7 +283,7 @@ Add `'craft'` and `'build'` to goal type union.
**Goal:** NPCs occasionally have "eureka moments" where the LLM invents new items, recipes, or workshops from existing materials. Requires Phase B complete.
**Status:** NOT STARTED
**Status:** COMPLETE
### C.1 Invention System
@@ -396,7 +396,7 @@ Once new items exist, they appear in future invention prompts:
**Goal:** Surface inventory, industry tasks, and inventions in the client UI. Can start after Phase A; grows with each subsequent phase.
**Status:** NOT STARTED
**Status:** D.1-D.3 COMPLETE, D.4 NOT STARTED
### D.1 NPC Info Panel — Inventory Display