|
1 | 1 | # Claudius - Embeddable AI Chat Widget |
2 | 2 |
|
3 | | -An open-source, embeddable AI chat widget powered by Claude. Drop it into any website with a single script tag. |
| 3 | +An open-source, embeddable AI chat widget powered by Claude. Drop it into any |
| 4 | +website with a single script tag. |
4 | 5 |
|
5 | | -## Demo |
| 6 | +**Full documentation: [claudius-docs.pages.dev](https://claudius-docs.pages.dev)** |
6 | 7 |
|
7 | | - |
| 8 | +> Try it live at [pmds.info](https://pmds.info) or on the |
| 9 | +> [docs home page](https://claudius-docs.pages.dev). |
8 | 10 |
|
9 | | -> Try it live at [pmds.info](https://pmds.info) |
| 11 | +## Quick start |
10 | 12 |
|
11 | | -## Features |
| 13 | +Claudius is two pieces: a Cloudflare Worker (keeps your Anthropic API key |
| 14 | +server-side) and a widget embed. |
12 | 15 |
|
13 | | -- Floating chat bubble with toggle |
14 | | -- Markdown rendering (bold, italic, links) |
15 | | -- Dark mode (light, dark, auto) |
16 | | -- Conversation persistence (localStorage) |
17 | | -- Rate limiting (KV-based, per IP) |
18 | | -- Accessible (WCAG 2.1 AA, Lighthouse 98/100) |
19 | | -- Responsive (mobile-first, works at 320px+) |
20 | | -- Configurable (title, colors, system prompt, theme) |
21 | | -- Lightweight (~150KB gzipped JS + 3KB CSS) |
22 | | - |
23 | | -## Quick Start |
24 | | - |
25 | | -### 1. Set up the worker (backend) |
| 16 | +### 1. Deploy the worker |
26 | 17 |
|
27 | 18 | ```bash |
28 | | -cd worker |
| 19 | +git clone https://github.com/PMDevSolutions/Claudius.git |
| 20 | +cd Claudius/worker |
29 | 21 | pnpm install |
30 | | -cp .dev.vars.example .dev.vars # Add your Anthropic API key |
31 | | -pnpm dev # Starts on http://localhost:8787 |
32 | | -``` |
33 | | - |
34 | | -### 2. Set up the widget (frontend) |
35 | | - |
36 | | -```bash |
37 | | -cd widget |
38 | | -pnpm install |
39 | | -pnpm dev # Starts on http://localhost:5173 |
40 | | -``` |
41 | | - |
42 | | -### 3. Open http://localhost:5173 and test the chat |
43 | | - |
44 | | -## Usage |
45 | | - |
46 | | -### React Component |
47 | | - |
48 | | -Import and use `ChatWidget` directly in your React app: |
49 | | - |
50 | | -```bash |
51 | | -# Install (when published to npm) |
52 | | -npm install claudius-chat-widget |
| 22 | +npx wrangler login |
| 23 | +npx wrangler kv namespace create RATE_LIMIT # paste the id into wrangler.toml |
| 24 | +npx wrangler secret put ANTHROPIC_API_KEY |
| 25 | +npx wrangler deploy |
53 | 26 | ``` |
54 | 27 |
|
55 | | -```tsx |
56 | | -import { ChatWidget } from "claudius-chat-widget"; |
57 | | -import "claudius-chat-widget/style.css"; |
58 | | - |
59 | | -function App() { |
60 | | - return ( |
61 | | - <ChatWidget |
62 | | - apiUrl="https://your-worker.workers.dev" |
63 | | - title="Support" |
64 | | - subtitle="Ask me anything" |
65 | | - theme="auto" |
66 | | - accentColor="#0057a3" |
67 | | - position="bottom-right" |
68 | | - /> |
69 | | - ); |
70 | | -} |
71 | | -``` |
| 28 | +Then set `ALLOWED_ORIGIN` to your site's origin (Workers → Settings → |
| 29 | +Variables). Comma-separate multiple origins. |
72 | 30 |
|
73 | | -### Standalone Embed Script |
| 31 | +### 2. Embed the widget |
74 | 32 |
|
75 | | -For non-React sites, use the IIFE bundle with `window.ClaudiusConfig`: |
| 33 | +Add before `</body>`: |
76 | 34 |
|
77 | 35 | ```html |
78 | 36 | <script> |
79 | 37 | window.ClaudiusConfig = { |
80 | | - apiUrl: "https://your-worker.workers.dev", |
| 38 | + apiUrl: "https://claudius-chat-worker.<you>.workers.dev", |
81 | 39 | title: "Support", |
82 | 40 | subtitle: "Ask me anything", |
83 | 41 | theme: "auto", |
84 | | - accentColor: "#0057a3", |
85 | 42 | }; |
86 | 43 | </script> |
87 | | -<link rel="stylesheet" href="/path/to/claudius.css" /> |
88 | | -<script src="/path/to/claudius.iife.js"></script> |
89 | | -``` |
90 | | - |
91 | | -To auto-update instead of self-hosting, load the version-pinned CDN channel |
92 | | -(`@1` resolves the latest `v1.x` release; requires allowing `cdn.jsdelivr.net` |
93 | | -in your CSP). See [DEPLOY.md](DEPLOY.md#embed-via-cdn-recommended-auto-updating). |
94 | | - |
95 | | -```html |
96 | 44 | <link |
97 | 45 | rel="stylesheet" |
98 | 46 | href="https://cdn.jsdelivr.net/gh/PMDevSolutions/Claudius@1/cdn/claudius.css" |
99 | 47 | /> |
100 | 48 | <script src="https://cdn.jsdelivr.net/gh/PMDevSolutions/Claudius@1/cdn/claudius.iife.js"></script> |
101 | 49 | ``` |
102 | 50 |
|
103 | | -## Configuration |
104 | | - |
105 | | -Both the React component and embed script accept these options: |
106 | | - |
107 | | -| Option | Default | Description | |
108 | | -|--------|---------|-------------| |
109 | | -| `apiUrl` | (required) | URL of your Cloudflare Worker | |
110 | | -| `title` | `"Chat"` | Header title | |
111 | | -| `subtitle` | `"Ask me anything"` | Header subtitle | |
112 | | -| `welcomeMessage` | `"Hi! How can I help you today?"` | First message shown | |
113 | | -| `placeholder` | `"Type your message..."` | Input placeholder | |
114 | | -| `persistMessages` | `true` | Save chat history to `sessionStorage` (survives page navigation, clears on tab close) | |
115 | | -| `storageKeyPrefix` | `"claudius:messages"` | Storage key prefix; set to a unique value per widget when embedding multiple widgets on one page | |
116 | | -| `requestTimeoutMs` | `30000` | Per-attempt request timeout in ms. The widget aborts and surfaces a retryable timeout error. Set to `0` to disable. | |
117 | | -| `theme` | `"light"` | Color scheme: `"light"`, `"dark"`, or `"auto"` | |
118 | | -| `accentColor` | `"#2563eb"` | Primary brand color override | |
119 | | -| `position` | `"bottom-right"` | Widget position: `"bottom-right"`, `"bottom-left"`, `"top-right"`, `"top-left"` | |
120 | | -| `translations` | (built-in) | Custom UI strings (React component only) | |
121 | | -| `triggers` | `undefined` | Proactive triggers that auto-open the widget or show a greeting bubble. See [Proactive Triggers](#proactive-triggers) below. | |
122 | | - |
123 | | -## Proactive Triggers |
124 | | - |
125 | | -Configure the widget to auto-open or pop a greeting bubble when a visitor hits a condition. The widget remembers a dismissal in `sessionStorage`, so once the visitor closes the auto-opened chat or dismisses the bubble, no further triggers fire that session. |
126 | | - |
127 | | -```tsx |
128 | | -<ChatWidget |
129 | | - apiUrl="..." |
130 | | - triggers={[ |
131 | | - // Pop a greeting bubble after 30s on any page |
132 | | - { on: "time", seconds: 30, action: { greeting: "Need a hand?" } }, |
133 | | - |
134 | | - // Auto-open after the visitor scrolls 80% down the pricing page |
135 | | - { |
136 | | - on: "scroll", |
137 | | - percent: 80, |
138 | | - matchUrl: "/pricing", |
139 | | - action: "open", |
140 | | - }, |
141 | | - |
142 | | - // Exit-intent greeting only on the contact page |
143 | | - { |
144 | | - on: "exit-intent", |
145 | | - matchUrl: /\/contact/, |
146 | | - action: { greeting: "Have a quick question before you go?" }, |
147 | | - }, |
148 | | - |
149 | | - // Greet immediately when someone lands on /docs |
150 | | - { |
151 | | - on: "url", |
152 | | - pattern: "/docs", |
153 | | - action: { greeting: "Looking for something specific in the docs?" }, |
154 | | - }, |
155 | | - ]} |
156 | | -/> |
157 | | -``` |
158 | | - |
159 | | -Each trigger has an `on` type, an `action`, and an optional `matchUrl` to scope to specific pages. `matchUrl` (and `url`-trigger `pattern`) accepts a substring string (case-insensitive) or a `RegExp`. |
160 | | - |
161 | | -| `on` | Extra fields | When it fires | |
162 | | -|------|--------------|---------------| |
163 | | -| `"time"` | `seconds: number` | After N seconds on the page | |
164 | | -| `"scroll"` | `percent: number` (0--100) | Once the visitor scrolls past N% of the page | |
165 | | -| `"exit-intent"` | -- | When the mouse leaves the viewport via the top edge | |
166 | | -| `"url"` | `pattern: string \| RegExp` | Immediately on mount if the current URL matches | |
167 | | - |
168 | | -| `action` | Behavior | |
169 | | -|----------|----------| |
170 | | -| `"open"` | Auto-opens the chat window | |
171 | | -| `{ greeting: string }` | Pops a dismissable greeting bubble next to the toggle button | |
172 | | - |
173 | | -## Customization |
174 | | - |
175 | | -### System Prompt |
176 | | - |
177 | | -Edit `worker/src/system-prompt.ts` to customize the AI's personality, knowledge base, services, pricing, and FAQ. This is where you make the chatbot yours. |
178 | | - |
179 | | -### Brand Colors |
180 | | - |
181 | | -Edit `widget/tailwind.config.ts` to change brand colors, fonts, and border radii. Colors use CSS custom properties so you can also override them at runtime via `accentColor`. |
182 | | - |
183 | | -### Theming |
184 | | - |
185 | | -The widget supports three theme modes: |
186 | | - |
187 | | -- **`"light"`** (default) -- Light background, dark text |
188 | | -- **`"dark"`** -- Dark background, light text |
189 | | -- **`"auto"`** -- Follows the user's OS preference via `prefers-color-scheme` |
190 | | - |
191 | | -## Rate Limiting |
192 | | - |
193 | | -The worker includes KV-based rate limiting to protect against API abuse: |
194 | | - |
195 | | -- **10 requests/minute** per IP |
196 | | -- **50 requests/hour** per IP |
197 | | - |
198 | | -### Setup |
199 | | - |
200 | | -Create a KV namespace for rate limiting: |
201 | | - |
202 | | -```bash |
203 | | -cd worker |
204 | | -npx wrangler kv namespace create RATE_LIMIT |
205 | | -``` |
206 | | - |
207 | | -Copy the output ID into `wrangler.toml`: |
208 | | - |
209 | | -```toml |
210 | | -[[kv_namespaces]] |
211 | | -binding = "RATE_LIMIT" |
212 | | -id = "your-namespace-id" |
213 | | -preview_id = "your-preview-namespace-id" |
214 | | -``` |
215 | | - |
216 | | -For local development, wrangler automatically creates a local KV store. |
217 | | - |
218 | | -## Deployment |
219 | | - |
220 | | -### Deploy the Worker |
221 | | - |
222 | | -```bash |
223 | | -cd worker |
224 | | -npx wrangler login |
225 | | -npx wrangler secret put ANTHROPIC_API_KEY |
226 | | -npx wrangler deploy |
227 | | -``` |
228 | | - |
229 | | -Set `ALLOWED_ORIGIN` in the Cloudflare dashboard (Workers > Settings > Variables) to your production domain. |
230 | | - |
231 | | -### Build the Widget |
| 51 | +The `@1` CDN channel auto-updates within v1.x. That's it. |
232 | 52 |
|
233 | | -```bash |
234 | | -cd widget |
235 | | -pnpm build:embed |
236 | | -``` |
| 53 | +## Documentation |
237 | 54 |
|
238 | | -Output: `dist/claudius.iife.js` and `dist/claudius.css` |
| 55 | +| Section | What's there | |
| 56 | +|---------|--------------| |
| 57 | +| [Getting started](https://claudius-docs.pages.dev/getting-started/introduction/) | Architecture, quick start, local development | |
| 58 | +| [Configuration](https://claudius-docs.pages.dev/configuration/widget/) | Every widget and worker option, triggers, localization, theming, multi-client configs | |
| 59 | +| [Deployment](https://claudius-docs.pages.dev/deployment/worker/) | Worker setup, CDN vs self-hosted embed, per-host guides (static, WordPress, Replit, React/Next.js) | |
| 60 | +| [API reference](https://claudius-docs.pages.dev/api/rest/) | REST endpoints, error codes, widget TypeScript API | |
| 61 | +| [Migration guides](https://claudius-docs.pages.dev/migration/) | Upgrading between versions | |
| 62 | +| [FAQ](https://claudius-docs.pages.dev/faq/) | CORS, rate limits, CSP, privacy | |
239 | 63 |
|
240 | | -Host these files on your site or a CDN, then add the embed snippet to your HTML. |
241 | | - |
242 | | -## Testing |
| 64 | +## Development |
243 | 65 |
|
244 | 66 | ```bash |
245 | | -cd widget |
246 | | -pnpm test # Unit + integration (Vitest) |
247 | | -pnpm test:coverage # Coverage report (target: 80%+) |
248 | | -pnpm e2e:install # One-time: download Chromium for Playwright |
249 | | -pnpm e2e # End-to-end (Playwright, against `pnpm dev`) |
250 | | -pnpm e2e:ui # Playwright UI mode |
| 67 | +cd worker && pnpm install && pnpm dev # http://localhost:8787 |
| 68 | +cd widget && pnpm install && pnpm dev # http://localhost:5173 |
251 | 69 | ``` |
252 | 70 |
|
253 | | -The E2E suite mocks `**/api/chat` via `page.route()` so the worker doesn't need to be running, and builds the embed bundle once in `globalSetup` to exercise it via `<script src>` and `<claudius-chat>` web component. |
| 71 | +Tests: `pnpm test` in `widget/` and `worker/`. Details in the |
| 72 | +[local development guide](https://claudius-docs.pages.dev/getting-started/local-development/). |
254 | 73 |
|
255 | | -## Tech Stack |
| 74 | +## Tech stack |
256 | 75 |
|
257 | | -- **Widget:** React 18, TypeScript, Tailwind CSS, Vite |
258 | | -- **Worker:** Cloudflare Workers, Hono, Anthropic SDK, KV |
259 | | -- **AI Model:** Claude Haiku 4.5 |
| 76 | +React 18 + TypeScript + Tailwind (widget) · Cloudflare Workers + Hono + |
| 77 | +Anthropic SDK (worker) · Claude Haiku 4.5 by default. |
260 | 78 |
|
261 | 79 | ## Contributing |
262 | 80 |
|
263 | | -Contributions are welcome. Before opening a pull request, please read: |
264 | | - |
265 | | -- [CONTRIBUTING.md](./CONTRIBUTING.md) -- setup, branch naming, PR process, and code style |
266 | | -- [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) -- community expectations |
267 | | -- [SECURITY.md](./SECURITY.md) -- how to report security issues responsibly |
268 | | - |
269 | | -Quick checklist before submitting a PR: |
270 | | - |
271 | | -1. Branch from `main` using a `feat/`, `fix/`, `docs/`, or `chore/` prefix |
272 | | -2. Add tests for new functionality |
273 | | -3. Run `pnpm test` in both `widget/` and `worker/` |
274 | | -4. Write a clear PR title (under 70 chars) and description |
| 81 | +Contributions are welcome — see [CONTRIBUTING.md](./CONTRIBUTING.md), |
| 82 | +[CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md), and [SECURITY.md](./SECURITY.md) |
| 83 | +for how to report security issues responsibly. |
275 | 84 |
|
276 | 85 | ## License |
277 | 86 |
|
|
0 commit comments