diff --git a/tui/monitor.tsx b/tui/monitor.tsx index f657bc0..546c57a 100644 --- a/tui/monitor.tsx +++ b/tui/monitor.tsx @@ -140,105 +140,73 @@ function foodColor(food: number | undefined) { // --- components ------------------------------------------------------------- -function Header({ snapshot, connectedIpc, width, startedAt }: { snapshot: Snapshot; connectedIpc: boolean; width: number; startedAt: number }) { +function StatusHeader({ snapshot, connectedIpc, width, startedAt }: { snapshot: Snapshot; connectedIpc: boolean; width: number; startedAt: number }) { const pos = snapshot.position - ? `(${Math.round(snapshot.position.x)}, ${Math.round(snapshot.position.y)}, ${Math.round(snapshot.position.z)})` + ? `(${Math.round(snapshot.position.x)},${Math.round(snapshot.position.y)},${Math.round(snapshot.position.z)})` : "?"; const hp = snapshot.health; const food = snapshot.food; - const day = snapshot.isDay ? "☀ day" : "🌙 night"; + const day = snapshot.isDay ? "☀" : "🌙"; const session = formatDuration(Date.now() - startedAt); const mcOnline = snapshot.connected; + const story = snapshot.storyStep; + const idx = story?.index ?? 0; + const cur = story?.step; + const want = story?.suggestion?.skillId; + // 11-step progress bar in 11 cells + const bar = Array.from({ length: 11 }, (_, i) => { + if (i < idx) return "▓"; + if (i === idx) return "▒"; + return "░"; + }).join(""); return ( - pepa-monitor - · - {mcOnline ? "● MC" : "○ MC"} - · - {connectedIpc ? "● IPC" : "○ IPC"} - · - session {session} - · - user {snapshot.username ?? "?"} - - - pos {pos} - - HP - {hp ?? "?"}/20 - - food - {food ?? "?"}/20 - + pepa + · + {mcOnline ? "●MC" : "○MC"} + · + {connectedIpc ? "●IPC" : "○IPC"} + · + {session} + · + {snapshot.username ?? "?"} + · + {pos} + · + HP {hp ?? "?"} + · + food {food ?? "?"} + · {day} - hostiles - 0 ? "red" : "gray"}>{snapshot.hostileCount ?? 0} - {snapshot.closestHostile ? closest {snapshot.closestHostile.name}@{snapshot.closestHostile.distance}b : null} + {(snapshot.hostileCount ?? 0) > 0 ? ( + <> + · + ⚔{snapshot.hostileCount} + {snapshot.closestHostile ? ({snapshot.closestHostile.name}@{Math.round(snapshot.closestHostile.distance)}b) : null} + + ) : null} - - ); -} - -function StorylinePanel({ snapshot, width }: { snapshot: Snapshot; width: number }) { - const story = snapshot.storyStep; - if (!story) { - return ( - - storyline not available (bot offline or storyline disabled) - - ); - } - const STEPS = [ - "orient_self", "first_wood", "crafting_basics", "first_tools", "first_food", - "shelter_minimal", "stone_tier", "food_security", "iron_age", "settle_base", "village_grow", - ]; - const TITLES: Record = { - orient_self: "Понять где я", - first_wood: "Собрать 8 поленьев", - crafting_basics: "Сделать верстак и палки", - first_tools: "Деревянные орудия", - first_food: "Найти первую еду", - shelter_minimal: "Простой шелтер с кроватью", - stone_tier: "Каменные орудия", - food_security: "Запас еды на 16+", - iron_age: "Железо и печь", - settle_base: "Постоянная база", - village_grow: "Развивать деревню", - }; - const idx = story.index ?? 0; - const cur = story.step; - const want = story.suggestion?.skillId; - const emergency = story.emergency; - return ( - - storyline - step + story + {bar} + {idx + 1}/11 - - {cur?.id} - {cur?.title} - {emergency ? [EMERGENCY PAUSE] : null} - - {want ? ( - - wants: - {want} - - ) : null} - - {STEPS.map((id, i) => { - const done = i < idx; - const current = i === idx; - const mark = done ? "✓" : current ? "→" : "○"; - const color = done ? "green" : current ? "yellow" : "gray"; - return ( - - {` ${mark} ${(i + 1).toString().padStart(2)}. ${id.padEnd(18)} ${TITLES[id] ?? ""}`} - - ); - })} + + {cur ? ( + <> + {cur.id} + · + {cur.title} + + ) : (no story)} + {want ? ( + <> + + {want} + + ) : null} + {story?.emergency ? [EMERGENCY] : null} ); @@ -300,17 +268,20 @@ function AdvisorPanel({ recs, width, height }: { recs: any[]; width: number; hei const ok = r.outcome_ok; const outcomeMark = ok == null ? "·" : ok ? "✓" : "✗"; const outcomeColor = ok == null ? "gray" : ok ? "green" : "red"; - const tail = `${r.tokens_in ?? "?"}/${r.tokens_out ?? "?"}t ${r.latency_ms ?? "?"}ms`; + const target = r.recommended_skill ?? r.action; + const trigger = String(r.trigger_reason ?? "?"); + // One line per recommendation: " ✓ wedged_60s → survive.flee 802t 1900ms" + const lhs = `${outcomeMark} ${trigger} → ${target}`; + const rhs = `${r.tokens_in ?? "?"}t ${r.latency_ms ?? "?"}ms`; + const free = Math.max(20, width - rhs.length - 6); return ( - - - {outcomeMark} - {r.trigger_reason} - - {r.recommended_skill ?? r.action} - - {tail}{r.rationale ? ` · ${String(r.rationale).slice(0, width - 14)}` : ""} - + + {outcomeMark} + {trigger} + + {String(target).slice(0, Math.max(8, free - trigger.length - 6))} + {rhs} + ); })} {visible.length === 0 ? (no advisor calls yet) : null} @@ -322,19 +293,14 @@ function ImprovementsPanel({ items, width, height }: { items: any[]; width: numb const visible = items.slice(0, height); return ( - Improvements (open: {items.length}) + Improvements (open {items.length}) {visible.map((r) => ( - - - #{r.id} - P{r.priority} - ×{r.votes} - {String(r.title).slice(0, width - 14)} - - {r.description ? ( - {String(r.description).slice(0, width - 8)} - ) : null} - + + #{r.id} + P{r.priority} + ×{r.votes} + {String(r.title).slice(0, Math.max(20, width - 14))} + ))} {visible.length === 0 ? (no improvement requests yet) : null} @@ -477,17 +443,20 @@ function App() { }); } - // Layout math: full-width header & storyline, then split 50/50. + // Layout math: compact 4-section vertical stack. + // row budget: + // header (story + status) ≈ 4 rows + // middle activity/chat ≈ floor((rows - 4 - 4 - 4) / 2) + // bottom advisor/improvements ≈ same + // footer ≈ 4 rows const totalWidth = Math.max(80, cols); const halfWidth = Math.floor(totalWidth / 2); - const totalRows = Math.max(24, rows); - // rough allocation: header 4, storyline ~14, footer 4. Rest split for middle panels. - const middleRows = Math.max(8, Math.floor((totalRows - 4 - 14 - 4) / 2)); + const totalRows = Math.max(20, rows); + const middleRows = Math.max(5, Math.floor((totalRows - 4 - 4 - 4) / 2)); return ( -
- +