@verba-ai/chat-sdk 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,102 +1,141 @@
1
- # Verba Chat Widget SDK
1
+ # @verba-ai/chat-sdk
2
2
 
3
- A lightweight, professional-grade TypeScript SDK designed to embed the Verba AI chat widget into any web application via a secure `<iframe>`.
3
+ A lightweight, TypeScript SDK for embedding the Verba AI chat widget into any web application.
4
4
 
5
- ## 1. Project Scope
5
+ The SDK acts as a secure, high-performance orchestration layer that manages the injection and lifecycle of the Verba chat `iframe`. It supports both floating and inline mounting strategies.
6
6
 
7
- The Verba Chat SDK acts as an orchestration layer between a host application and the Verba Chat service. It provides a zero-dependency, high-performance solution for developers to integrate chat capabilities with minimal effort.
7
+ ## Installation
8
8
 
9
- ### Core Features
10
- * **Isomorphic Design**: Works seamlessly in modern browsers.
11
- * **Dual Mounting Strategies**:
12
- * **Floating Bubble**: A fixed-position toggle button at the bottom-right (or left) corner.
13
- * **Inline Mounting**: Target any DOM element or CSS selector to embed the chat directly.
14
- * **Secure Communication**: Bi-directional `postMessage` bridge with strict origin validation and namespace protection.
15
- * **Lifecycle Management**: Robust methods for `init()`, `show()`, `hide()`, and `destroy()`.
16
- * **Zero Dependencies**: Optimized for minimum bundle size (< 5 KB gzipped).
9
+ Install via your preferred package manager:
17
10
 
18
- ---
19
-
20
- ## 2. Project Structure
21
-
22
- ```text
23
- sdk/
24
- ├── dist/ # Compiled distribution files (ESM/UMD)
25
- ├── public/ # Static assets
26
- ├── src/
27
- │ ├── index.ts # Main entry point (ChatSDK class)
28
- │ ├── messenger.ts # Multi-origin communication bridge
29
- │ ├── ui.ts # CSS-in-JS and DOM factory helpers
30
- │ └── types.ts # Shared TypeScript interfaces & enums
31
- ├── index.html # Interactive developer demo page
32
- ├── package.json # Library metadata & build scripts
33
- ├── tsconfig.json # TypeScript compiler configuration
34
- └── vite.config.ts # Vite library mode configuration
11
+ ```bash
12
+ npm install @verba-ai/chat-sdk
13
+ # or
14
+ yarn add @verba-ai/chat-sdk
15
+ # or
16
+ pnpm add @verba-ai/chat-sdk
35
17
  ```
36
18
 
37
- ### File Explanations
19
+ ---
38
20
 
39
- | File | Purpose |
40
- | :--- | :--- |
41
- | **`src/index.ts`** | The main entry point. Exports the `ChatSDK` class which manages the state machine and coordinates UI injection and messaging. |
42
- | **`src/messenger.ts`** | Encapsulates `window.postMessage` logic. It handles secure event dispatching, origin checks, and subscription management. |
43
- | **`src/ui.ts`** | Handles "CSS-in-JS". It injects required styles into the document head and contains factory functions for creating the iframe, bubble button, and containers. |
44
- | **`src/types.ts`** | Centralized location for all TypeScript definitions, ensuring type safety for the configuration and the messaging protocol. |
45
- | **`vite.config.ts`** | Configures Vite for **Library Mode**. It uses Rollup to generate optimized ESM and UMD bundles and handles `.d.ts` generation. |
46
- | **`index.html`** | A standalone demo page that allows developers to test the SDK's features in real-time, complete with a specialized event log. |
21
+ ## Usage
47
22
 
48
- ---
23
+ ### 1. Floating Bubble (Default)
24
+ The easiest way to integrate the chat. It creates a fixed-position action button in the bottom corner of your screen that toggles the chat window.
49
25
 
50
- ## 3. Getting Started
26
+ ```typescript
27
+ import { ChatSDK } from '@verba-ai/chat-sdk';
51
28
 
52
- ### Installation
29
+ const chat = new ChatSDK({
30
+ // Required: Your unique Verba tenant ID
31
+ tenant: 'your-tenant-id',
32
+
33
+ // Optional: Simple theme selection
34
+ theme: 'dark',
35
+
36
+ // Optional: Position of the bubble
37
+ position: 'bottom-right'
38
+ });
53
39
 
54
- ```bash
55
- npm install @verba/chat-sdk
40
+ // Inject the bubble into the DOM
41
+ chat.init();
42
+
43
+ // Optional programmatic controls
44
+ // chat.show();
45
+ // chat.hide();
46
+ // chat.destroy();
56
47
  ```
57
48
 
58
- ### Basic Usage (Floating)
49
+ ### 2. Inline Mounting
50
+ If you want to render the chat inside a specific part of your page (like a dashboard panel or a dedicated contact page).
59
51
 
60
52
  ```typescript
61
- import { ChatSDK } from '@verba/chat-sdk';
53
+ import { ChatSDK } from '@verba-ai/chat-sdk';
54
+
55
+ // Ensure the container exists in your DOM
56
+ // <div id="my-chat-container" style="height: 600px;"></div>
62
57
 
63
58
  const chat = new ChatSDK({
64
59
  tenant: 'your-tenant-id',
65
- theme: 'dark'
60
+
61
+ // Target a CSS selector or pass an HTMLElement directly
62
+ container: '#my-chat-container',
63
+
64
+ // Optional: Advanced visual theming
65
+ theme: {
66
+ primaryColor: '#6366f1',
67
+ backgroundColor: '#080b14',
68
+ textColor: '#f1f5f9',
69
+ borderRadius: '12px'
70
+ }
66
71
  });
67
72
 
68
- chat.init(); // Injects the bubble
73
+ chat.init();
69
74
  ```
70
75
 
71
- ### Inline Usage
76
+ ### 3. Usage via CDN (UMD)
77
+ If you aren't using a bundler (Webpack/Vite/Rollup), you can load the SDK directly via a script tag.
72
78
 
73
- ```typescript
74
- new ChatSDK({
75
- tenant: 'your-tenant-id',
76
- container: '#chat-container'
77
- }).init();
79
+ ```html
80
+ <script src="https://unpkg.com/@verba-ai/chat-sdk@latest/dist/chat-sdk.umd.cjs"></script>
81
+ <script>
82
+ const { ChatSDK } = window.VerbaChatSDK;
83
+
84
+ new ChatSDK({ tenant: 'your-tenant-id' }).init();
85
+ </script>
78
86
  ```
79
87
 
80
88
  ---
81
89
 
82
- ## 4. Development
90
+ ## 📚 API Reference
83
91
 
84
- ### Build the Library
85
- To generate the production-ready bundles in the `dist/` directory:
86
- ```bash
87
- npm run build:lib
88
- ```
92
+ ### `ChatSDK` Class
89
93
 
90
- ### Start Demo Server
91
- To view the interactive demo locally:
92
- ```bash
93
- npm run dev
94
+ The main entry point for the widget.
95
+
96
+ #### Methods
97
+
98
+ | Method | Description |
99
+ | :--- | :--- |
100
+ | `init()` | Injects the CSS and iframe into the DOM. Resolves the SDK state to `ready`. |
101
+ | `show()` | Makes the widget visible (animates the iframe open in floating mode). |
102
+ | `hide()` | Hides the widget (animates the iframe closed in floating mode). |
103
+ | `destroy()`| Completely tears down the SDK, removing all injected DOM elements, styles, and events. |
104
+
105
+ ---
106
+
107
+ ### Configuration Types
108
+
109
+ #### `ChatSDKConfig`
110
+
111
+ ```typescript
112
+ interface ChatSDKConfig {
113
+ /** Your Verba tenant ID (Required) */
114
+ tenant: string;
115
+
116
+ /** Where to mount the widget. Omit for floating bubble. */
117
+ container?: string | HTMLElement;
118
+
119
+ /** Visual theme. String preset or detailed config. */
120
+ theme?: 'light' | 'dark' | ThemeConfig;
121
+
122
+ /** Position of the floating bubble (Default: 'bottom-right') */
123
+ position?: 'bottom-right' | 'bottom-left';
124
+
125
+ /** Arbitrary metadata to pass to the widget */
126
+ userMetadata?: Record<string, unknown>;
127
+ }
94
128
  ```
95
129
 
130
+ #### `ThemeConfig`
96
131
 
97
- ## 5. Testing
132
+ ```typescript
133
+ interface ThemeConfig {
134
+ primaryColor?: string;
135
+ textColor?: string;
136
+ backgroundColor?: string;
137
+ fontFamily?: string;
138
+ borderRadius?: string | number;
139
+ }
140
+ ```
98
141
 
