@olotalk/assistant-loader
v0.26.0
Published
Embed the [Olotalk](https://olotalk.com) AI chat assistant on any website with a single `<script>` tag, no framework required.
Downloads
1,211
Readme
@olotalk/assistant-loader
Embed the Olotalk AI chat assistant on any website with a single <script> tag, no framework required.
The loader handles config fetching, browser compatibility detection, and dynamic loading of the assistant bundle from the CDN. Your visitors get a grounded, RAG-powered chat assistant that answers questions based on your own content.
Quick start
<script
src="https://cdn.jsdelivr.net/npm/@olotalk/assistant-loader@0/dist/loader.iife.js"
data-olotalk-assistant-id="YOUR_ASSISTANT_ID"
async
></script>Paste this before </body> on any page. The assistant appears immediately as a floating launcher in the bottom-right corner. The published bundle bakes in https://api.olotalk.com as the default API origin. Self-hosters override with data-olotalk-origin.
Get your assistant ID from the Olotalk dashboard.
Configuration
Via HTML attributes
All attributes are read from the <script> tag itself.
| Attribute | Type | Description |
|---|---|---|
| data-olotalk-assistant-id | string | Required. Your assistant ID from the dashboard. |
| data-olotalk-origin | string | Base URL of your Olotalk API. Optional for Olotalk Cloud (the published bundle defaults to https://api.olotalk.com); set this only when self-hosting the BFF. |
| data-olotalk-locale | string | Force a display language. Accepts any well-formed BCP-47 tag (e.g. "en", "fr", "vi", "ja-JP"). When omitted, the loader auto-detects from the visitor's stored preference, the page's <html lang>, then navigator.language, bounded by the assistant's supportedLocales so an unsupported visitor language falls through to the operator's default. |
| data-olotalk-mode | string | Which shape to mount (default bubble). One of bubble, starters, inline, drawer, commandk, expand, anchored, fullpage, sidebar, navfield, askbar, selection, corner, center. See Shapes. Read as a plain attribute so a dashboard-generated snippet can carry it. |
| data-olotalk-reach | string | Additive triggers over the mounted shape (ADR 0226): comma-separated trigger-capable shape ids (selection, commandk) whose triggers open this mount. data-olotalk-mode="bubble" data-olotalk-reach="selection,commandk" = a corner bubble that also opens from a text highlight and from ⌘K. The assistant owns the vocabulary and warns on unknown ids; the loader passes the string through. |
| data-olotalk-selection-answers | string | Where a selection opens the panel: beside (default) flies it to the highlighted words, in-place opens it where the assistant lives, for sites whose assistant is a fixture the visitor has already located. Only affects the selection trigger. Unrecognised values warn and fall back to beside. |
| data-olotalk-version | string | Pin a specific published @olotalk/assistant npm version. |
| data-olotalk-cdn-base | string | Override the assistant asset base URL (for self-hosting). |
| data-olotalk-sandbox-token | string | Test an assistant from your own local/staging app before going live. Paste the sandbox token from the dashboard (Embed → "Test in your own app"). It lets the assistant run on localhost and unlisted staging hosts without allowlisting, un-billed. Never ship it on your production embed. It stops working from your registered live domains anyway. See the Sandbox testing guide. |
Via window.OlotalkConfig
Declare a global config object before the loader script for programmatic setup:
<script>
window.OlotalkConfig = {
assistantId: "YOUR_ASSISTANT_ID",
locale: "fr",
theme: "dark",
mode: "inline",
mount: "#chat-container",
// origin: "https://bff.example.com", // self-hosted only
};
</script>
<script src="https://cdn.jsdelivr.net/npm/@olotalk/assistant-loader@0/dist/loader.iife.js"></script>Full config reference
| Option | Type | Default | Description |
|---|---|---|---|
| assistantId | string | — | Required. Your assistant ID. |
| origin | string | https://api.olotalk.com (cloud build) | Base URL of your Olotalk API. Self-hosters override; the source-level fallback is window.location.origin. |
| locale | string | Auto-detected | Force language. Any BCP-47 tag (e.g. "en", "fr", "vi", "ja-JP"). When omitted, the loader uses (in order): visitor's localStorage preference → page <html lang> → navigator.language, gated by the assistant's supportedLocales. |
| theme | "light" | "dark" | object | "light" | Color scheme or custom theme overrides. |
| mode | AssistantMode | "bubble" | Which shape to mount. See Shapes. |
| placement | "floating" | "embedded" | "floating" | Deprecated alias for mode: floating → bubble, embedded → inline. Prefer mode. |
| mount | string | document.body | CSS selector for the assistant mount point (use with inline, expand, fullpage). |
| version | string | matching assistant package version | Pin a specific @olotalk/assistant npm version (e.g. "1.2.3"). When omitted, the loader resolves the published assistant version that matches the loader build, then uses version.json for cache-busting when available. |
| cdnBase | string | jsDelivr CDN | Override assistant asset base URL for self-hosting. |
| cssUrl | string | Auto-resolved | Override the assistant CSS URL. In a future release this will be provided automatically by the assistant config from the dashboard. |
| sandboxToken | string | — | Sandbox test token (dashboard → Embed → "Test in your own app"). Runs the assistant un-billed against your real content on localhost/staging before go-live. Do not ship on production. |
Shapes
One bundle, fourteen mounts, one mode apart. shape-spec.ts in
@olotalk/assistant is the source of truth, and a test fails if a live shape is
missing here, so this list cannot quietly fall behind the code.
| mode | Where it lands | How it opens |
|---|---|---|
| bubble | Corner launcher (default) | Its own launcher |
| starters | Corner launcher, questions on hover | Its own launcher |
| corner | The same corner panel, with nothing at rest | A trigger you place, or reach |
| inline | Inside a container in your markup | Always open |
| drawer | Full height, flush to an edge, modal | A button you place |
| commandk | Centred over a dimmed page, modal | ⌘K / CtrlK |
| center | The same centred panel, without the ⌘K claim | A trigger you place, or reach |
| expand | A line in your copy that unfolds | A trigger you place |
| anchored | Beside one element, never covering it | A trigger you place |
| fullpage | The assistant is the page | Always open |
| sidebar | A fixed rail down one edge | Always open |
| navfield | Your own nav field | Your markup submits to it |
| askbar | Your own bar at the foot of the page | Your markup submits to it |
| selection | Nothing at rest; appears where text is highlighted | The visitor selecting text |
<button data-olotalk-open="drawer">Ask AI</button>
<script
src="https://cdn.jsdelivr.net/npm/@olotalk/assistant-loader@0/dist/loader.iife.js"
data-olotalk-assistant-id="ast_xxx"
data-olotalk-mode="drawer"
async
></script>The open panel is a window
Visitors can resize any panel that floats over the page, and drag the anchored one. Neither is configurable: there is nothing to switch on, and nothing an embed can turn off.
| | resize | drag |
|---|---|---|
| bubble · starters · corner · drawer · commandk · center | ✅ | — |
| anchored (select-to-ask, mode: "anchored") | ✅ | ✅ |
| inline · expand · fullpage · sidebar | — |, |
| any shape at phone width | — |, |
Eight handles: 10px edge bands with 20px corners. Floor 320×360, ceiling the viewport less a margin.
Drag is narrower than resize on purpose. A corner bubble keeps its launcher on screen while open (it morphs into the close control), so moving the panel away would orphan that button. The anchored panel has no such tether and deliberately covers the passage it is about, so moving it is the only way to read the original words.
The chosen size survives a close; the position never does. Press the launcher and the assistant comes back to its corner, at the size the visitor picked. Both gestures are pointer-only.
The in-flow shapes are your layout, so they are left alone entirely.
mobile is not a shape you pick. It is the presentation bubble, drawer,
commandk and anchored collapse into at phone width. One embed covers both.
drawer, anchored, navfield and askbar attach to something only you can
point at, so a script tag alone is not enough: call Olotalk('open') from your
own trigger. Every other shape mounts from the snippet. Full contract in the
docs.
JavaScript API
The loader exposes a global Olotalk() command queue. You can call it before or after the script loads.
// Open or close the panel programmatically
Olotalk('open')
Olotalk('close')
// Seed a question from your own control
Olotalk('open', { prefill: 'Do you support SSO?' })
// Change the language, keeping the conversation. Use this, not `init`,
// when your own language switcher does not reload the page: the assistant
// resolves its locale once, at mount, and cannot see a later change.
Olotalk('locale', 'fr')
// Re-initialize with a different config. Rebuilds the assistant, so the
// visitor's open conversation goes with it.
Olotalk('init', { assistantId: 'ast_oltk_…' })
// Remove the assistant from the page entirely
Olotalk('destroy')Commands queued before the assistant loads are replayed automatically once it is ready.
Error handling
If the assistant can't install, the loader never throws and never renders anything to real
visitors. Instead it logs one console.error (prefixed [Olotalk]) that states what failed,
the likely cause, and the concrete fix. For programmatic monitoring it also writes the last
error to window.__olotalkError and dispatches an olotalk:error CustomEvent:
window.addEventListener('olotalk:error', (e) => {
const { code, status, hostname } = e.detail;
// report to your own monitoring
});detail.code is a stable identifier (the human-readable message may change); detail.status
is the HTTP status when there was one.
| code | Meaning | Fix |
|---|---|---|
| sandbox_token_rejected | A sandboxToken was set but the request was refused (4xx) | Regenerate the token (Embed → "Test in your own app"); confirm it was minted for this assistantId. Do not add the domain to Install domains. That makes traffic live and billed. |
| domain_not_allowed | 403, or the client-side allowlist check: this domain isn't in the assistant's allowed domains | Add the domain in the dashboard, or use a sandboxToken if you're testing before go-live. |
| assistant_not_found | 404: wrong id, deleted, or not live yet | Verify data-olotalk-assistant-id. If the assistant exists but isn't live, go live or test it with a sandboxToken. |
| assistant_unavailable | 410: the assistant was archived or deleted | Reactivate it, or remove the embed. |
| config_fetch_failed | Network error or API outage (5xx / unreachable) | Check the origin and that the API is up, then retry. |
Multilingual
The assistant supports any BCP-47 locale out of the box. The operator declares which languages their site ships (supportedLocales) in the dashboard; the runtime handles the rest.
Resolution priority (chrome language)
- Explicit
data-olotalk-locale/OlotalkConfig.locale - Visitor's previously-accepted choice (localStorage, per assistant)
- Page's
<html lang>: primary signal on multilingual sites with one URL per language navigator.language: visitor's browser preference- Operator's
defaultLocalefrom the dashboard
Steps 3 and 4 are gated by supportedLocales: if the operator declares ["fr"], an English-speaking visitor falls through to French chrome (intentional: the site has no English content to ground on).
Adaptive switching
When a visitor types their first message in a different supported language than the current chrome (e.g. a French question on an English assistant configured for both), the bot replies in that language and the assistant surfaces a small inline toast: "Continue in French? · Yes / No". On Yes, the entire chrome flips to French and the choice is persisted to localStorage so the next session opens directly in French.
Static vs LLM-translated locales
| Tier | Locales | Latency |
|---|---|---|
| Hand-translated | en, fr, de (+ regional aliases) | Zero, bundled |
| LLM-translated | Any other BCP-47 (vi, ja, ar, sw, …) | One-time ~1–3s on first load per locale, then cached |
See ADR 0050 for the full design.
Version pinning
By default, the loader fetches the published @olotalk/assistant version that matches the loader build. To override that and lock a specific assistant version:
<!-- Pin assistant bundle to a specific version -->
<script
src="https://cdn.jsdelivr.net/npm/@olotalk/assistant-loader@0/dist/loader.iife.js"
data-olotalk-assistant-id="YOUR_ASSISTANT_ID"
data-olotalk-version="1.2.3"
></script>Or pin the loader itself by version:
<script src="https://cdn.jsdelivr.net/npm/@olotalk/assistant-loader@0/dist/loader.iife.js" ...></script>When version is pinned, the loader skips the build-version lookup and loads that exact package version directly.
Asset versioning
When version is omitted, the loader fetches ${base}/version.json with cache: "no-cache" and appends ?v=<buildVersion> to the assistant JS, IIFE, and auto-resolved CSS URLs. This keeps same-page reloads fresh during local development and makes immutable caching safe for self-hosted or CDN-served assistant bundles.
If version.json is unavailable or cannot be fetched, the loader falls back to the unversioned asset URLs.
Self-hosting
To serve assistant assets from your own server instead of jsDelivr:
<script
src="/path/to/loader.iife.js"
data-olotalk-assistant-id="YOUR_ASSISTANT_ID"
data-olotalk-cdn-base="/assets/assistant"
></script>The loader will fetch olotalk-assistant.js, olotalk-assistant.iife.js, olotalk-assistant.css, and version.json from the provided base URL.
If data-olotalk-cdn-base points to a different origin, version.json must be fetchable with CORS enabled for automatic cache-busting. If not, the loader still works but falls back to the unversioned asset URLs.
npm install
If you are using a bundler or framework, you can import the loader directly:
npm install @olotalk/assistant-loaderimport { ensureAssistant } from '@olotalk/assistant-loader'
ensureAssistant({
assistantId: 'YOUR_ASSISTANT_ID',
// origin: 'https://bff.example.com', // self-hosted only
})License
MIT © Olotalk
