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:

bash
npm install xpectrum

Or with no build step at all — one script tag (see Embed scripts below).

One key does everything

Every widget takes the same two values: your API base URL and the app's API key. The key determines which agent answers — for chat and voice alike.

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.

typescript
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

PropertyTypeRequiredDefaultDescription
baseUrlstringYesXpectrum API base URL.
apiKeystringYesThe app's API key.
logostringNoImage URL or data: URI shown in the header.
titlestringNoHeader title. Falls back to the app's configured title.
welcomeMessagestringNoGreeting for a new conversation. Falls back to the app's opening statement.
inputPlaceholderstringNoPlaceholder text in the message box.
userstringNoConversation owner. Omit it and each visitor gets a stable anonymous id automatically.
anonymousTtlDaysnumberNo30Days an auto-generated anonymous id survives.

Theme & layout

PropertyTypeRequiredDefaultDescription
theme'light' | 'dark' | 'auto'No'light'Colour scheme; 'auto' follows the visitor's OS setting.
primaryColorstringNo'#7C3AED'Brand colour — launcher, header, user bubbles, send button.
onPrimaryColorstringNo'#ffffff'Text colour on top of primaryColor.
backgroundColorstringNoWindow background.
textColorstringNoBody text colour.
fontFamilystringNoAny CSS font stack.
fontSizenumberNo14Base font size in px — everything scales from it.
borderRadiusnumberNo12Corner rounding in px.
position'bottom-right' | 'bottom-left'No'bottom-right'Screen corner for the launcher.
buttonSizenumberNo48Launcher diameter in px.
windowWidthnumberNo400Window width in px.
windowHeightnumberNo600Window height in px.
zIndexnumberNoStack order on the host page.
containerHTMLElementNoMount point. Defaults to document.body.

Style isolation

Widgets render inside a Shadow DOM — your page's CSS cannot leak in, and the widget's cannot leak out. All theming happens through the config above.

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.

typescript
import { VoiceWidget } from 'xpectrum';

new VoiceWidget({
  baseUrl: 'https://cloud.xpectrum.dev/v1',
  apiKey: 'xpectrum_...',
  position: 'bottom-right',
  buttonColor: '#7C3AED',
});
PropertyTypeRequiredDefaultDescription
baseUrlstringYesXpectrum API base URL — same one used for chat.
apiKeystringYesThe app's API key. The voice agent is determined by this key.
buttonColorstringNo'#7C3AED'Accent colour — the orb, launcher and glow.
position'bottom-right' | 'bottom-left'No'bottom-right'Screen corner for the launcher.
buttonSizenumberNo56Launcher diameter in px.
windowWidthnumberNo240Call-card width in px (height fits the content).
zIndexnumberNoStack order on the host page.
containerHTMLElementNoMount point. Defaults to document.body.
onTranscription(segment) => voidNoCalled for each live transcription segment.
onStateChange(state) => voidNoCalled on call state changes (connecting, connected, …).

livekit-client is a peer dependency

Voice calls need 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.

typescript
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',
});
PropertyTypeRequiredDefaultDescription
chatBaseUrlstringYesAPI base URL for chat.
chatApiKeystringYesAPI key used by the chat widget.
voiceBaseUrlstringYesAPI base URL for voice (same as chat).
apiKeystringYesAPI key used by the voice widget.
position'bottom-right' | 'bottom-left'No'bottom-right'Screen corner for the launcher.
buttonColorstringNo'#7C3AED'Accent colour for the launcher and menu.
userstringNoConversation owner, passed to the chat widget.
chatPartial<ChatWidgetConfig>NoExtra options forwarded to the chat widget.
voicePartial<VoiceWidgetConfig>NoExtra options forwarded to the voice widget.

4. Methods

Every widget instance exposes the same small API:

typescript
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 entirely

5. 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

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

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

Always keep the @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.