npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nimbusai/webchat-sdk

v1.1.2

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.

npm version License: MIT TypeScript 5.7 Tailwind CSS 3.4 Shadow DOM


Screenshots

Welcome screen

Layout options

Features

  • Zero runtime dependencies (Lucide icons are inlined as SVGs)
  • Fully customizable UI and theming through configuration
  • Responsive design with mobile breakpoint override
  • Shadow DOM isolation prevents CSS conflicts with the host page
  • Real-time WebSocket messaging with automatic reconnection
  • Markdown rendering for text messages (GFM, sanitized with DOMPurify)
  • TypeScript support with full type definitions
  • Multiple distribution formats (ESM, CommonJS, IIFE/UMD)
  • Flexible icons (Lucide icons or custom image URLs)
  • Conversation persistence in localStorage with optional cross-flow history
  • Sound + visual notifications when a bot message arrives while the chat is closed
  • Character limit with live counter
  • Multiple layouts (floating popup or sidepanel, left or right)
  • Developer-friendly debug mode with header tooltip, debug snapshot API, and config warnings

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) | | marked | ^14 | Markdown parser | | DOMPurify | ^3 | HTML sanitization | | Vitest | ^2.1.8 | Unit testing | | Lucide | inline SVGs | Icon library (1000+ icons) |

Installation

npm / yarn / pnpm

npm install @nimbusai/webchat-sdk
# or
yarn add @nimbusai/webchat-sdk
# or
pnpm add @nimbusai/webchat-sdk
import { 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 (subsequent calls return the existing instance).

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);
const instance = NimbusChat.getInstance(); // returns the singleton or null

Methods

| Method | Description | |--------|-------------| | chat.open() | Show the chat widget | | chat.close() | Hide the chat widget | | chat.toggle() | Toggle widget visibility | | chat.destroy() | Remove the widget from DOM and disconnect WebSocket. Not reusable afterwards. | | chat.getDebugInfo() | Returns a full debug snapshot (config, connection state, storage, recent events). Useful in support tickets. |


Configuration Reference

All properties are optional except agent_version_id. The SDK normalizes the config and fills in sensible defaults for everything else.

Null handling. Passing null for any top-level config object (resumeConversation, style, theme, bubble, userMessage, botMessage, header, input, sendButton, welcome, reconnect, waitForReply, isTypingIndicator, notifications) is equivalent to omitting it — defaults are applied in both cases. Sub-fields still require their declared types; null on a nested string/boolean still throws.

Required

| Property | Type | Description | |----------|------|-------------| | agent_version_id | string (UUID) | Required. Identifies the bot/agent version this widget talks to. |

Core settings

| Property | Type | Default | Description | |----------|------|---------|-------------| | dns | string | "api.nimbus.ai/api/v1/webchat" | Host (and path) of the WebSocket endpoint. Do not include the protocol — the SDK adds wss:// automatically. | | flow_id | string (UUID) | none | Forces a specific flow_id. When set, the SDK always reuses it and the "New Chat" button is disabled. | | test | boolean | false | Appends ?test=True to the WebSocket connection. | | debug | boolean | false | Enables console logging, auto-opens the widget on init, surfaces the debug tooltip in the header, and emits config warnings. | | renderMarkdown | boolean | true | Render message text as GitHub-flavored Markdown (sanitized via DOMPurify). | | allowNewChat | boolean | false | Show the "New Chat" button in the header. Ignored when flow_id is set. | | container | HTMLElement | none | Optional container to render the widget into (uses absolute positioning inside it). The container must have position: relative (or absolute/fixed) and overflow: hidden. |

style — layout & dimensions

style: {
  position: "bottom-right",      // "bottom-right" | "bottom-left" | "sidepanel-left" | "sidepanel-right"
  width: "380px",                // CSS width (e.g. "380px", "100%")
  height: "560px",               // CSS height — only used in popup mode
  font: '"Inter Variable", sans-serif',  // Global font family override
  background: "#f3f1ef",         // CSS color or image URL for the chat body
  mobile: {
    position: "bottom-right",    // Override position on mobile
    breakpoint: "480px"          // Viewport width to trigger mobile mode
  }
}

