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

@xorcloud/stack-ai-chat-sdk

v0.12.0

Published

Floating chat widget SDK for React apps — powered by Stack AI

Downloads

1,336

Readme

@xorcloud/stack-ai-chat-sdk

Floating chat widget SDK for React apps — powered by Stack AI.

Embed a fully-featured live chat widget into any React application in minutes. Connects to your Stack AI backend via Socket.IO with support for real-time messaging, agent presence, typing indicators, file attachments, markdown rendering, reference/quote injection, greeting messages, session dividers, expanded mode, dark/light mode, and Shadow DOM style isolation.


Installation

npm install @xorcloud/stack-ai-chat-sdk

Quick Start

import { StackAIChat } from '@xorcloud/stack-ai-chat-sdk'

StackAIChat.init({
  wsUrl: 'wss://your-server.com/ws/chat',
  token: '<your-jwt-token>',
})

The chat button will appear in the bottom-right corner of the screen immediately.

Anonymous flow: If your JWT has type: 'anonymous', the server automatically creates a conversation and the SDK receives the conversationId via presence:update. You don't need to pass conversationId manually.


Configuration

StackAIChat.init({
  // ── Connection (required) ──────────────────────────────
  wsUrl: 'wss://your-server.com/ws/chat',
  token: '<jwt-token>',
  conversationId: '<conversation-id>',  // optional — omit for anonymous flow

  // ── Pre-chat Form Fields ───────────────────────────────
  fields: [
    { name: 'fullName', label: 'Full Name',    type: 'text', required: true  },
    { name: 'phone',    label: 'Phone Number', type: 'tel',  required: true  },
    { name: 'idCard',   label: 'ID Card',      type: 'text', required: false },
  ],

  // ── Session Persistence ────────────────────────────────
  session: {
    persist: true,       // save form data to localStorage
    storageKey: 'chat_user',
    ttl: 86400,          // seconds (0 = forever), default 24h
  },

  // ── File Attachments ───────────────────────────────────
  attachments: {
    enabled: true,
    maxSize: 5,                              // MB
    accept: ['image/*', 'application/pdf'],
    maxCount: 5,
  },

  // ── UI ─────────────────────────────────────────────────
  position: 'bottom-right',   // 'bottom-right' | 'bottom-left'
  title: 'Customer Support',
  subtitle: 'Usually replies in minutes',

  // ── Theme ──────────────────────────────────────────────
  theme: {
    mode: 'auto',           // 'light' | 'dark' | 'auto' (follows OS)
    primaryColor: '#0066FF',
    borderRadius: '12px',
  },

  // ── Visible Message Types ──────────────────────────────
  // Control which action types from WebSocket are shown in the chat box.
  // Default: ['message'] — only regular chat messages are displayed.
  visibleMessageTypes: ['message', 'thinking', 'tool_use', 'tool_result', 'notice', 'system'],

  // ── Hidden Patterns ─────────────────────────────────────
  // Filter out messages by content using regex patterns.
  // Useful when server sends internal messages (e.g. tool calls) with type: 'message'.
  hiddenPatterns: [
    /^🧠\s?\*\*Knowledge Search\*\*/,
    /^Retrieved \d+ knowledge chunk/,
    /^No relevant knowledge found/,
  ],

  // ── Greeting ───────────────────────────────────────────
  // Message shown immediately when a fresh conversation starts (no history).
  // Not shown when resuming an existing conversation.
  greeting: 'Hello! How can I help you today?',

  // ── References ─────────────────────────────────────────
  // Show or hide reference documents attached to agent responses.
  // Default: true
  showReferences: false,

  // ── Custom Styles ──────────────────────────────────────
  // Override CSS for individual UI components (injected into Shadow DOM).
  customStyles: {
    chatButton:     '.chat-button { box-shadow: 0 4px 20px rgba(0,0,0,0.3); }',
    chatWindow:     '.chat-window { border: 2px solid #0066FF; }',
    chatHeader:     '.chat-header { background: linear-gradient(135deg, #0066FF, #7c3aed); }',
    messageList:    '.message-list { background: #fafafa; }',
    messageBubble:  '.message-bubble { border-radius: 8px; }',
    messageInput:   '.message-input-area { border-top: 2px solid #e5e7eb; }',
    preChatForm:    '.prechat-form { padding: 24px; }',
    typingIndicator:'.typing-indicator { opacity: 0.7; }',
    global:         ':host { --sai-primary: #7c3aed; --sai-primary-hover: #6d28d9; }',
  },

  // ── Callbacks ──────────────────────────────────────────
  onOpen:               () => console.log('Widget opened'),
  onClose:              () => console.log('Widget closed'),
  onConnected:          () => console.log('Socket connected'),
  onDisconnected:       () => console.log('Socket disconnected'),
  onConversationJoined: (id) => console.log('Joined conversation', id),
  onPresenceUpdate:     (payload) => console.log('Presence update', payload),
  onError:              (msg) => console.error('Error:', msg),
  onFormSubmit:         (data) => console.log('Form submitted', data),
  onMessage:            (msg) => console.log('New message', msg),
})

