@cas0570/chat-widget
v0.0.14
Published
Embeddable chat widget for GeoApps documentation assistant. Easy to integrate via npm
Maintainers
Readme
GeoApps Chat Widget
Embeddable React chat widget for the GeoApps documentation assistant.
Overview
| | |
| --- | --- |
| Type | Frontend Component Library |
| Framework | React 18 · TypeScript 5 · Vite 5 |
| Package | @cas0570/chat-widget |
| Formats | ESM, UMD |
| Language | Dutch (UI text) |
A floating chat button and dialog that connects to the GeoApps Chat API. Messages are sent via POST /api/chat; responses are retrieved by polling GET /api/sessions/{id}. All user-facing text is in Dutch.
Features
| Feature | Description |
| --- | --- |
| 💬 Floating Chat Button | Collapsible FAB with unread badge and popout indicator |
| 📐 Four Sizes | Small, medium, large, and fullscreen — cycleable via menu |
| 🪟 Popout Window | Open the chat in a separate browser popup that syncs styles |
| 📝 Markdown Rendering | Custom parser for headings, bold, italic, code blocks, links |
| 🔗 Source Citations | Displays up to 3 source links per assistant message |
| 📜 Session History | Browse, switch, and delete past chat sessions |
| ♿ Accessibility | WCAG 2.1: keyboard navigation, focus trapping, ARIA roles, reduced-motion support |
| 🔐 Auth Integration | Bearer token auth with re-check before every message send |
| 🔄 Cross-Tab Sync | Session changes propagate via StorageEvent |
| 🎨 Customisable | Primary colour, position, placeholder text, greeting, callbacks |
| 🛡️ XSS Protection | Markdown renderer only allows https:, http:, mailto: URLs |
Quick Start
Install
pnpm add @cas0570/chat-widget
# or
npm install @cas0570/chat-widgetUsage
import { ChatWidget } from '@cas0570/chat-widget'
function App() {
return (
<ChatWidget
apiUrl="https://your-api.example.com/api/chat"
authUrl="https://your-api.example.com/api/users/me"
title="GeoApps Help"
subtitle="Stel je vraag"
primaryColor="#3b82f6"
position="bottom-right"
/>
)
}React 18 or 19 is required as a peer dependency.
Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| apiUrl | string | required | Chat API base URL |
| authUrl | string | undefined | Auth verification endpoint (GET with Bearer token) |
| tenantId | string | "" | Tenant ID for multi-tenant isolation |
| title | string | "Chat" | Dialog header title |
| subtitle | string | "" | Dialog header subtitle |
| placeholder | string | "Typ een bericht..." | Input placeholder |
| greeting | string | "" | Initial assistant message shown on open |
| primaryColor | string | "#3b82f6" | Accent colour (hex or CSS var()) |
| position | WidgetPosition | "bottom-right" | Preset or custom { top?, bottom?, left?, right? } |
| defaultOpen | boolean | false | Whether the dialog starts open |
| defaultSize | WidgetSize | "medium" | Initial size (small · medium · large · fullscreen) |
| headers | Record<string, string> | {} | Extra headers sent with every API request |
| tokenStorageKey | string | undefined | sessionStorage key for the OIDC token |
Callbacks
| Prop | Type | Description |
| --- | --- | --- |
| onOpen | () => void | Fired when the chat dialog opens |
| onClose | () => void | Fired when the chat dialog closes |
| onMessageSent | (message: string) => void | Fired after a message is sent |
Project Structure
geoapps-chat-widget/
├── src/
│ ├── index.ts # Package entry (re-exports + CSS side-effect)
│ ├── index.css # All widget styles (BEM with gcw- prefix)
│ ├── main.tsx # Dev/demo entry point
│ ├── types.ts # All TypeScript interfaces and types
│ ├── components/
│ │ ├── ChatWidget.tsx # Top-level orchestrator
│ │ ├── ChatWindow.tsx # Chat window (header, messages, input)
│ │ ├── ChatBubble.tsx # Individual message bubble
│ │ ├── ToggleButton.tsx # Floating action button
│ │ ├── MessageList.tsx # Scrollable message area
│ │ ├── MessageInput.tsx # Auto-resizing textarea + send
│ │ ├── MarkdownRenderer.tsx # Custom markdown → React (no deps)
│ │ ├── HeaderMenu.tsx # Kebab dropdown menu
│ │ ├── SessionHistory.tsx # Past sessions panel
│ │ ├── ConfirmModal.tsx # Confirm/cancel dialog
│ │ ├── ErrorBoundary.tsx # React error boundary
│ │ ├── PopoutWindow.tsx # Browser popup via React portal
│ │ ├── TypingIndicator.tsx # Three bouncing dots
│ │ └── Icons.tsx # SVG icon components
│ ├── hooks/
│ │ ├── useAuth.ts # Auth verification + recheck
│ │ ├── useChat.ts # Chat + session logic (send, poll, CRUD)
│ │ └── usePopout.ts # Popout window state + localStorage
│ └── utils/
│ ├── chatPersistence.ts # localStorage: sessionId, popout, read state
│ └── token.ts # Read OIDC token from sessionStorage
├── package.json
├── tsconfig.json # Strict mode with noUncheckedIndexedAccess
├── vite.config.ts # Lib mode (npm) + SPA mode (dev)
├── vitest.config.ts # jsdom + V8 coverage
├── eslint.config.js # TS + React Hooks + jsx-a11y
├── nginx.conf # Production: gzip, security headers, API proxy
└── Dockerfile # Multi-stage: pnpm build → nginx:alpineDevelopment
Prerequisites
- Node.js 18+
- pnpm 10+
Setup
pnpm installCommands
| Command | Description |
| --- | --- |
| pnpm dev | Start dev server on http://localhost:3000 |
| pnpm build | Build SPA (demo) |
| pnpm build:lib | Build npm library (ESM + UMD + types) |
| pnpm test | Run Vitest in watch mode |
| pnpm test:run | Run tests once |
| pnpm test:coverage | Run tests with V8 coverage |
| pnpm lint | ESLint |
| pnpm type-check | TypeScript tsc --noEmit |
Testing
303 tests across 20 test files covering all components, hooks, and utilities.
pnpm test:runDocker
The Dockerfile builds a production SPA served by nginx with API reverse-proxying.
# Build (from monorepo root)
docker build -t geoapps-chat-widget -f clients/geoapps-chat-widget/Dockerfile .
# Run standalone
docker run -p 3000:80 geoapps-chat-widgetIn Docker Compose, the widget is behind the widget profile:
docker compose --profile widget up -dThe nginx config proxies /api/ requests to the chat-api container.
Related
- GeoApps Chatbot — Monorepo
- GeoApps Chat API — Backend service
- GeoApps Doc Indexer — Documentation indexer
