'use client'; import {useEffect,useState} from 'react'; import type {State} from '@/core/types'; import {Button} from '@/components/ui/button'; export function CommunityPanel({state,act,busy}:{state:State;act:(a:any)=>Promise;busy:boolean}){ const [filter,setFilter]=useState('all');const c=state.community; const parties=Object.entries(state.bots.filter(b=>b.status==='online'&&b.world?.groupId).reduce>((groups,b)=>{(groups[b.world!.groupId]??=[]).push(b);return groups;},{})); return <>

Cooperative play

Maximum 50 residents. Group and guild changes appear as confirmed only after the game accepts them.

Live parties

{parties.length?parties.map(([id,bots])=>
{bots?.map(b=>b.name).join(', ')}

{bots?.[0]?.zone} · {bots?.length} observed members

):

No parties currently observed.

}

Guild goals

{c?.guilds.length?c.guilds.map(g=>
{g.name}

{g.progress} confirmed contributions · milestone {g.target}

):

Waiting for guild observations.

}

Social activity

{[...(c?.feed??[])].reverse().filter(e=>filter==='all'||e.kind===filter).map(e=>
{new Date(e.at).toLocaleString()} · {e.kind} · {e.status??'observed'}

{e.names.join(' ↔ ')}

{e.text}

{e.affinityDelta!==undefined&&Trust change: {e.affinityDelta>0?'+':''}{e.affinityDelta}}
)}{!c?.feed.length&&

No confirmed social activity recorded yet.

}
; } export function OperationsPanel({state,act,busy}:{state:State;act:(a:any)=>Promise;busy:boolean}){ const [data,setData]=useState(null),[error,setError]=useState(''); useEffect(()=>{let alive=true;const refresh=async()=>{try{const r=await fetch('/api/operations');if(!r.ok)throw new Error('Health data unavailable');const d=await r.json();if(alive){setData(d);setError('');}}catch(e){if(alive)setError(String(e));}};void refresh();const timer=setInterval(refresh,10000);return()=>{alive=false;clearInterval(timer);};},[]); return <>
{['ubuntu','windows'].map(host=>{const s=data?.samples.find((s:any)=>s.host===host),stale=!s||Date.now()/1000-s.at>180;return

{host==='ubuntu'?'Ubuntu server':'Windows PC'}

{s?<>

{stale?'Telemetry stale — values may have changed':'Current telemetry'} · {new Date(s.at*1000).toLocaleTimeString()}

{[['CPU',s.cpuPercent.toFixed(1)+'%'],['Available RAM',Math.round(s.memoryAvailableMB)+' / '+Math.round(s.memoryTotalMB)+' MB'],['Free disk',s.diskFreeGB.toFixed(1)+' GB'],...(s.databaseMB!==undefined?[['Application database',s.databaseMB.toFixed(1)+' MB'],['Logs',s.logsMB.toFixed(1)+' MB'],['Model',s.modelAvailable?'Available':'Unavailable'],['Runtime recovery',s.runtimeService]]:[])].map(([label,value]:any)=>
{label}{value}
)}{s.memoryAvailableMB<1024&&

Memory pressure: keep the population capped at 50.

}

{s.maintenance}

:

Waiting for telemetry from this machine.

}
;})}

Storage and stalled activity

{error&&

{error}

}

Memories, relationships and memorials are preserved. Acknowledged game transport expires after one day; archived snapshots use the selected retention. Logs rotate at 10 MB with three archives. The social feed holds 250 entries.

Possible stalled bots

Stationary for over 10 minutes while intending to quest or explore. This is a review signal, not proof of being stuck; no automatic teleporting.

{data?.stuck?.length?data.stuck.map((b:any)=>

{b.name} · {b.zone} · {b.minutes} minutes

):

No candidates detected.

}
; }