Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
name: Typecheck · Test · Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: Install
run: pnpm install --frozen-lockfile

- name: Typecheck
run: pnpm typecheck

- name: Typecheck (e2e)
run: pnpm typecheck:e2e

- name: Unit tests
run: pnpm test

- name: Build
run: pnpm build
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Changelog

All notable changes to `@smooai/chat-widget` are documented here. This project
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 0.2.0

The widget is back as a standalone package — and got a complete visual redesign.

### Added

- **"Aurora Glass" design system** — a full rebuild of the widget's visual layer:
- Spring launcher with a live "presence pulse" breathing ring and a crafted
chat-spark icon (no more emoji).
- Glass-depth panel with layered ambient shadows, a brand-tinted top glow, and
a spring entrance animation.
- Header with a gradient brand avatar (agent monogram), a live connection
status dot (green online / amber connecting / red error), and an icon close.
- Message rows with assistant mini-avatars, a real animated **typing
indicator**, message rise-in, and a refined streaming cursor.
- Refined **Sources** disclosure with a count pill and accent cards.
- Icon composer: a focus-lit field with a circular gradient send button,
auto-growing textarea, and a "powered by smooth-operator" footer.
- **Derived theme tokens** — `primary-2` (gradient depth) and `surface-2` (inset
wash) are computed in CSS from `primary`/`text`, so a single `primary` color
themes the whole widget and adapts to light or dark automatically.
- Unit + render test suite (vitest + jsdom) covering config resolution, style
injection, the XSS-safe citation URL guard, and the rendered shadow tree.
- GitHub Actions CI (typecheck · test · build) and a backend-free interactive
showcase (`index.html`) driven by an in-page protocol mock.

### Changed

- Default `theme.border` is now a translucent value (`rgba(255,255,255,.1)`) for
the glass aesthetic.
- Depends on `@smooai/smooth-operator@^0.2.0`.

### Notes

This release reverses the brief deprecation in which the widget shipped only as a
subpath of `@smooai/smooth-operator`. The widget is once again its own package,
consuming `@smooai/smooth-operator` purely for the protocol client — one source of
truth for the wire protocol, one dedicated home for the embeddable UI.
49 changes: 49 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Contributing to @smooai/chat-widget

