npm.io
0.5.0 • Published yesterday

@hachther/akilio-widget

Licence
UNLICENSED
Version
0.5.0
Deps
5
Size
1.7 MB
Vulns
0
Weekly
0

Akilio Widget

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/akilio-widget.min.js

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

<script
  data-akilio-widget
  src="/assets/akilio-widget.min.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="/assets/akilio-widget.min.js" data-auto-init="false"></script>
<script>
  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>

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 automatically displays its products below the assistant answer. No additional configuration is required.

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

Product links open in a new tab. The widget also sends an authenticated commerce event to /api/v1/commerce/events/ using the response capability (search or recommendation).

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.