theme — global colors

theme: {
  primary: "#ffce1c",   // Send button bg, header bg, message bubble bg
  secondary: "#f3f1ef"  // Foreground/accent color paired with primary
}

bubble — floating trigger button

bubble: {
  position: "bottom-right",  // "bottom-right" | "bottom-left"
  autoHide: false,           // Hide bubble when chat is open
  icon: {
    img: "message-circle",   // Lucide icon name OR image URL
    size: { width: 35, height: 35 }
  }
}

header

header: {
  icon: {
    img: "https://example.com/logo.svg",
    size: { width: 125, height: 19 }
  },
  text: {
    display: true,         // Set to `false` to hide the title entirely (e.g. when the logo already shows the brand)
    value: "Support",
    color: "#1e293b",
    font: "Inter"
  },
  color: {
    primary: "#f3f1ef",    // Header background
    secondary: "#ffce1c"   // Close/new-chat icon color
  }
}

welcome — empty-state message

welcome: {
  display: true,
  preTitle: { value: "Welcome to :", text: { color: "#1e293b" } },
  title:    { value: " Nimbus Chat!", text: { color: "#1e293b" } },
  subtitle: { value: "Send a message to start a conversation", text: { color: "gray" } }
}

Automatically hidden once the first message lands.

userMessage / botMessage

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 } }
}

Pass icon: null to hide the avatar; omit the icon key entirely to use the default Lucide icon.

input

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
  },
  maxCharacters: {
    limit: 10000,
    text: { color: "gray", size: 12 }
  }
}

sendButton

sendButton: {
  align: false,             // true = input and button on the same row
  icon: {
    img: "send",
    size: { width: 20, height: 20 }
  }
}

reconnect

reconnect: {
  attempts: 5,      // Maximum reconnection attempts
  timeout: 5000     // Delay between attempts (ms)
}

waitForReply — block input while waiting for the bot

waitForReply: {
  timeout: 30000,           // Max wait (ms) before re-enabling input. Default: 10000
  firstReply: false         // Wait for the bot's first message in a new conversation. Default: false
}

Omit waitForReply entirely to disable input blocking.

isTypingIndicator

isTypingIndicator: {
  position: "bottom",       // "top" (header status area) | "bottom" (above input)
  title: {
    value: "AI Assistant is typing...",
    text: { color: "#1e293b", font: "" }
  }
}

Independent from waitForReply. Omit to disable.

resumeConversation — message persistence in localStorage

resumeConversation: {
  enabled: true,       // master switch — default true
  keepHistory: true    // default true: keep messages of past flowIds (so the user can return to them)
                       // false: wipe past flowIds whenever the current flowId changes
}

When enabled is true, the SDK persists messages and per-flow metadata under a single localStorage key. When the user reloads or reopens the widget, messages of the current flow_id are restored automatically.

Default behavior (omitting the field entirely) is { enabled: true, keepHistory: true } — fully on.

| Field | Type | Default | Description | |-------|------|---------|-------------| | enabled | boolean | true | Master switch for persistence. When false, no messages are saved in localStorage. | | keepHistory | boolean | true | When true, messages of past flow_ids are preserved. When false, only the current flow's data is kept; switching flow wipes the rest. |

Clicking the in-app New Chat button always clears everything (regardless of keepHistory) — it represents an explicit user-driven reset.

notifications — sound + visual badge

When a bot message arrives while the chat panel is closed, the SDK plays a sound and shows a counter badge on the bubble.

notifications: {
  enabled: true,            // Master switch
  sound: {
    enabled: true,
    src: "https://example.com/ping.mp3"  // Optional custom audio (.mp3, .wav, .ogg)
  },
  visual: {
    enabled: true,
    icon: null,             // IconConfig | null (hide the badge icon)
    color: {
      primary: "#ef4444",   // Badge background
      secondary: "#ffffff"  // Badge foreground
    },
    text: { color: "#ffffff", size: 11 }
  }
}

Icon System

The SDK supports two types of icons throughout all configuration:

