@saytv/chat-sdk
v2.1.4
Published
Embeddable chat widget for SayTV — a fully featured, themeable web component with framework wrappers for React and Vue.
Readme
@saytv/chat-sdk
Embeddable chat widget for SayTV — a fully featured, themeable web component with framework wrappers for React and Vue.
Features
- Web Component — Drop a
<saytv-chat>tag into any page, framework-agnostic - Shadow DOM — Fully encapsulated styles, zero CSS conflicts
- Real-time Chat — Pusher-powered WebSocket messaging
- Authentication — Built-in login, registration, and token management
- Theming — Light/dark modes + 70+ CSS custom properties for full control
- Framework Wrappers — First-class React and Vue packages
- Mobile Bridge — Native integration for React Native, Flutter, iOS, and Android
- Async Loader — Tiny (< 1 kB) loader with command queue for deferred loading
- IIFE Bundle — Script-tag friendly build for legacy environments
Quick Start
npm install @saytv/chat-sdk<saytv-chat
app-id="your-app-id"
theme="light"
></saytv-chat>
<script type="module">
import '@saytv/chat-sdk/widget';
</script>Installation
Package Manager
# npm
npm install @saytv/chat-sdk
# pnpm
pnpm add @saytv/chat-sdk
# yarn
yarn add @saytv/chat-sdkCDN (IIFE)
<script src="https://sdk.saytv.net/@saytv/chat-sdk@latest/dist/sdk.iife.js"></script>The IIFE bundle exposes the SDK on window.SayTV.
Async Loader
For pages where you want to defer loading the full widget:
<script type="module" src="https://sdk.saytv.net/@saytv/chat-sdk@latest/dist/loader.mjs"></script>
<script>
SayTVChat('init', {
appId: 'your-app-id',
authToken: 'user-token-here',
theme: 'dark',
height: 700,
width: '100%',
});
</script>The loader queues commands until the full widget is loaded, then flushes them automatically. It auto-creates a <saytv-chat> element if one is not already on the page.
Usage
Web Component
<saytv-chat
app-id="your-app-id"
auth-token="eyJhbGci..."
built-in-auth="true"
height="850"
width="450px"
theme="light"
></saytv-chat>All attributes are reactive — update them at any time via setAttribute():
const chat = document.querySelector('saytv-chat');
chat.setAttribute('theme', 'dark');
chat.setAttribute('auth-token', newToken);Programmatic API
The main entry point exports the API client and all TypeScript types:
import { apiRequest, ApiError } from '@saytv/chat-sdk';
import type { User, Comment, Episode, Room, PaginatedResponse } from '@saytv/chat-sdk';Package Exports
| Import Path | Description |
|---|---|
| @saytv/chat-sdk | API client, error class, and all types |
| @saytv/chat-sdk/widget | Registers the <saytv-chat> custom element |
| @saytv/chat-sdk/loader | Async loader with command queue |
| @saytv/chat-sdk/mobile | Mobile webview HTML shell |
Framework Wrappers
React
npm install @saytv/chat-reactimport { SayTVChat, useSayTVChat } from '@saytv/chat-react';
function App() {
const { chatRef, setToken, setTheme } = useSayTVChat();
return (
<SayTVChat
ref={chatRef}
appId="your-app-id"
theme="light"
height={700}
width="100%"
onMessageSent={(e) => console.log('Sent:', e.detail)}
onError={(e) => console.error('Error:', e.detail)}
/>
);
}Props:
| Prop | Type | Required | Description |
|---|---|---|---|
| appId | string | No | App identifier (resolves API URL) |
| env | 'production' \| 'staging' | No | Environment (default: 'production') |
| apiUrl | string | No | API base URL (overrides appId/env) |
| authToken | string | No | User access token |
| builtInAuth | boolean | No | Enable built-in login UI (default: true) |
| height | number | No | Height in pixels (default: 850) |
| width | string | No | CSS width (default: '450px') |
| theme | 'light' \| 'dark' | No | Color theme (default: 'light') |
| className | string | No | CSS class on wrapper div |
| style | CSSProperties | No | Inline styles on wrapper div |
| onMessageSent | (e: CustomEvent) => void | No | Message sent callback |
| onUserLogin | (e: CustomEvent) => void | No | Login callback |
| onUserLogout | (e: CustomEvent) => void | No | Logout callback |
| onError | (e: CustomEvent) => void | No | Error callback |
Hook:
const { chatRef, getElement, setToken, setTheme } = useSayTVChat();chatRef— Pass to<SayTVChat ref={chatRef}>getElement()— Returns the underlying<saytv-chat>HTMLElementsetToken(token)— Update the access tokensetTheme(theme)— Switch between'light'and'dark'
Vue
npm install @saytv/chat-vue<script setup lang="ts">
import { SayTVChat, useSayTVChat } from '@saytv/chat-vue';
const { chatRef, setToken, setTheme } = useSayTVChat();
</script>
<template>
<SayTVChat
ref="chatRef"
app-id="your-app-id"
theme="light"
:height="700"
width="100%"
@message-sent="(e) => console.log('Sent:', e.detail)"
@error="(e) => console.error('Error:', e.detail)"
/>
</template>Props: Same as React (except className/style). Uses kebab-case in templates.
Composable:
const { chatRef, getElement, setToken, setTheme } = useSayTVChat();Mobile Integration
The SDK includes a native bridge for embedding the chat widget inside mobile app webviews.
Supported Platforms
| Platform | Detection |
|---|---|
| React Native | window.ReactNativeWebView |
| Flutter | window.flutter_inappwebview |
| iOS (WKWebView) | window.webkit.messageHandlers.saytvBridge |
| Android (WebView) | window.SayTVAndroidBridge |
Mobile HTML Shell
Use the pre-built mobile HTML for webview integration:
import mobileHtml from '@saytv/chat-sdk/mobile';The shell listens for an INIT_CONFIG postMessage to configure the widget with your API URL and token.
Theming
The widget uses CSS custom properties scoped inside its Shadow DOM. Override them via inline styles on the <saytv-chat> element:
<saytv-chat
app-id="your-app-id"
theme="light"
style="--stv-color-primary: #4f46e5; --stv-color-accent: #10b981;"
></saytv-chat>Dark Mode
Set theme="dark" to activate the built-in dark theme. The dark theme overrides background, text, and border colors automatically.
CSS Custom Properties Reference
Colors
| Property | Default (Light) | Description |
|---|---|---|
| --stv-color-primary | #7e171c | Primary brand color |
| --stv-color-primary-dark | #5d1014 | Darker primary variant |
| --stv-color-primary-light | #ad0000 | Lighter primary variant |
| --stv-color-accent | #0143d9 | Accent / link color |
| --stv-color-danger | #dc3545 | Error / danger color |
| --stv-color-success | #28a745 | Success color |
| --stv-color-warning | #ffc107 | Warning color |
| --stv-color-info | #87ceeb | Info color |
Backgrounds
| Property | Default | Description |
|---|---|---|
| --stv-bg-primary | #ffffff | Main background |
| --stv-bg-secondary | #eae7e1 | Secondary background |
| --stv-bg-dark | #212529 | Dark surfaces |
| --stv-bg-light | #f5f5f5 | Light surfaces |
| --stv-bg-hover | #f0f0f0 | Hover state |
| --stv-bg-input | #ffffff | Input fields |
| --stv-bg-chat-bubble | #28292d | Chat message bubbles |
| --stv-bg-overlay | rgba(0,0,0,0.75) | Modal overlays |
Text
| Property | Default | Description |
|---|---|---|
| --stv-text-primary | #1a1a1a | Primary text |
| --stv-text-secondary | #4a4a4a | Secondary text |
| --stv-text-muted | #6f6f6f | Muted / hint text |
| --stv-text-light | #ffffff | Text on dark backgrounds |
| --stv-text-link | #0143d9 | Link text |
| --stv-text-placeholder | #999999 | Input placeholders |
Typography
| Property | Default | Description |
|---|---|---|
| --stv-font-primary | 'Lato', system-ui, sans-serif | Body font |
| --stv-font-heading | 'Exo', 'Lato', sans-serif | Heading font |
| --stv-font-size-xs | 0.6875rem | Extra small text |
| --stv-font-size-sm | 0.8125rem | Small text |
| --stv-font-size-base | 0.875rem | Base text size |
| --stv-font-size-md | 1rem | Medium text |
| --stv-font-size-lg | 1.25rem | Large text |
Spacing
| Property | Default |
|---|---|
| --stv-spacing-xs | 0.25rem |
| --stv-spacing-sm | 0.5rem |
| --stv-spacing-md | 1rem |
| --stv-spacing-lg | 1.5rem |
| --stv-spacing-xl | 2rem |
| --stv-spacing-2xl | 3rem |
Borders
| Property | Default | Description |
|---|---|---|
| --stv-border-color | #dddddd | Default border |
| --stv-border-focus | #0143d9 | Focus ring color |
| --stv-border-error | #dc3545 | Validation error border |
| --stv-border-radius-sm | 0.5rem | Small radius |
| --stv-border-radius-md | 0.75rem | Medium radius |
| --stv-border-radius-lg | 1rem | Large radius |
Shadows
| Property | Default |
|---|---|
| --stv-shadow-sm | 0 1px 2px rgba(0,0,0,0.06) |
| --stv-shadow-md | 0 2px 8px rgba(0,0,0,0.1) |
| --stv-shadow-lg | 0 4px 16px rgba(0,0,0,0.12) |
| --stv-shadow-xl | 0 8px 32px rgba(0,0,0,0.16) |
Transitions
| Property | Default |
|---|---|
| --stv-transition-fast | 150ms ease |
| --stv-transition-normal | 250ms ease |
| --stv-transition-slow | 400ms ease |
Configuration Reference
| Attribute | Type | Default | Description |
|---|---|---|---|
| app-id | string | '' | App identifier (resolves API URL) |
| env | 'production' \| 'staging' | 'production' | Environment |
| apiurl | string | — | API base URL (overrides app-id/env) |
| auth-token | string | — | User access token |
| built-in-auth | 'true' \| 'false' | 'true' | Show login/register UI |
| height | number | 850 | Widget height in pixels |
| width | string | '450px' | Widget width (any CSS unit) |
| theme | 'light' \| 'dark' | 'light' | Color theme |
| fanzone | 'true' \| 'false' | 'false' | Enable fanzone features |
| mode | 'page-chat' | — | Widget mode (page-chat embeds chat on current page) |
| page-id | string | Current URL | Page identifier (used with mode="page-chat") |
| page-name | string | Document title | Page display name (used with mode="page-chat") |
License
Proprietary. All rights reserved.
