- Change app button positioning from left-top to bottom-right anchor - Add reverse mode to useDraggable for bottom-right coordinate system - Reset button position to default on window resize - Use useMemo to cache message list computation - Use startTransition to prevent UI blocking when sending messages - Add generic type parameters to useShareRef and useTriggerAway - Add CLAUDE.md for repository guidance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
7.4 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
WebChat is a decentralized, serverless browser extension that enables anonymous P2P chat on any website using WebRTC. Built with WXT framework for cross-browser support (Chrome, Firefox, Edge).
Key Technologies
- WXT: Browser extension framework (config:
wxt.config.ts) - Remesh: DDD framework for domain logic with true UI/logic separation (RxJS-based reactive state management)
- Artico (@rtco/client): WebRTC P2P communication library (replaces previous trystero dependency)
- React 19 with TypeScript
- Tailwind CSS v4 with shadcn/ui components
- Valibot: Runtime schema validation
Development Commands
# Development
npm run dev # Chrome dev mode with hot reload
npm run dev:firefox # Firefox dev mode
# Type checking
npm run check # Run TypeScript compiler without emitting files
# Linting
npm run lint # ESLint with auto-fix and cache
# Building
npm run build # Production build for all browsers
npm run build:chrome # Chrome production build only
npm run build:firefox # Firefox production build only
# Packaging
npm run pack # Create zip files for all browsers
npm run pack:chrome # Create Chrome zip only
npm run pack:firefox # Create Firefox zip only
# Maintenance
npm run clear # Remove .output directory
npm run prepare # Setup husky git hooks
npm run postinstall # WXT preparation (auto-runs after install)
Architecture
Extension Structure
WebChat uses WXT's app-based structure (not entrypoints):
- src/app/content/ - Content script injected into web pages (main chat UI)
- src/app/background/ - Service worker handling notifications and extension actions
- src/app/options/ - Options page UI for user profile settings
- Entry files:
index.tsorindex.tsxin each app directory
Domain-Driven Design (Remesh)
Business logic is fully decoupled from UI using Remesh domains:
Core Domains (src/domain/):
ChatRoom.ts- Site-specific P2P chat room (messages, users, sync)VirtualRoom.ts- Global virtual room for cross-site user discoveryMessageList.ts- Message management and persistenceUserInfo.ts- User profile stateAppStatus.ts- Application state (open/minimized)Danmaku.ts- Danmaku/bullet comments displayNotification.ts- Browser notificationsToast.ts- In-app toast messages
Domain Pattern:
domain/- Remesh domain definitions (pure logic, queries, commands, events)domain/externs/- External dependency interfaces (define contracts)domain/impls/- Concrete implementations of externs (WebRTC, storage, etc.)domain/modules/- Reusable domain sub-modules
P2P Communication Architecture
Two-Layer Room System:
-
ChatRoom (Site-specific):
- RoomId: Hash of current page's origin
- Users on same site chat in isolated rooms
- Message types: Text, Like, Hate, SyncUser, SyncHistory
- History sync: Last 90 days (
SYNC_HISTORY_MAX_DAYS) - Message size limit: 256KiB (
WEB_RTC_MAX_MESSAGE_SIZE)
-
VirtualRoom (Global):
- RoomId:
WEB_CHAT_VIRTUAL_ROOMconstant - Cross-site user discovery
- Shares online user presence across different websites
- Message types: SyncUser only
- RoomId:
Connection Flow:
- User joins VirtualRoom (global presence)
- User joins ChatRoom (site-specific, based on
location.origin) - On peer join: Exchange SyncUser messages
- Sync message history if peer's lastMessageTime is older
- WebRTC data channels handle all message transport
Storage Strategy
Three-tier storage implemented in src/domain/impls/Storage.ts:
- LocalStorage - Fast, synchronous access (volatile)
- IndexDB - Large data persistence (message history)
- BrowserSyncStorage - Cross-device user profile sync (8kb limit per key)
Key storage keys in src/constants/config.ts:
USER_INFO_STORAGE_KEY- User profile (synced)MESSAGE_LIST_STORAGE_KEY- Message history (IndexDB)APP_STATUS_STORAGE_KEY- App UI state (local)
Message Sync Logic
Important sync behavior (documented in ChatRoom.ts:337-355):
- New peer joins → existing peers with newer messages push history
- Only messages newer than peer's
lastMessageTimeare synced - Messages chunked to respect WebRTC size limits
- Incremental sync (not full 90-day diff) - may result in incomplete history for peers joining at different times
Code Organization
src/
├── app/ # WXT applications (content, background, options)
├── domain/ # Remesh domains (business logic)
│ ├── externs/ # External dependency interfaces
│ ├── impls/ # Concrete implementations
│ └── modules/ # Reusable domain modules
├── components/ # React UI components
│ ├── ui/ # shadcn/ui base components
│ └── magicui/ # Magic UI animated components
├── utils/ # Pure utility functions
├── constants/ # App constants and config
├── hooks/ # React hooks
├── messenger/ # Extension messaging (webext-bridge)
├── lib/ # Third-party library integrations
└── assets/ # Static assets (images, styles)
Path Aliases
TypeScript paths configured in tsconfig.json:
@/*→./src/*
Import example: import { ChatRoomDomain } from '@/domain/ChatRoom'
Important Constants
In src/constants/config.ts:
MESSAGE_MAX_LENGTH = 500- Max message lengthMAX_AVATAR_SIZE = 5120- Max avatar size (bytes) for sync storageSYNC_HISTORY_MAX_DAYS = 90- Message history retentionWEB_RTC_MAX_MESSAGE_SIZE = 262144- 256KiB WebRTC limitVIRTUAL_ROOM_ID = 'WEB_CHAT_VIRTUAL_ROOM'- Global room identifier
Browser Extension Specifics
Manifest configuration (wxt.config.ts):
- Permissions:
storage,notifications,tabs - Matches:
https://*/* - Excludes: localhost, 127.0.0.1, csdn.net, csdn.com
- Browser-specific manifests for Chrome and Firefox
Content Script Injection:
- Shadow DOM mode:
open - Position:
inlinein body (appended last) - CSS isolation with
cssInjectionMode: 'ui' - Event isolation: keyup, keydown, keypress
Working with Remesh Domains
Creating/Using Domains:
// In content script
const store = Remesh.store({
externs: [
LocalStorageImpl,
IndexDBStorageImpl,
BrowserSyncStorageImpl,
ChatRoomImpl,
VirtualRoomImpl,
// ... other implementations
]
})
// In React component
const send = domain.useRemeshSend()
const userList = domain.useRemeshQuery(chatRoomDomain.query.UserListQuery())
// Send command
send(chatRoomDomain.command.SendTextMessageCommand('Hello'))
// Subscribe to event
domain.useRemeshEvent(chatRoomDomain.event.OnTextMessageEvent, (message) => {
console.log('New message:', message)
})
Validation with Valibot
All runtime message validation uses Valibot (not Zod):
import * as v from 'valibot'
const schema = v.object({ /* ... */ })
const isValid = v.safeParse(schema, data).success
Linting & Git Hooks
- Husky pre-commit hooks configured
- lint-staged auto-fixes JS/TS files on commit
- commitlint enforces conventional commit messages
Node Version
Minimum Node.js version: >=20.0.0 (see engines in package.json)