@nimbusai/webchat-sdk
v1.0.5
Published
Embeddable WebChat SDK with Shadow DOM isolation and WebSocket messaging
Readme
Nimbus WebChat SDK
A modern, embeddable WebChat SDK with Shadow DOM isolation and real-time WebSocket messaging. Built with TypeScript, Tailwind CSS, and Lucide icons.
Screenshots
Full Dashboard View
Theme Variations
Layout Options
File Upload & Media Handling
Features
- Zero dependencies (except Lucide icons)
- Fully customizable UI and theming
- Responsive design with mobile support
- Shadow DOM isolation prevents CSS conflicts
- Real-time WebSocket messaging
- TypeScript support with full type definitions
- Multiple formats (ESM, CommonJS, UMD)
- Flexible icons (Lucide icons or custom images)
- Automatic reconnection with configurable retries
- Optional message history loading with resume conversation
- Multiple layouts (floating popup or sidepanel)
- File upload support with image previews (config-driven)
- Character limits with counter display
- Developer-friendly debug mode and logging
Tech Stack
| Technology | Version | Purpose |
|---|---|---|
| TypeScript | ^5.7.2 | Type-safe development |
| Tailwind CSS | ^3.4.17 | Utility-first styling (pre-compiled into Shadow DOM) |
| tsup | ^8.3.5 | Bundler (ESM + CJS + IIFE) |
| PostCSS | ^8.4.49 | CSS processing |
| Autoprefixer | ^10.4.20 | Vendor prefix automation |
| Vitest | ^2.1.8 | Unit testing |
| Lucide | inline SVGs | Icons (Shadow DOM compatible) |
Installation
npm / yarn / pnpm
npm install @nimbusai/webchat-sdk
# or
yarn add @nimbusai/webchat-sdk
# or
pnpm add @nimbusai/webchat-sdkimport { NimbusChat } from "@nimbusai/webchat-sdk";
const chat = new NimbusChat({
agent_version_id: "550e8400-e29b-41d4-a716-446655440000",
});
chat.open();CDN (script tag)
<script src="https://cdn.nimbus.ai/sdk/nimbus-chat.umd.global.js"></script>
<script>
NimbusChat.init({
agent_version_id: "550e8400-e29b-41d4-a716-446655440000",
});
</script>The CDN bundle exposes a global NimbusChat object with a singleton init() method — safe to call multiple times.
Quick Start
const chat = new NimbusChat({
agent_version_id: "550e8400-e29b-41d4-a716-446655440000",
style: { position: "bottom-right" }
});The widget renders a floating chat bubble in the corner of the page. Click it to open the chat panel. The WebSocket connection is established lazily — only when the user opens the chat for the first time.
API Reference
Initialization
// NPM usage
const chat = new NimbusChat(config);
// CDN usage
const chat = NimbusChat.init(config);Methods
| Method | Description |
|--------|-------------|
| chat.open() | Show the chat widget |
| chat.close() | Hide the chat widget |
| chat.toggle() | Toggle chat widget visibility |
| chat.destroy() | Remove the widget from DOM completely |
Configuration Reference
The SDK provides extensive configuration options to customize every aspect of the chat widget. All properties are optional except agent_version_id.
Required Configuration
| Property | Type | Description |
|----------|------|-------------|
| agent_version_id | string | Required - Your agent ID (UUID) |
Core Settings
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| dns | string | "api.nimbus.ai/api/v1/webchat" | API endpoint for chat services |
| debug | boolean | false | Enable debug logging to console. When true, the widget auto-opens on init. |
| allowNewChat | boolean | false | Show "New Chat" button in header |
Style Configuration
Controls layout, dimensions, and visual appearance of the widget:
style: {
position: "bottom-right",
width: "380px", // CSS width string (e.g. "380px", "100%")
height: "560px", // CSS height string (window mode only)
font: '"Inter Variable", sans-serif', // Global font family override
background: "#ffffff", // CSS color or image URL
mobile: {
position: "bottom-right", // Override position on mobile devices
breakpoint: "480px" // Viewport width to trigger mobile mode
}
}| Property | Type | Default | Description |
|----------|------|---------|-------------|
| style.position | ChatPosition | "bottom-right" | Widget position on page. Options: "bottom-right", "bottom-left", "sidepanel-left", "sidepanel-right" |
| style.width | string | "380px" | Chat window width (CSS value) |
| style.height | string | "560px" | Chat window height (CSS value, window mode only) |
| style.font | string | System font | Global font family override |
| style.background | string | "#f3f1ef" | Chat body background (CSS color or image URL) |
| style.mobile.position | ChatPosition | Same as style.position | Widget position on mobile devices |
| style.mobile.breakpoint | string | undefined | Viewport width breakpoint for mobile mode (e.g. "480px") |
Theme Configuration
Global theme colors used throughout the widget:
theme: {
primary: "#ffce1c", // Primary color (buttons, headers, bubbles)
secondary: "#f3f1ef" // Secondary color (text, icons on primary bg)
}Chat Bubble Settings
The floating trigger button that opens/closes the chat:
bubble: {
position: "bottom-right",
autoHide: false, // Hide bubble when chat is open
icon: {
img: "message-circle", // Lucide icon or image URL
size: { width: 35, height: 35 }
}
}| Property | Type | Default | Description |
|----------|------|---------|-------------|
| position | BubblePosition | "bottom-right" | Bubble position. Options: "bottom-right", "bottom-left" |
| autoHide | boolean | false | Hide the bubble when the chat panel is open |
| icon | IconConfig \| null | Default Lucide icon | Custom icon for the bubble |
Header Configuration
header: {
icon: {
img: "https://example.com/logo.svg", // Custom image URL or Lucide icon
size: { width: 125, height: 19 }
},
text: {
value: "Support Chat",
color: "#1e293b",
font: "Inter",
},
color: {
primary: "#f3f1ef", // Header background color
secondary: "#ffce1c" // Close button icon color
}
}Welcome Message
Shown when chat is empty, automatically hidden after first message:
welcome: {
display: true, // Show/hide welcome message
preTitle: {
value: "Welcome to :",
text: { color: "#1e293b", font: "" }
},
title: {
value: " Nimbus Chat!",
text: { color: "#1e293b", font: "" }
},
subtitle: {
value: "Send a message to start a conversation",
text: { color: "gray", font: "" }
}
}Message Styling
Configure user and bot message bubbles independently:
userMessage: {
background: "#DCF8C6",
width: "80%", // Max width of message bubble (CSS value). Default: "80%"
text: {
color: "#111B21",
font: "",
size: 13
},
icon: {
img: "user", // Lucide icon or custom image URL
size: { width: 20, height: 20 }
}
}
botMessage: {
background: "#FFFFFF",
width: "80%", // Max width of message bubble (CSS value). Default: "80%"
text: {
color: "#111B21",
font: "",
size: 13
},
icon: {
img: "bot", // Lucide icon or custom image URL
size: { width: 20, height: 20 }
}
}Note: Set
icon: nullto explicitly hide the avatar. Omiticonentirely to use the default.
Input Configuration
input: {
placeholder: "Ask Nimbus",
expandable: true, // Auto-expanding textarea (up to 8 lines)
text: {
color: "#1e293b",
font: "",
},
background: {
primary: "white", // Input field background
secondary: "#f3f1ef" // Container background
},
upload: { // File upload settings (omit or set null to disable)
maxFileSize: 5242880, // 5MB in bytes
errorDisplayDuration: 2000, // Error message duration (ms)
allowedFileTypes: [
"image/jpeg",
"image/png",
"image/gif",
"application/pdf"
],
icon: {
img: "paperclip",
size: { width: 20, height: 20 }
}
},
maxCharacters: { // Character limit with counter
limit: 10000, // Maximum characters allowed
text: {
color: "gray",
size: 12
}
}
}Note: File upload is config-driven. Omit the
uploadproperty or set it tonullto disable file uploads. Provide anuploadobject to enable the upload button.
Send Button
sendButton: {
align: false, // true = input and button on same row
icon: {
img: "send", // Lucide icon or custom image URL
size: { width: 20, height: 20 }
}
}Reconnection Settings
Control automatic reconnection behavior:
reconnect: {
attempts: 5, // Maximum reconnection attempts
timeout: 5000 // Delay between attempts (ms)
}Wait for Reply Configuration
Control message sending while waiting for bot responses. Works independently from the typing indicator.
waitForReply: {
timeout: 30000, // Max wait time (ms) before allowing next message
firstReply: false // Wait for bot's first message in new chat
}Note: Omit
waitForReplyto disable input blocking entirely.
Typing Indicator Configuration
Show a typing indicator when waiting for bot responses. Independent from waitForReply — you can use either or both.
isTypingIndicator: {
position: "bottom",
title: {
value: "AI Assistant is typing...",
text: {
color: "#1e293b",
font: ""
}
}
}| Property | Type | Default | Description |
|----------|------|---------|-------------|
| position | "top" \| "bottom" | "top" | Where to display the indicator. Options: "top" (header status area), "bottom" (above input area) |
| title.value | string | "AI Assistant is typing..." | Indicator text |
| title.text | TextConfig | { color: "#1e293b" } | Text styling |
Note: Omit
isTypingIndicatorto disable the typing indicator.
Resume Conversation
When provided, enables loading message history on reconnect and shows a "Show More" button to load older messages. Omit or set to null to disable (default). The flow_id is always persisted and reused regardless.
resumeConversation: {
messagesPerPage: 10, // Messages loaded per page
showMore: {
value: "Show More",
sticky: true, // Stick to top of message list
background: "#FFF4CC",
text: {
color: "black",
size: 13
},
icon: {
img: "chevron-down",
size: { width: 16, height: 16 }
}
}
}| Field | Type | Default | Description |
|-------|------|---------|-------------|
| messagesPerPage | number | 10 | Number of messages loaded per page when scrolling history |
| showMore.value | string | "Show More" | Button label text |
| showMore.sticky | boolean | true | Stick button to top of message list while scrolling |
| showMore.background | string | theme secondary | Button background color |
| showMore.text | TextConfig | { color: "black", size: 13 } | Button text styling |
| showMore.icon | IconConfig | { img: "chevron-down", size: 16x16 } | Button icon |
Icon System
The SDK supports two types of icons throughout all configuration:
Lucide Icons (Recommended)
Text-based icon names that render as inline SVG:
icon: {
img: "message-circle",
size: { width: 24, height: 24 },
color: "#ffffff"
}Popular Lucide Icons:
"message-circle"- Chat bubble"send"- Send arrow"x"- Close/X mark"rotate-cw"- Refresh/new chat"chevron-down"/"chevron-up"- Arrows"user"- User avatar"bot"- Bot avatar"paperclip"- File attachment
Browse all 1000+ Lucide icons →
Custom Images
URL-based custom images:
icon: {
img: "https://example.com/logo.png",
size: { width: 32, height: 32 }
// color is ignored for image URLs
}Hiding Icons
icon: null // Explicitly hide the icon
// or
// Omit the icon property entirely to use defaultShared Type Definitions
TextConfig
Used for styling text elements throughout the SDK:
interface TextConfig {
display?: boolean; // Show/hide text (not used everywhere)
value?: string; // The text content
color?: string; // CSS color value
font?: string; // Font family override
size?: number; // Font size in pixels
}IconConfig
Used for all icons in the SDK:
interface IconConfig {
img?: string; // Lucide icon name or image URL
size?: {
width: number; // Width in pixels
height: number; // Height in pixels
};
color?: string; // Icon color (CSS color). Only applies to Lucide icons, ignored for image URLs.
}ColorPair
Used for primary/secondary color combinations:
interface ColorPair {
primary?: string; // Primary color (usually background)
secondary?: string; // Secondary color (usually foreground)
}Complete Configuration Example
const chat = new NimbusChat({
// Required
agent_version_id: "550e8400-e29b-41d4-a716-446655440000",
// Core Settings
dns: "api.nimbus.ai/api/v1/chat/ws",
debug: true,
allowNewChat: true,
// Theme
theme: {
primary: "#ffce1c",
secondary: "#f3f1ef"
},
// Style
style: {
position: "bottom-right",
width: "450px",
height: "500px",
font: "",
background: "#f3f1ef",
mobile: {
position: "bottom-right",
breakpoint: "480px"
}
},
// Chat Bubble
bubble: {
position: "bottom-right",
autoHide: false,
icon: {
img: "message-circle",
size: { width: 35, height: 35 }
}
},
// Header
header: {
icon: {
img: "https://example.com/logo.svg",
size: { width: 125, height: 19 }
},
text: {
value: "",
color: "#1e293b",
font: "",
},
color: {
primary: "#f3f1ef",
secondary: "#ffce1c"
}
},
// Welcome Message
welcome: {
display: true,
preTitle: {
value: "Welcome to :",
text: { color: "#1e293b", font: "" }
},
title: {
value: " Nimbus Chat!",
text: { color: "#1e293b", font: "" }
},
subtitle: {
value: "Send a message to start a conversation",
text: { color: "gray", font: "" }
}
},
// Messages
userMessage: {
background: "#DCF8C6",
width: "80%",
text: { color: "#111B21", font: "", size: 13 },
icon: {
img: "user",
size: { width: 20, height: 20 }
}
},
botMessage: {
background: "#FFFFFF",
width: "80%",
text: { color: "#111B21", font: "", size: 13 },
icon: {
img: "bot",
size: { width: 20, height: 20 }
}
},
// Input Area
input: {
placeholder: "Ask Nimbus",
expandable: true,
text: {
color: "#1e293b",
font: "",
},
background: {
primary: "white",
secondary: "#f3f1ef"
},
maxCharacters: {
limit: 10000,
text: {
color: "gray",
font: "",
size: 12
}
},
upload: null
},
// Send Button
sendButton: {
align: false,
icon: {
img: "send",
size: { width: 20, height: 20 }
}
},
// Reconnection
reconnect: {
attempts: 5,
timeout: 5000
},
// Wait for Reply
waitForReply: {
timeout: 30000,
firstReply: false
},
// Typing Indicator (independent from waitForReply)
isTypingIndicator: {
position: "bottom",
title: {
value: "AI Assistant is typing...",
text: { color: "#1e293b", font: "" }
}
},
// Resume Conversation (omit or set null to disable)
resumeConversation: {
messagesPerPage: 10,
showMore: {
value: "Show More",
sticky: true,
background: "#FFF4CC",
text: {
color: "black",
font: "",
size: 13
},
icon: {
img: "chevron-down",
size: { width: 16, height: 16 }
}
}
},
});Debug Mode
Enable comprehensive logging for development and troubleshooting:
const chat = new NimbusChat({
agent_version_id: "your-uuid-here",
debug: true
});Debug mode provides:
- WebSocket connection events
- Message send/receive logs
- Session state changes
- Configuration resolution details
- Error stack traces
Development
Prerequisites
- Node.js >= 18
- npm >= 9
Setup
git clone https://github.com/nichemarket/chat-sdk.git
cd chat-sdk
npm installScripts
npm run build # Full build (CSS + TypeScript)
npm run build:css # Compile Tailwind only
npm run build:ts # Compile TypeScript only
npm run type-check # Type-check without emitting
npm run dev # Watch mode
npm test # Run tests
npm run test:watch # Watch mode testsTesting
The SDK uses Vitest with jsdom for unit testing. Tests cover configuration resolution, validation, core modules, UI utilities, and DOM components.
npm test # Run all tests
npm run test:watch # Run in watch modeTest suite structure:
| File | Covers |
|---|---|
| tests/types/resolveConfig.test.ts | Config resolution, defaults, overrides, deep merging |
| tests/utils/validateConfig.test.ts | Required fields, type validation, nested config validation |
| tests/core/EventBus.test.ts | Pub/sub, on/off/once/emit, error isolation, typed events |
| tests/core/ChatSession.test.ts | Session ID, messages, history load/merge, dedup, clear |
| tests/utils/FileUtils.test.ts | File size formatting, Base64 conversion |
| tests/utils/DOMBuilder.test.ts | Element creation, props, chaining, append, conditionals, fragments |
| tests/ui/ThemeManager.test.ts | CSS variables, theme colors, backgrounds, fonts, input styles |
| tests/utils/IconRenderer.test.ts | Lucide SVG rendering, URL images, unknown icons, wrapper/simple modes |
Common Development Commands
# Clean installation (recommended for troubleshooting)
rm -rf node_modules package-lock.json && npm install && npm run build
# Fast build and serve for testing
npm run build && npx http-server -p 8081 -c-1Code Standards
Logging: Always use the custom logger instead of console.* statements:
import { createLogger } from './utils/logger';
// For utility files - create logger at top level
const logger = createLogger('ComponentName');
// For classes - create logger in constructor
class MyComponent {
private logger: Logger;
constructor(debug = false) {
this.logger = createLogger('MyComponent', debug);
}
}
// Usage
logger.info('Info message');
logger.warn('Warning message');
logger.error('Error message');Output Files
| Format | File | Size |
|---|---|---|
| ESM | dist/index.mjs | ~48 KB |
| CJS | dist/index.js | ~49 KB |
| IIFE/UMD | dist/nimbus-chat.umd.global.js | ~49 KB |
| Types | dist/index.d.ts | ~8.5 KB |
Project Structure
chat-sdk/
├── src/
│ ├── core/ # EventBus, ChatSession, WebSocketManager, ApiClient
│ ├── icons/ # Lucide SVG icon definitions
│ ├── styles/ # Tailwind globals + compiled CSS
│ ├── types/ # TypeScript interfaces (config, message)
│ ├── ui/ # UI components (Header, MessageList, InputArea, etc.)
│ ├── utils/ # Utilities (logger, validator, parser, etc.)
│ ├── NimbusChat.ts # Main SDK orchestrator
│ └── index.ts # Public exports
├── examples/
│ ├── cdn-usage.html # CDN integration example (full config)
│ ├── cdn-usage-basic.html # CDN integration example (minimal config)
│ └── cdn-usage-black.html # CDN integration example (dark theme)
├── dist/ # Build output
├── tsconfig.json
├── tsup.config.js
├── tailwind.config.js
├── postcss.config.js
└── package.jsonArchitecture
The SDK uses Shadow DOM (open mode) to fully isolate styles from the host page. Tailwind CSS is pre-compiled at build time and injected into the shadow root at runtime alongside CSS custom properties for theming.
Component Architecture
NimbusChat (orchestrator)
├── EventBus (pub/sub messaging)
├── ChatSession (message state + history)
├── WebSocketManager (connection lifecycle)
├── ApiClient (REST API)
└── UI Components
├── ShadowContainer (DOM isolation)
├── ThemeManager (CSS custom properties)
├── ChatWindow/SidepanelWindow (containers)
├── Header
├── MessageList
├── InputArea (with FileUpload)
├── MessageBubble
└── ChatBubble (floating trigger)Connection Lifecycle
- Initial Connection: Client opens WebSocket with
agent_version_id(andflow_idif available from localStorage) - Connected: Server responds with
{ "type": "connected", "flow_id": "uuid" } - Persistence:
flow_idis always stored in localStorage and reused on reconnect - History Loading: If
resumeConversationis configured (not null), fetches message history via REST API after connection - Messaging: Simple message exchange with
direction: "inbound"(user) anddirection: "outbound"(bot)
WebSocket Events
Incoming:
connected- Connection established, providesflow_idmessage- New message from server (direction: "outbound")
Outgoing:
message- User message (direction: "inbound")
Message Schema
// User message (sent to server)
{ "type": "message", "direction": "inbound", "content": "Hello!" }
// Bot message (received from server)
{ "type": "message", "direction": "outbound", "content": "Hi there!" }
// Connected event (received from server)
{ "type": "connected", "flow_id": "550e8400-e29b-41d4-a716-446655440000" }