Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/ts-group-chat/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Anthropic API Key for Murf AI assistant
# Anthropic API Key for @Claude mentions in group chat
# Get your API key from: https://console.anthropic.com/
ANTHROPIC_API_KEY=sk-ant-your-api-key-here
261 changes: 68 additions & 193 deletions examples/ts-group-chat/README.md
Original file line number Diff line number Diff line change
@@ -1,229 +1,104 @@
# Cap'n Web Chat Demo
# Cap'n Web Group Chat

A real-time multi-user chat application demonstrating the integration of **Cap'n Web RPC** with **TanStack Start**. This project showcases bidirectional WebSocket communication, server-side rendering, and modern React patterns.
A real-time multi-user chat demonstrating **Cap'n Web RPC** bidirectional push with **TanStack Start**. Includes a shared in-memory todo list that users and Claude can manage. Mention `@Claude` in passive mode, or switch to active mode so Claude watches the chat for todo intent.

## 🎥 Demo
## Features

![Demo video](./assets/demo.gif)
- Real-time messaging via Cap'n Web server→client push (no polling)
- Online presence updates when users join or leave
- Shared in-memory todo list (manual add/remove + Claude tools)
- Claude **passive / active** mode toggle for todo help
- Auto-connect on page load
- Optional `@Claude` AI responses via `@tanstack/ai`
- TanStack Start SSR shell + file-based routing
- Tailwind CSS chat UI

## ✨ Features
## Architecture

- **Real-time messaging** - Instant message delivery across all connected users
- **Online presence** - See who's currently in the chat
- **Auto-connection** - Seamlessly connects when the page loads
- **Responsive design** - Works on desktop and mobile devices
- **Modern chat UI** - Messages appear like iMessage/WhatsApp (yours on right, others on left)
- **Username-based** - Simple username entry, no registration required

## 🏗️ Architecture

This demo combines several powerful technologies:

