Loading…
Loading…
A messaging platform where the people you talk to and the AI you talk to share one inbox — across a web app, a React Native app and one real-time backend.

AI personas
API routes
Data models
Most chat products treat AI as a bolt-on: a separate tab, a separate app, a separate mental model. ChatApp puts a conversation with a person and a conversation with a model in the same list, opened the same way, backed by the same message table. Five conversation types — direct, group, assistant, character and multi-bot — flow through one schema, so read receipts, typing indicators, replies, mentions and media behave identically whether the other side is a friend or GPT-4o-mini. Around that sit voice and video calls over WebRTC, LiveKit-backed meetings with waiting rooms and breakout rooms, and a personal AI drive that saves any answer worth keeping. The backend is plain Bun.serve — no Express — with a strict handlers → services → repositories layering over Prisma, and it serves the Next.js web client and an Expo React Native client from the same contract.
Three clients had to agree on one contract. The web app and the React Native app both mirror the server’s response shapes by hand, so a change to a payload is a change in three repositories — the discipline that keeps it honest is that the server owns one shared Prisma field list, and every endpoint that exposes a user selects from it rather than hand-picking columns.
AI providers do not agree on anything. A single dispatch keyed on the conversation’s provider slug routes to OpenAI, Gemini or OpenRouter, and the eleven characters reuse the OpenAI path with a personality prompt instead of getting a code path each — an unknown slug falls through to the character branch rather than erroring, so adding a character is a data change, not a deployment.
A chat pane is a fixed shell wrapped around exactly one scrolling region, and the browser is happy to break that. The message list, the composer and the header live in a grid whose column is explicitly minmax(0, 1fr): left implicit, the column tracks the header’s nowrap width and quietly pushes the send button off a phone screen while everything still measures fine on a desktop.
Real-time state is easy to fragment into a socket per feature. Everything — new messages, presence, typing, incoming calls, participants joining a meeting — travels over one authenticated WebSocket with a typed event union, so a client subscribes once and the reconnect story is written once.








Have a product in mind? Let's turn it into something users love — fast, scalable, and beautifully engineered.