AI-powered chat panel SDK with programmatic lifecycle control
npm install @kite-copilot/chat-panelAn AI-powered chat panel SDK for embedding intelligent chat assistants. This package provides a side panel interface that integrates with any AI backend agent. The panel slides in from the right edge and automatically adjusts page content - no layout changes required.
- Side Panel UX: Slides in from the right edge, pushes page content (no overlay)
- Zero-Config: Drop-in integration - no layout changes required from host apps
- Automatic URL Tracking: Captures the exact browser URL (including path and query params) for context
- AI-Powered Chat: Connects to your AI backend agent for intelligent responses
- CSV Bulk Operations: Upload CSV files for bulk data processing
- Interactive Guides: Built-in guided tours with animated cursor
- Dynamic Starting Questions: Customizable per-user questions from backend
- Customizable Themes: Light and dark mode support
- Responsive Design: Works on desktop and mobile
- Easy Integration: Programmatic API or React component
- PWA-Ready: Fully bundled by your build tool, no CDN required
- Offline-Safe: Works with service workers and precaching
``bash`
npm install @kite-copilot/chat-panelor
yarn add @kite-copilot/chat-panelor
pnpm add @kite-copilot/chat-panel
Make sure you have the following peer dependencies installed:
`bash`
npm install react react-dom
The programmatic API provides explicit lifecycle control - ideal for PWAs, Vue, Angular, or any framework.
`ts
import { createKiteChat } from '@kite-copilot/chat-panel';
import '@kite-copilot/chat-panel/style.css';
// Create the chat instance
const chat = createKiteChat({
userId: 'user-123',
orgId: 'org-456', // Organization ID for multi-tenant data isolation
agentUrl: 'https://your-api.example.com',
// Optional: Pass user info for session tracking in control center
userName: 'John Doe',
userEmail: 'john@example.com',
// Optional: Provide authentication headers for API requests
getAuthHeaders: async () => {
return {
Authorization: Bearer ${await getCurrentToken()}/${page}${subtab ?
};
},
onNavigate: (page, subtab) => {
router.push(?tab=${subtab} : ''});
},
});
// Mount - that's it! No container needed, panel handles its own positioning
chat.mount(document.body);
// Control the panel programmatically
chat.open(); // Open the side panel
chat.close(); // Close the side panel
chat.toggle(); // Toggle open/closed
// Check state
if (chat.isOpen()) {
console.log('Panel is open');
}
// Clean up when done
chat.unmount();
// Note: The chat panel automatically captures the browser URL (window.location.href)
// so you don't need to manually track or pass the current page.
`
Create a composable for reusable chat functionality:
`ts
// composables/useKiteChat.ts
import { ref, onMounted, onUnmounted } from 'vue';
import { createKiteChat, type KiteChatInstance, type KiteChatConfig } from '@kite-copilot/chat-panel';
import '@kite-copilot/chat-panel/style.css';
export function useKiteChat(config: KiteChatConfig) {
const chat = ref
const isOpen = ref(false);
onMounted(() => {
chat.value = createKiteChat({
...config,
onNavigate: config.onNavigate,
});
chat.value.mount(document.body);
});
onUnmounted(() => {
chat.value?.unmount();
});
const open = () => {
chat.value?.open();
isOpen.value = true;
};
const close = () => {
chat.value?.close();
isOpen.value = false;
};
const toggle = () => {
chat.value?.toggle();
isOpen.value = !isOpen.value;
};
return { isOpen, open, close, toggle };
}
`
Use in your App.vue or any component:
`vue
`
Or initialize directly in main.ts for app-wide availability:
`ts
// main.ts
import { createApp } from 'vue';
import { createKiteChat } from '@kite-copilot/chat-panel';
import '@kite-copilot/chat-panel/style.css';
import App from './App.vue';
import router from './router';
const app = createApp(App);
app.use(router);
app.mount('#app');
// Initialize chat after Vue app is mounted
const chat = createKiteChat({
userId: 'user-123',
agentUrl: 'https://your-api.example.com',
onNavigate: (page) => router.push(/${page}),
});
chat.mount(document.body);
// Expose globally if needed
window.kiteChat = chat;
`
For React applications, use the ChatPanelWithToggle component:
`tsx
import { ChatPanelWithToggle } from '@kite-copilot/chat-panel';
import '@kite-copilot/chat-panel/style.css';
function App() {
return (
<>
{/ Just add this - no layout changes needed /}
userId="user-123"
userName="John Doe"
userEmail="john@example.com"
onNavigate={(page) => router.push(/${page})}`
/>
>
);
}
For controlled mode:
`tsx
import { useState } from 'react';
import { ChatPanelWithToggle } from '@kite-copilot/chat-panel';
function App() {
const [isPanelOpen, setIsPanelOpen] = useState(false);
return (
<>
onOpenChange={setIsPanelOpen}
agentUrl="https://your-api.example.com"
/>
>
);
}
`
The side panel uses fixed positioning and automatically manages the body's padding-right:
``
┌─────────────────────────────────┐ ┌────────────────────────┬────────┐
│ │ │ │ │
│ │ │ │ Side │
│ Your Page Content │ → │ Your Page Content │ Panel │
│ │ │ (shifted left) │ │
│ [◀] │ │ [▶] │ │
└─────────────────────────────────┘ └────────────────────────┴────────┘
Panel Closed Panel Open
- Closed: Only the toggle arrow (◀) is visible on the right edge
- Open: Panel slides in, toggle becomes (▶), body gets padding-right: 400px
- No overlays: Content is pushed, not covered
The panel shows suggested questions when empty. These can be customized per-user:
`ts
// Option 1: Pass questions directly
const chat = createKiteChat({
userId: 'user-123',
startingQuestions: [
{ id: '1', label: 'Changing layouts', prompt: 'How do I customize the layout?' },
{ id: '2', label: 'Bulk uploads', prompt: 'How do I upload data in bulk?' },
{ id: '3', label: 'Example setups', prompt: 'Show me example configurations' },
],
});
// Option 2: Fetch from backend (per-user)
const chat = createKiteChat({
userId: 'user-123',
startingQuestionsEndpoint: '/api/user/starting-questions',
});
`
The endpoint should return:
`json`
[
{ "id": "1", "label": "Question label", "prompt": "Full prompt to send" },
{ "id": "2", "label": "Another question", "prompt": "Another prompt" }
]
Pass user information to enable session tracking in the control center. This allows support agents to see who they're chatting with when conversations are escalated.
`ts`
const chat = createKiteChat({
userId: 'user-123',
// Optional user info for control center display
userName: 'John Doe',
userEmail: 'john@example.com',
});
| Field | Description |
|-------|-------------|
| userName | Displayed in conversations list and live chat |userEmail
| | Shown in user details panel |
Note: If user info is not provided, placeholder values ("Anonymous User", "Not provided") will be displayed in the control center.
Creates a new chat instance with explicit lifecycle control.
#### KiteChatConfig
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| userId | string | Yes | Unique identifier for the current user |orgId
| | string | No | Organization ID for multi-tenant data isolation |agentUrl
| | string | No | Backend agent API URL |currentPage
| ~~~~ | string | No | Deprecated - URL is now captured automatically |userName
| | string | No | User's display name (for control center) |userEmail
| | string | No | User's email address (for control center) |theme
| | 'light' \| 'dark' \| 'system' | No | Theme preference |startingQuestions
| | StartingQuestion[] | No | Custom starting questions |startingQuestionsEndpoint
| | string | No | URL to fetch per-user questions |getAuthHeaders
| | () => Promise | No | Async function to provide auth headers |onNavigate
| | (page: string, subtab?: string) => void | No | Navigation callback |onActionComplete
| | (actionType: string, data: any) => void | No | Action completion callback |
#### KiteChatInstance
| Method | Description |
|--------|-------------|
| mount(container) | Mount the chat widget (container is just a React mount point) |unmount()
| | Unmount and clean up the chat widget |open()
| | Open the side panel |close()
| | Close the side panel |toggle()
| | Toggle the panel open/closed |isOpen()
| | Check if the panel is currently open |setCurrentPage(page)
| ~~~~ | Deprecated - URL is now captured automatically |updateConfig(config)
| | Update configuration options |isMounted()
| | Check if the widget is currently mounted |
The recommended React component with integrated toggle button.
`tsx`
import { ChatPanelWithToggle } from '@kite-copilot/chat-panel';
#### Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| agentUrl | string | localhost:5002 | Backend agent URL |currentPage
| ~~~~ | string | - | Deprecated - URL is now captured automatically |userId
| | string | - | Unique user identifier |orgId
| | string | - | Organization ID for multi-tenant data isolation |userName
| | string | - | User's display name (for control center) |userEmail
| | string | - | User's email address (for control center) |defaultOpen
| | boolean | false | Initial open state (uncontrolled) |isOpen
| | boolean | - | Controlled open state |onOpenChange
| | (isOpen: boolean) => void | - | Called when open state changes |startingQuestions
| | StartingQuestion[] | - | Custom starting questions |startingQuestionsEndpoint
| | string | - | URL to fetch questions |getAuthHeaders
| | () => Promise | - | Async function to provide auth headers |onNavigate
| | (page, subtab?) => void | - | Navigation callback |onActionComplete
| | (type, data) => void | - | Action completion callback |
For advanced customization, you can use the individual components:
`tsx
import { ChatPanel, PanelToggle } from '@kite-copilot/chat-panel';
function CustomPanel() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
>
);
}
`
`ts
import { Injectable, OnDestroy } from '@angular/core';
import { createKiteChat, KiteChatInstance } from '@kite-copilot/chat-panel';
@Injectable({ providedIn: 'root' })
export class ChatService implements OnDestroy {
private chat: KiteChatInstance;
constructor(private router: Router) {
this.chat = createKiteChat({
userId: 'user-123',
orgId: 'org-456',
onNavigate: (page) => this.router.navigate([page]),
});
this.chat.mount(document.body);
}
open() { this.chat.open(); }
close() { this.chat.close(); }
toggle() { this.chat.toggle(); }
ngOnDestroy() {
this.chat.unmount();
}
}
`
`tsx
// app/layout.tsx
import { ChatPanelWithToggle } from '@kite-copilot/chat-panel';
import '@kite-copilot/chat-panel/style.css';
export default function RootLayout({ children }) {
return (
Backend Integration
The ChatPanel expects your backend agent to expose the following SSE endpoints.
$3
Main chat endpoint for streaming responses.
Request Body:
`json
{
"session_id": "uuid",
"user_id": "user-123",
"org_id": "org-456",
"message": "user message",
"current_page": "https://example.com/dashboard/123?tab=settings",
"user_name": "John Doe",
"user_email": "john@example.com"
}
`SSE Events:
-
progress: Processing status updates
- response: AI response with optional actions
- error: Error messages
- done: Stream completion$3
CSV file upload endpoint for bulk operations.
Request: FormData with
file, message, session_id, user_id, org_id, current_page (full browser URL)$3
Confirm and execute bulk operation.
$3
Return per-user starting questions.
Response:
`json
[
{ "id": "1", "label": "Changing layouts", "prompt": "How do I change the layout?" },
{ "id": "2", "label": "Bulk uploads", "prompt": "How do I upload in bulk?" }
]
`Styling
$3
`tsx
import '@kite-copilot/chat-panel/style.css';
`$3
Override theme variables in your CSS:
`css
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
}.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
}
`TypeScript
Full TypeScript support with exported types:
`tsx
import type {
KiteChatConfig,
KiteChatInstance,
ChatPanelProps,
ChatPanelWithToggleProps,
PanelToggleProps,
StartingQuestion,
ActionType,
ActionData,
Page,
SettingsTab,
} from '@kite-copilot/chat-panel';
`Migration from Overlay Version
If upgrading from a previous version with overlay/floating panel:
1. Remove layout changes: You no longer need flex containers or special positioning
2. Update imports: Use
ChatPanelWithToggle instead of ChatPanel for the full experience
3. Update API calls: Use open()/close()/toggle() instead of managing isCollapsed state`tsx
// Before (overlay)
...
// After (side panel)
...
`Local Development with Test Page
To test the embed script locally using the included test page:
1. Install dependencies:
`bash
npm install
`2. Build the embed script:
`bash
npm run build
`3. Serve the test page:
`bash
npx serve .
`4. Open in browser:
Navigate to
http://localhost:3000/test/test-embed.htmlThe test page (
test/test-embed.html) provides:
- Interactive controls to open/close/toggle the panel
- Page context switching buttons
- Backend URL toggle (localhost vs production)
- Event logging for navigation and action callbacksNote: Make sure your backend agent is running on
http://localhost:5002` (or toggle to production in the test page).MIT © Kite