@zoomrx/zena-chatbot-client
v1.0.2
Published
Zena AI chatbot web component for ZoomRx applications
Maintainers
Readme
Zena Chatbot
@zoomrx/zena-chatbot-client is a standalone web component (<zena-chatbot>) built with Svelte 5 and bundled via Vite into a single IIFE file. It is embedded inside host applications as a script tag — no framework dependency required on the host side.
Quick start
npm install
npm run dev # dev server at http://localhost:5173Requires Node.js 24 or newer. Use
nvm use --ltsif you manage multiple runtimes.
Installation
npm install @zoomrx/zena-chatbot-clientTech stack
- Framework: Svelte 5 (runes:
$state,$derived,$effect,$props) - Build tool: Vite 8 (outputs ES module + IIFE)
- Styles: Tailwind CSS 4
- HTTP: ky with a memoized instance factory
- Testing: Vitest + @testing-library/svelte + MSW
- Error tracking: Sentry (
@sentry/browser) - Analytics: PostHog
- E2E: Playwright
Scripts
| Command | Purpose |
|---|---|
| npm run dev | Dev server at http://localhost:5173 with MSW + PostHog stub |
| npm run build | Production IIFE + ES module in dist/ |
| npm run preview | Serve the production build locally |
| npm test -- --run | Run all unit tests once |
| npm run test:coverage | Coverage report |
| node e2e-test.cjs | Playwright E2E test against localhost:5173 |
Web Component API
Usage
The recommended approach is to pass all configuration via get-config — one event, one place.
<script src="path/to/zena-chatbot-client.iife.js"></script>
<zena-chatbot></zena-chatbot>
<script>
const chatbot = document.querySelector('zena-chatbot');
// All config — URLs, user ID, tokens, analytics keys — resolved in one place.
chatbot.on('get-config', ({ resolve }) => {
resolve({
authToken: 'Bearer eyJ...', // required
zenaServerUrl: 'https://your-zena-api.com',
panelistServerUrl: 'https://your-panelist-server.com',
userId: '12345',
availableSurveyIds: '[1, 2, 3]',
nativeApp: false,
fabStyle: { bottom: '20px', right: '20px' },
fabShape: 'circle',
autoSuggestions: ["I'm stuck in the survey"],
posthogKey: 'phc_xxx',
posthogHost: 'https://app.posthog.com',
sentryDsn: 'https://[email protected]/123',
sentryRelease: '[email protected]', // optional
});
});
</script>HTML attributes are also supported as a fallback (e.g. for static HTML pages), but get-config values take precedence when both are provided.
HTML Attributes (fallback)
All of these can alternatively be passed via get-config (see Events below). When both are provided, get-config wins.
| Attribute | Type | Required | Default | Description |
|---|---|---|---|---|
| zena-server-url | string | Yes* | — | Base URL for the Zena API |
| user-id | string | No | '' | User identifier passed to analytics |
| available-survey-ids | string (JSON array) | No | '[]' | Array of IDs (or objects with survey_id) currently on the user's dashboard |
| panelist-server-url | string | No | '' | Base URL for the panelist server (survey fetching) |
| native-app | boolean (presence) | No | false | Set to enable native app mode |
| fab-style | string (JSON object) | No | {} | CSS properties for FAB position/size (e.g. {"bottom":"20px","right":"20px"}) |
| fab-shape | string | No | 'circle' | 'circle' or 'rectangle' |
| auto-suggestions | string (JSON array) | No | [] | Priority suggestions prepended to defaults |
*Not required if zenaServerUrl is passed via get-config.
Methods
| Method | Returns | Description |
|---|---|---|
| close() | void | Programmatically close the chat panel |
| generateConversationSummary() | string | Returns last user + bot message as plain text (useful for support ticket context) |
| setFabStyle(style) | void | Update FAB position/style without remounting — preserves chat state |
| on(event, handler) | void | Subscribe to a component event |
| off(event, handler) | void | Unsubscribe from a component event |
Events
| Event | Payload | Description |
|---|---|---|
| get-config | { resolve } | Fired once on init. Call resolve(config) to provide sensitive credentials. See below. |
| before-send | { message, resolve } | Fired before each message is sent. Call resolve(data) with any extra data to merge into the request. |
| stuck-survey-selected | { survey } | Fire-and-forget. Fired when the user selects a survey from a stuck-survey dropdown, before the support ticket is created. |
get-config
Fired once when the component connects. The host must call resolve with a
ZenaChatbotConfig object. If the host does not handle the event within 2 seconds
the component mounts with an empty auth token (API calls will fail).
chatbot.on('get-config', ({ resolve }) => {
resolve({
// Credentials (required / recommended)
authToken: 'Bearer eyJ...', // required
sentryDsn: 'https://...', // optional — omit to disable Sentry
sentryRelease: '1.0.0', // optional — defaults to package version
posthogKey: 'phc_xxx', // optional — omit to disable analytics
posthogHost: 'https://app.posthog.com', // optional
// Component config (can also be set via HTML attributes as fallback)
zenaServerUrl: 'https://your-zena-api.com', // required
panelistServerUrl: 'https://your-panelist.com', // optional
userId: '12345', // optional
availableSurveyIds: '[1, 2, 3]', // optional
nativeApp: false, // optional
fabStyle: { bottom: '20px', right: '20px' }, // optional
fabShape: 'circle', // optional — 'circle' | 'rectangle'
autoSuggestions: ["I'm stuck in the survey"], // optional
defaultOpen: false, // optional — open panel on mount
initialMessage: { // optional — hidden trigger sent to server;
type: 'assistant', // only the assistant response is shown.
text: 'Hello! How can I help?', // Skipped on page reload automatically.
},
});
});before-send
chatbot.on('before-send', ({ message, resolve }) => {
resolve({ userId: 42, sessionToken: 'abc' });
});stuck-survey-selected
Fired when the user picks a survey from a stuck_survey_dropdown response. Fires before the support-ticket API call — the host receives it even if ticket creation subsequently fails.
chatbot.on('stuck-survey-selected', ({ survey }) => {
// survey — the selected survey object { name, survey_id, wave_id, users_wave_id, ... }
console.log('User is stuck in survey:', survey.name);
});Sentry Error Tracking
The package ships with Sentry support but does not auto-initialize it. Provide
sentryDsn via the get-config event — the component initialises Sentry after
credentials are resolved:
chatbot.on('get-config', ({ resolve }) => {
resolve({
authToken: '...',
sentryDsn: 'https://[email protected]/XXXXXX',
sentryRelease: '[email protected]', // optional
});
});sentryReleaseis optional. When omitted it defaults tozena-chatbot-client@<package version>and must match the release name used when uploading sourcemaps in CI (sentry-cli sourcemaps upload).- Omit
sentryDsnfrom the resolved config in local / UAT environments and Sentry will never initialise. tracesSampleRateis always0— errors only, no performance tracing overhead.
Capturing errors manually
captureError is the only Sentry helper exported from the package:
import { captureError } from '@zoomrx/zena-chatbot-client';
try {
// ...
} catch (err) {
captureError(err);
}For contribution guidelines, staging workflow, and publishing instructions see CONTRIBUTING.md.
