Documentation · v0.3

Getting started

Sir Chats-a-Lot is an open-source React widget that drops an LLM-driven chat panel onto your site. It answers visitor questions from your published content via Gnosys Web, and renders forms / buttons / confirms inline when a text reply isn't enough.

This doc walks you through the five-minute install, the configuration surface, the API contract between the widget and your server, and the basics of theming and self-hosting.

Note
The widget is browser-only. API keys, rate limiting, and content moderation live in YOUR server route — never in the package. That's an intentional liability boundary.
01 · Quick start

Install

Add the package:

npm install sir-chats-a-lot

Mount it in your React app (Next.js App Router shown; the same component pattern works anywhere you can render React):

app/scal.tsx
"use client";

import {
  SirChatsALotProvider,
  SirChatsALotLauncher,
  SirChatsALotWidget,
} from "sir-chats-a-lot";
import type { AgentRequest, SirChatsALotApi } from "sir-chats-a-lot";
import "sir-chats-a-lot/styles";

const api: SirChatsALotApi = async (request: AgentRequest) => {
  const res = await fetch("/api/scal", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(request),
  });
  if (!res.ok) throw new Error(await res.text());
  return res.json();
};

export function AppScal() {
  return (
    <SirChatsALotProvider productName="My SaaS" api={api}>
      <SirChatsALotLauncher />
      <SirChatsALotWidget />
    </SirChatsALotProvider>
  );
}

You'll also need a server route at /api/scal that talks to your LLM provider. The repo ships examples/nextjs-anthropic and examples/nextjs-openai as reference implementations.

02 · Configure

Configuration

The provider accepts these props (all optional except productName and api):

PropTypeDefaultPurpose
productNamestringShown in the launcher and header
api(req) => Promise&lt;res&gt;Your server-route caller
rateLimitMsnumber2000Cooldown between sends/submits
maxMessageLengthnumber2000Character cap per message
maxMessagesnumber50Conversation history cap
greetingstringFirst assistant message when chat opens
subtitlestring"AI assistant"Header subtitle
themeobjectColor/radius/font overrides
userobjectOptional visitor context sent to your route
onErrorfnObservability hook
onMessagefnPer-message hook
onClosefnRuns when the panel closes
persist"none" | "session" | "local""none"Conversation storage mode
persistKeystringRequired when persist is not "none"
clearOnClosebooleanfalseClears the thread when the panel closes
idleTimeoutMsnumberAuto-closes after inactivity
modesModeRegistryNamed flows opened by intent
launcherobjectLauncher position, label, and aria label
03 · Grounding

Knowledge layer

SCAL pairs with gnosys/web — a zero-dependency static knowledge index. The CLI crawls your site (sitemap, directory, or URL list), produces markdown, and builds a TF-IDF index. Your server route loads the index and feeds matches to the LLM each turn.

Setup

Install Gnosys, initialize a web config, and build the index:

npm install gnosys
npx gnosys web init
npx gnosys web build

Then add "postbuild": "gnosys web build" to your package.json scripts so the index regenerates on every deploy.

Note
You don't have to use Gnosys. The widget contract is provider-agnostic; bring your own RAG, vector DB, or whatever your stack already has.
04 · Dynamic UI

Surfaces API

Every turn, your server returns an AgentResponse. It can include text, a surface, or both. The LLM picks which surface (if any) to send based on what the visitor needs next.

Built-in surface types

TypePurpose
quickActionsRow of tappable suggestion cards
formLabeled fields with validation and a submit button
confirmYes/no dialog
composedMixed text/field/button/divider blocks

Response shape

AgentResponse
{
  "message": "Two paths to install — pick one:",
  "surface": {
    "type": "quickActions",
    "props": {
      "actions": [
        { "id": "npm",    "label": "NPM package" },
        { "id": "cli",    "label": "scal init" },
        { "id": "manual", "label": "Manual route" }
      ]
    }
  },
  "sources": [
    { "title": "Installation guide", "path": "/docs/install" }
  ]
}

When the visitor interacts with a surface, the widget sends an AgentAction back with the payload. Your server feeds that to the LLM and the next turn decides what to do next — reply, send a follow-up surface, or finish.

05 · Look & feel

Theming

Every visual aspect is driven by CSS custom properties (--scal-color-accent, --scal-radius-panel, etc.). Override via the theme prop or define CSS variables on the host page. Host theme detection also recognizes common aliases such as --scl-accent.

import { SirChatsALotProvider } from "sir-chats-a-lot";

<SirChatsALotProvider
  productName="My SaaS"
  api={api}
  theme={{
    colors: {
      accent: "#d49b3a",
      background: "#0a100e",
      elevated: "#17211f",
      foreground: "#e3ddca",
      muted: "#a8a091",
      border: "#2b3431",
      accentForeground: "#0a100e",
    },
    radius: {
      panel: "14px",
      button: "999px",
      input: "10px",
    },
    typography: {
      fontFamily: "Inter, system-ui, sans-serif",
    },
  }}
>
  ...
</SirChatsALotProvider>
06 · Ownership

Self-hosting

SCAL is browser-only. Your server route owns the API key, rate limiting (server-side, in addition to the widget's client-side cooldown), prompt-injection defense, and content moderation. The package never makes outbound LLM calls itself.

Note
Need less route boilerplate? Pro adds a drop-in server handler, provider adapters, skill files, debug mode, and compliance hooks under a commercial license. The OSS widget does not require it.

The minimum your route needs to do:

  • Accept a POST with the AgentRequest JSON body
  • Retrieve relevant content from your knowledge index (Gnosys Web or other)
  • Call your LLM with the system prompt + retrieved context + conversation history
  • Validate the LLM's JSON response, returning an AgentResponse
  • Return { blocked: true } for moderation failures, or { error: "..." } for system failures
07 · FAQ

FAQ