- **[Cap'n Web RPC](https://github.com/cloudflare/capnweb)** - Bidirectional RPC over WebSockets
- **[TanStack Start](https://tanstack.com/start)** - Full-stack React framework
- **[TanStack Router](https://tanstack.com/router)** - Type-safe routing
- **[Tailwind CSS](https://tailwindcss.com/)** - Utility-first styling
- **WebSocket Server** - Real-time communication layer

### Key Components
- **[Cap'n Web](https://github.com/cloudflare/capnweb)** — `RpcTarget` server + typed `RpcStub<ChatApi>` client over WebSocket
- **[TanStack Start](https://tanstack.com/start)** — Vite + Nitro 3 full-stack React
- **Vite dev plugin** — WebSocket upgrade at `/api/websocket` (Node `ws` pattern from Cap'n Web docs)

```
chat-server/
├── chat-logic.ts # Core chat business logic
├── capnweb-rpc.ts # Cap'n Web RPC server implementation
└── vite-plugin.ts # WebSocket integration with Vite
├── chat-api.ts # Shared ChatApi interface and types
├── chat-logic.ts # In-memory chat state
├── todo-logic.ts # In-memory shared todo list
├── claude-service.ts # Claude queue + todo tools
├── capnweb-rpc.ts # ChatServer RpcTarget + push broadcasts
└── vite-plugin.ts # Dev-server WebSocket upgrade handler

src/
├── components/
│ ├── ChatInterface.tsx # Main chat message display
│ ├── OnlineUsers.tsx # Online user list
│ └── UsernameInput.tsx # Username entry
├── hooks/
│ ├── useChatConnection.ts # WebSocket connection management
│ └── useChatMessages.ts # Message state and polling
└── routes/
└── index.tsx # Main chat page
│ ├── useChatConnection.ts # RpcStub session + Symbol.dispose
│ ├── useChatMessages.ts # joinChat onNotify push handler
│ ├── useTodos.ts # Todo list + Claude mode sync
│ └── useClaude.ts # Claude queue status
└── routes/index.tsx # Main chat page
```

## 🚀 Getting Started
> **Note:** The Cap'n Web WebSocket room runs on the Vite dev server (`pnpm dev`). Production `pnpm build` / `pnpm serve` builds the Start app but does not host the live chat room — use `pnpm dev` to exercise real-time RPC.

### Prerequisites
## Getting Started

- Node.js 18+
- pnpm (recommended) or npm

### Installation
From the monorepo root:

```bash
# Clone the repository
git clone <repo-url>
cd capnweb-chat-demo

# Install dependencies
pnpm install
```

### Development

```bash
# Start the development server
cd examples/ts-group-chat
cp .env.example .env # optional, for @Claude
pnpm dev
```

Open [http://localhost:3000](http://localhost:3000) in multiple browser tabs to test the chat functionality.
Open [http://localhost:3000](http://localhost:3000) in multiple browser tabs, pick different usernames, and send messages.

### Building for Production
## How It Works

```bash
# Build the application
pnpm build

# Start production server
pnpm start
```

## 💬 How It Works

### 1. **Connection Flow**

- User opens the chat page
- Application auto-connects to WebSocket server via Cap'n Web RPC
- Connection status is managed transparently

### 2. **Chat Flow**

- User enters a username
- Application automatically joins the chat room
- Messages are sent via RPC calls to the server
- Server broadcasts messages to all connected users
- Client polls for new messages every second

### 3. **Message Broadcasting**
1. Client opens `newWebSocketRpcSession<ChatApi>(url, chatNotifier)` — the notifier is exported as Cap'n Web `localMain`.
2. Server receives the client's notifier stub when the WebSocket session starts (`vite-plugin.ts`).
3. Client calls `joinChat(username)`; the server registers that connection's notifier for push.
4. When any user sends a message, `broadcastToAll` invokes each client's `notify()` stub — instant push, no polling.

```typescript
// Server-side message broadcasting
await ChatServer.broadcastToAll({
type: 'message',
message: message.message,
username: message.username,
timestamp: message.timestamp,
id: message.id,
})
// Client: export ChatNotifier as localMain, call typed ChatApi methods
const notifier = new ChatNotifier()
notifier.onNotification = (n) => {
/* update UI */
}
const api = newWebSocketRpcSession<ChatApi>(wsUrl, notifier)
await api.joinChat(username)

// Server (vite plugin): receive client notifier stub from session setup
const clientNotifier = newWebSocketRpcSession(ws, chatServer)
chatServer.setClientNotifier(clientNotifier)
```

### 4. **Real-time Updates**

- **Messages**: Polled every 1 second
- **Online users**: Updated every 5 seconds
- **Connection status**: Real-time via WebSocket events

## 🔧 Configuration

### WebSocket Setup

The WebSocket server is configured in `chat-server/vite-plugin.ts`:

```typescript
// WebSocket endpoint: /api/websocket
wss.handleUpgrade(request, socket, head, (ws) => {
const chatServer = new ChatServer()
chatServer.setWebSocket(ws)
newWebSocketRpcSession(ws, chatServer)
})
```

### Message Queue System

Messages are queued per user to ensure delivery:

```typescript
// Each user gets their own message queue
export const userMessageQueues = new Map<string, Array<any>>()

// Messages are polled and cleared
const messages = userMessageQueues.get(username) || []
userMessageQueues.set(username, []) // Clear after reading
```

## 🧪 Testing

```bash
# Run tests
pnpm test
```

To test the chat functionality:

1. Open multiple browser tabs to [http://localhost:3000](http://localhost:3000)
2. Enter different usernames in each tab
3. Send messages and observe real-time delivery
4. Check the online users list updates

## 📚 Key Technologies Explained

### Cap'n Web RPC

- **Bidirectional**: Both client and server can call methods on each other
- **Type-safe**: Full TypeScript support for RPC calls
- **WebSocket-based**: Built on standard WebSocket protocol
- **Efficient**: Binary protocol with message queuing

### TanStack Start

- **Full-stack**: Single framework for client and server
- **Server-side rendering**: Pages render on the server first
- **File-based routing**: Routes defined by file structure
- **Type-safe**: End-to-end TypeScript support

### Message Flow

```
User types message
useChatMessages.sendMessage()
Cap'n Web RPC call
ChatServer.sendMessage()
ChatLogic.sendMessage()
Broadcast to all users
Message queues updated
Clients poll for updates
UI updates with new message
```

## 🎯 Why This Demo?

This application demonstrates:
## Scripts

1. **Real-world WebSocket usage** - Not just a simple echo server
2. **RPC over WebSockets** - More structured than plain message passing
3. **Modern React patterns** - Hooks, effects, and clean component architecture
4. **Full-stack TypeScript** - Shared types between client and server
5. **Production-ready patterns** - Error handling, reconnection, message queuing
| Script | Description |
| ----------------- | ----------------------------------------------------- |
| `pnpm dev` | Start dev server with Cap'n Web WebSocket (port 3000) |
| `pnpm build` | Build TanStack Start app |
| `pnpm serve` | Preview production build |
| `pnpm test:types` | Typecheck |

Perfect for learning how to build real-time applications with modern web technologies!
## Todo List + Claude Modes

## 🤝 Contributing
The room shares one in-memory todo list. Anyone can add/remove items from the UI. Claude has `listTodos`, `addTodo`, and `removeTodo` tools.

This is a demonstration project, but improvements are welcome:
| Mode | Behavior |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Passive** (default) | Claude only runs when a message mentions `@Claude` (or starts with `Claude`). Use that to add/remove todos or ask what's on the list. |
| **Active** | Claude reviews every chat message. If it detects todo add/remove intent or a todo question, it uses tools and replies. Unrelated messages get no reply. |

- Enhanced error handling
- Message persistence
- User authentication
- Private messaging
- File sharing
- Emoji reactions
Mode is shared for the whole room and syncs live via Cap'n Web push.

## 📄 License
## @Claude Integration

This project is open source and available under the [MIT License](LICENSE).
Set `ANTHROPIC_API_KEY` in `.env`. Responses use `claude-sonnet-4-5`. Only one Claude request runs at a time; others wait in queue.

---
## Key Technologies

**Built with ❤️ using Cap'n Web RPC and TanStack Start**
- **Cap'n Web 0.10** — bidirectional RPC, pass-by-reference callbacks, `RpcStub` + `[Symbol.dispose]()`
- **TanStack Start 1.159** — `tanstackStart()` + Nitro 3
- **TanStack AI** — Anthropic adapter + server tools for the shared todo list
Loading
Loading