Preact (3KB) instead of React — minimal bundle size
esbuild for bundling — fast, zero-config
Built output in web/dist/, embedded via embed.FS
Total JS bundle: ~19KB minified
UI Design (IRC-style)
Tab bar (top): Server tab, channel tabs, DM tabs with close buttons
Messages (center): Timestamped, colored nicks, <nick> format
User list (right): Full-height panel for current channel, click to DM
Input bar (bottom): Message input with send button
Join dialog: Inline in tab bar for joining channels
Features
Nick registration with token persistence (localStorage)
Channel join/part/create
Real-time polling (1.5s interval)
DM windows as separate tabs
IRC commands: /join, /part, /msg, /nick
Per-nick color hashing
Dark theme, monospace font
Mobile-responsive (user list hidden on small screens)
Engineering Decisions
Preact over React: 3KB vs 40KB, same API, embedded binary size matters
Polling over WebSocket: Simpler, works through all proxies, adequate for chat at this scale. Can upgrade later.
Unified messages table: Channel msgs and DMs in one table with is_dm flag — simpler polling query
Bearer tokens: Simple auth model, no sessions to manage. Token in localStorage.
embed.FS: Zero external dependencies at runtime, single binary deployment
esbuild: Sub-second builds, no webpack config complexity
## Overview
Build an IRC-style web chat client embedded directly in the Go binary, served at `GET /`.
## Architecture
### Backend (C2S HTTP API)
New API endpoints under `/api/v1/`:
| Method | Path | Description |
|--------|------|-------------|
| GET | `/server` | Server name + MOTD |
| POST | `/register` | Register nick, get bearer token |
| GET | `/me` | Current user info |
| GET | `/channels` | List joined channels |
| GET | `/channels/all` | List all channels |
| POST | `/channels/join` | Join/create channel |
| DELETE | `/channels/{ch}/part` | Leave channel |
| GET | `/channels/{ch}/members` | Channel user list |
| GET | `/channels/{ch}/messages` | Get messages (with `?after=` cursor) |
| POST | `/channels/{ch}/messages` | Send message |
| GET | `/dm/{nick}/messages` | Get DMs |
| POST | `/dm/{nick}/messages` | Send DM |
| GET | `/poll` | Poll all new messages (channels + DMs) |
Auth: Bearer token in Authorization header.
### Database (migration 003)
- `users` — nick, token, last_seen
- `channel_members` — channel↔user join table
- `messages` — channel messages + DMs (unified table with `is_dm` flag)
- Proper indexes on message queries
### Frontend (Preact SPA)
- **Preact** (3KB) instead of React — minimal bundle size
- **esbuild** for bundling — fast, zero-config
- Built output in `web/dist/`, embedded via `embed.FS`
- Total JS bundle: ~19KB minified
### UI Design (IRC-style)
- **Tab bar** (top): Server tab, channel tabs, DM tabs with close buttons
- **Messages** (center): Timestamped, colored nicks, `<nick>` format
- **User list** (right): Full-height panel for current channel, click to DM
- **Input bar** (bottom): Message input with send button
- **Join dialog**: Inline in tab bar for joining channels
### Features
- Nick registration with token persistence (localStorage)
- Channel join/part/create
- Real-time polling (1.5s interval)
- DM windows as separate tabs
- IRC commands: `/join`, `/part`, `/msg`, `/nick`
- Per-nick color hashing
- Dark theme, monospace font
- Mobile-responsive (user list hidden on small screens)
### Engineering Decisions
1. **Preact over React**: 3KB vs 40KB, same API, embedded binary size matters
2. **Polling over WebSocket**: Simpler, works through all proxies, adequate for chat at this scale. Can upgrade later.
3. **Unified messages table**: Channel msgs and DMs in one table with `is_dm` flag — simpler polling query
4. **Bearer tokens**: Simple auth model, no sessions to manage. Token in localStorage.
5. **embed.FS**: Zero external dependencies at runtime, single binary deployment
6. **esbuild**: Sub-second builds, no webpack config complexity
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Overview
Build an IRC-style web chat client embedded directly in the Go binary, served at
GET /.Architecture
Backend (C2S HTTP API)
New API endpoints under
/api/v1/:/server/register/me/channels/channels/all/channels/join/channels/{ch}/part/channels/{ch}/members/channels/{ch}/messages?after=cursor)/channels/{ch}/messages/dm/{nick}/messages/dm/{nick}/messages/pollAuth: Bearer token in Authorization header.
Database (migration 003)
users— nick, token, last_seenchannel_members— channel↔user join tablemessages— channel messages + DMs (unified table withis_dmflag)Frontend (Preact SPA)
web/dist/, embedded viaembed.FSUI Design (IRC-style)
<nick>formatFeatures
/join,/part,/msg,/nickEngineering Decisions
is_dmflag — simpler polling query