Lucide icons (recommended)

icon: {
  img: "message-circle",
  size: { width: 24, height: 24 },
  color: "#ffffff"
}

Popular Lucide icons:

  • "message-circle" — chat bubble
  • "send" — send arrow
  • "x" — close
  • "rotate-cw" — refresh / new chat
  • "chevron-down" / "chevron-up" — arrows
  • "user" — user avatar
  • "bot" — bot avatar
  • "info" — info indicator (used by the debug tooltip)

Browse all 1000+ Lucide icons →

Custom image URLs

icon: {
  img: "https://example.com/logo.png",
  size: { width: 32, height: 32 }
  // color is ignored for image URLs
}

Hiding icons

icon: null   // Hide explicitly
// Omit the `icon` property entirely to keep the default Lucide icon.

All six icon fields respect null to hide: bubble.icon, userMessage.icon, botMessage.icon, header.icon, sendButton.icon, notifications.visual.icon.


Shared Type Definitions

interface TextConfig {
  display?: boolean;
  value?: string;
  color?: string;
  font?: string;
  size?: number;   // pixels
}

interface IconConfig {
  img?: string;    // Lucide icon name OR image URL
  size?: { width: number; height: number };
  color?: string;  // Applied only to Lucide icons (ignored for image URLs)
}

interface ColorPair {
  primary?: string;
  secondary?: string;
}

interface ChatMessage {
  direction: "INBOUND" | "OUTBOUND";   // INBOUND = user, OUTBOUND = bot
  content: string;
  created_at: number;                  // epoch seconds
}

Debug Mode

Enable comprehensive runtime diagnostics for development and support:

const chat = new NimbusChat({
  agent_version_id: "your-uuid",
  debug: true
});

Debug mode provides:

  • Colorized console logs — WebSocket lifecycle, message send/receive, session changes, perf metrics
  • Debug tooltip in the header — hover the i icon (left of the close button) to see Flow ID, created/last-message timestamps, SDK version, agent ID, DNS, connection state, reconnect attempts, message counts, stored flows + storage size
  • Auto-open — the widget opens automatically on init
  • Config warnings — non-fatal misconfiguration warnings emitted to the console at init time
  • Performance tracking — when waitForReply.firstReply: true, logs the time-to-first-bot-message; always logs user→bot response latency

Programmatic debug snapshot

const info = chat.getDebugInfo();
// {
//   sdkVersion: "1.0.9",
//   config: ResolvedConfig,
//   configWarnings: string[],
//   connection: {
//     state, reconnectAttempts, maxReconnectAttempts,
//     lastCloseCode, lastCloseReason,
//     lastConnectedAt, lastDisconnectedAt,
//     dns, agentVersionId, testMode, url, fixedFlowId
//   },
//   session: { flowId, messageCount },
//   storage: { enabled, keepHistory, activeFlow, sizeBytes, flows: [...] },
//   recentEvents: [{ at, name, argsPreview }, ...]   // ring buffer of last 50 events
// }

Works with the singleton too: NimbusChat.getInstance().getDebugInfo().

Config warnings detected