API

StackAIChat.version

Read-only string containing the current SDK version. Useful for debugging when integrating into consumer apps.

console.log(StackAIChat.version) // e.g. "0.11.0"

StackAIChat.init(config)

Initialize and mount the chat widget. Can only be called once — call destroy() first to re-initialize. Logs [StackAIChat] SDK v<version> to the console on startup.

StackAIChat.open()

Programmatically open the chat window.

StackAIChat.close()

Programmatically close the chat window.

StackAIChat.setReference(text)

Inject a reference/quote into the message input. The quoted text will be prepended to the next message as a blockquote (> text). Useful for "reply to selection" flows — call this when the user selects text on your page.

// Example: quote selected text when user opens chat
document.addEventListener('mouseup', () => {
  const selected = window.getSelection()?.toString().trim()
  if (selected) {
    StackAIChat.setReference(selected)
    StackAIChat.open()
  }
})

StackAIChat.clearReference()

Clear any pending reference/quote from the input.

StackAIChat.updateConfig(partial)

Update configuration after initialization (e.g. change theme or title).

StackAIChat.destroy()

Unmount the widget and clean up all resources.


SDKConfig Reference

| Field | Type | Required | Description | |-------|------|----------|-------------| | wsUrl | string | ✓ | WebSocket server URL | | token | string | ✓ | JWT token (agent, user, or anonymous) | | conversationId | string | — | Resume a specific conversation. Omit for anonymous flow. | | socketPath | string | — | Socket.IO path. Default: '/ws/chat' | | fields | FieldConfig[] | — | Pre-chat form fields | | session | SessionConfig | — | Form session persistence | | attachments | AttachmentsConfig | — | File attachment settings | | position | 'bottom-right' \| 'bottom-left' | — | Widget position | | title | string | — | Chat header title | | subtitle | string | — | Chat header subtitle | | theme | ThemeConfig | — | Theme settings | | visibleMessageTypes | MessageType[] | — | Action types to display. Default: ['message'] | | hiddenPatterns | RegExp[] | — | Regex patterns to filter out messages by content | | greeting | string | — | Welcome message shown when a fresh conversation starts (no history). Omit to disable. | | showReferences | boolean | — | Show/hide reference documents attached to agent responses. Default: true | | customStyles | CustomStylesConfig | — | Per-component CSS overrides (injected into Shadow DOM) | | onOpen | () => void | — | Called when widget opens | | onClose | () => void | — | Called when widget closes | | onConnected | () => void | — | Called when socket connects | | onDisconnected | () => void | — | Called when socket disconnects | | onConversationJoined | (id: string) => void | — | Called when conversation ID is known | | onPresenceUpdate | (payload: PresenceUpdatePayload) => void | — | Called on agent/user presence changes | | onError | (message: string) => void | — | Called on errors | | onMessage | (message: Message) => void | — | Called on new incoming messages | | onRawMessage | (payload: Record<string, unknown>) => void | — | Debug: raw WebSocket payload before filtering | | onFormSubmit | (data: Record<string, string>) => void | — | Called when pre-chat form is submitted |


Visible Message Types

By default only 'message' type messages are rendered. Use visibleMessageTypes to opt-in to additional action types:

| Type | Description | |------|-------------| | message | Regular chat messages (default) | | thinking | AI internal reasoning / chain-of-thought steps | | tool_use | Tool/function calls initiated by the agent | | tool_result | Results returned from tool/function calls | | notice | Informational notices from the system | | system | System-level messages |

StackAIChat.init({
  wsUrl: '...',
  token: '...',
  visibleMessageTypes: ['message', 'thinking'],  // also show reasoning steps
})

Hidden Patterns

Some servers send internal messages (e.g. knowledge search, tool calls) with type: 'message', making them indistinguishable by type alone. Use hiddenPatterns to filter them out by content:

