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

@hachther/akilio-widget

v1.0.0

Published

Embeddable React AI assistant widget for Akilio backend.

Readme

Akilio Widget

Style isolation

The widget renders in a Shadow DOM by default. Host-site resets and framework styles cannot override its buttons, text areas, images, or layout, and widget styles do not leak into the surrounding page.

<AkilioWidget
  apiBaseUrl='https://app.akilio-ai.com'
  projectKey='your-project-key'
  styleIsolation='shadow'
/>

Use styleIsolation="scoped" for a normal-DOM integration with Akilio-prefixed CSS, or styleIsolation="none" when loading @hachther/akilio-widget/style.css yourself. Standalone scripts accept data-style-isolation="shadow"; Shadow DOM is also their default.

Streaming responses

The widget uses the authenticated SSE endpoint by default. It displays retrieval and answer-preparation status, appends answer.delta content as it arrives, and then replaces the transient answer with the canonical final payload (including citations, products, and suggestions).

<AkilioWidget
  apiBaseUrl='https://app.akilio-ai.com'
  projectKey='your-project-key'
  streaming
/>

Use streaming={false} or data-streaming="false" to use POST /api/v1/chat/ for compatibility with an older backend or proxy.

Response notification sound

An optional dependency-free chime can notify visitors when the final response arrives, including while the browser tab is in the background:

<AkilioWidget
  projectKey='your-project-key'
  enableResponseSound
  responseSoundVolume={0.22}
/>

Standalone scripts use data-enable-response-sound="true" and optionally data-response-sound-volume="0.22". The sound is disabled by default. It is prepared when the visitor sends a question to comply with browser autoplay rules, plays only for successful final responses, downloads no audio file, and never blocks message delivery if audio is unavailable or denied.

Launcher position

The built-in launcher defaults to the bottom-right corner. It can also be placed at the bottom-left or displayed as a vertical tab in the middle of the right viewport edge:

<AkilioWidget projectKey='your-project-key' launcherPosition='right-middle' />

Supported values are bottom-right, bottom-left, and right-middle. The right-middle position automatically opens a full-height right drawer on desktop, even when the configured mode is floating. On mobile it returns to the compact bottom-right launcher and the normal mobile presentation.

The standalone equivalent is data-launcher-position="right-middle".

Adaptive launcher

The built-in launcher defaults to Akilio’s compact adaptive experience: it appears as an icon, expands once with a page-aware invitation, then stays quiet. It can show a single answer-ready signal when a response completes while chat is closed. Discovery and dismissal are remembered per project, and motion is automatically reduced for visitors who request it.

Use the original persistent label while validating the new experience:

<AkilioWidget launcherExperience='static' />

Standalone scripts use data-launcher-experience="static".

Page-end contextual prompt

Documentation pages can show a compact “Ask about this page” input when the reader approaches the end of the document:

<AkilioWidget
  projectKey='your-project-key'
  contextualPrompt={{
    enabled: true,
    trigger: 'near-page-end',
    threshold: 320,
    placeholder: 'Ask a question about this page...',
    dismissible: true,
  }}
/>

Submitting opens the main chat and sends the question through its normal authenticated streaming flow with the current page URL and title. The prompt does not steal focus, stays hidden while chat is open, supports Escape and a dismiss button, and does not appear before required privacy consent is granted. It uses an IntersectionObserver end marker with a scroll fallback.

Standalone attributes are data-contextual-prompt="true", data-contextual-prompt-threshold="320", data-contextual-prompt-placeholder="Ask about this page...", and data-contextual-prompt-dismissible="true".

Server widget policy

The public project configuration may include an authoritative widget_policy. The widget applies it automatically:

  • available disables questions while the project is unavailable.
  • assistant_mode is sent to chat instead of a client routing hint.
  • commerce_enabled controls product rendering and commerce event tracking.
  • branding enforces required, removable, or white-label presentation.

For projects with branding: "removable", the host can hide Akilio branding:

<AkilioWidget showAkilioBranding={false} />

The standalone equivalent is data-show-akilio-branding="false". A policy of akilio_required always shows Akilio branding, while white_label always hides it regardless of the client option. Older API responses without widget_policy retain the previous widget behavior.

