feat: standalone web UI (React + Vite)
React + TypeScript UI on port 5274. Connects to local daemon via WebSocket IPC (DaemonAdapter). Full chat layout with sidebar, message pane, and peer list. Mirrors the TUI feature set. - src/types.ts — IPC types mirroring proto.go - src/adapter/daemon — WebSocket IPC adapter with auto-reconnect - src/store/index.ts — Zustand store; handles all daemon events - src/pages/ — Onboarding (connect + join) and Chat - src/components/ — Sidebar, MessagePane, PeerList Dev: cd web && npm run dev (port 5274) Build: cd web && npm run build Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
35
web/src/components/PeerList.tsx
Normal file
35
web/src/components/PeerList.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { useWaste } from '../store'
|
||||
|
||||
export function PeerList() {
|
||||
const { connectedPeers, activeNetworkId, setActiveRoom, send } = useWaste()
|
||||
|
||||
function openDM(peerId: string) {
|
||||
const room = `dm:${peerId}`
|
||||
setActiveRoom(room)
|
||||
}
|
||||
|
||||
function requestFiles(peerId: string) {
|
||||
send({ type: 'get_file_list', network_id: activeNetworkId ?? undefined, peer_id: peerId })
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className="peer-list">
|
||||
<span className="sidebar-label">Peers ({connectedPeers.length})</span>
|
||||
{connectedPeers.map(p => (
|
||||
<div key={p.id} className="peer-entry">
|
||||
<div className="peer-entry-info">
|
||||
<span className="alias">{p.alias}</span>
|
||||
<span className="peer-id">{p.id.slice(0, 8)}…</span>
|
||||
</div>
|
||||
<div className="peer-entry-actions">
|
||||
<button onClick={() => openDM(p.id)} title="Direct message">DM</button>
|
||||
<button onClick={() => requestFiles(p.id)} title="Browse files">Files</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{connectedPeers.length === 0 && (
|
||||
<p className="empty">No peers connected yet</p>
|
||||
)}
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user