Widget Integration
Embed the BABEH search widget on any webpage — a floating, self-contained panel that gives your visitors AI-powered search with zero dependencies.
Overview
The KBaaS widget is a self-contained IIFE (Immediately Invoked Function Expression) that renders a fixed-position floating panel at the bottom-center of any webpage. It uses Shadow DOM for full CSS isolation, so it won't conflict with your site's styles.
The widget is fully self-contained (~1250 lines). The only runtime dependency is marked.js (Markdown renderer), which is lazy-loaded from jsDelivr CDN on the first search.
Installation
There are two ways to install the widget — both use a simple <script> tag.
Method A: Script + JS Init
Add the container, load the script, then initialize with options:
<!-- 1. Container -->
<div id="kbaas-widget"></div>
<!-- 2. Load widget -->
<script src="js/kbaas-widget.js"></script>
<!-- 3. Initialize -->
<script>
KBaaSWidget.init({
apiUrl: 'https://your-domain.com/api',
apiKey: 'your-api-key',
apiBaseUrl: 'https://your-domain.com'
});
</script> Method B: Auto-Init via Data Attributes
No JavaScript required — the widget reads configuration from data attributes on the container element:
<div id="kbaas-widget"
data-api-url="https://your-domain.com/api"
data-api-key="your-api-key"
data-api-base-url="https://your-domain.com">
</div>
<script src="js/kbaas-widget.js"></script> On DOMContentLoaded, the widget checks if a #kbaas-widget element exists and hasn't been initialized yet. If found, it reads the
data-api-url, data-api-key, and data-api-base-url attributes, then calls KBaaSWidget.init() automatically.
Configuration Options
All options are optional — defaults are provided:
| Option | Type | Default | Description |
|---|---|---|---|
apiUrl | string | https://dev-api.babeh.com/api | Backend API base URL for search queries |
apiKey | string | xxx | API key sent via X-API-Key header |
containerId | string | kbaas-widget | ID of the HTML element to mount the widget into |
apiBaseUrl | string | https://dev-api.babeh.com | Base URL for the /health endpoint check |
When using auto-init (Method B), the fallback defaults are
http://localhost:8000/api and http://localhost:8000 instead of the production URLs. Always set your data attributes explicitly in production.
API Methods
The widget exposes a global KBaaSWidget object with the following methods:
| Method | Signature | Description |
|---|---|---|
init(options) | init({ apiUrl, apiKey, containerId, apiBaseUrl }) | Mount widget into Shadow DOM, inject styles/HTML, set up listeners, run health check. Warns if called twice. |
search(query) | search(queryString) | Programmatically set the textarea value and trigger a search. Requires init() first. |
clear() | clear() | Empty the textarea, hide results and LLM panels, clear all content. |
configure(opts) | configure({ apiUrl, apiKey, ... }) | Merge new options into config at runtime. Does not reinitialize the API client. |
getConfig() | getConfig() | Returns a shallow copy of the current configuration object. |
isInitialized() | isInitialized() | Returns true if the widget has been initialized. |
Shadow DOM Isolation
The widget uses attachShadow({ mode: 'open' }) for full CSS isolation.
This means:
- Widget CSS never leaks — all styles are injected inside the shadow root as a
<style>element - Host CSS never affects the widget — your site's styles won't break the widget
- DOM queries are scoped — all internal
querySelectorcalls target the shadow root
Two items are injected into the global <head>:
- Google Fonts link for "Boogaloo" font (guarded by ID to prevent duplicates)
- marked.js script from jsDelivr CDN (lazy-loaded on first search)
Theme & Dark Mode
The widget supports three layers of theme control:
System Auto-Detect
The widget uses @media (prefers-color-scheme: dark) inside the shadow CSS
to automatically match the user's OS theme.
Manual Toggle
A "Light / Dark" pill button in the title bar lets users override the system preference.
This adds .kbaas-widget--light or .kbaas-widget--dark classes.
JS Detection on Init
On initialization, the widget detects the current system preference and sets the appropriate class before first render.
CSS Custom Properties
The widget uses CSS custom properties that automatically change with the theme:
| Variable | Light | Dark |
|---|---|---|
--kbaas-bg-primary | #ffffff | #1a1a1a |
--kbaas-bg-secondary | #f8f9fa | #2d2d2d |
--kbaas-text-primary | #212529 | #e0e0e0 |
--kbaas-text-secondary | #6c757d | #a0a0a0 |
--kbaas-border-color | #dee2e6 | #404040 |
--kbaas-accent-color | #0d6efd | #4a9eff |
--kbaas-shadow | 0 2px 8px rgba(0,0,0,0.1) | 0 2px 8px rgba(0,0,0,0.4) |
Responsive Behavior
The widget adapts to four viewport widths:
| Breakpoint | Widget Width | Position | Notes |
|---|---|---|---|
| > 768px | 520px fixed | Fixed bottom-center | Default |
| ≤ 768px | calc(100vw - 32px) | Fixed bottom-left | Max 520px |
| ≤ 480px | calc(100vw - 20px) | Fixed bottom-left | Smaller fonts (14px), tighter padding |
| ≤ 360px | calc(100vw - 16px) | Fixed bottom-left | Even smaller fonts (13px), minimal padding |
The widget body uses max-height: 80vh with overflow scrolling when expanded
(85vh on ≤ 480px).
Keyboard Shortcuts
| Shortcut | Context | Action |
|---|---|---|
| Ctrl + Enter | Focus in query textarea | Submit the search query |
Widget UI Structure
The widget renders as a floating panel with these parts:
- Title bar — always visible; click to expand/collapse. Displays "Coba Tanya BABEH!" in Boogaloo font with gradient text.
- LLM Response — markdown-rendered AI answer with a blue left border, scrollable (max 400px), blinking cursor during streaming.
- Source Results — collapsible section with chevron toggle; cited sources as a card grid with filename and color-coded relevance score badges.
- Query textarea — multi-line input with placeholder text.
- "Ask!" button — disables and shows " Searching..." during queries.
Relevance Score Colors
Source cards display color-coded relevance badges:
| Score Range | Color | Meaning |
|---|---|---|
| ≥ 0.90 | Green | Highly relevant |
| ≥ 0.80 | Blue | Very relevant |
| ≥ 0.70 | Orange | Relevant |
| < 0.70 | Gray | Marginally relevant |
Streaming Protocol (SSE)
The widget uses Server-Sent Events for real-time responses:
data: [{id, content, score, ...}] loop Token by token S-->>W: event: llm_chunk
data: "partial text..." end S-->>W: event: cited_sources
data: [{filename, score, ...}] S-->>W: [stream complete] W->>W: Final render + citations
| SSE Event | Payload | Behavior |
|---|---|---|
search_results | Array of search result objects | Stores results; rendering deferred until stream completes |
llm_chunk | String fragment of LLM response | Appended to running text; markdown-rendered with blinking cursor |
cited_sources | Array of source objects | Deduplicated by filename, sorted by score, rendered as cards |
Session & Security
- API Key — every request includes
X-API-Keyheader - Session ID — a UUID v4 is generated per session and stored in
sessionStorageunder keykbaas_session_id; sent viaX-Session-IDheader - HTML Escaping — filenames and user input are escaped before rendering to prevent XSS
- External Links — all source URL links include
rel="noopener noreferrer"andtarget="_blank"
The widget does not implement client-side rate limiting. Rate limiting should be enforced on the server side.
Framework Integration Examples
The widget works with any framework. Load the script globally, then call init() in the appropriate lifecycle hook.
React
import { useEffect } from 'react';
export default function App() {
useEffect(() => {
KBaaSWidget.init({
apiUrl: 'https://your-domain.com/api',
apiKey: 'your-api-key',
apiBaseUrl: 'https://your-domain.com'
});
}, []);
return <div id="kbaas-widget"></div>;
} Vue
<template>
<div id="kbaas-widget"></div>
</template>
<script>
export default {
mounted() {
KBaaSWidget.init({
apiUrl: 'https://your-domain.com/api',
apiKey: 'your-api-key',
apiBaseUrl: 'https://your-domain.com'
});
}
};
</script> Angular
declare var KBaaSWidget: any;
@Component({
selector: 'app-root',
template: '<div id="kbaas-widget"></div>'
})
export class AppComponent implements OnInit {
ngOnInit() {
KBaaSWidget.init({
apiUrl: 'https://your-domain.com/api',
apiKey: 'your-api-key',
apiBaseUrl: 'https://your-domain.com'
});
}
} Toast Notifications
The widget displays toast notifications for connection status and errors. Toasts appear at the top-right of the viewport and auto-dismiss after 3 seconds.
| Type | Color | Example |
|---|---|---|
| Success | Green | Health check passed |
| Error | Red | API connection failed |
| Warning | Orange | Network timeout |