99
- | File | Purpose |
100
- | :--- | :--- |
101
- | **`index.html`** | A standalone demo page that allows testing SDK's features in real-time, complete with a specialized event log. |
102
- | **`test.html`** | Testing the actual builded library. |
@@ -1 +1 @@
1
- {"version":3,"file":"chat-sdk.es.js","sources":["../src/ui.ts","../src/index.ts"],"sourcesContent":["/**\r\n * @module @verba/chat-sdk/ui\r\n * CSS-in-JS helpers for creating and managing the widget's DOM elements.\r\n * All styles are scoped to avoid conflicts with host application CSS.\r\n */\r\n\r\nimport type { BubblePosition } from './types.ts';\r\n\r\n// ─── Constants ───────────────────────────────────────────────────────────────\r\n\r\nconst STYLE_ID = 'verba-chat-sdk-styles';\r\nconst CONTAINER_CLASS = 'verba-widget-container';\r\nconst IFRAME_CLASS = 'verba-widget-iframe';\r\nconst BUBBLE_CLASS = 'verba-widget-bubble';\r\nconst BUBBLE_OPEN_CLASS = 'verba-widget-bubble--open';\r\n\r\n// ─── Style Injection ─────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Injects a `<style>` tag into the document `<head>` with all widget styles.\r\n * Idempotent — safe to call multiple times.\r\n */\r\nexport function injectStyles(): void {\r\n if (document.getElementById(STYLE_ID)) return;\r\n\r\n const css = `\r\n /* ── Verba Chat SDK ── */\r\n\r\n .${CONTAINER_CLASS} {\r\n position: fixed;\r\n bottom: 24px;\r\n right: 24px;\r\n z-index: 9999;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: flex-end;\r\n gap: 12px;\r\n font-family: system-ui, -apple-system, sans-serif;\r\n }\r\n\r\n .${CONTAINER_CLASS}[data-position=\"bottom-left\"] {\r\n right: unset;\r\n left: 24px;\r\n align-items: flex-start;\r\n }\r\n\r\n .${CONTAINER_CLASS}.verba-widget--inline {\r\n position: static;\r\n bottom: unset;\r\n right: unset;\r\n left: unset;\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n align-items: stretch;\r\n }\r\n\r\n .${IFRAME_CLASS} {\r\n border: none;\r\n border-radius: 16px;\r\n width: 380px;\r\n height: 600px;\r\n box-shadow: 0 24px 64px rgba(0, 0, 0, 0.18), 0 8px 24px rgba(0, 0, 0, 0.12);\r\n opacity: 0;\r\n transform: translateY(12px) scale(0.97);\r\n transform-origin: bottom right;\r\n transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1),\r\n transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\r\n pointer-events: none;\r\n background: transparent;\r\n }\r\n\r\n .${CONTAINER_CLASS}[data-position=\"bottom-left\"] .${IFRAME_CLASS} {\r\n transform-origin: bottom left;\r\n }\r\n\r\n .${IFRAME_CLASS}.verba-iframe--visible {\r\n opacity: 1;\r\n transform: translateY(0) scale(1);\r\n pointer-events: all;\r\n }\r\n\r\n .${CONTAINER_CLASS}.verba-widget--inline .${IFRAME_CLASS} {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 0;\r\n box-shadow: none;\r\n opacity: 1;\r\n transform: none;\r\n pointer-events: all;\r\n }\r\n\r\n /* ── Bubble Button ── */\r\n\r\n .${BUBBLE_CLASS} {\r\n width: 56px;\r\n height: 56px;\r\n border-radius: 50%;\r\n border: none;\r\n cursor: pointer;\r\n background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);\r\n box-shadow: 0 4px 20px rgba(99, 102, 241, 0.45), 0 2px 6px rgba(0,0,0,0.15);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1),\r\n box-shadow 0.22s ease;\r\n flex-shrink: 0;\r\n outline: none;\r\n overflow: hidden;\r\n }\r\n\r\n .${BUBBLE_CLASS}:hover {\r\n transform: scale(1.1);\r\n box-shadow: 0 6px 28px rgba(99, 102, 241, 0.55), 0 2px 10px rgba(0,0,0,0.18);\r\n }\r\n\r\n .${BUBBLE_CLASS}:active {\r\n transform: scale(0.95);\r\n }\r\n\r\n .${BUBBLE_CLASS} .verba-bubble-icon {\r\n position: absolute;\r\n transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);\r\n }\r\n\r\n .${BUBBLE_CLASS} .verba-bubble-icon--chat { opacity: 1; transform: scale(1) rotate(0deg); }\r\n .${BUBBLE_CLASS} .verba-bubble-icon--close { opacity: 0; transform: scale(0.5) rotate(-90deg); }\r\n\r\n .${BUBBLE_CLASS}.${BUBBLE_OPEN_CLASS} .verba-bubble-icon--chat {\r\n opacity: 0;\r\n transform: scale(0.5) rotate(90deg);\r\n }\r\n .${BUBBLE_CLASS}.${BUBBLE_OPEN_CLASS} .verba-bubble-icon--close {\r\n opacity: 1;\r\n transform: scale(1) rotate(0deg);\r\n }\r\n\r\n /* ── Ripple pulse on load ── */\r\n @keyframes verba-pulse {\r\n 0% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 0 rgba(99,102,241,0.4); }\r\n 70% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 14px rgba(99,102,241,0); }\r\n 100% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 0 rgba(99,102,241,0); }\r\n }\r\n\r\n .${BUBBLE_CLASS}.verba-bubble--pulse {\r\n animation: verba-pulse 1.8s ease-out 0.4s 2;\r\n }\r\n\r\n /* ── Mobile ── */\r\n @media (max-width: 480px) {\r\n .${IFRAME_CLASS} {\r\n width: calc(100vw - 24px);\r\n height: 75vh;\r\n max-height: 600px;\r\n }\r\n\r\n .${CONTAINER_CLASS} {\r\n right: 12px;\r\n bottom: 12px;\r\n }\r\n\r\n .${CONTAINER_CLASS}[data-position=\"bottom-left\"] {\r\n left: 12px;\r\n right: unset;\r\n }\r\n }\r\n `;\r\n\r\n const style = document.createElement('style');\r\n style.id = STYLE_ID;\r\n style.textContent = css;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/** Removes the injected `<style>` tag from the document. */\r\nexport function removeInjectedStyles(): void {\r\n document.getElementById(STYLE_ID)?.remove();\r\n}\r\n\r\n// ─── Element Factories ───────────────────────────────────────────────────────\r\n\r\n/**\r\n * Creates the fixed-position outer container div used in floating mode.\r\n */\r\nexport function createFloatingContainer(position: BubblePosition): HTMLDivElement {\r\n const div = document.createElement('div');\r\n div.className = CONTAINER_CLASS;\r\n div.dataset.position = position;\r\n return div;\r\n}\r\n\r\n/**\r\n * Creates an inline container — wrapper inside a user-supplied element.\r\n */\r\nexport function createInlineContainer(): HTMLDivElement {\r\n const div = document.createElement('div');\r\n div.className = `${CONTAINER_CLASS} verba-widget--inline`;\r\n div.style.width = '100%';\r\n div.style.height = '100%';\r\n return div;\r\n}\r\n\r\n/**\r\n * Creates the `<iframe>` element with correct sandbox + security attributes.\r\n * The iframe starts **hidden** — call `showIframe()` to animate it in.\r\n */\r\nexport function createIframeElement(src: string): HTMLIFrameElement {\r\n const iframe = document.createElement('iframe');\r\n iframe.className = IFRAME_CLASS;\r\n iframe.src = src;\r\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups');\r\n iframe.setAttribute('allow', 'microphone; camera');\r\n iframe.setAttribute('loading', 'lazy');\r\n iframe.setAttribute('title', 'Verba AI Chat Widget');\r\n iframe.setAttribute('aria-label', 'Chat support widget');\r\n return iframe;\r\n}\r\n\r\n/**\r\n * Creates the floating bubble toggle button with animated chat / close icons (pure SVG).\r\n */\r\nexport function createBubbleButton(): HTMLButtonElement {\r\n const button = document.createElement('button');\r\n button.className = `${BUBBLE_CLASS} verba-bubble--pulse`;\r\n button.setAttribute('aria-label', 'Open chat');\r\n button.setAttribute('aria-expanded', 'false');\r\n button.setAttribute('type', 'button');\r\n\r\n // Chat icon\r\n const chatIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\r\n chatIcon.setAttribute('class', 'verba-bubble-icon verba-bubble-icon--chat');\r\n chatIcon.setAttribute('width', '26');\r\n chatIcon.setAttribute('height', '26');\r\n chatIcon.setAttribute('viewBox', '0 0 24 24');\r\n chatIcon.setAttribute('fill', 'none');\r\n chatIcon.setAttribute('stroke', '#ffffff');\r\n chatIcon.setAttribute('stroke-width', '2');\r\n chatIcon.setAttribute('stroke-linecap', 'round');\r\n chatIcon.setAttribute('stroke-linejoin', 'round');\r\n chatIcon.innerHTML = `\r\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/>\r\n `;\r\n\r\n // Close (X) icon\r\n const closeIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\r\n closeIcon.setAttribute('class', 'verba-bubble-icon verba-bubble-icon--close');\r\n closeIcon.setAttribute('width', '22');\r\n closeIcon.setAttribute('height', '22');\r\n closeIcon.setAttribute('viewBox', '0 0 24 24');\r\n closeIcon.setAttribute('fill', 'none');\r\n closeIcon.setAttribute('stroke', '#ffffff');\r\n closeIcon.setAttribute('stroke-width', '2.5');\r\n closeIcon.setAttribute('stroke-linecap', 'round');\r\n closeIcon.innerHTML = `\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\r\n `;\r\n\r\n button.appendChild(chatIcon);\r\n button.appendChild(closeIcon);\r\n return button;\r\n}\r\n\r\n// ─── State Helpers ───────────────────────────────────────────────────────────\r\n\r\n/** Animate the iframe into view. */\r\nexport function showIframe(iframe: HTMLIFrameElement): void {\r\n // rAF ensures the browser has painted the element before adding the class\r\n requestAnimationFrame(() => {\r\n iframe.classList.add('verba-iframe--visible');\r\n });\r\n}\r\n\r\n/** Animate the iframe out of view (hides it). */\r\nexport function hideIframe(iframe: HTMLIFrameElement): void {\r\n iframe.classList.remove('verba-iframe--visible');\r\n}\r\n\r\n/** Toggle bubble button's open/close icon state. */\r\nexport function setBubbleOpen(button: HTMLButtonElement, isOpen: boolean): void {\r\n button.classList.toggle(BUBBLE_OPEN_CLASS, isOpen);\r\n button.setAttribute('aria-label', isOpen ? 'Close chat' : 'Open chat');\r\n button.setAttribute('aria-expanded', String(isOpen));\r\n}\r\n","/**\r\n * @module @verba/chat-sdk\r\n *\r\n * Verba Chat Widget SDK\r\n * ---------------------\r\n * A lightweight, zero-dependency TypeScript SDK that embeds the Verba AI chat\r\n * widget into any web page via a secure `<iframe>`.\r\n *\r\n * @example\r\n * ```ts\r\n * import { ChatSDK } from '@verba/chat-sdk';\r\n *\r\n * const sdk = new ChatSDK({ tenant: 'your-tenant-id', theme: 'dark' });\r\n * sdk.init();\r\n * ```\r\n */\r\n\r\nimport type { ChatSDKConfig, WidgetState } from './types.ts';\r\nimport {\r\n injectStyles,\r\n removeInjectedStyles,\r\n createFloatingContainer,\r\n createInlineContainer,\r\n createIframeElement,\r\n createBubbleButton,\r\n showIframe,\r\n hideIframe,\r\n setBubbleOpen,\r\n} from './ui.ts';\r\n\r\n// ─── Constants ───────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * The origin of the hosted widget page.\r\n * All postMessage communication is scoped to this origin.\r\n * Update this when deploying to a real environment.\r\n */\r\nconst WIDGET_ORIGIN = 'https://embed.verba.chat';\r\n\r\n/** Full URL of the embeddable HTML page inside the widget origin. */\r\nconst WIDGET_URL = `${WIDGET_ORIGIN}/embeddable.html`;\r\n\r\n// ─── SDK Error ───────────────────────────────────────────────────────────────\r\n\r\n/** Thrown when an SDK method is called in an invalid lifecycle state. */\r\nexport class ChatSDKError extends Error {\r\n constructor(message: string) {\r\n super(`[VerbaChatSDK] ${message}`);\r\n this.name = 'ChatSDKError';\r\n }\r\n}\r\n\r\n// ─── ChatSDK ─────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Main class for the Verba Chat Widget SDK.\r\n *\r\n * ### Lifecycle\r\n * ```\r\n * new ChatSDK(config) → .init() → .show() / .hide() → .destroy()\r\n * ```\r\n *\r\n * ### Floating mode (default)\r\n * When no `container` is provided a floating bubble button is created in the\r\n * bottom-right corner of the viewport. Clicking the bubble opens/closes the\r\n * iframe with a smooth CSS animation.\r\n *\r\n * ### Inline mode\r\n * When `container` is a CSS selector or `HTMLElement`, the iframe is mounted\r\n * directly inside that element and the bubble button is omitted.\r\n */\r\nexport class ChatSDK {\r\n // ── Config ─────────────────────────────────────────────────────────────────\r\n private readonly config: Required<\r\n Omit<ChatSDKConfig, 'container' | 'userMetadata' | 'tenant'>\r\n > & Pick<ChatSDKConfig, 'container' | 'userMetadata'> & { tenant: string };\r\n\r\n // ── State ──────────────────────────────────────────────────────────────────\r\n private state: WidgetState = 'uninitialized';\r\n private isVisible = false;\r\n private isInline = false;\r\n\r\n // ── DOM refs ───────────────────────────────────────────────────────────────\r\n private container: HTMLElement | null = null;\r\n private iframe: HTMLIFrameElement | null = null;\r\n private bubble: HTMLButtonElement | null = null;\r\n\r\n\r\n\r\n // ── Cleanup ────────────────────────────────────────────────────────────────\r\n private readonly cleanupCallbacks: Array<() => void> = [];\r\n\r\n // ─────────────────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Create a new `ChatSDK` instance.\r\n *\r\n * @param config - SDK configuration. Only `tenant` is required.\r\n *\r\n * @throws {ChatSDKError} if `tenant` is empty.\r\n */\r\n constructor(config: ChatSDKConfig) {\r\n const tenant = config.tenant\r\n if (!tenant?.trim()) {\r\n throw new ChatSDKError('`tenant` is required and must not be empty.');\r\n }\r\n\r\n this.config = {\r\n tenant: tenant.trim(),\r\n theme: config.theme ?? 'light',\r\n position: config.position ?? 'bottom-right',\r\n container: config.container,\r\n userMetadata: config.userMetadata,\r\n };\r\n }\r\n\r\n // ─── Public API ───────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Inject the widget into the DOM and establish the postMessage bridge.\r\n *\r\n * In **floating mode** a chat bubble is rendered in the viewport corner.\r\n * In **inline mode** the iframe is mounted directly into the configured container.\r\n *\r\n * @throws {ChatSDKError} if called more than once or after `destroy()`.\r\n */\r\n init(): this {\r\n this.assertState('uninitialized', 'init');\r\n\r\n injectStyles();\r\n this.state = 'ready';\r\n\r\n this.isInline = !!this.config.container;\r\n\r\n if (this.isInline) {\r\n this.mountInline();\r\n } else {\r\n this.mountFloating();\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Make the widget visible.\r\n * In floating mode this opens the iframe above the bubble.\r\n *\r\n * @throws {ChatSDKError} if `init()` has not been called.\r\n */\r\n show(): this {\r\n this.assertNotState('uninitialized', 'show');\r\n this.assertNotState('destroyed', 'show');\r\n\r\n if (this.isVisible || !this.iframe) return this;\r\n\r\n this.isVisible = true;\r\n\r\n if (!this.isInline) {\r\n showIframe(this.iframe);\r\n if (this.bubble) setBubbleOpen(this.bubble, true);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Hide the widget (does not destroy it — all state is preserved).\r\n *\r\n * @throws {ChatSDKError} if `init()` has not been called.\r\n */\r\n hide(): this {\r\n this.assertNotState('uninitialized', 'hide');\r\n this.assertNotState('destroyed', 'hide');\r\n\r\n if (!this.isVisible || !this.iframe) return this;\r\n\r\n this.isVisible = false;\r\n\r\n if (!this.isInline) {\r\n hideIframe(this.iframe);\r\n if (this.bubble) setBubbleOpen(this.bubble, false);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Completely tear down the SDK:\r\n * - Removes all DOM elements it created.\r\n * - Removes all event listeners.\r\n * - Resets state to `'destroyed'`.\r\n *\r\n * After calling `destroy()` this instance cannot be reused — create a new one.\r\n */\r\n destroy(): void {\r\n if (this.state === 'destroyed') return;\r\n\r\n this.container?.remove();\r\n this.container = null;\r\n this.iframe = null;\r\n this.bubble = null;\r\n\r\n for (const cb of this.cleanupCallbacks) cb();\r\n this.cleanupCallbacks.length = 0;\r\n\r\n removeInjectedStyles();\r\n\r\n this.state = 'destroyed';\r\n this.isVisible = false;\r\n }\r\n\r\n /** Current lifecycle state of this SDK instance. */\r\n get currentState(): WidgetState {\r\n return this.state;\r\n }\r\n\r\n // ─── Private: Mount Strategies ───────────────────────────────────────────\r\n\r\n private mountInline(): void {\r\n const host = this.resolveContainer();\r\n if (!host) {\r\n console.error('[VerbaChatSDK] Container element not found. Falling back to floating mode.');\r\n this.isInline = false;\r\n this.mountFloating();\r\n return;\r\n }\r\n\r\n const wrapper = createInlineContainer();\r\n const iframe = createIframeElement(this.buildWidgetUrl());\r\n\r\n wrapper.appendChild(iframe);\r\n host.appendChild(wrapper);\r\n\r\n this.container = wrapper;\r\n this.iframe = iframe;\r\n this.isVisible = true;\r\n }\r\n\r\n private mountFloating(): void {\r\n const wrapper = createFloatingContainer(this.config.position ?? 'bottom-right');\r\n const iframe = createIframeElement(this.buildWidgetUrl());\r\n const bubble = createBubbleButton();\r\n\r\n // Bubble click → toggle show/hide\r\n const onBubbleClick = (): void => {\r\n this.isVisible ? this.hide() : this.show();\r\n };\r\n bubble.addEventListener('click', onBubbleClick);\r\n this.cleanupCallbacks.push(() =>\r\n bubble.removeEventListener('click', onBubbleClick)\r\n );\r\n\r\n wrapper.appendChild(iframe);\r\n wrapper.appendChild(bubble);\r\n document.body.appendChild(wrapper);\r\n\r\n this.container = wrapper;\r\n this.iframe = iframe;\r\n this.bubble = bubble;\r\n this.isVisible = false;\r\n }\r\n\r\n\r\n\r\n // ─── Private: State Guards ───────────────────────────────────────────────\r\n\r\n private assertState(expected: WidgetState, method: string): void {\r\n if (this.state !== expected) {\r\n throw new ChatSDKError(\r\n `Cannot call \\`${method}()\\` in state \"${this.state}\". Expected \"${expected}\".`\r\n );\r\n }\r\n }\r\n\r\n private assertNotState(forbidden: WidgetState, method: string): void {\r\n if (this.state === forbidden) {\r\n throw new ChatSDKError(\r\n `Cannot call \\`${method}()\\` in state \"${forbidden}\".`\r\n );\r\n }\r\n }\r\n\r\n // ─── Private: Helpers ────────────────────────────────────────────────────\r\n\r\n private resolveContainer(): HTMLElement | null {\r\n const { container } = this.config;\r\n if (!container) return null;\r\n if (typeof container === 'string') {\r\n return document.querySelector<HTMLElement>(container);\r\n }\r\n return container;\r\n }\r\n\r\n private buildWidgetUrl(): string {\r\n // If WIDGET_URL has search params already, URL() can parse it properly.\r\n // However, the base WIDGET_URL we use is clean `.../embeddable.html`\r\n const url = new URL(WIDGET_URL);\r\n \r\n // Core parameters\r\n url.searchParams.set('tnt', this.config.tenant);\r\n \r\n // Theme parameters\r\n if (typeof this.config.theme === 'object') {\r\n const t = this.config.theme;\r\n if (t.primaryColor) url.searchParams.set('color', t.primaryColor);\r\n if (t.textColor) url.searchParams.set('textColor', t.textColor);\r\n if (t.backgroundColor) url.searchParams.set('backgroundColor', t.backgroundColor);\r\n if (t.fontFamily) url.searchParams.set('fontFamily', t.fontFamily);\r\n if (t.borderRadius !== undefined) url.searchParams.set('borderRadius', String(t.borderRadius));\r\n }\r\n \r\n return url.toString();\r\n }\r\n}\r\n\r\n// ─── Re-exports ───────────────────────────────────────────────────────────────\r\n\r\nexport type {\r\n ChatSDKConfig,\r\n Theme,\r\n ThemeConfig,\r\n BubblePosition,\r\n WidgetState,\r\n} from './types.ts';\r\n"],"names":["STYLE_ID","CONTAINER_CLASS","IFRAME_CLASS","BUBBLE_CLASS","BUBBLE_OPEN_CLASS","injectStyles","css","style","removeInjectedStyles","createFloatingContainer","position","div","createInlineContainer","createIframeElement","src","iframe","createBubbleButton","button","chatIcon","closeIcon","showIframe","hideIframe","setBubbleOpen","isOpen","WIDGET_ORIGIN","WIDGET_URL","ChatSDKError","message","ChatSDK","config","tenant","cb","host","wrapper","bubble","onBubbleClick","expected","method","forbidden","container","url","t"],"mappings":"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"}
1
+ {"version":3,"file":"chat-sdk.es.js","sources":["../src/ui.ts","../src/index.ts"],"sourcesContent":["/**\n * @module @verba/chat-sdk/ui\n * CSS-in-JS helpers for creating and managing the widget's DOM elements.\n * All styles are scoped to avoid conflicts with host application CSS.\n */\n\nimport type { BubblePosition } from './types.ts';\n\n// ─── Constants ───────────────────────────────────────────────────────────────\n\nconst STYLE_ID = 'verba-chat-sdk-styles';\nconst CONTAINER_CLASS = 'verba-widget-container';\nconst IFRAME_CLASS = 'verba-widget-iframe';\nconst BUBBLE_CLASS = 'verba-widget-bubble';\nconst BUBBLE_OPEN_CLASS = 'verba-widget-bubble--open';\n\n// ─── Style Injection ─────────────────────────────────────────────────────────\n\n/**\n * Injects a `<style>` tag into the document `<head>` with all widget styles.\n * Idempotent — safe to call multiple times.\n */\nexport function injectStyles(): void {\n if (document.getElementById(STYLE_ID)) return;\n\n const css = `\n /* ── Verba Chat SDK ── */\n\n .${CONTAINER_CLASS} {\n position: fixed;\n bottom: 24px;\n right: 24px;\n z-index: 9999;\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 12px;\n font-family: system-ui, -apple-system, sans-serif;\n }\n\n .${CONTAINER_CLASS}[data-position=\"bottom-left\"] {\n right: unset;\n left: 24px;\n align-items: flex-start;\n }\n\n .${CONTAINER_CLASS}.verba-widget--inline {\n position: static;\n bottom: unset;\n right: unset;\n left: unset;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: stretch;\n }\n\n .${IFRAME_CLASS} {\n border: none;\n border-radius: 16px;\n width: 380px;\n height: 600px;\n box-shadow: 0 24px 64px rgba(0, 0, 0, 0.18), 0 8px 24px rgba(0, 0, 0, 0.12);\n opacity: 0;\n transform: translateY(12px) scale(0.97);\n transform-origin: bottom right;\n transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1),\n transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n background: transparent;\n }\n\n .${CONTAINER_CLASS}[data-position=\"bottom-left\"] .${IFRAME_CLASS} {\n transform-origin: bottom left;\n }\n\n .${IFRAME_CLASS}.verba-iframe--visible {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: all;\n }\n\n .${CONTAINER_CLASS}.verba-widget--inline .${IFRAME_CLASS} {\n width: 100%;\n height: 100%;\n border-radius: 0;\n box-shadow: none;\n opacity: 1;\n transform: none;\n pointer-events: all;\n }\n\n /* ── Bubble Button ── */\n\n .${BUBBLE_CLASS} {\n width: 56px;\n height: 56px;\n border-radius: 50%;\n border: none;\n cursor: pointer;\n background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);\n box-shadow: 0 4px 20px rgba(99, 102, 241, 0.45), 0 2px 6px rgba(0,0,0,0.15);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1),\n box-shadow 0.22s ease;\n flex-shrink: 0;\n outline: none;\n overflow: hidden;\n }\n\n .${BUBBLE_CLASS}:hover {\n transform: scale(1.1);\n box-shadow: 0 6px 28px rgba(99, 102, 241, 0.55), 0 2px 10px rgba(0,0,0,0.18);\n }\n\n .${BUBBLE_CLASS}:active {\n transform: scale(0.95);\n }\n\n .${BUBBLE_CLASS} .verba-bubble-icon {\n position: absolute;\n transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n .${BUBBLE_CLASS} .verba-bubble-icon--chat { opacity: 1; transform: scale(1) rotate(0deg); }\n .${BUBBLE_CLASS} .verba-bubble-icon--close { opacity: 0; transform: scale(0.5) rotate(-90deg); }\n\n .${BUBBLE_CLASS}.${BUBBLE_OPEN_CLASS} .verba-bubble-icon--chat {\n opacity: 0;\n transform: scale(0.5) rotate(90deg);\n }\n .${BUBBLE_CLASS}.${BUBBLE_OPEN_CLASS} .verba-bubble-icon--close {\n opacity: 1;\n transform: scale(1) rotate(0deg);\n }\n\n /* ── Ripple pulse on load ── */\n @keyframes verba-pulse {\n 0% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 0 rgba(99,102,241,0.4); }\n 70% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 14px rgba(99,102,241,0); }\n 100% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 0 rgba(99,102,241,0); }\n }\n\n .${BUBBLE_CLASS}.verba-bubble--pulse {\n animation: verba-pulse 1.8s ease-out 0.4s 2;\n }\n\n /* ── Mobile ── */\n @media (max-width: 480px) {\n .${IFRAME_CLASS} {\n width: calc(100vw - 24px);\n height: 75vh;\n max-height: 600px;\n }\n\n .${CONTAINER_CLASS} {\n right: 12px;\n bottom: 12px;\n }\n\n .${CONTAINER_CLASS}[data-position=\"bottom-left\"] {\n left: 12px;\n right: unset;\n }\n }\n `;\n\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = css;\n document.head.appendChild(style);\n}\n\n/** Removes the injected `<style>` tag from the document. */\nexport function removeInjectedStyles(): void {\n document.getElementById(STYLE_ID)?.remove();\n}\n\n// ─── Element Factories ───────────────────────────────────────────────────────\n\n/**\n * Creates the fixed-position outer container div used in floating mode.\n */\nexport function createFloatingContainer(position: BubblePosition): HTMLDivElement {\n const div = document.createElement('div');\n div.className = CONTAINER_CLASS;\n div.dataset.position = position;\n return div;\n}\n\n/**\n * Creates an inline container — wrapper inside a user-supplied element.\n */\nexport function createInlineContainer(): HTMLDivElement {\n const div = document.createElement('div');\n div.className = `${CONTAINER_CLASS} verba-widget--inline`;\n div.style.width = '100%';\n div.style.height = '100%';\n return div;\n}\n\n/**\n * Creates the `<iframe>` element with correct sandbox + security attributes.\n * The iframe starts **hidden** — call `showIframe()` to animate it in.\n */\nexport function createIframeElement(src: string): HTMLIFrameElement {\n const iframe = document.createElement('iframe');\n iframe.className = IFRAME_CLASS;\n iframe.src = src;\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups');\n iframe.setAttribute('allow', 'microphone; camera');\n iframe.setAttribute('loading', 'lazy');\n iframe.setAttribute('title', 'Verba AI Chat Widget');\n iframe.setAttribute('aria-label', 'Chat support widget');\n return iframe;\n}\n\n/**\n * Creates the floating bubble toggle button with animated chat / close icons (pure SVG).\n */\nexport function createBubbleButton(): HTMLButtonElement {\n const button = document.createElement('button');\n button.className = `${BUBBLE_CLASS} verba-bubble--pulse`;\n button.setAttribute('aria-label', 'Open chat');\n button.setAttribute('aria-expanded', 'false');\n button.setAttribute('type', 'button');\n\n // Chat icon\n const chatIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n chatIcon.setAttribute('class', 'verba-bubble-icon verba-bubble-icon--chat');\n chatIcon.setAttribute('width', '26');\n chatIcon.setAttribute('height', '26');\n chatIcon.setAttribute('viewBox', '0 0 24 24');\n chatIcon.setAttribute('fill', 'none');\n chatIcon.setAttribute('stroke', '#ffffff');\n chatIcon.setAttribute('stroke-width', '2');\n chatIcon.setAttribute('stroke-linecap', 'round');\n chatIcon.setAttribute('stroke-linejoin', 'round');\n chatIcon.innerHTML = `\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/>\n `;\n\n // Close (X) icon\n const closeIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n closeIcon.setAttribute('class', 'verba-bubble-icon verba-bubble-icon--close');\n closeIcon.setAttribute('width', '22');\n closeIcon.setAttribute('height', '22');\n closeIcon.setAttribute('viewBox', '0 0 24 24');\n closeIcon.setAttribute('fill', 'none');\n closeIcon.setAttribute('stroke', '#ffffff');\n closeIcon.setAttribute('stroke-width', '2.5');\n closeIcon.setAttribute('stroke-linecap', 'round');\n closeIcon.innerHTML = `\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n `;\n\n button.appendChild(chatIcon);\n button.appendChild(closeIcon);\n return button;\n}\n\n// ─── State Helpers ───────────────────────────────────────────────────────────\n\n/** Animate the iframe into view. */\nexport function showIframe(iframe: HTMLIFrameElement): void {\n // rAF ensures the browser has painted the element before adding the class\n requestAnimationFrame(() => {\n iframe.classList.add('verba-iframe--visible');\n });\n}\n\n/** Animate the iframe out of view (hides it). */\nexport function hideIframe(iframe: HTMLIFrameElement): void {\n iframe.classList.remove('verba-iframe--visible');\n}\n\n/** Toggle bubble button's open/close icon state. */\nexport function setBubbleOpen(button: HTMLButtonElement, isOpen: boolean): void {\n button.classList.toggle(BUBBLE_OPEN_CLASS, isOpen);\n button.setAttribute('aria-label', isOpen ? 'Close chat' : 'Open chat');\n button.setAttribute('aria-expanded', String(isOpen));\n}\n","/**\n * @module @verba/chat-sdk\n *\n * Verba Chat Widget SDK\n * ---------------------\n * A lightweight, zero-dependency TypeScript SDK that embeds the Verba AI chat\n * widget into any web page via a secure `<iframe>`.\n *\n * @example\n * ```ts\n * import { ChatSDK } from '@verba/chat-sdk';\n *\n * const sdk = new ChatSDK({ tenant: 'your-tenant-id', theme: 'dark' });\n * sdk.init();\n * ```\n */\n\nimport type { ChatSDKConfig, WidgetState } from './types.ts';\nimport {\n injectStyles,\n removeInjectedStyles,\n createFloatingContainer,\n createInlineContainer,\n createIframeElement,\n createBubbleButton,\n showIframe,\n hideIframe,\n setBubbleOpen,\n} from './ui.ts';\n\n// ─── Constants ───────────────────────────────────────────────────────────────\n\n/**\n * The origin of the hosted widget page.\n * All postMessage communication is scoped to this origin.\n * Update this when deploying to a real environment.\n */\nconst WIDGET_ORIGIN = 'https://embed.verba.chat';\n\n/** Full URL of the embeddable HTML page inside the widget origin. */\nconst WIDGET_URL = `${WIDGET_ORIGIN}/embeddable.html`;\n\n// ─── SDK Error ───────────────────────────────────────────────────────────────\n\n/** Thrown when an SDK method is called in an invalid lifecycle state. */\nexport class ChatSDKError extends Error {\n constructor(message: string) {\n super(`[VerbaChatSDK] ${message}`);\n this.name = 'ChatSDKError';\n }\n}\n\n// ─── ChatSDK ─────────────────────────────────────────────────────────────────\n\n/**\n * Main class for the Verba Chat Widget SDK.\n *\n * ### Lifecycle\n * ```\n * new ChatSDK(config) → .init() → .show() / .hide() → .destroy()\n * ```\n *\n * ### Floating mode (default)\n * When no `container` is provided a floating bubble button is created in the\n * bottom-right corner of the viewport. Clicking the bubble opens/closes the\n * iframe with a smooth CSS animation.\n *\n * ### Inline mode\n * When `container` is a CSS selector or `HTMLElement`, the iframe is mounted\n * directly inside that element and the bubble button is omitted.\n */\nexport class ChatSDK {\n // ── Config ─────────────────────────────────────────────────────────────────\n private readonly config: Required<\n Omit<ChatSDKConfig, 'container' | 'userMetadata' | 'tenant'>\n > & Pick<ChatSDKConfig, 'container' | 'userMetadata'> & { tenant: string };\n\n // ── State ──────────────────────────────────────────────────────────────────\n private state: WidgetState = 'uninitialized';\n private isVisible = false;\n private isInline = false;\n\n // ── DOM refs ───────────────────────────────────────────────────────────────\n private container: HTMLElement | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private bubble: HTMLButtonElement | null = null;\n\n\n\n // ── Cleanup ────────────────────────────────────────────────────────────────\n private readonly cleanupCallbacks: Array<() => void> = [];\n\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Create a new `ChatSDK` instance.\n *\n * @param config - SDK configuration. Only `tenant` is required.\n *\n * @throws {ChatSDKError} if `tenant` is empty.\n */\n constructor(config: ChatSDKConfig) {\n const tenant = config.tenant\n if (!tenant?.trim()) {\n throw new ChatSDKError('`tenant` is required and must not be empty.');\n }\n\n this.config = {\n tenant: tenant.trim(),\n theme: config.theme ?? 'light',\n position: config.position ?? 'bottom-right',\n container: config.container,\n userMetadata: config.userMetadata,\n };\n }\n\n // ─── Public API ───────────────────────────────────────────────────────────\n\n /**\n * Inject the widget into the DOM and establish the postMessage bridge.\n *\n * In **floating mode** a chat bubble is rendered in the viewport corner.\n * In **inline mode** the iframe is mounted directly into the configured container.\n *\n * @throws {ChatSDKError} if called more than once or after `destroy()`.\n */\n init(): this {\n this.assertState('uninitialized', 'init');\n\n injectStyles();\n this.state = 'ready';\n\n this.isInline = !!this.config.container;\n\n if (this.isInline) {\n this.mountInline();\n } else {\n this.mountFloating();\n }\n\n return this;\n }\n\n /**\n * Make the widget visible.\n * In floating mode this opens the iframe above the bubble.\n *\n * @throws {ChatSDKError} if `init()` has not been called.\n */\n show(): this {\n this.assertNotState('uninitialized', 'show');\n this.assertNotState('destroyed', 'show');\n\n if (this.isVisible || !this.iframe) return this;\n\n this.isVisible = true;\n\n if (!this.isInline) {\n showIframe(this.iframe);\n if (this.bubble) setBubbleOpen(this.bubble, true);\n }\n\n return this;\n }\n\n /**\n * Hide the widget (does not destroy it — all state is preserved).\n *\n * @throws {ChatSDKError} if `init()` has not been called.\n */\n hide(): this {\n this.assertNotState('uninitialized', 'hide');\n this.assertNotState('destroyed', 'hide');\n\n if (!this.isVisible || !this.iframe) return this;\n\n this.isVisible = false;\n\n if (!this.isInline) {\n hideIframe(this.iframe);\n if (this.bubble) setBubbleOpen(this.bubble, false);\n }\n\n return this;\n }\n\n /**\n * Completely tear down the SDK:\n * - Removes all DOM elements it created.\n * - Removes all event listeners.\n * - Resets state to `'destroyed'`.\n *\n * After calling `destroy()` this instance cannot be reused — create a new one.\n */\n destroy(): void {\n if (this.state === 'destroyed') return;\n\n this.container?.remove();\n this.container = null;\n this.iframe = null;\n this.bubble = null;\n\n for (const cb of this.cleanupCallbacks) cb();\n this.cleanupCallbacks.length = 0;\n\n removeInjectedStyles();\n\n this.state = 'destroyed';\n this.isVisible = false;\n }\n\n /** Current lifecycle state of this SDK instance. */\n get currentState(): WidgetState {\n return this.state;\n }\n\n // ─── Private: Mount Strategies ───────────────────────────────────────────\n\n private mountInline(): void {\n const host = this.resolveContainer();\n if (!host) {\n console.error('[VerbaChatSDK] Container element not found. Falling back to floating mode.');\n this.isInline = false;\n this.mountFloating();\n return;\n }\n\n const wrapper = createInlineContainer();\n const iframe = createIframeElement(this.buildWidgetUrl());\n\n wrapper.appendChild(iframe);\n host.appendChild(wrapper);\n\n this.container = wrapper;\n this.iframe = iframe;\n this.isVisible = true;\n }\n\n private mountFloating(): void {\n const wrapper = createFloatingContainer(this.config.position ?? 'bottom-right');\n const iframe = createIframeElement(this.buildWidgetUrl());\n const bubble = createBubbleButton();\n\n // Bubble click → toggle show/hide\n const onBubbleClick = (): void => {\n this.isVisible ? this.hide() : this.show();\n };\n bubble.addEventListener('click', onBubbleClick);\n this.cleanupCallbacks.push(() =>\n bubble.removeEventListener('click', onBubbleClick)\n );\n\n wrapper.appendChild(iframe);\n wrapper.appendChild(bubble);\n document.body.appendChild(wrapper);\n\n this.container = wrapper;\n this.iframe = iframe;\n this.bubble = bubble;\n this.isVisible = false;\n }\n\n\n\n // ─── Private: State Guards ───────────────────────────────────────────────\n\n private assertState(expected: WidgetState, method: string): void {\n if (this.state !== expected) {\n throw new ChatSDKError(\n `Cannot call \\`${method}()\\` in state \"${this.state}\". Expected \"${expected}\".`\n );\n }\n }\n\n private assertNotState(forbidden: WidgetState, method: string): void {\n if (this.state === forbidden) {\n throw new ChatSDKError(\n `Cannot call \\`${method}()\\` in state \"${forbidden}\".`\n );\n }\n }\n\n // ─── Private: Helpers ────────────────────────────────────────────────────\n\n private resolveContainer(): HTMLElement | null {\n const { container } = this.config;\n if (!container) return null;\n if (typeof container === 'string') {\n return document.querySelector<HTMLElement>(container);\n }\n return container;\n }\n\n private buildWidgetUrl(): string {\n // If WIDGET_URL has search params already, URL() can parse it properly.\n // However, the base WIDGET_URL we use is clean `.../embeddable.html`\n const url = new URL(WIDGET_URL);\n \n // Core parameters\n url.searchParams.set('tnt', this.config.tenant);\n \n // Theme parameters\n if (typeof this.config.theme === 'object') {\n const t = this.config.theme;\n if (t.primaryColor) url.searchParams.set('color', t.primaryColor);\n if (t.textColor) url.searchParams.set('textColor', t.textColor);\n if (t.backgroundColor) url.searchParams.set('backgroundColor', t.backgroundColor);\n if (t.fontFamily) url.searchParams.set('fontFamily', t.fontFamily);\n if (t.borderRadius !== undefined) url.searchParams.set('borderRadius', String(t.borderRadius));\n }\n \n return url.toString();\n }\n}\n\n// ─── Re-exports ───────────────────────────────────────────────────────────────\n\nexport type {\n ChatSDKConfig,\n Theme,\n ThemeConfig,\n BubblePosition,\n WidgetState,\n} from './types.ts';\n"],"names":["STYLE_ID","CONTAINER_CLASS","IFRAME_CLASS","BUBBLE_CLASS","BUBBLE_OPEN_CLASS","injectStyles","css","style","removeInjectedStyles","createFloatingContainer","position","div","createInlineContainer","createIframeElement","src","iframe","createBubbleButton","button","chatIcon","closeIcon","showIframe","hideIframe","setBubbleOpen","isOpen","WIDGET_ORIGIN","WIDGET_URL","ChatSDKError","message","ChatSDK","config","tenant","cb","host","wrapper","bubble","onBubbleClick","expected","method","forbidden","container","url","t"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"chat-sdk.umd.cjs","sources":["../src/ui.ts","../src/index.ts"],"sourcesContent":["/**\r\n * @module @verba/chat-sdk/ui\r\n * CSS-in-JS helpers for creating and managing the widget's DOM elements.\r\n * All styles are scoped to avoid conflicts with host application CSS.\r\n */\r\n\r\nimport type { BubblePosition } from './types.ts';\r\n\r\n// ─── Constants ───────────────────────────────────────────────────────────────\r\n\r\nconst STYLE_ID = 'verba-chat-sdk-styles';\r\nconst CONTAINER_CLASS = 'verba-widget-container';\r\nconst IFRAME_CLASS = 'verba-widget-iframe';\r\nconst BUBBLE_CLASS = 'verba-widget-bubble';\r\nconst BUBBLE_OPEN_CLASS = 'verba-widget-bubble--open';\r\n\r\n// ─── Style Injection ─────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Injects a `<style>` tag into the document `<head>` with all widget styles.\r\n * Idempotent — safe to call multiple times.\r\n */\r\nexport function injectStyles(): void {\r\n if (document.getElementById(STYLE_ID)) return;\r\n\r\n const css = `\r\n /* ── Verba Chat SDK ── */\r\n\r\n .${CONTAINER_CLASS} {\r\n position: fixed;\r\n bottom: 24px;\r\n right: 24px;\r\n z-index: 9999;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: flex-end;\r\n gap: 12px;\r\n font-family: system-ui, -apple-system, sans-serif;\r\n }\r\n\r\n .${CONTAINER_CLASS}[data-position=\"bottom-left\"] {\r\n right: unset;\r\n left: 24px;\r\n align-items: flex-start;\r\n }\r\n\r\n .${CONTAINER_CLASS}.verba-widget--inline {\r\n position: static;\r\n bottom: unset;\r\n right: unset;\r\n left: unset;\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n align-items: stretch;\r\n }\r\n\r\n .${IFRAME_CLASS} {\r\n border: none;\r\n border-radius: 16px;\r\n width: 380px;\r\n height: 600px;\r\n box-shadow: 0 24px 64px rgba(0, 0, 0, 0.18), 0 8px 24px rgba(0, 0, 0, 0.12);\r\n opacity: 0;\r\n transform: translateY(12px) scale(0.97);\r\n transform-origin: bottom right;\r\n transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1),\r\n transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\r\n pointer-events: none;\r\n background: transparent;\r\n }\r\n\r\n .${CONTAINER_CLASS}[data-position=\"bottom-left\"] .${IFRAME_CLASS} {\r\n transform-origin: bottom left;\r\n }\r\n\r\n .${IFRAME_CLASS}.verba-iframe--visible {\r\n opacity: 1;\r\n transform: translateY(0) scale(1);\r\n pointer-events: all;\r\n }\r\n\r\n .${CONTAINER_CLASS}.verba-widget--inline .${IFRAME_CLASS} {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 0;\r\n box-shadow: none;\r\n opacity: 1;\r\n transform: none;\r\n pointer-events: all;\r\n }\r\n\r\n /* ── Bubble Button ── */\r\n\r\n .${BUBBLE_CLASS} {\r\n width: 56px;\r\n height: 56px;\r\n border-radius: 50%;\r\n border: none;\r\n cursor: pointer;\r\n background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);\r\n box-shadow: 0 4px 20px rgba(99, 102, 241, 0.45), 0 2px 6px rgba(0,0,0,0.15);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1),\r\n box-shadow 0.22s ease;\r\n flex-shrink: 0;\r\n outline: none;\r\n overflow: hidden;\r\n }\r\n\r\n .${BUBBLE_CLASS}:hover {\r\n transform: scale(1.1);\r\n box-shadow: 0 6px 28px rgba(99, 102, 241, 0.55), 0 2px 10px rgba(0,0,0,0.18);\r\n }\r\n\r\n .${BUBBLE_CLASS}:active {\r\n transform: scale(0.95);\r\n }\r\n\r\n .${BUBBLE_CLASS} .verba-bubble-icon {\r\n position: absolute;\r\n transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);\r\n }\r\n\r\n .${BUBBLE_CLASS} .verba-bubble-icon--chat { opacity: 1; transform: scale(1) rotate(0deg); }\r\n .${BUBBLE_CLASS} .verba-bubble-icon--close { opacity: 0; transform: scale(0.5) rotate(-90deg); }\r\n\r\n .${BUBBLE_CLASS}.${BUBBLE_OPEN_CLASS} .verba-bubble-icon--chat {\r\n opacity: 0;\r\n transform: scale(0.5) rotate(90deg);\r\n }\r\n .${BUBBLE_CLASS}.${BUBBLE_OPEN_CLASS} .verba-bubble-icon--close {\r\n opacity: 1;\r\n transform: scale(1) rotate(0deg);\r\n }\r\n\r\n /* ── Ripple pulse on load ── */\r\n @keyframes verba-pulse {\r\n 0% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 0 rgba(99,102,241,0.4); }\r\n 70% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 14px rgba(99,102,241,0); }\r\n 100% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 0 rgba(99,102,241,0); }\r\n }\r\n\r\n .${BUBBLE_CLASS}.verba-bubble--pulse {\r\n animation: verba-pulse 1.8s ease-out 0.4s 2;\r\n }\r\n\r\n /* ── Mobile ── */\r\n @media (max-width: 480px) {\r\n .${IFRAME_CLASS} {\r\n width: calc(100vw - 24px);\r\n height: 75vh;\r\n max-height: 600px;\r\n }\r\n\r\n .${CONTAINER_CLASS} {\r\n right: 12px;\r\n bottom: 12px;\r\n }\r\n\r\n .${CONTAINER_CLASS}[data-position=\"bottom-left\"] {\r\n left: 12px;\r\n right: unset;\r\n }\r\n }\r\n `;\r\n\r\n const style = document.createElement('style');\r\n style.id = STYLE_ID;\r\n style.textContent = css;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/** Removes the injected `<style>` tag from the document. */\r\nexport function removeInjectedStyles(): void {\r\n document.getElementById(STYLE_ID)?.remove();\r\n}\r\n\r\n// ─── Element Factories ───────────────────────────────────────────────────────\r\n\r\n/**\r\n * Creates the fixed-position outer container div used in floating mode.\r\n */\r\nexport function createFloatingContainer(position: BubblePosition): HTMLDivElement {\r\n const div = document.createElement('div');\r\n div.className = CONTAINER_CLASS;\r\n div.dataset.position = position;\r\n return div;\r\n}\r\n\r\n/**\r\n * Creates an inline container — wrapper inside a user-supplied element.\r\n */\r\nexport function createInlineContainer(): HTMLDivElement {\r\n const div = document.createElement('div');\r\n div.className = `${CONTAINER_CLASS} verba-widget--inline`;\r\n div.style.width = '100%';\r\n div.style.height = '100%';\r\n return div;\r\n}\r\n\r\n/**\r\n * Creates the `<iframe>` element with correct sandbox + security attributes.\r\n * The iframe starts **hidden** — call `showIframe()` to animate it in.\r\n */\r\nexport function createIframeElement(src: string): HTMLIFrameElement {\r\n const iframe = document.createElement('iframe');\r\n iframe.className = IFRAME_CLASS;\r\n iframe.src = src;\r\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups');\r\n iframe.setAttribute('allow', 'microphone; camera');\r\n iframe.setAttribute('loading', 'lazy');\r\n iframe.setAttribute('title', 'Verba AI Chat Widget');\r\n iframe.setAttribute('aria-label', 'Chat support widget');\r\n return iframe;\r\n}\r\n\r\n/**\r\n * Creates the floating bubble toggle button with animated chat / close icons (pure SVG).\r\n */\r\nexport function createBubbleButton(): HTMLButtonElement {\r\n const button = document.createElement('button');\r\n button.className = `${BUBBLE_CLASS} verba-bubble--pulse`;\r\n button.setAttribute('aria-label', 'Open chat');\r\n button.setAttribute('aria-expanded', 'false');\r\n button.setAttribute('type', 'button');\r\n\r\n // Chat icon\r\n const chatIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\r\n chatIcon.setAttribute('class', 'verba-bubble-icon verba-bubble-icon--chat');\r\n chatIcon.setAttribute('width', '26');\r\n chatIcon.setAttribute('height', '26');\r\n chatIcon.setAttribute('viewBox', '0 0 24 24');\r\n chatIcon.setAttribute('fill', 'none');\r\n chatIcon.setAttribute('stroke', '#ffffff');\r\n chatIcon.setAttribute('stroke-width', '2');\r\n chatIcon.setAttribute('stroke-linecap', 'round');\r\n chatIcon.setAttribute('stroke-linejoin', 'round');\r\n chatIcon.innerHTML = `\r\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/>\r\n `;\r\n\r\n // Close (X) icon\r\n const closeIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\r\n closeIcon.setAttribute('class', 'verba-bubble-icon verba-bubble-icon--close');\r\n closeIcon.setAttribute('width', '22');\r\n closeIcon.setAttribute('height', '22');\r\n closeIcon.setAttribute('viewBox', '0 0 24 24');\r\n closeIcon.setAttribute('fill', 'none');\r\n closeIcon.setAttribute('stroke', '#ffffff');\r\n closeIcon.setAttribute('stroke-width', '2.5');\r\n closeIcon.setAttribute('stroke-linecap', 'round');\r\n closeIcon.innerHTML = `\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\r\n `;\r\n\r\n button.appendChild(chatIcon);\r\n button.appendChild(closeIcon);\r\n return button;\r\n}\r\n\r\n// ─── State Helpers ───────────────────────────────────────────────────────────\r\n\r\n/** Animate the iframe into view. */\r\nexport function showIframe(iframe: HTMLIFrameElement): void {\r\n // rAF ensures the browser has painted the element before adding the class\r\n requestAnimationFrame(() => {\r\n iframe.classList.add('verba-iframe--visible');\r\n });\r\n}\r\n\r\n/** Animate the iframe out of view (hides it). */\r\nexport function hideIframe(iframe: HTMLIFrameElement): void {\r\n iframe.classList.remove('verba-iframe--visible');\r\n}\r\n\r\n/** Toggle bubble button's open/close icon state. */\r\nexport function setBubbleOpen(button: HTMLButtonElement, isOpen: boolean): void {\r\n button.classList.toggle(BUBBLE_OPEN_CLASS, isOpen);\r\n button.setAttribute('aria-label', isOpen ? 'Close chat' : 'Open chat');\r\n button.setAttribute('aria-expanded', String(isOpen));\r\n}\r\n","/**\r\n * @module @verba/chat-sdk\r\n *\r\n * Verba Chat Widget SDK\r\n * ---------------------\r\n * A lightweight, zero-dependency TypeScript SDK that embeds the Verba AI chat\r\n * widget into any web page via a secure `<iframe>`.\r\n *\r\n * @example\r\n * ```ts\r\n * import { ChatSDK } from '@verba/chat-sdk';\r\n *\r\n * const sdk = new ChatSDK({ tenant: 'your-tenant-id', theme: 'dark' });\r\n * sdk.init();\r\n * ```\r\n */\r\n\r\nimport type { ChatSDKConfig, WidgetState } from './types.ts';\r\nimport {\r\n injectStyles,\r\n removeInjectedStyles,\r\n createFloatingContainer,\r\n createInlineContainer,\r\n createIframeElement,\r\n createBubbleButton,\r\n showIframe,\r\n hideIframe,\r\n setBubbleOpen,\r\n} from './ui.ts';\r\n\r\n// ─── Constants ───────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * The origin of the hosted widget page.\r\n * All postMessage communication is scoped to this origin.\r\n * Update this when deploying to a real environment.\r\n */\r\nconst WIDGET_ORIGIN = 'https://embed.verba.chat';\r\n\r\n/** Full URL of the embeddable HTML page inside the widget origin. */\r\nconst WIDGET_URL = `${WIDGET_ORIGIN}/embeddable.html`;\r\n\r\n// ─── SDK Error ───────────────────────────────────────────────────────────────\r\n\r\n/** Thrown when an SDK method is called in an invalid lifecycle state. */\r\nexport class ChatSDKError extends Error {\r\n constructor(message: string) {\r\n super(`[VerbaChatSDK] ${message}`);\r\n this.name = 'ChatSDKError';\r\n }\r\n}\r\n\r\n// ─── ChatSDK ─────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Main class for the Verba Chat Widget SDK.\r\n *\r\n * ### Lifecycle\r\n * ```\r\n * new ChatSDK(config) → .init() → .show() / .hide() → .destroy()\r\n * ```\r\n *\r\n * ### Floating mode (default)\r\n * When no `container` is provided a floating bubble button is created in the\r\n * bottom-right corner of the viewport. Clicking the bubble opens/closes the\r\n * iframe with a smooth CSS animation.\r\n *\r\n * ### Inline mode\r\n * When `container` is a CSS selector or `HTMLElement`, the iframe is mounted\r\n * directly inside that element and the bubble button is omitted.\r\n */\r\nexport class ChatSDK {\r\n // ── Config ─────────────────────────────────────────────────────────────────\r\n private readonly config: Required<\r\n Omit<ChatSDKConfig, 'container' | 'userMetadata' | 'tenant'>\r\n > & Pick<ChatSDKConfig, 'container' | 'userMetadata'> & { tenant: string };\r\n\r\n // ── State ──────────────────────────────────────────────────────────────────\r\n private state: WidgetState = 'uninitialized';\r\n private isVisible = false;\r\n private isInline = false;\r\n\r\n // ── DOM refs ───────────────────────────────────────────────────────────────\r\n private container: HTMLElement | null = null;\r\n private iframe: HTMLIFrameElement | null = null;\r\n private bubble: HTMLButtonElement | null = null;\r\n\r\n\r\n\r\n // ── Cleanup ────────────────────────────────────────────────────────────────\r\n private readonly cleanupCallbacks: Array<() => void> = [];\r\n\r\n // ─────────────────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Create a new `ChatSDK` instance.\r\n *\r\n * @param config - SDK configuration. Only `tenant` is required.\r\n *\r\n * @throws {ChatSDKError} if `tenant` is empty.\r\n */\r\n constructor(config: ChatSDKConfig) {\r\n const tenant = config.tenant\r\n if (!tenant?.trim()) {\r\n throw new ChatSDKError('`tenant` is required and must not be empty.');\r\n }\r\n\r\n this.config = {\r\n tenant: tenant.trim(),\r\n theme: config.theme ?? 'light',\r\n position: config.position ?? 'bottom-right',\r\n container: config.container,\r\n userMetadata: config.userMetadata,\r\n };\r\n }\r\n\r\n // ─── Public API ───────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Inject the widget into the DOM and establish the postMessage bridge.\r\n *\r\n * In **floating mode** a chat bubble is rendered in the viewport corner.\r\n * In **inline mode** the iframe is mounted directly into the configured container.\r\n *\r\n * @throws {ChatSDKError} if called more than once or after `destroy()`.\r\n */\r\n init(): this {\r\n this.assertState('uninitialized', 'init');\r\n\r\n injectStyles();\r\n this.state = 'ready';\r\n\r\n this.isInline = !!this.config.container;\r\n\r\n if (this.isInline) {\r\n this.mountInline();\r\n } else {\r\n this.mountFloating();\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Make the widget visible.\r\n * In floating mode this opens the iframe above the bubble.\r\n *\r\n * @throws {ChatSDKError} if `init()` has not been called.\r\n */\r\n show(): this {\r\n this.assertNotState('uninitialized', 'show');\r\n this.assertNotState('destroyed', 'show');\r\n\r\n if (this.isVisible || !this.iframe) return this;\r\n\r\n this.isVisible = true;\r\n\r\n if (!this.isInline) {\r\n showIframe(this.iframe);\r\n if (this.bubble) setBubbleOpen(this.bubble, true);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Hide the widget (does not destroy it — all state is preserved).\r\n *\r\n * @throws {ChatSDKError} if `init()` has not been called.\r\n */\r\n hide(): this {\r\n this.assertNotState('uninitialized', 'hide');\r\n this.assertNotState('destroyed', 'hide');\r\n\r\n if (!this.isVisible || !this.iframe) return this;\r\n\r\n this.isVisible = false;\r\n\r\n if (!this.isInline) {\r\n hideIframe(this.iframe);\r\n if (this.bubble) setBubbleOpen(this.bubble, false);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Completely tear down the SDK:\r\n * - Removes all DOM elements it created.\r\n * - Removes all event listeners.\r\n * - Resets state to `'destroyed'`.\r\n *\r\n * After calling `destroy()` this instance cannot be reused — create a new one.\r\n */\r\n destroy(): void {\r\n if (this.state === 'destroyed') return;\r\n\r\n this.container?.remove();\r\n this.container = null;\r\n this.iframe = null;\r\n this.bubble = null;\r\n\r\n for (const cb of this.cleanupCallbacks) cb();\r\n this.cleanupCallbacks.length = 0;\r\n\r\n removeInjectedStyles();\r\n\r\n this.state = 'destroyed';\r\n this.isVisible = false;\r\n }\r\n\r\n /** Current lifecycle state of this SDK instance. */\r\n get currentState(): WidgetState {\r\n return this.state;\r\n }\r\n\r\n // ─── Private: Mount Strategies ───────────────────────────────────────────\r\n\r\n private mountInline(): void {\r\n const host = this.resolveContainer();\r\n if (!host) {\r\n console.error('[VerbaChatSDK] Container element not found. Falling back to floating mode.');\r\n this.isInline = false;\r\n this.mountFloating();\r\n return;\r\n }\r\n\r\n const wrapper = createInlineContainer();\r\n const iframe = createIframeElement(this.buildWidgetUrl());\r\n\r\n wrapper.appendChild(iframe);\r\n host.appendChild(wrapper);\r\n\r\n this.container = wrapper;\r\n this.iframe = iframe;\r\n this.isVisible = true;\r\n }\r\n\r\n private mountFloating(): void {\r\n const wrapper = createFloatingContainer(this.config.position ?? 'bottom-right');\r\n const iframe = createIframeElement(this.buildWidgetUrl());\r\n const bubble = createBubbleButton();\r\n\r\n // Bubble click → toggle show/hide\r\n const onBubbleClick = (): void => {\r\n this.isVisible ? this.hide() : this.show();\r\n };\r\n bubble.addEventListener('click', onBubbleClick);\r\n this.cleanupCallbacks.push(() =>\r\n bubble.removeEventListener('click', onBubbleClick)\r\n );\r\n\r\n wrapper.appendChild(iframe);\r\n wrapper.appendChild(bubble);\r\n document.body.appendChild(wrapper);\r\n\r\n this.container = wrapper;\r\n this.iframe = iframe;\r\n this.bubble = bubble;\r\n this.isVisible = false;\r\n }\r\n\r\n\r\n\r\n // ─── Private: State Guards ───────────────────────────────────────────────\r\n\r\n private assertState(expected: WidgetState, method: string): void {\r\n if (this.state !== expected) {\r\n throw new ChatSDKError(\r\n `Cannot call \\`${method}()\\` in state \"${this.state}\". Expected \"${expected}\".`\r\n );\r\n }\r\n }\r\n\r\n private assertNotState(forbidden: WidgetState, method: string): void {\r\n if (this.state === forbidden) {\r\n throw new ChatSDKError(\r\n `Cannot call \\`${method}()\\` in state \"${forbidden}\".`\r\n );\r\n }\r\n }\r\n\r\n // ─── Private: Helpers ────────────────────────────────────────────────────\r\n\r\n private resolveContainer(): HTMLElement | null {\r\n const { container } = this.config;\r\n if (!container) return null;\r\n if (typeof container === 'string') {\r\n return document.querySelector<HTMLElement>(container);\r\n }\r\n return container;\r\n }\r\n\r\n private buildWidgetUrl(): string {\r\n // If WIDGET_URL has search params already, URL() can parse it properly.\r\n // However, the base WIDGET_URL we use is clean `.../embeddable.html`\r\n const url = new URL(WIDGET_URL);\r\n \r\n // Core parameters\r\n url.searchParams.set('tnt', this.config.tenant);\r\n \r\n // Theme parameters\r\n if (typeof this.config.theme === 'object') {\r\n const t = this.config.theme;\r\n if (t.primaryColor) url.searchParams.set('color', t.primaryColor);\r\n if (t.textColor) url.searchParams.set('textColor', t.textColor);\r\n if (t.backgroundColor) url.searchParams.set('backgroundColor', t.backgroundColor);\r\n if (t.fontFamily) url.searchParams.set('fontFamily', t.fontFamily);\r\n if (t.borderRadius !== undefined) url.searchParams.set('borderRadius', String(t.borderRadius));\r\n }\r\n \r\n return url.toString();\r\n }\r\n}\r\n\r\n// ─── Re-exports ───────────────────────────────────────────────────────────────\r\n\r\nexport type {\r\n ChatSDKConfig,\r\n Theme,\r\n ThemeConfig,\r\n BubblePosition,\r\n WidgetState,\r\n} from './types.ts';\r\n"],"names":["STYLE_ID","CONTAINER_CLASS","IFRAME_CLASS","BUBBLE_CLASS","BUBBLE_OPEN_CLASS","injectStyles","css","style","removeInjectedStyles","createFloatingContainer","position","div","createInlineContainer","createIframeElement","src","iframe","createBubbleButton","button","chatIcon","closeIcon","showIframe","hideIframe","setBubbleOpen","isOpen","WIDGET_URL","ChatSDKError","message","ChatSDK","config","tenant","cb","host","wrapper","bubble","onBubbleClick","expected","method","forbidden","container","url","t"],"mappings":"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"}
1
+ {"version":3,"file":"chat-sdk.umd.cjs","sources":["../src/ui.ts","../src/index.ts"],"sourcesContent":["/**\n * @module @verba/chat-sdk/ui\n * CSS-in-JS helpers for creating and managing the widget's DOM elements.\n * All styles are scoped to avoid conflicts with host application CSS.\n */\n\nimport type { BubblePosition } from './types.ts';\n\n// ─── Constants ───────────────────────────────────────────────────────────────\n\nconst STYLE_ID = 'verba-chat-sdk-styles';\nconst CONTAINER_CLASS = 'verba-widget-container';\nconst IFRAME_CLASS = 'verba-widget-iframe';\nconst BUBBLE_CLASS = 'verba-widget-bubble';\nconst BUBBLE_OPEN_CLASS = 'verba-widget-bubble--open';\n\n// ─── Style Injection ─────────────────────────────────────────────────────────\n\n/**\n * Injects a `<style>` tag into the document `<head>` with all widget styles.\n * Idempotent — safe to call multiple times.\n */\nexport function injectStyles(): void {\n if (document.getElementById(STYLE_ID)) return;\n\n const css = `\n /* ── Verba Chat SDK ── */\n\n .${CONTAINER_CLASS} {\n position: fixed;\n bottom: 24px;\n right: 24px;\n z-index: 9999;\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 12px;\n font-family: system-ui, -apple-system, sans-serif;\n }\n\n .${CONTAINER_CLASS}[data-position=\"bottom-left\"] {\n right: unset;\n left: 24px;\n align-items: flex-start;\n }\n\n .${CONTAINER_CLASS}.verba-widget--inline {\n position: static;\n bottom: unset;\n right: unset;\n left: unset;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: stretch;\n }\n\n .${IFRAME_CLASS} {\n border: none;\n border-radius: 16px;\n width: 380px;\n height: 600px;\n box-shadow: 0 24px 64px rgba(0, 0, 0, 0.18), 0 8px 24px rgba(0, 0, 0, 0.12);\n opacity: 0;\n transform: translateY(12px) scale(0.97);\n transform-origin: bottom right;\n transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1),\n transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n background: transparent;\n }\n\n .${CONTAINER_CLASS}[data-position=\"bottom-left\"] .${IFRAME_CLASS} {\n transform-origin: bottom left;\n }\n\n .${IFRAME_CLASS}.verba-iframe--visible {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: all;\n }\n\n .${CONTAINER_CLASS}.verba-widget--inline .${IFRAME_CLASS} {\n width: 100%;\n height: 100%;\n border-radius: 0;\n box-shadow: none;\n opacity: 1;\n transform: none;\n pointer-events: all;\n }\n\n /* ── Bubble Button ── */\n\n .${BUBBLE_CLASS} {\n width: 56px;\n height: 56px;\n border-radius: 50%;\n border: none;\n cursor: pointer;\n background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);\n box-shadow: 0 4px 20px rgba(99, 102, 241, 0.45), 0 2px 6px rgba(0,0,0,0.15);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1),\n box-shadow 0.22s ease;\n flex-shrink: 0;\n outline: none;\n overflow: hidden;\n }\n\n .${BUBBLE_CLASS}:hover {\n transform: scale(1.1);\n box-shadow: 0 6px 28px rgba(99, 102, 241, 0.55), 0 2px 10px rgba(0,0,0,0.18);\n }\n\n .${BUBBLE_CLASS}:active {\n transform: scale(0.95);\n }\n\n .${BUBBLE_CLASS} .verba-bubble-icon {\n position: absolute;\n transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n .${BUBBLE_CLASS} .verba-bubble-icon--chat { opacity: 1; transform: scale(1) rotate(0deg); }\n .${BUBBLE_CLASS} .verba-bubble-icon--close { opacity: 0; transform: scale(0.5) rotate(-90deg); }\n\n .${BUBBLE_CLASS}.${BUBBLE_OPEN_CLASS} .verba-bubble-icon--chat {\n opacity: 0;\n transform: scale(0.5) rotate(90deg);\n }\n .${BUBBLE_CLASS}.${BUBBLE_OPEN_CLASS} .verba-bubble-icon--close {\n opacity: 1;\n transform: scale(1) rotate(0deg);\n }\n\n /* ── Ripple pulse on load ── */\n @keyframes verba-pulse {\n 0% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 0 rgba(99,102,241,0.4); }\n 70% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 14px rgba(99,102,241,0); }\n 100% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 0 rgba(99,102,241,0); }\n }\n\n .${BUBBLE_CLASS}.verba-bubble--pulse {\n animation: verba-pulse 1.8s ease-out 0.4s 2;\n }\n\n /* ── Mobile ── */\n @media (max-width: 480px) {\n .${IFRAME_CLASS} {\n width: calc(100vw - 24px);\n height: 75vh;\n max-height: 600px;\n }\n\n .${CONTAINER_CLASS} {\n right: 12px;\n bottom: 12px;\n }\n\n .${CONTAINER_CLASS}[data-position=\"bottom-left\"] {\n left: 12px;\n right: unset;\n }\n }\n `;\n\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = css;\n document.head.appendChild(style);\n}\n\n/** Removes the injected `<style>` tag from the document. */\nexport function removeInjectedStyles(): void {\n document.getElementById(STYLE_ID)?.remove();\n}\n\n// ─── Element Factories ───────────────────────────────────────────────────────\n\n/**\n * Creates the fixed-position outer container div used in floating mode.\n */\nexport function createFloatingContainer(position: BubblePosition): HTMLDivElement {\n const div = document.createElement('div');\n div.className = CONTAINER_CLASS;\n div.dataset.position = position;\n return div;\n}\n\n/**\n * Creates an inline container — wrapper inside a user-supplied element.\n */\nexport function createInlineContainer(): HTMLDivElement {\n const div = document.createElement('div');\n div.className = `${CONTAINER_CLASS} verba-widget--inline`;\n div.style.width = '100%';\n div.style.height = '100%';\n return div;\n}\n\n/**\n * Creates the `<iframe>` element with correct sandbox + security attributes.\n * The iframe starts **hidden** — call `showIframe()` to animate it in.\n */\nexport function createIframeElement(src: string): HTMLIFrameElement {\n const iframe = document.createElement('iframe');\n iframe.className = IFRAME_CLASS;\n iframe.src = src;\n iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups');\n iframe.setAttribute('allow', 'microphone; camera');\n iframe.setAttribute('loading', 'lazy');\n iframe.setAttribute('title', 'Verba AI Chat Widget');\n iframe.setAttribute('aria-label', 'Chat support widget');\n return iframe;\n}\n\n/**\n * Creates the floating bubble toggle button with animated chat / close icons (pure SVG).\n */\nexport function createBubbleButton(): HTMLButtonElement {\n const button = document.createElement('button');\n button.className = `${BUBBLE_CLASS} verba-bubble--pulse`;\n button.setAttribute('aria-label', 'Open chat');\n button.setAttribute('aria-expanded', 'false');\n button.setAttribute('type', 'button');\n\n // Chat icon\n const chatIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n chatIcon.setAttribute('class', 'verba-bubble-icon verba-bubble-icon--chat');\n chatIcon.setAttribute('width', '26');\n chatIcon.setAttribute('height', '26');\n chatIcon.setAttribute('viewBox', '0 0 24 24');\n chatIcon.setAttribute('fill', 'none');\n chatIcon.setAttribute('stroke', '#ffffff');\n chatIcon.setAttribute('stroke-width', '2');\n chatIcon.setAttribute('stroke-linecap', 'round');\n chatIcon.setAttribute('stroke-linejoin', 'round');\n chatIcon.innerHTML = `\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/>\n `;\n\n // Close (X) icon\n const closeIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n closeIcon.setAttribute('class', 'verba-bubble-icon verba-bubble-icon--close');\n closeIcon.setAttribute('width', '22');\n closeIcon.setAttribute('height', '22');\n closeIcon.setAttribute('viewBox', '0 0 24 24');\n closeIcon.setAttribute('fill', 'none');\n closeIcon.setAttribute('stroke', '#ffffff');\n closeIcon.setAttribute('stroke-width', '2.5');\n closeIcon.setAttribute('stroke-linecap', 'round');\n closeIcon.innerHTML = `\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n `;\n\n button.appendChild(chatIcon);\n button.appendChild(closeIcon);\n return button;\n}\n\n// ─── State Helpers ───────────────────────────────────────────────────────────\n\n/** Animate the iframe into view. */\nexport function showIframe(iframe: HTMLIFrameElement): void {\n // rAF ensures the browser has painted the element before adding the class\n requestAnimationFrame(() => {\n iframe.classList.add('verba-iframe--visible');\n });\n}\n\n/** Animate the iframe out of view (hides it). */\nexport function hideIframe(iframe: HTMLIFrameElement): void {\n iframe.classList.remove('verba-iframe--visible');\n}\n\n/** Toggle bubble button's open/close icon state. */\nexport function setBubbleOpen(button: HTMLButtonElement, isOpen: boolean): void {\n button.classList.toggle(BUBBLE_OPEN_CLASS, isOpen);\n button.setAttribute('aria-label', isOpen ? 'Close chat' : 'Open chat');\n button.setAttribute('aria-expanded', String(isOpen));\n}\n","/**\n * @module @verba/chat-sdk\n *\n * Verba Chat Widget SDK\n * ---------------------\n * A lightweight, zero-dependency TypeScript SDK that embeds the Verba AI chat\n * widget into any web page via a secure `<iframe>`.\n *\n * @example\n * ```ts\n * import { ChatSDK } from '@verba/chat-sdk';\n *\n * const sdk = new ChatSDK({ tenant: 'your-tenant-id', theme: 'dark' });\n * sdk.init();\n * ```\n */\n\nimport type { ChatSDKConfig, WidgetState } from './types.ts';\nimport {\n injectStyles,\n removeInjectedStyles,\n createFloatingContainer,\n createInlineContainer,\n createIframeElement,\n createBubbleButton,\n showIframe,\n hideIframe,\n setBubbleOpen,\n} from './ui.ts';\n\n// ─── Constants ───────────────────────────────────────────────────────────────\n\n/**\n * The origin of the hosted widget page.\n * All postMessage communication is scoped to this origin.\n * Update this when deploying to a real environment.\n */\nconst WIDGET_ORIGIN = 'https://embed.verba.chat';\n\n/** Full URL of the embeddable HTML page inside the widget origin. */\nconst WIDGET_URL = `${WIDGET_ORIGIN}/embeddable.html`;\n\n// ─── SDK Error ───────────────────────────────────────────────────────────────\n\n/** Thrown when an SDK method is called in an invalid lifecycle state. */\nexport class ChatSDKError extends Error {\n constructor(message: string) {\n super(`[VerbaChatSDK] ${message}`);\n this.name = 'ChatSDKError';\n }\n}\n\n// ─── ChatSDK ─────────────────────────────────────────────────────────────────\n\n/**\n * Main class for the Verba Chat Widget SDK.\n *\n * ### Lifecycle\n * ```\n * new ChatSDK(config) → .init() → .show() / .hide() → .destroy()\n * ```\n *\n * ### Floating mode (default)\n * When no `container` is provided a floating bubble button is created in the\n * bottom-right corner of the viewport. Clicking the bubble opens/closes the\n * iframe with a smooth CSS animation.\n *\n * ### Inline mode\n * When `container` is a CSS selector or `HTMLElement`, the iframe is mounted\n * directly inside that element and the bubble button is omitted.\n */\nexport class ChatSDK {\n // ── Config ─────────────────────────────────────────────────────────────────\n private readonly config: Required<\n Omit<ChatSDKConfig, 'container' | 'userMetadata' | 'tenant'>\n > & Pick<ChatSDKConfig, 'container' | 'userMetadata'> & { tenant: string };\n\n // ── State ──────────────────────────────────────────────────────────────────\n private state: WidgetState = 'uninitialized';\n private isVisible = false;\n private isInline = false;\n\n // ── DOM refs ───────────────────────────────────────────────────────────────\n private container: HTMLElement | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private bubble: HTMLButtonElement | null = null;\n\n\n\n // ── Cleanup ────────────────────────────────────────────────────────────────\n private readonly cleanupCallbacks: Array<() => void> = [];\n\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Create a new `ChatSDK` instance.\n *\n * @param config - SDK configuration. Only `tenant` is required.\n *\n * @throws {ChatSDKError} if `tenant` is empty.\n */\n constructor(config: ChatSDKConfig) {\n const tenant = config.tenant\n if (!tenant?.trim()) {\n throw new ChatSDKError('`tenant` is required and must not be empty.');\n }\n\n this.config = {\n tenant: tenant.trim(),\n theme: config.theme ?? 'light',\n position: config.position ?? 'bottom-right',\n container: config.container,\n userMetadata: config.userMetadata,\n };\n }\n\n // ─── Public API ───────────────────────────────────────────────────────────\n\n /**\n * Inject the widget into the DOM and establish the postMessage bridge.\n *\n * In **floating mode** a chat bubble is rendered in the viewport corner.\n * In **inline mode** the iframe is mounted directly into the configured container.\n *\n * @throws {ChatSDKError} if called more than once or after `destroy()`.\n */\n init(): this {\n this.assertState('uninitialized', 'init');\n\n injectStyles();\n this.state = 'ready';\n\n this.isInline = !!this.config.container;\n\n if (this.isInline) {\n this.mountInline();\n } else {\n this.mountFloating();\n }\n\n return this;\n }\n\n /**\n * Make the widget visible.\n * In floating mode this opens the iframe above the bubble.\n *\n * @throws {ChatSDKError} if `init()` has not been called.\n */\n show(): this {\n this.assertNotState('uninitialized', 'show');\n this.assertNotState('destroyed', 'show');\n\n if (this.isVisible || !this.iframe) return this;\n\n this.isVisible = true;\n\n if (!this.isInline) {\n showIframe(this.iframe);\n if (this.bubble) setBubbleOpen(this.bubble, true);\n }\n\n return this;\n }\n\n /**\n * Hide the widget (does not destroy it — all state is preserved).\n *\n * @throws {ChatSDKError} if `init()` has not been called.\n */\n hide(): this {\n this.assertNotState('uninitialized', 'hide');\n this.assertNotState('destroyed', 'hide');\n\n if (!this.isVisible || !this.iframe) return this;\n\n this.isVisible = false;\n\n if (!this.isInline) {\n hideIframe(this.iframe);\n if (this.bubble) setBubbleOpen(this.bubble, false);\n }\n\n return this;\n }\n\n /**\n * Completely tear down the SDK:\n * - Removes all DOM elements it created.\n * - Removes all event listeners.\n * - Resets state to `'destroyed'`.\n *\n * After calling `destroy()` this instance cannot be reused — create a new one.\n */\n destroy(): void {\n if (this.state === 'destroyed') return;\n\n this.container?.remove();\n this.container = null;\n this.iframe = null;\n this.bubble = null;\n\n for (const cb of this.cleanupCallbacks) cb();\n this.cleanupCallbacks.length = 0;\n\n removeInjectedStyles();\n\n this.state = 'destroyed';\n this.isVisible = false;\n }\n\n /** Current lifecycle state of this SDK instance. */\n get currentState(): WidgetState {\n return this.state;\n }\n\n // ─── Private: Mount Strategies ───────────────────────────────────────────\n\n private mountInline(): void {\n const host = this.resolveContainer();\n if (!host) {\n console.error('[VerbaChatSDK] Container element not found. Falling back to floating mode.');\n this.isInline = false;\n this.mountFloating();\n return;\n }\n\n const wrapper = createInlineContainer();\n const iframe = createIframeElement(this.buildWidgetUrl());\n\n wrapper.appendChild(iframe);\n host.appendChild(wrapper);\n\n this.container = wrapper;\n this.iframe = iframe;\n this.isVisible = true;\n }\n\n private mountFloating(): void {\n const wrapper = createFloatingContainer(this.config.position ?? 'bottom-right');\n const iframe = createIframeElement(this.buildWidgetUrl());\n const bubble = createBubbleButton();\n\n // Bubble click → toggle show/hide\n const onBubbleClick = (): void => {\n this.isVisible ? this.hide() : this.show();\n };\n bubble.addEventListener('click', onBubbleClick);\n this.cleanupCallbacks.push(() =>\n bubble.removeEventListener('click', onBubbleClick)\n );\n\n wrapper.appendChild(iframe);\n wrapper.appendChild(bubble);\n document.body.appendChild(wrapper);\n\n this.container = wrapper;\n this.iframe = iframe;\n this.bubble = bubble;\n this.isVisible = false;\n }\n\n\n\n // ─── Private: State Guards ───────────────────────────────────────────────\n\n private assertState(expected: WidgetState, method: string): void {\n if (this.state !== expected) {\n throw new ChatSDKError(\n `Cannot call \\`${method}()\\` in state \"${this.state}\". Expected \"${expected}\".`\n );\n }\n }\n\n private assertNotState(forbidden: WidgetState, method: string): void {\n if (this.state === forbidden) {\n throw new ChatSDKError(\n `Cannot call \\`${method}()\\` in state \"${forbidden}\".`\n );\n }\n }\n\n // ─── Private: Helpers ────────────────────────────────────────────────────\n\n private resolveContainer(): HTMLElement | null {\n const { container } = this.config;\n if (!container) return null;\n if (typeof container === 'string') {\n return document.querySelector<HTMLElement>(container);\n }\n return container;\n }\n\n private buildWidgetUrl(): string {\n // If WIDGET_URL has search params already, URL() can parse it properly.\n // However, the base WIDGET_URL we use is clean `.../embeddable.html`\n const url = new URL(WIDGET_URL);\n \n // Core parameters\n url.searchParams.set('tnt', this.config.tenant);\n \n // Theme parameters\n if (typeof this.config.theme === 'object') {\n const t = this.config.theme;\n if (t.primaryColor) url.searchParams.set('color', t.primaryColor);\n if (t.textColor) url.searchParams.set('textColor', t.textColor);\n if (t.backgroundColor) url.searchParams.set('backgroundColor', t.backgroundColor);\n if (t.fontFamily) url.searchParams.set('fontFamily', t.fontFamily);\n if (t.borderRadius !== undefined) url.searchParams.set('borderRadius', String(t.borderRadius));\n }\n \n return url.toString();\n }\n}\n\n// ─── Re-exports ───────────────────────────────────────────────────────────────\n\nexport type {\n ChatSDKConfig,\n Theme,\n ThemeConfig,\n BubblePosition,\n WidgetState,\n} from './types.ts';\n"],"names":["STYLE_ID","CONTAINER_CLASS","IFRAME_CLASS","BUBBLE_CLASS","BUBBLE_OPEN_CLASS","injectStyles","css","style","removeInjectedStyles","createFloatingContainer","position","div","createInlineContainer","createIframeElement","src","iframe","createBubbleButton","button","chatIcon","closeIcon","showIframe","hideIframe","setBubbleOpen","isOpen","WIDGET_URL","ChatSDKError","message","ChatSDK","config","tenant","cb","host","wrapper","bubble","onBubbleClick","expected","method","forbidden","container","url","t"],"mappings":"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"}
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "@verba-ai/chat-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Lightweight TypeScript SDK for embedding the Verba AI chat widget via iframe.",
5
- "keywords": ["chat", "widget", "sdk", "iframe", "verba"],
5
+ "keywords": [
6
+ "chat",
7
+ "widget",
8
+ "sdk",
9
+ "iframe",
10
+ "verba"
11
+ ],
6
12
  "license": "MIT",
7
13
  "type": "module",
8
14
  "main": "./dist/chat-sdk.umd.cjs",
@@ -14,7 +20,9 @@
14
20
  "require": "./dist/chat-sdk.umd.cjs"
15
21
  }
16
22
  },
17
- "files": ["dist"],
23
+ "files": [
24
+ "dist"
25
+ ],
18
26
  "scripts": {
19
27
  "dev": "vite",
20
28
  "build:lib": "tsc --noEmit && vite build --config vite.config.ts",
@@ -25,12 +33,7 @@
25
33
  "devDependencies": {
26
34
  "@eslint/js": "^9.39.1",
27
35
  "@types/node": "^22.0.0",
28
- "@types/react": "^19.2.7",
29
- "@types/react-dom": "^19.2.3",
30
- "@vitejs/plugin-react": "^5.1.1",
31
36
  "eslint": "^9.39.1",
32
- "eslint-plugin-react-hooks": "^7.0.1",
33
- "eslint-plugin-react-refresh": "^0.4.24",
34
37
  "globals": "^16.5.0",
35
38
  "typescript": "^5.7.0",
36
39
  "vite": "^7.3.1",