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.

flowchart LR A[Your Website] -->|Load script| B[kbaas-widget.js] B -->|Shadow DOM| C[Widget Panel] C -->|API calls| D[BABEH Backend] D -->|SSE stream| C C -->|Render| E[AI Response + Sources] style B fill:#3B82F6,color:#fff style C fill:#8B5CF6,color:#fff style D fill:#10B981,color:#fff
Zero Dependencies

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:

HTML
<!-- 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:

HTML
<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>
How Auto-Init Works

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:

OptionTypeDefaultDescription
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
Data Attribute Defaults Differ

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:

MethodSignatureDescription
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:

Global Exceptions

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:

VariableLightDark
--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-shadow0 2px 8px rgba(0,0,0,0.1)0 2px 8px rgba(0,0,0,0.4)
Widget light and dark modes
Screenshot: Widget in light mode (left) and dark mode (right)

Responsive Behavior

The widget adapts to four viewport widths:

BreakpointWidget WidthPositionNotes
> 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).

Widget on mobile
Screenshot: Widget on mobile viewport (≤ 480px)

Keyboard Shortcuts

ShortcutContextAction
Ctrl + Enter Focus in query textarea Submit the search query

Widget UI Structure

The widget renders as a floating panel with these parts:

flowchart TD A[Widget Container - z-index 9999] --> B[Title Bar] A --> C[Collapsible Body] B --> B1["'Coba Tanya BABEH!' text"] B --> B2[Theme toggle pill] B --> B3[Collapse chevron] C --> D[LLM Response Section] C --> E[Source Results] C --> F[Query Textarea] C --> G["'Ask!' Button"] C --> H["'Powered by Babeh' footer"] D --> D1[Markdown-rendered AI answer] E --> E1[Collapsible source cards grid] style A fill:#3B82F6,color:#fff style C fill:#8B5CF6,color:#fff

Relevance Score Colors

Source cards display color-coded relevance badges:

Score RangeColorMeaning
≥ 0.90 GreenHighly relevant
≥ 0.80 BlueVery relevant
≥ 0.70 OrangeRelevant
< 0.70 GrayMarginally relevant

Streaming Protocol (SSE)

The widget uses Server-Sent Events for real-time responses:

sequenceDiagram participant W as Widget participant S as Backend W->>S: POST /search/stream S-->>W: event: search_results
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 EventPayloadBehavior
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

No Client-Side Rate Limiting

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

React (JSX)
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

Vue (SFC)
<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

Angular (TypeScript)
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.

TypeColorExample
SuccessGreenHealth check passed
ErrorRedAPI connection failed
WarningOrangeNetwork timeout