Widgets
Drop-in chat and voice UI for any website — a floating chat window, a voice-call orb, or one launcher offering both. No UI code required.
Installation
Via npm for apps with a build step:
npm install xpectrumOr with no build step at all — one script tag (see Embed scripts below).
ℹ One key does everything
1. ChatWidget
A floating launcher that opens a chat window: streaming replies, Markdown rendering, and a greeting from your app's configured opening statement. It opens straight into a fresh conversation — no history is shown or loaded, so it is safe to use with a key that has history access disabled.
import { ChatWidget } from 'xpectrum';
new ChatWidget({
baseUrl: 'https://cloud.xpectrum.dev/v1',
apiKey: 'xpectrum_...',
welcomeMessage: 'Hi! How can I help?',
logo: 'https://yoursite.com/logo.png',
title: 'Acme Support',
primaryColor: '#7C3AED',
});Branding & identity
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
| baseUrl | string | Yes | — | Xpectrum API base URL. |
| apiKey | string | Yes | — | The app's API key. |
| logo | string | No | — | Image URL or data: URI shown in the header. |
| title | string | No | — | Header title. Falls back to the app's configured title. |
| welcomeMessage | string | No | — | Greeting for a new conversation. Falls back to the app's opening statement. |
| inputPlaceholder | string | No | — | Placeholder text in the message box. |
| user | string | No | — | Conversation owner. Omit it and each visitor gets a stable anonymous id automatically. |
| anonymousTtlDays | number | No | 30 | Days an auto-generated anonymous id survives. |
Theme & layout
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
| theme | 'light' | 'dark' | 'auto' | No | 'light' | Colour scheme; 'auto' follows the visitor's OS setting. |
| primaryColor | string | No | '#7C3AED' | Brand colour — launcher, header, user bubbles, send button. |
| onPrimaryColor | string | No | '#ffffff' | Text colour on top of primaryColor. |
| backgroundColor | string | No | — | Window background. |
| textColor | string | No | — | Body text colour. |
| fontFamily | string | No | — | Any CSS font stack. |
| fontSize | number | No | 14 | Base font size in px — everything scales from it. |
| borderRadius | number | No | 12 | Corner rounding in px. |
| position | 'bottom-right' | 'bottom-left' | No | 'bottom-right' | Screen corner for the launcher. |
| buttonSize | number | No | 48 | Launcher diameter in px. |
| windowWidth | number | No | 400 | Window width in px. |
| windowHeight | number | No | 600 | Window height in px. |
| zIndex | number | No | — | Stack order on the host page. |
| container | HTMLElement | No | — | Mount point. Defaults to document.body. |
ℹ Style isolation
2. VoiceWidget
A voice-assistant call card, not a chat window: a floating launcher opens a compact card with an animated orb. Tap the orb to start the call. While the agent speaks, a live analyser reads its audio and drives the orb and the coloured waves around it, so the motion follows the actual sound. Status text ("Listening… / Speaking…"), a call timer, a one-line caption of what was last said, and round mute / end-call controls complete the card.
import { VoiceWidget } from 'xpectrum';
new VoiceWidget({
baseUrl: 'https://cloud.xpectrum.dev/v1',
apiKey: 'xpectrum_...',
position: 'bottom-right',
buttonColor: '#7C3AED',
});| Property | Type | Required | Default | Description |
|---|---|---|---|---|
| baseUrl | string | Yes | — | Xpectrum API base URL — same one used for chat. |
| apiKey | string | Yes | — | The app's API key. The voice agent is determined by this key. |
| buttonColor | string | No | '#7C3AED' | Accent colour — the orb, launcher and glow. |
| position | 'bottom-right' | 'bottom-left' | No | 'bottom-right' | Screen corner for the launcher. |
| buttonSize | number | No | 56 | Launcher diameter in px. |
| windowWidth | number | No | 240 | Call-card width in px (height fits the content). |
| zIndex | number | No | — | Stack order on the host page. |
| container | HTMLElement | No | — | Mount point. Defaults to document.body. |
| onTranscription | (segment) => void | No | — | Called for each live transcription segment. |
| onStateChange | (state) => void | No | — | Called on call state changes (connecting, connected, …). |
⚠ livekit-client is a peer dependency
livekit-client. With npm, install it alongside the SDK: npm install livekit-client. Without a bundler, add an import map so the browser can resolve it from a CDN. It is loaded only when a call starts — chat-only pages never download it.3. OmnichannelWidget
One launcher for both channels. Clicking it opens a small menu — Chat and Voice Call — and each option opens the matching widget. Use it when a page should offer both ways to talk without two floating buttons.
import { OmnichannelWidget } from 'xpectrum';
new OmnichannelWidget({
chatBaseUrl: 'https://cloud.xpectrum.dev/v1',
chatApiKey: 'xpectrum_...',
voiceBaseUrl: 'https://cloud.xpectrum.dev/v1',
apiKey: 'xpectrum_...',
position: 'bottom-right',
buttonColor: '#7C3AED',
});| Property | Type | Required | Default | Description |
|---|---|---|---|---|
| chatBaseUrl | string | Yes | — | API base URL for chat. |
| chatApiKey | string | Yes | — | API key used by the chat widget. |
| voiceBaseUrl | string | Yes | — | API base URL for voice (same as chat). |
| apiKey | string | Yes | — | API key used by the voice widget. |
| position | 'bottom-right' | 'bottom-left' | No | 'bottom-right' | Screen corner for the launcher. |
| buttonColor | string | No | '#7C3AED' | Accent colour for the launcher and menu. |
| user | string | No | — | Conversation owner, passed to the chat widget. |
| chat | Partial<ChatWidgetConfig> | No | — | Extra options forwarded to the chat widget. |
| voice | Partial<VoiceWidgetConfig> | No | — | Extra options forwarded to the voice widget. |
4. Methods
Every widget instance exposes the same small API:
const widget = new ChatWidget({ ... });
widget.open(); // open the window / card
widget.close(); // close it (a live voice call is hung up cleanly)
widget.toggle(); // flip between open and closed
widget.destroy(); // remove the widget from the page entirely5. Embed scripts — no build step
For WordPress, Shopify, or any plain HTML site: define a small config object, include one script tag, done. The script loads the SDK and mounts the widget by itself.
Chat
<script>
window.XpectrumChatConfig = {
apiKey: 'xpectrum_...',
baseUrl: 'https://cloud.xpectrum.dev/v1',
// Optional branding:
logo: 'https://yoursite.com/logo.png',
title: 'Acme Support',
welcomeMessage: 'Hi! How can I help?',
primaryColor: '#7C3AED',
theme: 'light',
};
</script>
<script src="https://unpkg.com/xpectrum@1.0.0/dist/chat-embed.min.js" defer></script>Voice
<script>
window.XpectrumVoiceConfig = {
apiKey: 'xpectrum_...',
baseUrl: 'https://cloud.xpectrum.dev/v1',
buttonColor: '#7C3AED',
};
</script>
<script src="https://unpkg.com/xpectrum@1.0.0/dist/voice-embed.min.js" defer></script>Every option from the widget tables above can be set on the config object — logo, colours, fonts, position, all of it.
⚠ Pin the version
@1.0.0 in the script URL. An unpinned URL resolves to whatever is latest, so a future release would reach your live site without you upgrading deliberately.6. Your API key in a widget
A widget runs in the visitor's browser, so the API key is visible to anyone who opens developer tools — that is inherent to every client-side widget. Limit what an exposed key can do in your app's publish settings:
- Keep Conversation history over API switched off — the widget never needs it, and an exposed key then cannot read any transcripts.
- Switch Voice calls off for apps that only chat.
The widgets are built for this: chat opens fresh conversations without touching history endpoints, and the voice agent is fixed by the key itself — a caller cannot pick a different agent.