When debug: true, the SDK warns about common misconfigurations without throwing:

  • dns containing a protocol (ws://, wss://, http://, https://) or a trailing slash
  • flow_id set together with allowNewChat: true (the button is disabled)
  • flow_id set together with resumeConversation.enabled: true (keepHistory becomes a no-op)
  • theme.primary / theme.secondary values that don't look like valid CSS colors
  • resumeConversation.keepHistory: false while enabled: false (no effect)

The warnings are also exposed in chat.getDebugInfo().configWarnings regardless of debug mode.

What gets persisted in localStorage

Two keys, both scoped to the host origin:

| Key | Shape | When written | |---|---|---| | nimbus_webchat_flow_id | Plain string (UUID) of the active flow | Whenever the server assigns or rotates the flow_id. Independent of resumeConversation. | | nimbus_webchat_messages | { [flowId]: { created_at, last_message_at, messages: ChatMessage[] } } | Only when resumeConversation.enabled === true. created_at is set once per flow and never updated. last_message_at refreshes on every message. |

No personal data, tokens, or widget configuration are stored.


Development

Prerequisites

  • Node.js >= 18
  • npm >= 9

Setup

git clone https://github.com/nichemarket/chat-sdk.git
cd chat-sdk
npm install

Scripts

npm run build          # Full build (CSS + TypeScript bundles)
npm run build:css      # Compile Tailwind only
npm run build:ts       # Compile TypeScript only
npm run type-check     # tsc --noEmit
npm run dev            # Watch mode
npm test               # Run all tests
npm run test:watch     # Watch mode tests

Common workflows

# Clean reinstall (for troubleshooting)
rm -rf node_modules package-lock.json && npm install && npm run build

# Local testing of the UMD bundle
npm run build && npx http-server -p 8081 -c-1
# then open one of the examples/*.html files

Test suite

The SDK uses Vitest with jsdom. Tests cover:

| File | Covers | |---|---| | tests/types/resolveConfig.test.ts | Config resolution, defaults, deep merging | | tests/utils/validateConfig.test.ts | Required fields, type validation | | tests/utils/warnConfig.test.ts | Non-fatal config warnings (dns protocol, flow_id mismatches, theme colors, ...) | | tests/utils/PerformanceTracker.test.ts | Debug latency metrics (first AI message, response time) | | tests/core/EventBus.test.ts | Pub/sub, on/off/once/emit, error isolation | | tests/core/ChatSession.test.ts | Session ID, messages, setMessages, clear | | tests/core/MessageStorage.test.ts | Per-flow localStorage persistence, metadata, clearOthers, corruption tolerance | | tests/utils/DOMBuilder.test.ts | Element creation, props, chaining, fragments | | tests/ui/ThemeManager.test.ts | CSS variables, theme colors, backgrounds | | tests/utils/IconRenderer.test.ts | Lucide SVG rendering, URL images, unknown icons |

Output files

| Format | Path | Approximate size | |---|---|---| | ESM | dist/index.mjs | ~88 KB | | CJS | dist/index.js | ~88 KB | | IIFE/UMD | dist/nimbus-chat.umd.global.js | ~577 KB (unminified deps inlined) | | Types | dist/index.d.ts | ~17 KB |

The SDK version is injected at build time via tsup (define: { __SDK_VERSION__ }) from package.json.

Project structure

chat-sdk/
├── src/
│   ├── core/
│   │   ├── ChatSession.ts        # Flow + in-memory messages
│   │   ├── EventBus.ts           # Pub/sub + debug ring buffer
│   │   ├── MessageStorage.ts     # localStorage persistence (per-flow)
│   │   ├── NotificationManager.ts# Sound + badge counter
│   │   └── WebSocketManager.ts   # Connection lifecycle + diagnostics
│   ├── icons/
│   │   └── lucide.ts             # Lucide SVG resolution
│   ├── styles/                   # Tailwind globals + compiled CSS string
│   ├── types/                    # Config interfaces + message types + globals.d.ts
│   ├── ui/
│   │   ├── BaseWindow.ts         # Shared window logic
│   │   ├── ChatWindow.ts         # Popup variant
│   │   ├── SidepanelWindow.ts    # Sidepanel variant
│   │   ├── ShadowContainer.ts    # Shadow DOM + CSS injection
│   │   ├── ThemeManager.ts       # CSS custom properties
│   │   ├── Header.ts             # Header + debug tooltip
│   │   ├── MessageList.ts        # Scrollable message container
│   │   ├── MessageBubble.ts      # Single message (markdown rendering)
│   │   ├── InputArea.ts          # Textarea + send button
│   │   └── ChatBubble.ts         # Floating bubble + badge
│   ├── utils/                    # DOMBuilder, IconRenderer, logger, validator, ...
│   ├── NimbusChat.ts             # Orchestrator + getDebugInfo()
│   └── index.ts                  # Public exports
├── examples/
│   ├── cdn-usage.html            # Light-theme CDN example
│   ├── cdn-usage-black.html      # Dark-theme CDN example
│   └── npm-usage.html            # npm/TS usage reference
├── dist/                         # Build output
└── tests/

Architecture

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 graph

NimbusChat (orchestrator)
├── EventBus           — typed pub/sub, optional ring buffer in debug
├── ChatSession        — flow_id + in-memory messages
├── WebSocketManager   — connect/reconnect, flow_id storage, diagnostics
├── MessageStorage     — per-flow message persistence in localStorage
├── NotificationManager — sound + badge when chat is closed
└── UI components
    ├── ShadowContainer — Shadow DOM root + injected CSS
    ├── ThemeManager    — CSS variables driven by theme config
    ├── ChatWindow      — popup variant
    ├── SidepanelWindow — sidepanel variant
    ├── Header          — title, status dot, new-chat, close, debug tooltip
    ├── MessageList     — scrollable container + welcome screen
    ├── MessageBubble   — single bubble (text or markdown)
    ├── InputArea       — textarea + send button + character counter
    └── ChatBubble      — floating trigger + notification badge

Connection lifecycle

  1. The user opens the widget → SDK emits ws:request-connect.
  2. The WebSocket opens at wss://{dns}?agent_version_id=...&flow_id=... (the stored flow_id is reused if available).
  3. Server replies with { "type": "connected", "flow_id": "<uuid>" }. The SDK persists the flow_id.
  4. If resumeConversation.enabled, the SDK restores stored messages for this flow_id.
  5. Bidirectional messaging: user → { type: "message", direction: "inbound", content }, bot → { type: "message", direction: "outbound", content }.

Wire schema

// → server
{ "type": "message", "direction": "inbound", "content": "Hello!" }

// ← server (handshake)
{ "type": "connected", "flow_id": "550e8400-e29b-41d4-a716-446655440000" }

// ← server (bot reply)
{ "type": "message", "direction": "outbound", "content": "Hi there!" }

WebSocket close codes recognised

| Code | Meaning | |---|---| | 1000 | Normal closure | | 4403 | Session unavailable (server-side) |

The reconnect handler retries up to reconnect.attempts times with reconnect.timeout ms between attempts.


Publishing

The package is published on the public npm registry as @nimbusai/webchat-sdk.

Prerequisites

You need an npm account that is a member of the @nimbusai organization with publish permission. Two-factor authentication is required.

1. Login to npm

npm login
# Then follow the browser prompt to authenticate.
# Verify with:
npm whoami

To switch to a different registry temporarily:

npm login --registry=https://registry.npmjs.org/

2. Build and test before publishing

npm install
npm run type-check
npm test
npm run build

All three must be green before tagging a release.

3. Bump the version

# Patch release (1.0.9 → 1.0.10) — bug fixes
npm version patch

# Minor release (1.0.9 → 1.1.0) — new features, backwards-compatible
npm version minor

# Major release (1.0.9 → 2.0.0) — breaking changes
npm version major

npm version updates package.json, creates a git commit, and tags it (e.g. v1.0.10). It does not push anything yet.

The version is also baked into the bundle at build time via tsup (__SDK_VERSION__), so you should re-run npm run build after bumping if you want the dist artifacts to reflect the new version locally.

4. Push the tag

git push origin main
git push origin --tags

If the repo uses a different default branch, replace main accordingly.

5. Publish to the public registry

# Public access is required for scoped packages on the free tier
npm publish --access public

For a dry run (no upload, just verification):

npm publish --access public --dry-run

One-shot release flow

# From a clean working tree on the default branch:
npm install
npm run type-check && npm test && npm run build
npm version patch          # or minor / major
npm publish --access public
git push origin main --follow-tags

Common pitfalls

  • 403 Forbidden — you are not authenticated (npm whoami should return your username) or you lack publish permission on the @nimbusai scope.
  • E402 Payment Required — when publishing a scoped package without --access public on the free tier.
  • EEXIST / version already published — npm forbids overwriting an existing version. Bump the version and republish.
  • OTP required — you have 2FA enabled. Add --otp=<code> or follow the prompt.
  • Unpublishing — only possible within 72 hours of publishing, and not recommended. Prefer publishing a fixed patch version.

License

MIT