Thanks for your interest! This is a small, focused package — the embeddable
`<smooth-agent-chat>` web component for the
[smooth-operator](https://github.com/SmooAI/smooth-operator) protocol.

## Setup

```bash
pnpm install
pnpm check # typecheck + unit tests + build — run this before pushing
```

## Project layout

| Path | What it is |
| -------------------- | ---------------------------------------------------------------- |
| `src/element.ts` | The custom element — render, DOM wiring, security, public API. |
| `src/styles.ts` | The "Aurora Glass" scoped stylesheet (`buildStyles`). |
| `src/config.ts` | `ChatWidgetConfig` / `ChatWidgetTheme` + `resolveConfig` defaults.|
| `src/conversation.ts`| `ConversationController` — bridges the UI to the protocol client. |
| `src/standalone.ts` | IIFE entry: auto-registers the element + exposes `SmoothAgentChat`.|
| `index.html` | Backend-free interactive showcase (in-page protocol mock). |
| `e2e/` | Playwright live e2e (gated on a real server + gateway key). |

## Ground rules

- **Keep it framework-light.** No runtime UI framework, no CSS framework, no icon
fonts. The widget must stay tiny and embeddable anywhere.
- **The protocol lives in smooth-operator.** Don't reimplement wire shapes here;
consume `@smooai/smooth-operator`. UI-only changes belong in this repo.
- **Security:** never assign untrusted strings to `innerHTML` or to a link `href`.
Message/citation text → `textContent`; citation URLs → `safeHttpUrl` (http/https
only). There are tests guarding this — keep them green.
- **Theme through tokens.** New colors should be `--sac-*` custom properties driven
by the theme, not hard-coded, so brand adaptation keeps working.
- **Add a test.** Pure helpers get unit tests; rendered structure gets a jsdom
smoke test (see `src/*.test.ts`).

## Pull requests

1. Branch, make your change, add/adjust tests.
2. `pnpm check` must pass (CI runs the same).
3. Add a `CHANGELOG.md` entry under the next version.
4. Open the PR with a clear before/after — a screenshot or GIF for visual changes.

## License

By contributing you agree your contributions are licensed under the MIT License.
214 changes: 192 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,207 @@
# chat-widget — DEPRECATED ⚠️
<div align="center">

> This repo is **deprecated and archived.** The embeddable chat widget now ships
> as a **subpath export of the single `@smooai/smooth-operator` SDK**, not as a
> standalone package.
# @smooai/chat-widget

## Where it went
### Embeddable AI chat as a framework-light web component.

The widget source moved into the smooth-operator repo at
[`typescript/src/widget/`](https://github.com/SmooAI/smooth-operator/tree/main/typescript/src/widget)
and is published as part of `@smooai/smooth-operator`:
**Streaming replies · grounded sources · popover or full-page · inherits _your_ brand color.**

[![npm](https://img.shields.io/npm/v/@smooai/chat-widget?color=00a6a6&label=npm)](https://www.npmjs.com/package/@smooai/chat-widget)
[![CI](https://github.com/SmooAI/chat-widget/actions/workflows/ci.yml/badge.svg)](https://github.com/SmooAI/chat-widget/actions/workflows/ci.yml)
[![bundle](https://img.shields.io/badge/gzip-~19%20kB-4f8bff)](https://github.com/SmooAI/chat-widget)
[![license](https://img.shields.io/badge/license-MIT-8b5cf6)](./LICENSE)

</div>

---

`<smooth-agent-chat>` is a self-contained chat web component for the
[smooth-operator](https://github.com/SmooAI/smooth-operator) protocol. Drop in one
`<script>` tag (or import the ESM module) and you get a polished, streaming,
shadow-DOM-isolated chat experience — **the "Aurora Glass" design** — that adapts
to any host brand from a single accent color.

No React on the host page. No CSS to import. ~19 kB gzipped, protocol client included.

```html
<script src="https://unpkg.com/@smooai/chat-widget/dist/chat-widget.global.js"></script>
<smooth-agent-chat endpoint="wss://your-host/ws" agent-id="…" agent-name="Support"></smooth-agent-chat>
```

> **Live demo:** open [`index.html`](./index.html) (it runs entirely in the browser
> against an in-page mock of the protocol — no backend needed).

## Why this widget

- **Aurora Glass design** — a spring launcher with a live presence pulse, a
glass-depth panel, gradient brand avatar + status dot, an animated typing
indicator, message rise-in, refined source cards, and an icon composer.
- **Brand-adaptive** — pass one `primary` color and the gradients, bubbles, glow,
and depth shades derive themselves. Works light or dark.
- **Streaming + sources** — token-by-token replies with a typing indicator and a
collapsible "Sources" disclosure for knowledge-grounded answers.
- **Popover or full-page** — a floating launcher, or a full-bleed `/chat` surface.
Same component, one attribute.
- **Framework-light & isolated** — a standard custom element with Shadow-DOM style
isolation. Use it with React, Vue, Svelte, Astro, or no framework at all.
- **Secure by construction** — message + citation text is rendered via
`textContent`, and citation links are restricted to `http(s)` (no
`javascript:`/`data:` XSS).

## Install

```bash
pnpm add @smooai/smooth-operator
pnpm add @smooai/chat-widget
# or: npm i @smooai/chat-widget · yarn add @smooai/chat-widget
```

`@smooai/smooth-operator` (the protocol client) is a runtime dependency and is
installed automatically.

## Quick start

### 1. Declarative — a `<script>` tag + the element

```html
<script src="https://unpkg.com/@smooai/chat-widget/dist/chat-widget.global.js"></script>

<smooth-agent-chat
endpoint="wss://your-host/ws"
agent-id="11111111-1111-4111-8111-111111111111"
agent-name="Support"
greeting="Hi! How can I help?"
></smooth-agent-chat>
```

The IIFE bundle auto-registers the element on load.

### 2. Programmatic — bundler / ESM

```ts
import { mountChatWidget } from '@smooai/chat-widget';

const widget = mountChatWidget({
endpoint: 'wss://your-host/ws',
agentId: '11111111-1111-4111-8111-111111111111',
agentName: 'Support',
theme: { primary: '#8b5cf6' },
});

// Drive it imperatively:
widget.openChat();
widget.closeChat();
```

Or register the element and place it yourself:

```ts
// Programmatic mount (any framework, with a bundler):
import { mountChatWidget } from '@smooai/smooth-operator/widget';
mountChatWidget({ endpoint: 'wss://your-host/ws', agentId });
import { defineChatWidget } from '@smooai/chat-widget';
defineChatWidget(); // now <smooth-agent-chat> works anywhere in your markup
```

## Theming

Every color is a CSS custom property under the hood, so the simplest possible
theme is a single `primary` — the launcher gradient, send button, user bubbles,
ambient glow, and derived depth shades all follow from it.

```ts
mountChatWidget({
endpoint, agentId,
theme: {
primary: '#ff5d6e', // the one color most brands need to set
background: '#ffffff', // light panel
text: '#1f2430',
},
});
```

| Token | Drives | Default |
| ---------------------- | --------------------------------------------------- | ------------------ |
| `primary` | Launcher, send button, user bubble, glow, avatar | `#00a6a6` |
| `primaryText` | Text/icon on top of `primary` | `#f8fafc` |
| `background` | Panel background | `#040d30` |
| `text` | Foreground text + derived inset surfaces | `#f8fafc` |
| `assistantBubble` | Inbound (assistant) bubble background | `#06134b` |
| `assistantBubbleText` | Inbound bubble text | `#f8fafc` |
| `userBubble` | Outbound (user) bubble background | `primary` |
| `userBubbleText` | Outbound bubble text | `primaryText` |
| `border` | Panel + input hairline | `rgba(255,255,255,.1)` |

Two further tokens — a darker `primary-2` (gradient depth) and a `surface-2`
inset wash — are **derived in CSS** from `primary`/`text`, so you never have to
supply them and they adapt automatically to light or dark themes.

## Full-page mode

Render the chat as a full-bleed surface (a `/chat` route, a docs sidebar, an
iframe) instead of a floating launcher:

```html
<!-- Or a no-build <script> embed (the prebuilt IIFE bundle): -->
<script src="https://unpkg.com/@smooai/smooth-operator/dist/widget/chat-widget.iife.js"></script>
<smooth-agent-chat endpoint="wss://your-host/ws" agent-id="…"></smooth-agent-chat>
<smooth-agent-chat mode="fullpage" endpoint="wss://…/ws" agent-id="…"></smooth-agent-chat>
```

```ts
import { mountFullPageChat } from '@smooai/chat-widget';
mountFullPageChat({ endpoint, agentId, agentName: 'Support' }, document.getElementById('chat'));
```

## Configuration

Declarative attributes mirror the programmatic [`ChatWidgetConfig`](./src/config.ts):

| Attribute | Config key | Notes |
| --------------- | ---------------- | -------------------------------------------------- |
| `endpoint` | `endpoint` | **Required.** smooth-operator WebSocket URL. |
| `agent-id` | `agentId` | **Required.** UUID of the agent. |
| `agent-name` | `agentName` | Header label + monogram. Default `Assistant`. |
| `placeholder` | `placeholder` | Composer placeholder. |
| `greeting` | `greeting` | Shown before the first message. |
| `mode` | `mode` | `popover` (default) or `fullpage`. |
| `start-open` | `startOpen` | Open the panel immediately (no launcher click). |
| — | `theme` | Brand colors (see [Theming](#theming)). |
| — | `userName` / `userEmail` | Optional participant identity. |

### Programmatic API

`mountChatWidget(config, target?)` and `mountFullPageChat(config, target?)` return
the element, which exposes:

- `configure(partialConfig)` — merge overrides (e.g. live theme tweaks) and re-render.
- `openChat()` / `closeChat()` — toggle the popover panel.

## How it works

The widget is pure view + a thin `ConversationController` that speaks the
smooth-operator protocol via [`@smooai/smooth-operator`](https://github.com/SmooAI/smooth-operator):

```
connect() → WebSocket + create_conversation_session
send(text) → send_message → stream_token … (live) … → eventual_response (+ citations)
```

The wire protocol is owned by smooth-operator; this package only renders it. That
keeps one source of truth for the protocol and lets the widget stay tiny.

## Browser support

Evergreen browsers (Chrome/Edge 111+, Safari 16.4+, Firefox 113+). The design uses
`color-mix()` to derive brand shades and `::part()` for host-side customization.

## Local development

```bash
pnpm install
pnpm build # tsdown → dist/ (ESM + IIFE)
pnpm typecheck # tsc --noEmit
pnpm test # vitest (jsdom) unit + render smoke tests
pnpm check # typecheck + test + build
pnpm test:e2e # Playwright live e2e (gated — see e2e/)
```

The web component (`<smooth-agent-chat>`), its config/theme surface, and the
full-page mode are unchanged — only the package boundary moved.
Open `index.html` (e.g. `pnpm dlx serve .`) after a build for the interactive,
backend-free showcase.

## Why
## License

One package, one version, one install. The client (`.`), React bindings
(`./react`), and this widget (`./widget`) are now subpath exports of
`@smooai/smooth-operator` so they never drift out of lockstep. See the
[React Components and Custom UIs guide](https://github.com/SmooAI/smooth-operator/blob/main/docs/Guides/React%20Components%20and%20Custom%20UIs.md).
MIT © [SmooAI](https://github.com/SmooAI)
Loading
Loading