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:
Fredrik Johansson
2026-06-22 21:57:32 +02:00
parent 77830a3b3f
commit ff14e955ea
27 changed files with 3782 additions and 0 deletions

13
web/src/pages/Chat.tsx Normal file
View File

@@ -0,0 +1,13 @@
import { Sidebar } from '../components/Sidebar'
import { MessagePane } from '../components/MessagePane'
import { PeerList } from '../components/PeerList'
export function Chat() {
return (
<div className="chat-layout">
<Sidebar />
<MessagePane />
<PeerList />
</div>
)
}

View File

@@ -0,0 +1,35 @@
import { useWaste } from '../store'
interface Props {
status: 'disconnected' | 'connecting' | 'connected'
}
export function Onboarding({ status }: Props) {
const { send } = useWaste()
const label = status === 'connecting'
? 'Connecting to daemon…'
: 'Waiting for daemon — is waste-daemon running?'
function joinNetwork(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
const form = e.currentTarget
const name = (form.elements.namedItem('network') as HTMLInputElement).value.trim()
if (!name) return
send({ type: 'join_network', network_name: name })
}
return (
<div className="onboarding">
<h1>waste</h1>
<p className="status">{label}</p>
{status === 'connected' && (
<form onSubmit={joinNetwork} className="join-form">
<input name="network" placeholder="network name" autoComplete="off" autoFocus />
<button type="submit">Join</button>
</form>
)}
</div>
)
}