StackAIChat.init({
  wsUrl: '...',
  token: '...',
  hiddenPatterns: [
    /^🧠\s?\*\*Knowledge Search\*\*/,   // "🧠 **Knowledge Search**\n..."
    /^Retrieved \d+ knowledge chunk/,   // "Retrieved 5 knowledge chunk(s)."
    /^No relevant knowledge found/,     // "No relevant knowledge found."
  ],
})

Patterns are applied to both message history and real-time messages. Messages matching any pattern are silently dropped from the UI (but still delivered to onRawMessage if set).


Custom Styles

Inject CSS into the Shadow DOM to override the default widget appearance. Each key targets a specific UI region:

StackAIChat.init({
  wsUrl: '...',
  token: '...',
  customStyles: {
    // Override individual components
    chatButton:      '.chat-button { background: #7c3aed !important; }',
    chatWindow:      '.chat-window { border-radius: 16px; }',
    chatHeader:      '.chat-header { background: linear-gradient(135deg, #0066FF, #7c3aed); }',
    messageList:     '.message-list { background: #f8f9fa; }',
    messageBubble:   '.message-bubble { font-size: 14px; }',
    messageInput:    '.message-input-area { background: #fff; }',
    preChatForm:     '.prechat-form { padding: 32px; }',
    typingIndicator: '.typing-indicator { opacity: 0.6; }',
    // Or write arbitrary CSS including CSS variable overrides
    global:          ':host { --sai-primary: #7c3aed; --sai-primary-hover: #6d28d9; }',
  },
})

| Key | Target element | CSS selector | |-----|---------------|--------------| | chatButton | Floating action button | .chat-button | | chatWindow | Main chat window container | .chat-window | | chatHeader | Header bar with title & controls | .chat-header | | messageList | Messages scrollable area | .message-list | | messageBubble | Individual message bubbles | .message-bubble | | messageInput | Input area at the bottom | .message-input-area | | preChatForm | Pre-chat collection form | .prechat-form | | typingIndicator | Agent typing animation | .typing-indicator | | global | Arbitrary CSS / CSS variable overrides | (any selector) |

All styles are scoped inside a Shadow DOM — they will not leak into or be affected by your application's CSS.


Token & Flow Types

The server determines the flow based on your JWT type claim:

| Token type | Flow | |------------|------| | anonymous | Server auto-creates a conversation. SDK receives conversationId via presence:update. | | user | Standard user flow. Pass conversationId to resume or let server create one. | | agent | Agent/staff flow. Joins an existing conversation by conversationId. |


Features

  • Floating button — fixed position, bottom-left or bottom-right; auto-hides when chat is open
  • Pre-chat form — fully configurable fields with validation
  • Session persistence — skip form on return visits via localStorage
  • Real-time messaging — Socket.IO with optimistic UI; input disabled while agent is processing
  • Agent presence — live online/offline status indicator with conversation ID display
  • Typing indicator — animated dots when agent is typing (8s timeout)
  • Markdown rendering — agent messages rendered full-width without bubble; supports bold, italic, code blocks, lists, blockquotes, links, tables
  • Claude-style message UI — agent messages full-width no-bubble; thinking/tool_use/tool_result as collapsible pills; notice/system as inline banners
  • Greeting message — configurable welcome message on fresh conversations; skipped when resuming history
  • Session divider — visual separator between history and new session when resuming a conversation
  • File attachments — image preview + file chips, configurable limits
  • Reference/quote injection — inject quoted text into the input via setReference(), useful for "reply to selection"
  • Expanded mode — toggle to 50vw × 80vh (100vw × 100vh on mobile) for more reading space
  • Shadow DOM — complete CSS isolation from host application
  • Dark / Light / Auto — theme system via CSS custom properties
  • Anonymous flow — zero-config conversation creation for anonymous visitors
  • Visible message types — opt-in to thinking, tool_use, tool_result, notice, system action types
  • Reference documents — agent responses with attached sources/citations rendered as interactive chips with detail modal; toggle via showReferences
  • Custom styles — per-component CSS overrides injected into Shadow DOM
  • Version exposureStackAIChat.version readable by consumer apps; logged to console on init
  • TypeScript — full type definitions included

Field Types

| Type | Description | |------|-------------| | text | Plain text input | | tel | Phone number input | | email | Email input | | number | Numeric input |


Theme

The widget uses CSS Custom Properties scoped inside a Shadow DOM — no style conflicts with your app.

| Mode | Description | |------|-------------| | light | Light theme (default) | | dark | Dark theme | | auto | Follows OS prefers-color-scheme |


Requirements

  • React >= 18.0.0
  • A compatible Stack AI WebSocket backend (NestJS Socket.IO gateway)

License

MIT © X-OR Cloud