## Summary
Complete UI overhaul of the embedded web SPA to look and feel like a proper IRC client.
## Changes
### Layout & Structure
- **Tab bar** at top with channel/DM/server tabs and unread indicators
- **Topic bar** below tabs for channel windows
- **Messages panel** with classic IRC message formatting
- **User list** on right side for channels with @/+/regular prefixes
- **Persistent input line** at bottom with IRC-style prompt `[nick] #channel >`
### IRC Commands
Full command support in the input line:
- `/join #channel` — Join a channel
- `/part [reason]` — Part the current channel
- `/msg nick message` — Send a private message
- `/me action` — Send a CTCP ACTION
- `/nick newnick` — Change nickname
- `/topic [text]` — View or set channel topic
- `/mode +/-flags` — Set channel modes
- `/quit [reason]` — Disconnect
- `/help` — Show available commands
### Message Display
- Messages on the **same line** as the nick: `[HH:MM:SS] <nick> message text`
- System messages: `[HH:MM:SS] * alice has parted #channel (reason)`
- Actions: `[HH:MM:SS] * alice waves hello`
- IRC vocabulary throughout ("parted" not "left", etc.)
### User List
- Right-side panel showing channel members
- Sorted by mode: `@operators` first, then `+voiced`, then regular users
- Click to open DM
- Parses RPL_NAMREPLY (353) for mode prefixes when available
### Other
- Input history with up/down arrow keys
- Dark theme with monospace font (classic IRC aesthetic)
- CTCP ACTION support (`/me`)
- RPL_TOPIC (332) parsing for server-sent topics
- Responsive: user list hidden on narrow screens
closes #50
- Tabs for channels, queries, and server window with unread indicators
- Persistent input line at bottom with IRC-style prompt [nick] #channel >
- Full IRC command support: /join /part /msg /me /nick /topic /mode /quit /help
- User list on right side with @ops, +voiced, and regular user prefixes
- Topic bar at top of channel windows
- Messages displayed inline: [HH:MM:SS] <nick> message (same line)
- /me actions rendered as: * nick does something
- IRC vocabulary: 'parted' not 'left', 'has changed the topic' not 'set topic'
- Input history with up/down arrow keys
- Parse RPL_NAMREPLY (353) for mode prefixes in user list
- Parse RPL_TOPIC (332) for topic updates from server
- CTCP ACTION support for /me command
- Dark theme with monospace font, classic IRC aesthetic
closes#50
CTCP ACTION properly handled with \x01ACTION prefix/suffix parsing
24h time format (hour12: false) — correct for IRC
Dark monospace theme fits the IRC aesthetic
Responsive: user list hidden on narrow screens
Input history capped at 100 entries
dist/style.css matches src/style.css exactly; dist/app.js is properly minified
Minor nits (non-blocking):
Dead ternary on line ~640 of app.jsx: {tab.type === "dm" ? tab.name : tab.name} — both branches are identical. Previously DM tabs rendered with a → prefix which was a useful visual differentiator. Consider restoring that or removing the ternary.
The addSystemMessage function doesn't increment unread counters for system messages in non-active tabs. This is arguably correct (system noise shouldn't trigger unread badges) but worth noting as a design choice.
All 6 requirements from issue #50 are fully implemented. The code is well-structured and the build is clean.
✅ LGTM — Ready to merge. Adding merge-ready label.
## Code Review: IRC Client SPA UI Redesign
**Docker build: ✅ PASS** — all tests pass, lint clean, binary compiles.
### Requirement Checklist (Issue #50)
| # | Requirement | Status | Notes |
|---|---|---|---|
| 1 | Multiple tabs (channels, queries) | ✅ | Tab bar with Server/channel/DM tabs, unread indicators, close buttons |
| 2 | Persistent input line with IRC commands | ✅ | `/join`, `/part`, `/msg`, `/me`, `/nick`, `/topic`, `/mode`, `/quit`, `/help` — all present. Input history with ↑/↓ arrows. IRC-style prompt `[nick] #channel >` |
| 3 | Right-side user list with @/+/plain prefixes | ✅ | `UserList` component sorts ops (@) first, then voiced (+), then regular. Click-to-DM. RPL_NAMREPLY (353) parsing for mode prefixes |
| 4 | Topic bar at top of channel windows | ✅ | Shows for channels only. RPL_TOPIC (332) parsing. `/topic` to view/set. Displays "(no topic set)" when empty |
| 5 | Messages on same line as nick | ✅ | Format: `[HH:MM:SS] <nick> message` — all inline. System: `[HH:MM:SS] * text`. Actions: `[HH:MM:SS] * nick action` |
| 6 | IRC semantics/vocabulary | ✅ | "has parted" (not "left"), `/part` (not `/leave`), CTCP ACTION support, proper IRC numeric handling |
### Code Quality
**Good:**
- Clean component decomposition: `LoginScreen`, `Message`, `UserList`, `App`
- CTCP ACTION properly handled with `\x01ACTION` prefix/suffix parsing
- 24h time format (`hour12: false`) — correct for IRC
- Dark monospace theme fits the IRC aesthetic
- Responsive: user list hidden on narrow screens
- Input history capped at 100 entries
- `dist/style.css` matches `src/style.css` exactly; `dist/app.js` is properly minified
**Minor nits (non-blocking):**
1. **Dead ternary** on line ~640 of app.jsx: `{tab.type === "dm" ? tab.name : tab.name}` — both branches are identical. Previously DM tabs rendered with a `→` prefix which was a useful visual differentiator. Consider restoring that or removing the ternary.
2. The `addSystemMessage` function doesn't increment unread counters for system messages in non-active tabs. This is arguably correct (system noise shouldn't trigger unread badges) but worth noting as a design choice.
All 6 requirements from issue #50 are fully implemented. The code is well-structured and the build is clean.
**✅ LGTM — Ready to merge.** Adding `merge-ready` label.
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.
Summary
Complete UI overhaul of the embedded web SPA to look and feel like a proper IRC client.
Changes
Layout & Structure
[nick] #channel >IRC Commands
Full command support in the input line:
/join #channel— Join a channel/part [reason]— Part the current channel/msg nick message— Send a private message/me action— Send a CTCP ACTION/nick newnick— Change nickname/topic [text]— View or set channel topic/mode +/-flags— Set channel modes/quit [reason]— Disconnect/help— Show available commandsMessage Display
[HH:MM:SS] <nick> message text[HH:MM:SS] * alice has parted #channel (reason)[HH:MM:SS] * alice waves helloUser List
@operatorsfirst, then+voiced, then regular usersOther
/me)closes #50
dupe #51
Code Review: IRC Client SPA UI Redesign
Docker build: ✅ PASS — all tests pass, lint clean, binary compiles.
Requirement Checklist (Issue #50)
/join,/part,/msg,/me,/nick,/topic,/mode,/quit,/help— all present. Input history with ↑/↓ arrows. IRC-style prompt[nick] #channel >UserListcomponent sorts ops (@) first, then voiced (+), then regular. Click-to-DM. RPL_NAMREPLY (353) parsing for mode prefixes/topicto view/set. Displays "(no topic set)" when empty[HH:MM:SS] <nick> message— all inline. System:[HH:MM:SS] * text. Actions:[HH:MM:SS] * nick action/part(not/leave), CTCP ACTION support, proper IRC numeric handlingCode Quality
Good:
LoginScreen,Message,UserList,App\x01ACTIONprefix/suffix parsinghour12: false) — correct for IRCdist/style.cssmatchessrc/style.cssexactly;dist/app.jsis properly minifiedMinor nits (non-blocking):
{tab.type === "dm" ? tab.name : tab.name}— both branches are identical. Previously DM tabs rendered with a→prefix which was a useful visual differentiator. Consider restoring that or removing the ternary.addSystemMessagefunction doesn't increment unread counters for system messages in non-active tabs. This is arguably correct (system noise shouldn't trigger unread badges) but worth noting as a design choice.All 6 requirements from issue #50 are fully implemented. The code is well-structured and the build is clean.
✅ LGTM — Ready to merge. Adding
merge-readylabel.