The message composer automatically expands for multiline text up to its bounded maximum height, after which it scrolls internally.

When branding is required, a compact “Powered by Akilio” notice is displayed below the composer at the bottom of the widget. It is omitted for removable and white-label policies.

The default widget primary color follows the Akilio palette (#0F766E) with #115E59 for hover and pressed states. Project configuration and the theme prop continue to override these defaults; theme.primaryDark can customize the interactive hover color.

Floating, drawer, side-panel, and hybrid Shadow DOM widgets use the browser top layer when supported. This keeps the widget above host-site chat, search, cookie, and navigation overlays—even when those elements activate after the composer receives focus. Older browsers use a fixed, isolated host at the maximum safe z-index. Inline widgets remain in the normal document layout.

Browser control API

After the widget fires akilio:ready, integrations can control the real widget without querying its DOM. The frozen API works with normal DOM and Shadow DOM:

await window.AkilioWidget.open();
await window.AkilioWidget.typeDraft('How can I request a quote?', {
  interval: 35,
  focus: true,
  clearExisting: true,
});

// Let the visitor click Send, or submit through the same SSE workflow:
const response = await window.AkilioWidget.submit();

Available methods are open, close, setDraft, typeDraft, clearDraft, cancelTyping, submit, ask, reset, and getState. Rejected operations include a stable code such as EMPTY_DRAFT, BUSY, TYPING_CANCELLED, or STREAM_FAILED. getState() returns only safe UI and conversation state and never exposes authentication data.

Lifecycle events are dispatched on window: akilio:ready, akilio:open, akilio:close, akilio:draft-change, akilio:typing-start, akilio:typing-complete, akilio:typing-cancel, akilio:question, akilio:answer-start, akilio:answer-delta, akilio:answer-complete, akilio:error, and akilio:reset.

Launcher coachmark

The default launcher includes a dependency-free, one-time discovery coachmark. It appears after a short delay, never submits or opens the widget automatically, and is dismissed when the visitor closes it, opens the widget, or presses Escape. Dismissal is stored per project and tour version.

<AkilioWidget enableTour tourDelayMs={3000} tourVersion='v1' />

Standalone attributes are data-enable-tour, data-tour-delay-ms, and data-tour-version. Set enableTour={false} to disable it. To replay the coachmark programmatically, dispatch:

window.dispatchEvent(new Event('akilio:start-tour'));

The coachmark stays inside the widget’s style boundary, does not darken or block the host page, does not steal keyboard focus, and respects reduced-motion preferences.

Capability-driven chat actions

The widget reads capabilities.chat from the public project configuration. An enabled summarize_page intent that supports the widget surface is shown in an action tray immediately above the composer when its page.url requirement is available. Selecting it sends intent: "summarize_page" and the current page context to the chat endpoint. Unsupported, disabled, wrong-surface, or unmet-requirement actions are not displayed. The action tray remains available throughout the conversation and is temporarily hidden while a response streams. Older API responses without the capability catalog retain the previous suggestion behavior.

An enabled contact intent adds a “Contact the team” action. Contact responses can display validated email, phone, and form links from contact_options. When the response includes a start_contact_request action and public configuration enables capabilities.chat.contact_request, the widget renders the advertised fields as an inline contact form. It applies the server-provided consent text, submits through the authenticated /contact-requests/ endpoint, prevents submission without required consent, and displays success, progress, failure, dark-mode, and mobile states. Repeating the source message remains idempotent on the API.

Production React and standalone JavaScript widget for the Akilio backend.

Included modes

  • floating: compact chat popover anchored above the floating launcher.
  • side-panel: persistent desktop side panel with a mobile drawer fallback.
  • inline: renders inside the supplied container.
  • hybrid: backward-compatible alias for side panel on desktop and drawer on mobile.
  • drawer: full-height overlay drawer opened from the floating launcher.

Build

npm install
npm run typecheck
npm run build

Production files:

dist/index.min.js
dist/index.umd.min.cjs
dist/index.d.ts
dist/style.css
dist/loader.js
dist/widget-manifest.json
dist/releases/0.7.0/akilio-widget.min.js
dist/akilio-widget.min.js
dist/_headers

React

import { AkilioWidget } from '@hachther/akilio-widget';
import '@hachther/akilio-widget/style.css';

<AkilioWidget
  apiBaseUrl='https://api.akilio-ai.com'
  projectKey='ask_pk_xxx'
  mode='floating'
  colorMode='system'
  auth={{
    getAccessToken: async () => sessionStorage.getItem('akilio_access_token'),
  }}
  requireConsent
  privacyNotice='Questions are processed to provide an AI-generated answer.'
  privacyNoticeUrl='https://example.com/privacy'
/>;

Custom launcher and controlled state

Hide the built-in launcher and control the floating popup from any host element, such as a header navigation button:

const [chatOpen, setChatOpen] = useState(false);

<button onClick={() => setChatOpen(true)}>Ask Akilio</button>
<AkilioWidget
  apiBaseUrl='https://api.akilio-ai.com'
  projectKey='ask_pk_xxx'
  mode='floating'
  launcher='hidden'
  open={chatOpen}
  onOpenChange={setChatOpen}
/>

Or supply a custom React launcher:

<AkilioWidget
  apiBaseUrl='https://api.akilio-ai.com'
  projectKey='ask_pk_xxx'
  mode='floating'
  launcher={({ open, toggleChat }) => (
    <button className='header-assistant' onClick={toggleChat}>
      {open ? 'Close assistant' : 'Ask Akilio'}
    </button>
  )}
/>

Authentication supports:

auth={{ accessToken: 'short-lived-jwt' }}
auth={{ getAccessToken: refreshAndReturnToken }}
auth={{ apiKey: 'widget-key', apiKeyHeader: 'X-Akilio-Key' }}
auth={{ credentials: 'include' }}

The token resolver is evaluated for every request, so a host application can rotate short-lived backend tokens without remounting the widget.

Standalone script

For production websites, use the stable loader URL:

<script
  data-akilio-widget
  data-cfasync="false"
  src="https://cdn.example.com/widget/loader.js"
  data-api-base-url="https://api.akilio-ai.com"
  data-project-key="ask_pk_xxx"
  data-mode="floating"
  data-color-mode="system"
  data-access-token="short-lived-widget-token"
  data-require-consent="true"
  data-privacy-notice="Questions are processed to provide an AI-generated answer."
  data-privacy-notice-url="https://example.com/privacy"
></script>

For an inline widget:

<div id="support-assistant" style="height: 680px"></div>
<script
  src="https://cdn.example.com/widget/loader.js"
  data-akilio-widget
  data-auto-init="false"
></script>
<script>
  window.addEventListener('akilio:loader-ready', () => {
    window.Akilio.mount({
      target: '#support-assistant',
      apiBaseUrl: 'https://api.akilio-ai.com',
      projectKey: 'ask_pk_xxx',
      mode: 'inline',
      colorMode: 'system',
      accessToken: 'short-lived-widget-token',
    });
  });
</script>

loader.js is revalidated by the browser and loads the immutable release file for the current package version. Deploy the complete dist directory without deleting earlier dist/releases/* versions. Each build also keeps akilio-widget.min.js as a non-immutable compatibility file.

For optional Cloudflare Pages deployments, the generated dist/_headers requires browser and edge revalidation for loader.js and widget-manifest.json. Files under releases/* receive a one-year immutable browser and edge cache. Configure Cloudflare Browser Cache TTL to Respect Existing Headers for this path so a cache rule does not keep the loader stale.

Cloudflare R2 does not use _headers; the R2 deployment command writes the equivalent Cache-Control metadata on each uploaded object.

Publishing a new package version updates only the loader and manifest pointers; existing embed snippets do not change and a manual cache purge is not normally required. The manifest includes the active version, release path, byte size, and SHA-256 digest for deployment verification.

Deploy to Cloudflare R2

Set the R2 bucket name in your local .env or shell environment:

CLOUDFLARE_R2_BUCKET=your-r2-bucket
# Optional folder within the bucket:
CLOUDFLARE_R2_PREFIX=widget

Then validate, build, and publish the CDN assets with:

npm run deploy:r2

You can also provide the target without storing it in .env:

npm run deploy:r2 -- --bucket your-r2-bucket --prefix widget

Wrangler uses an existing local Cloudflare login. In CI, provide CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID as protected environment variables. Never commit a Cloudflare API token.

The deploy command uploads the immutable versioned bundle first, followed by the compatibility bundle, manifest, and finally loader.js. The versioned file receives a one-year immutable cache policy. Stable files receive max-age=0, must-revalidate, so the CDN entry point can discover each release. Add --dry-run to build and inspect the target object paths without uploading.

The standalone API can hide the launcher and control the chat imperatively:

const widget = window.Akilio.mount({
  target: '#akilio-root',
  apiBaseUrl: 'https://api.akilio-ai.com',
  projectKey: 'ask_pk_xxx',
  mode: 'floating',
  launcher: 'hidden',
});

document.querySelector('#header-chat').addEventListener('click', widget.toggle);
// widget.open(); widget.close(); widget.destroy();

Script-tag configuration also accepts data-launcher="hidden".

Supported authentication attributes are data-access-token, data-widget-token, data-api-key, and data-api-key-header.

Backend contract

GET  /api/v1/projects/{project_key}/config/
POST /api/v1/chat/
GET  /api/v1/conversations/{conversation_id}/?project_key=...
POST /api/v1/messages/{message_id}/feedback/

Every request uses the configured authentication headers. Chat requests also include consent metadata when available.

Rendering performance

The composer maintains its own local input state. Typing therefore does not update the widget root or re-render the message history. Message bubbles and Markdown answers are memoized, and automatic scrolling depends on message count rather than text input changes.

Local history retains the latest 60 messages. Server history is restored from the persisted conversation ID when the local message cache is empty.

Manual light/dark switch

The widget displays an accessible light/dark toggle in its header by default. The visitor's choice is persisted per project in local storage. When no manual choice exists, colorMode="system" follows the operating-system preference.

<AkilioWidget
  apiBaseUrl='https://api.example.com'
  projectKey='project-key'
  colorMode='system'
  allowColorModeToggle
/>

For the standalone build, use data-allow-color-mode-toggle="true". Set the React prop or data attribute to false to hide the control.

Widget authentication flow

By default, the widget now implements the Akilio Public API bootstrap flow automatically:

  1. Load public configuration from GET /api/v1/projects/{public_key}/config/.
  2. Create or reuse a stable browser subject.
  3. Request a short-lived token from POST /api/v1/projects/{public_key}/widget-token/.
  4. Send Authorization: Bearer <token> to chat, conversation, and feedback endpoints.
  5. Refresh the token before expires_in elapses, and retry once after an HTTP 401.

No token configuration is required for a normal public widget:

<AkilioWidget
  apiBaseUrl='https://api.akilio-ai.com'
  projectKey='your-public-project-key'
/>

Use a known subject when the host application has one:

<AkilioWidget
  apiBaseUrl='https://api.akilio-ai.com'
  projectKey='your-public-project-key'
  externalUserId='customer-123'
/>

Host-managed tokens remain supported and take precedence over automatic issuance:

<AkilioWidget
  apiBaseUrl='https://api.akilio-ai.com'
  projectKey='your-public-project-key'
  auth={{ getAccessToken: refreshAndReturnToken }}
/>

Advanced token controls:

<AkilioWidget
  apiBaseUrl='https://api.akilio-ai.com'
  projectKey='your-public-project-key'
  auth={{
    autoIssueWidgetToken: true,
    tokenRefreshLeewaySeconds: 30,
    onWidgetToken: ({ expiresAt }) =>
      console.debug('Akilio token expires at', expiresAt),
  }}
/>

Next.js (App Router)

Render the widget from a Client Component because it uses browser functionality after hydration:

'use client';

import { AkilioWidget } from '@hachther/akilio-widget';
import '@hachther/akilio-widget/style.css';

export function AkilioAssistant() {
  return (
    <AkilioWidget
      apiBaseUrl={process.env.NEXT_PUBLIC_AKILIO_API_URL!}
      projectKey={process.env.NEXT_PUBLIC_AKILIO_PROJECT_KEY!}
    />
  );
}

The package itself is now safe to import in an SSR module. The component must still be rendered beneath a use client boundary. A dynamic import with ssr: false is optional, not required.

Ecommerce responses

When the chat API returns a commerce capability, the widget renders a compact commerce surface inside the assistant message. Direct matches, recommendations, comparisons, compatible products, bundles, upsells, and cross-sells receive capability-aware headings and layouts.

<AkilioWidget
  apiBaseUrl='https://api.example.com'
  projectKey='public-project-key'
  onProductClick={(product, variant) => {
    console.log('Product selected', product, variant);
  }}
/>

Product links open through the existing product callback. Trusted commerce.actions are matched only to products returned in the same response. add_to_cart proposals require a second explicit confirmation, then use the backend's one-time capability-confirmation flow and an idempotency key before a cart tool is executed. The provider cart ID is retained in sessionStorage for subsequent additions during the same browser session. The widget also records authenticated commerce events.

The widget refreshes GET /commerce/cart/current/ whenever its chat panel is opened. The header cart control shows the returned line items and only exposes a checkout link supplied by the backend. A chat response with cart_state causes an immediate refresh, ensuring the local cart display follows server-side cart changes without treating the compact summary as the source of truth.

/chat/ response contract

AkilioChatResponse follows the public OpenAPI ChatResponse schema. It supports documented conversation context, answer content, citations, commerce, suggestions, warnings, market resolution, grounding/refusal flags, escalation, contact options, customer actions, agent tools, and intent decisions. Commerce products and actions are runtime-validated before they are rendered because the OpenAPI schema deliberately leaves those objects extensible.

The documented response does not include top-level images, rich_actions, forms, or cart fields. The widget does not treat those fields as part of its public chat contract. Add them to the backend OpenAPI schema before introducing matching widget rendering.

Human handoff

When a configured contact action is submitted, the widget sends its consented request to POST /chat/contact-requests/ with the conversation and assistant message that offered the action. POST /chat/handoffs/ is an equivalent backend alias. The backend derives the conversation summary, intent, customer context, and action context from the validated conversation. The widget renders the browser-safe handoff delivery status returned by the API.

Assistant mode

Use assistantMode to pass the optional assistant_mode routing hint to the chat endpoint:

<AkilioWidget
  apiBaseUrl='https://api.example.com'
  projectKey='public-project-key'
  assistantMode='ecommerce'
/>

Supported values are auto, general, knowledge, ecommerce, and hybrid. The project configuration remains authoritative; this option only narrows behavior permitted by the backend.

For the standalone build:

<script
  src="/akilio-widget.min.js"
  data-akilio-widget
  data-api-base-url="https://api.example.com"
  data-project-key="public-project-key"
  data-assistant-mode="ecommerce"
></script>

Follow-up suggestions

When /api/v1/chat/ returns a suggestions array, the widget displays accessible suggestion chips under that assistant response. Selecting a chip sends it as the next user message. Suggestions are retained in local history and restored from server conversation message metadata or structured content when supplied by the API.

Widget Core (AKI-WIDGET-001)

Built-in interface translations cover English and French (including regional locales such as fr-CA). Supply locale and copy for other languages; missing translations fall back to English. The widget sets its language and direction for common right-to-left locales. The standalone script reads the page language. The backend receives the requested locale for generated responses.

White-label policy hides Akilio marks in the launcher, discovery tip, header, messages, and confirmation cards. Default text becomes neutral; explicitly configured project titles and copy remain under the integrator's control.

History, preferences, configuration, and anonymous continuation credentials use browser storage with an in-memory fallback when storage is blocked or full. Persistent storage is required to resume after a full page reload; memory-only sessions remain usable during the current visit. History is scoped by project and locale and retains the latest 60 messages.

Mobile forms use 16px inputs and main controls have 44px touch targets, alongside viewport sizing and safe-area padding. Physical-device keyboard testing and live backend acceptance testing are still required before production sign-off.

Adding languages

Translations live in src/locales/en.ts and src/locales/fr.ts. To ship a new built-in language:

  1. Copy en.ts to a new file such as es.ts and rename the export to es.
  2. Translate every message, the starter questions, and the whiteLabel wording. Preserve placeholders such as {seconds}. Set direction to ltr or rtl.
  3. Import the pack in src/locales/index.ts and add it to builtInLanguages.
  4. Run npm run typecheck, npm test, and npm run build.

Each complete pack uses satisfies AkilioLanguagePack, so missing translation keys are reported by TypeScript when the interface grows.

You can also add languages without rebuilding the widget using the translations option. Partial packs are supported; untranslated text falls back to English:

import type { AkilioTranslations } from '@hachther/akilio-widget';

const translations = {
  es: {
    direction: 'ltr',
    messages: {
      title: 'Asistente',
      launcher: 'Hacer una pregunta',
      submit: 'Enviar',
      placeholder: 'Escribe tu pregunta...',
      suggestions: ['¿Cómo puedo contactar con el equipo?'],
    },
    whiteLabel: {
      title: 'Asistente',
      launcher: 'Hacer una pregunta',
      configFailed: 'No se puede cargar la configuración.',
    },
  },
} satisfies AkilioTranslations;

<AkilioWidget
  projectKey='your-project-key'
  locale='es-MX'
  translations={translations}
/>;

The same dictionary works with window.Akilio.mount({ projectKey: 'your-project-key', locale: 'es-MX', translations }). For script integrations, use programmatic mounting with auto-init disabled when supplying dictionaries.

Locale matching ignores case and accepts underscores. The fallback order is the most specific locale, then its parent language/script, then English. For example, zh-Hant-TW inherits from zh-Hant, zh, and en. Custom dictionaries override built-ins at the same locale. Existing project title, placeholder, and starter questions still take precedence; the copy prop remains the final text override. White-label wording is stored separately from branded defaults. Dictionaries are scoped to each widget, so one integration cannot change another's translations.

Changing locale or translations props updates React UI copy. The requested locale is still sent to the backend; adding UI translations does not configure backend response languages. This framework uses plain text messages and existing named placeholders, not ICU plural rules.

Business branding (AKI-WIDGET-002)

<AkilioWidget
  projectKey='your-project-key'
  agentName='Alex'
  welcomeMessage='Welcome to Acme. How can I help?'
  starterPrompts={['Explore our services', 'Contact the team']}
  launcherPosition='bottom-left'
  theme={{
    logo_url: 'https://example.com/logo.png',
    brand_name: 'Acme',
    primary_color: '#123456',
    primary_dark_color: '#102030',
    primary_foreground_color: '#ffffff',
  }}
/>

Business logos appear in the launcher, discovery tip, header, empty state, assistant answers, and human-help confirmation cards. HTTP(S) and relative image URLs are accepted. Failed or invalid images fall back to the Akilio mark when branding is enabled, or a neutral icon when branding is hidden. Required “Powered by Akilio” attribution remains visible with a business logo.

Name priority: copy.title, agentName, host theme.brand_name, server theme_config.brand_name, server title, then language defaults. Welcome text uses copy.emptyState, welcomeMessage, server welcome_message, then language defaults. The welcome message appears before the first conversation message.

Before configuration arrives, starter prompts use starterPrompts and then language suggestions. Once available, starter_questions from the public configuration endpoint is authoritative, including an explicit empty array that hides prompts. Up to four are shown; server-advertised capability actions remain independent of starter prompts.

Standalone embeds accept the same options via Akilio.mount() or these attributes:

<script
  src="https://your-cdn.example/widget/loader.js"
  data-akilio-widget
  data-project-key="your-project-key"
  data-agent-name="Alex"
  data-brand-name="Acme"
  data-logo-url="https://example.com/logo.png"
  data-welcome-message="Welcome to Acme. How can I help?"
  data-starter-prompts='["Explore our services", "Contact the team"]'
  data-theme='{"primary_color":"#123456"}'
  data-launcher-position="bottom-left"
></script>

Use data-starter-prompts='[]' to hide prompts. Dedicated logo/name attributes override corresponding values in data-theme. Malformed prompt JSON falls back to configuration defaults. Custom React launchers and external HTML buttons using launcher: 'hidden' with open(), close(), or toggle() remain supported.