AI chatbot widget powered by Claude. Features a beautiful liquid glass UI with video background support.
- Dual Mode - Full-page container or floating popup widget
- Streaming Responses - Real-time message streaming from Claude
- Liquid Glass UI - iOS-style glassmorphism effects
- Video Background - Animated wave background support
- Persistent Sessions - Chat history via configurable endpoints
- RAG Support - Vector search with Cohere embeddings
- Mobile Responsive - Works on all screen sizes
- Edge Compatible - Cloudflare Workers ready
npm install embeddable-chatbot<script>
import { Chat } from 'embeddable-chatbot';
</script>
<Chat
mode="container"
apiEndpoint="/api/chat"
loadEndpoint="/api/chat/load"
videoSrc="/wave-background.mp4"
/><script>
import { ChatPopup } from 'embeddable-chatbot';
</script>
<ChatPopup
apiEndpoint="/api/chat"
loadEndpoint="/api/chat/load"
/>The package exports createChatHandler for SvelteKit API routes.
Basic usage:
import { createChatHandler } from 'embeddable-chatbot/server';
const handler = createChatHandler({
apiKey: env.ANTHROPIC_API_KEY,
systemPrompt: 'You are a helpful assistant.',
onSave: async (sessionId, messages) => { /* save to DB */ }
});Full examples with RAG: See examples/api-chat-server.ts
| Option | Type | Default | Description |
|---|---|---|---|
apiKey |
string |
required | Anthropic API key |
systemPrompt |
string |
Generic prompt | System prompt for Claude |
model |
string |
claude-sonnet-4-5-20250929 |
Claude model |
maxTokens |
number |
1024 |
Max response tokens |
onSave |
function |
- | Callback to persist chat |
CREATE TABLE chats (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
session_id TEXT NOT NULL UNIQUE,
messages JSONB NOT NULL DEFAULT '[]',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_chats_session_id ON chats(session_id);
ALTER TABLE chats ENABLE ROW LEVEL SECURITY;TypeScript helpers: See examples/supabase.ts
Add knowledge retrieval using Cohere embeddings + reranking.
- Embed your content with Cohere
embed-v4.0 - Store in Supabase pgvector
- At query time: vector search → rerank → inject into Claude context
CREATE EXTENSION IF NOT EXISTS vector WITH SCHEMA extensions;
CREATE TABLE site_embeddings (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
content TEXT NOT NULL,
embedding extensions.vector(1536),
page_url TEXT NOT NULL,
page_title TEXT,
section_heading TEXT,
chunk_index INT DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX site_embeddings_embedding_idx
ON site_embeddings USING hnsw (embedding vector_cosine_ops);
ALTER TABLE site_embeddings ENABLE ROW LEVEL SECURITY;CREATE OR REPLACE FUNCTION match_site_content(
query_embedding extensions.vector(1536),
match_threshold FLOAT DEFAULT 0.3,
match_count INT DEFAULT 10
)
RETURNS TABLE (
id BIGINT, content TEXT, page_url TEXT,
page_title TEXT, section_heading TEXT, similarity FLOAT
)
LANGUAGE plpgsql AS $$
BEGIN
RETURN QUERY
SELECT se.id, se.content, se.page_url, se.page_title, se.section_heading,
1 - (se.embedding <=> query_embedding) AS similarity
FROM site_embeddings se
WHERE 1 - (se.embedding <=> query_embedding) > match_threshold
ORDER BY se.embedding <=> query_embedding
LIMIT match_count;
END;
$$;Full implementation: See examples/rag.ts and examples/embed-site-content.ts
Complete working examples in the examples/ directory:
| File | Description |
|---|---|
supabase.ts |
Supabase client + chat save/load |
rag.ts |
RAG retrieval with Cohere embed + rerank |
embed-site-content.ts |
Script to embed your content |
api-chat-server.ts |
Chat endpoint with RAG |
api-chat-load-server.ts |
Chat history endpoint |
chat-page.svelte |
Example chat page |
| Prop | Type | Default | Description |
|---|---|---|---|
mode |
'container' | 'popup' |
'container' |
Display mode |
apiEndpoint |
string |
'/api/chat' |
Chat API URL |
loadEndpoint |
string |
'' |
History load URL |
welcomeText |
string |
"Hi! How can I help?" |
Initial message |
placeholder |
string |
'Type a message...' |
Input placeholder |
videoSrc |
string |
- | Video background URL |
videoPoster |
string |
'' |
Video poster image |
glassContrast |
'dark' | 'light' |
'light' |
Glass color scheme |
glassRoundness |
number |
24 |
Border radius (px) |
glassBlur |
number |
8 |
Backdrop blur (px) |
glassOpacity |
number |
0.3 |
Background opacity |
| Prop | Type | Default | Description |
|---|---|---|---|
apiEndpoint |
string |
'/api/chat' |
Chat API URL |
loadEndpoint |
string |
'' |
History load URL |
welcomeText |
string |
"Hi! How can I help?" |
Initial message |
position |
'bottom-right' | 'bottom-left' |
'bottom-right' |
Button position |
headerTitle |
string |
'Chat' |
Header text |
bodyBg |
string |
'transparent' |
Background (color/video) |
buttonBg |
string |
'transparent' |
Toggle button bg |
buttonIconColor |
string |
'#ffffff' |
Button icon color |
| Prop | Type | Default | Description |
|---|---|---|---|
contrast |
'dark' | 'light' |
'dark' |
Color scheme |
roundness |
number |
32 |
Border radius (px) |
blur |
number |
20 |
Backdrop blur (px) |
opacity |
number |
0.6 |
Background opacity |
ANTHROPIC_API_KEY=sk-ant-... # Required
SUPABASE_URL=https://... # For persistence
SUPABASE_SECRET_KEY=... # For persistence
COHERE_API_KEY=... # For RAGUse $env/dynamic/private for Cloudflare Workers compatibility.
interface ChatMessage {
sender: 'user' | 'bot';
text: string;
}
interface ChatHandlerOptions {
apiKey: string;
systemPrompt?: string;
model?: string;
maxTokens?: number;
onSave?: (sessionId: string, history: ChatMessage[]) => Promise<void>;
}// Components
import { Chat, ChatPopup, ChatWidget, ChatInput, LiquidGlass } from 'embeddable-chatbot';
// Server
import { createChatHandler } from 'embeddable-chatbot/server';
import type { ChatMessage, ChatHandlerOptions } from 'embeddable-chatbot/server';- Svelte 5 / SvelteKit 2
- Node.js 18+
- Anthropic API key
MIT

