npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cas0570/chat-widget

v0.0.14

Published

Embeddable chat widget for GeoApps documentation assistant. Easy to integrate via npm

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-widget

Usage

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

Development

Prerequisites

  • Node.js 18+
  • pnpm 10+

Setup

pnpm install

Commands

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

Docker

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-widget

In Docker Compose, the widget is behind the widget profile:

docker compose --profile widget up -d

The nginx config proxies /api/ requests to the chat-api container.


Related