@kite-copilot/chat-panel
v0.2.52
Published
AI-powered chat panel SDK with programmatic lifecycle control
Readme
@kite-copilot/chat-panel
An 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.
Features
- 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
- 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
Installation
npm install @kite-copilot/chat-panel
# or
yarn add @kite-copilot/chat-panel
# or
pnpm add @kite-copilot/chat-panelPeer Dependencies
Make sure you have the following peer dependencies installed:
npm install react react-domQuick Start
Programmatic API (Recommended)
The programmatic API provides explicit lifecycle control - ideal for PWAs, Vue, Angular, or any framework.
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: '[email protected]',
// Optional: Provide authentication headers for API requests
getAuthHeaders: async () => {
return {
Authorization: `Bearer ${await getCurrentToken()}`
};
},
onNavigate: (page, subtab) => {
router.push(`/${page}${subtab ? `?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');
}
// Update context as user navigates
chat.setCurrentPage('settings');
// Clean up when done
chat.unmount();Vue.js
Create a composable for reusable chat functionality:
// 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<KiteChatInstance | null>(null);
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;
};
const setCurrentPage = (page: string) => {
chat.value?.setCurrentPage(page);
};
return { isOpen, open, close, toggle, setCurrentPage };
}Use in your App.vue or any component:
<!-- App.vue -->
<script setup lang="ts">
import { useKiteChat } from './composables/useKiteChat';
import { useRouter } from 'vue-router';
const router = useRouter();
const { open, close, toggle, setCurrentPage } = useKiteChat({
userId: 'user-123',
agentUrl: 'https://your-api.example.com',
onNavigate: (page, subtab) => {
router.push(`/${page}${subtab ? `?tab=${subtab}` : ''}`);
},
});
// Update context when route changes
router.afterEach((to) => {
setCurrentPage(to.name as string);
});
</script>
<template>
<div>
<button @click="toggle">Toggle Help Panel</button>
<!-- Your app content -->
<router-view />
</div>
</template>Or initialize directly in main.ts for app-wide availability:
// 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;React Component
For React applications, use the ChatPanelWithToggle component:
import { ChatPanelWithToggle } from '@kite-copilot/chat-panel';
import '@kite-copilot/chat-panel/style.css';
function App() {
return (
<>
<YourExistingApp />
{/* Just add this - no layout changes needed */}
<ChatPanelWithToggle
agentUrl="https://your-api.example.com"
userId="user-123"
userName="John Doe"
userEmail="[email protected]"
onNavigate={(page) => router.push(`/${page}`)}
/>
</>
);
}For controlled mode:
import { useState } from 'react';
import { ChatPanelWithToggle } from '@kite-copilot/chat-panel';
function App() {
const [isPanelOpen, setIsPanelOpen] = useState(false);
return (
<>
<button onClick={() => setIsPanelOpen(true)}>Open Help</button>
<ChatPanelWithToggle
isOpen={isPanelOpen}
onOpenChange={setIsPanelOpen}
agentUrl="https://your-api.example.com"
/>
</>
);
}How It Works
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
Dynamic Starting Questions
The panel shows suggested questions when empty. These can be customized per-user:
// 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:
[
{ "id": "1", "label": "Question label", "prompt": "Full prompt to send" },
{ "id": "2", "label": "Another question", "prompt": "Another prompt" }
]User Info & Session Tracking
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.
const chat = createKiteChat({
userId: 'user-123',
// Optional user info for control center display
userName: 'John Doe',
userEmail: '[email protected]',
});| 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.
API Reference
createKiteChat(config)
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 | Current page context |
| 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<Record<string, string>> | 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) | Update the current page context |
| updateConfig(config) | Update configuration options |
| isMounted() | Check if the widget is currently mounted |
ChatPanelWithToggle (React)
The recommended React component with integrated toggle button.
import { ChatPanelWithToggle } from '@kite-copilot/chat-panel';Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| agentUrl | string | localhost:5002 | Backend agent URL |
| currentPage | string | - | Current page for context |
| 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<Record<string, string>> | - | Async function to provide auth headers |
| onNavigate | (page, subtab?) => void | - | Navigation callback |
| onActionComplete | (type, data) => void | - | Action completion callback |
Low-Level Components
For advanced customization, you can use the individual components:
import { ChatPanel, PanelToggle } from '@kite-copilot/chat-panel';
function CustomPanel() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<PanelToggle isOpen={isOpen} onClick={() => setIsOpen(!isOpen)} />
<ChatPanel isOpen={isOpen} onClose={() => setIsOpen(false)} />
</>
);
}Framework Examples
Angular
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();
}
}Next.js App Router
// app/layout.tsx
import { ChatPanelWithToggle } from '@kite-copilot/chat-panel';
import '@kite-copilot/chat-panel/style.css';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<ChatPanelWithToggle
agentUrl={process.env.NEXT_PUBLIC_AGENT_URL}
/>
</body>
</html>
);
}Backend Integration
The ChatPanel expects your backend agent to expose the following SSE endpoints.
/chat/stream (POST)
Main chat endpoint for streaming responses.
Request Body:
{
"session_id": "uuid",
"user_id": "user-123",
"org_id": "org-456",
"message": "user message",
"current_page": "dashboard",
"user_name": "John Doe",
"user_email": "[email protected]"
}SSE Events:
progress: Processing status updatesresponse: AI response with optional actionserror: Error messagesdone: Stream completion
/chat/bulk/stream (POST)
CSV file upload endpoint for bulk operations.
Request: FormData with file, message, session_id, user_id, org_id, current_page
/chat/bulk/confirm (POST)
Confirm and execute bulk operation.
/api/user/starting-questions (GET) - Optional
Return per-user starting questions.
Response:
[
{ "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
Using the Default Styles
import '@kite-copilot/chat-panel/style.css';CSS Variables
Override theme variables in your 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:
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:
- Remove layout changes: You no longer need flex containers or special positioning
- Update imports: Use
ChatPanelWithToggleinstead ofChatPanelfor the full experience - Update API calls: Use
open()/close()/toggle()instead of managingisCollapsedstate
// Before (overlay)
<div style={{ display: 'flex' }}>
<main style={{ flex: 1 }}>...</main>
<div id="chat-container" />
</div>
// After (side panel)
<main>...</main>
<ChatPanelWithToggle />Local Development with Test Page
To test the embed script locally using the included test page:
Install dependencies:
npm installBuild the embed script:
npm run buildServe the test page:
npx serve .Open in browser: Navigate to
http://localhost:3000/test/test-embed.html
The 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 callbacks
Note: Make sure your backend agent is running on http://localhost:5002 (or toggle to production in the test page).
License
MIT © Kite
