@verba-ai/chat-sdk 1.0.0
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 +102 -0
- package/dist/chat-sdk.es.js +324 -0
- package/dist/chat-sdk.es.js.map +1 -0
- package/dist/chat-sdk.umd.cjs +149 -0
- package/dist/chat-sdk.umd.cjs.map +1 -0
- package/dist/types/index.d.ts +80 -0
- package/dist/types/types.d.ts +57 -0
- package/dist/types/ui.d.ts +31 -0
- package/dist/vite.svg +1 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Verba Chat Widget SDK
|
|
2
|
+
|
|
3
|
+
A lightweight, professional-grade TypeScript SDK designed to embed the Verba AI chat widget into any web application via a secure `<iframe>`.
|
|
4
|
+
|
|
5
|
+
## 1. Project Scope
|
|
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.
|
|
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).
|
|
17
|
+
|
|
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
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### File Explanations
|
|
38
|
+
|
|
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. |
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 3. Getting Started
|
|
51
|
+
|
|
52
|
+
### Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install @verba/chat-sdk
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Basic Usage (Floating)
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { ChatSDK } from '@verba/chat-sdk';
|
|
62
|
+
|
|
63
|
+
const chat = new ChatSDK({
|
|
64
|
+
tenant: 'your-tenant-id',
|
|
65
|
+
theme: 'dark'
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
chat.init(); // Injects the bubble
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Inline Usage
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
new ChatSDK({
|
|
75
|
+
tenant: 'your-tenant-id',
|
|
76
|
+
container: '#chat-container'
|
|
77
|
+
}).init();
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 4. Development
|
|
83
|
+
|
|
84
|
+
### Build the Library
|
|
85
|
+
To generate the production-ready bundles in the `dist/` directory:
|
|
86
|
+
```bash
|
|
87
|
+
npm run build:lib
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Start Demo Server
|
|
91
|
+
To view the interactive demo locally:
|
|
92
|
+
```bash
|
|
93
|
+
npm run dev
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
## 5. Testing
|
|
98
|
+
|
|
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. |
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
const l = "verba-chat-sdk-styles", a = "verba-widget-container", n = "verba-widget-iframe", s = "verba-widget-bubble", b = "verba-widget-bubble--open";
|
|
2
|
+
function d() {
|
|
3
|
+
if (document.getElementById(l)) return;
|
|
4
|
+
const i = `
|
|
5
|
+
/* ── Verba Chat SDK ── */
|
|
6
|
+
|
|
7
|
+
.${a} {
|
|
8
|
+
position: fixed;
|
|
9
|
+
bottom: 24px;
|
|
10
|
+
right: 24px;
|
|
11
|
+
z-index: 9999;
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
align-items: flex-end;
|
|
15
|
+
gap: 12px;
|
|
16
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.${a}[data-position="bottom-left"] {
|
|
20
|
+
right: unset;
|
|
21
|
+
left: 24px;
|
|
22
|
+
align-items: flex-start;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.${a}.verba-widget--inline {
|
|
26
|
+
position: static;
|
|
27
|
+
bottom: unset;
|
|
28
|
+
right: unset;
|
|
29
|
+
left: unset;
|
|
30
|
+
width: 100%;
|
|
31
|
+
height: 100%;
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: stretch;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.${n} {
|
|
37
|
+
border: none;
|
|
38
|
+
border-radius: 16px;
|
|
39
|
+
width: 380px;
|
|
40
|
+
height: 600px;
|
|
41
|
+
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.18), 0 8px 24px rgba(0, 0, 0, 0.12);
|
|
42
|
+
opacity: 0;
|
|
43
|
+
transform: translateY(12px) scale(0.97);
|
|
44
|
+
transform-origin: bottom right;
|
|
45
|
+
transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1),
|
|
46
|
+
transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
47
|
+
pointer-events: none;
|
|
48
|
+
background: transparent;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.${a}[data-position="bottom-left"] .${n} {
|
|
52
|
+
transform-origin: bottom left;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.${n}.verba-iframe--visible {
|
|
56
|
+
opacity: 1;
|
|
57
|
+
transform: translateY(0) scale(1);
|
|
58
|
+
pointer-events: all;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.${a}.verba-widget--inline .${n} {
|
|
62
|
+
width: 100%;
|
|
63
|
+
height: 100%;
|
|
64
|
+
border-radius: 0;
|
|
65
|
+
box-shadow: none;
|
|
66
|
+
opacity: 1;
|
|
67
|
+
transform: none;
|
|
68
|
+
pointer-events: all;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* ── Bubble Button ── */
|
|
72
|
+
|
|
73
|
+
.${s} {
|
|
74
|
+
width: 56px;
|
|
75
|
+
height: 56px;
|
|
76
|
+
border-radius: 50%;
|
|
77
|
+
border: none;
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
|
80
|
+
box-shadow: 0 4px 20px rgba(99, 102, 241, 0.45), 0 2px 6px rgba(0,0,0,0.15);
|
|
81
|
+
display: flex;
|
|
82
|
+
align-items: center;
|
|
83
|
+
justify-content: center;
|
|
84
|
+
transition: transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1),
|
|
85
|
+
box-shadow 0.22s ease;
|
|
86
|
+
flex-shrink: 0;
|
|
87
|
+
outline: none;
|
|
88
|
+
overflow: hidden;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.${s}:hover {
|
|
92
|
+
transform: scale(1.1);
|
|
93
|
+
box-shadow: 0 6px 28px rgba(99, 102, 241, 0.55), 0 2px 10px rgba(0,0,0,0.18);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.${s}:active {
|
|
97
|
+
transform: scale(0.95);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.${s} .verba-bubble-icon {
|
|
101
|
+
position: absolute;
|
|
102
|
+
transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.${s} .verba-bubble-icon--chat { opacity: 1; transform: scale(1) rotate(0deg); }
|
|
106
|
+
.${s} .verba-bubble-icon--close { opacity: 0; transform: scale(0.5) rotate(-90deg); }
|
|
107
|
+
|
|
108
|
+
.${s}.${b} .verba-bubble-icon--chat {
|
|
109
|
+
opacity: 0;
|
|
110
|
+
transform: scale(0.5) rotate(90deg);
|
|
111
|
+
}
|
|
112
|
+
.${s}.${b} .verba-bubble-icon--close {
|
|
113
|
+
opacity: 1;
|
|
114
|
+
transform: scale(1) rotate(0deg);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* ── Ripple pulse on load ── */
|
|
118
|
+
@keyframes verba-pulse {
|
|
119
|
+
0% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 0 rgba(99,102,241,0.4); }
|
|
120
|
+
70% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 14px rgba(99,102,241,0); }
|
|
121
|
+
100% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 0 rgba(99,102,241,0); }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.${s}.verba-bubble--pulse {
|
|
125
|
+
animation: verba-pulse 1.8s ease-out 0.4s 2;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ── Mobile ── */
|
|
129
|
+
@media (max-width: 480px) {
|
|
130
|
+
.${n} {
|
|
131
|
+
width: calc(100vw - 24px);
|
|
132
|
+
height: 75vh;
|
|
133
|
+
max-height: 600px;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.${a} {
|
|
137
|
+
right: 12px;
|
|
138
|
+
bottom: 12px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.${a}[data-position="bottom-left"] {
|
|
142
|
+
left: 12px;
|
|
143
|
+
right: unset;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
`, t = document.createElement("style");
|
|
147
|
+
t.id = l, t.textContent = i, document.head.appendChild(t);
|
|
148
|
+
}
|
|
149
|
+
function m() {
|
|
150
|
+
document.getElementById(l)?.remove();
|
|
151
|
+
}
|
|
152
|
+
function p(i) {
|
|
153
|
+
const t = document.createElement("div");
|
|
154
|
+
return t.className = a, t.dataset.position = i, t;
|
|
155
|
+
}
|
|
156
|
+
function f() {
|
|
157
|
+
const i = document.createElement("div");
|
|
158
|
+
return i.className = `${a} verba-widget--inline`, i.style.width = "100%", i.style.height = "100%", i;
|
|
159
|
+
}
|
|
160
|
+
function h(i) {
|
|
161
|
+
const t = document.createElement("iframe");
|
|
162
|
+
return t.className = n, t.src = i, t.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-popups"), t.setAttribute("allow", "microphone; camera"), t.setAttribute("loading", "lazy"), t.setAttribute("title", "Verba AI Chat Widget"), t.setAttribute("aria-label", "Chat support widget"), t;
|
|
163
|
+
}
|
|
164
|
+
function g() {
|
|
165
|
+
const i = document.createElement("button");
|
|
166
|
+
i.className = `${s} verba-bubble--pulse`, i.setAttribute("aria-label", "Open chat"), i.setAttribute("aria-expanded", "false"), i.setAttribute("type", "button");
|
|
167
|
+
const t = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
168
|
+
t.setAttribute("class", "verba-bubble-icon verba-bubble-icon--chat"), t.setAttribute("width", "26"), t.setAttribute("height", "26"), t.setAttribute("viewBox", "0 0 24 24"), t.setAttribute("fill", "none"), t.setAttribute("stroke", "#ffffff"), t.setAttribute("stroke-width", "2"), t.setAttribute("stroke-linecap", "round"), t.setAttribute("stroke-linejoin", "round"), t.innerHTML = `
|
|
169
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
|
170
|
+
`;
|
|
171
|
+
const e = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
172
|
+
return e.setAttribute("class", "verba-bubble-icon verba-bubble-icon--close"), e.setAttribute("width", "22"), e.setAttribute("height", "22"), e.setAttribute("viewBox", "0 0 24 24"), e.setAttribute("fill", "none"), e.setAttribute("stroke", "#ffffff"), e.setAttribute("stroke-width", "2.5"), e.setAttribute("stroke-linecap", "round"), e.innerHTML = `
|
|
173
|
+
<line x1="18" y1="6" x2="6" y2="18"/>
|
|
174
|
+
<line x1="6" y1="6" x2="18" y2="18"/>
|
|
175
|
+
`, i.appendChild(t), i.appendChild(e), i;
|
|
176
|
+
}
|
|
177
|
+
function x(i) {
|
|
178
|
+
requestAnimationFrame(() => {
|
|
179
|
+
i.classList.add("verba-iframe--visible");
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function w(i) {
|
|
183
|
+
i.classList.remove("verba-iframe--visible");
|
|
184
|
+
}
|
|
185
|
+
function u(i, t) {
|
|
186
|
+
i.classList.toggle(b, t), i.setAttribute("aria-label", t ? "Close chat" : "Open chat"), i.setAttribute("aria-expanded", String(t));
|
|
187
|
+
}
|
|
188
|
+
const v = "https://embed.verba.chat", y = `${v}/embeddable.html`;
|
|
189
|
+
class o extends Error {
|
|
190
|
+
constructor(t) {
|
|
191
|
+
super(`[VerbaChatSDK] ${t}`), this.name = "ChatSDKError";
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
class C {
|
|
195
|
+
// ── Config ─────────────────────────────────────────────────────────────────
|
|
196
|
+
config;
|
|
197
|
+
// ── State ──────────────────────────────────────────────────────────────────
|
|
198
|
+
state = "uninitialized";
|
|
199
|
+
isVisible = !1;
|
|
200
|
+
isInline = !1;
|
|
201
|
+
// ── DOM refs ───────────────────────────────────────────────────────────────
|
|
202
|
+
container = null;
|
|
203
|
+
iframe = null;
|
|
204
|
+
bubble = null;
|
|
205
|
+
// ── Cleanup ────────────────────────────────────────────────────────────────
|
|
206
|
+
cleanupCallbacks = [];
|
|
207
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
208
|
+
/**
|
|
209
|
+
* Create a new `ChatSDK` instance.
|
|
210
|
+
*
|
|
211
|
+
* @param config - SDK configuration. Only `tenant` is required.
|
|
212
|
+
*
|
|
213
|
+
* @throws {ChatSDKError} if `tenant` is empty.
|
|
214
|
+
*/
|
|
215
|
+
constructor(t) {
|
|
216
|
+
const e = t.tenant;
|
|
217
|
+
if (!e?.trim())
|
|
218
|
+
throw new o("`tenant` is required and must not be empty.");
|
|
219
|
+
this.config = {
|
|
220
|
+
tenant: e.trim(),
|
|
221
|
+
theme: t.theme ?? "light",
|
|
222
|
+
position: t.position ?? "bottom-right",
|
|
223
|
+
container: t.container,
|
|
224
|
+
userMetadata: t.userMetadata
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
// ─── Public API ───────────────────────────────────────────────────────────
|
|
228
|
+
/**
|
|
229
|
+
* Inject the widget into the DOM and establish the postMessage bridge.
|
|
230
|
+
*
|
|
231
|
+
* In **floating mode** a chat bubble is rendered in the viewport corner.
|
|
232
|
+
* In **inline mode** the iframe is mounted directly into the configured container.
|
|
233
|
+
*
|
|
234
|
+
* @throws {ChatSDKError} if called more than once or after `destroy()`.
|
|
235
|
+
*/
|
|
236
|
+
init() {
|
|
237
|
+
return this.assertState("uninitialized", "init"), d(), this.state = "ready", this.isInline = !!this.config.container, this.isInline ? this.mountInline() : this.mountFloating(), this;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Make the widget visible.
|
|
241
|
+
* In floating mode this opens the iframe above the bubble.
|
|
242
|
+
*
|
|
243
|
+
* @throws {ChatSDKError} if `init()` has not been called.
|
|
244
|
+
*/
|
|
245
|
+
show() {
|
|
246
|
+
return this.assertNotState("uninitialized", "show"), this.assertNotState("destroyed", "show"), this.isVisible || !this.iframe ? this : (this.isVisible = !0, this.isInline || (x(this.iframe), this.bubble && u(this.bubble, !0)), this);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Hide the widget (does not destroy it — all state is preserved).
|
|
250
|
+
*
|
|
251
|
+
* @throws {ChatSDKError} if `init()` has not been called.
|
|
252
|
+
*/
|
|
253
|
+
hide() {
|
|
254
|
+
return this.assertNotState("uninitialized", "hide"), this.assertNotState("destroyed", "hide"), !this.isVisible || !this.iframe ? this : (this.isVisible = !1, this.isInline || (w(this.iframe), this.bubble && u(this.bubble, !1)), this);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Completely tear down the SDK:
|
|
258
|
+
* - Removes all DOM elements it created.
|
|
259
|
+
* - Removes all event listeners.
|
|
260
|
+
* - Resets state to `'destroyed'`.
|
|
261
|
+
*
|
|
262
|
+
* After calling `destroy()` this instance cannot be reused — create a new one.
|
|
263
|
+
*/
|
|
264
|
+
destroy() {
|
|
265
|
+
if (this.state !== "destroyed") {
|
|
266
|
+
this.container?.remove(), this.container = null, this.iframe = null, this.bubble = null;
|
|
267
|
+
for (const t of this.cleanupCallbacks) t();
|
|
268
|
+
this.cleanupCallbacks.length = 0, m(), this.state = "destroyed", this.isVisible = !1;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/** Current lifecycle state of this SDK instance. */
|
|
272
|
+
get currentState() {
|
|
273
|
+
return this.state;
|
|
274
|
+
}
|
|
275
|
+
// ─── Private: Mount Strategies ───────────────────────────────────────────
|
|
276
|
+
mountInline() {
|
|
277
|
+
const t = this.resolveContainer();
|
|
278
|
+
if (!t) {
|
|
279
|
+
console.error("[VerbaChatSDK] Container element not found. Falling back to floating mode."), this.isInline = !1, this.mountFloating();
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const e = f(), r = h(this.buildWidgetUrl());
|
|
283
|
+
e.appendChild(r), t.appendChild(e), this.container = e, this.iframe = r, this.isVisible = !0;
|
|
284
|
+
}
|
|
285
|
+
mountFloating() {
|
|
286
|
+
const t = p(this.config.position ?? "bottom-right"), e = h(this.buildWidgetUrl()), r = g(), c = () => {
|
|
287
|
+
this.isVisible ? this.hide() : this.show();
|
|
288
|
+
};
|
|
289
|
+
r.addEventListener("click", c), this.cleanupCallbacks.push(
|
|
290
|
+
() => r.removeEventListener("click", c)
|
|
291
|
+
), t.appendChild(e), t.appendChild(r), document.body.appendChild(t), this.container = t, this.iframe = e, this.bubble = r, this.isVisible = !1;
|
|
292
|
+
}
|
|
293
|
+
// ─── Private: State Guards ───────────────────────────────────────────────
|
|
294
|
+
assertState(t, e) {
|
|
295
|
+
if (this.state !== t)
|
|
296
|
+
throw new o(
|
|
297
|
+
`Cannot call \`${e}()\` in state "${this.state}". Expected "${t}".`
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
assertNotState(t, e) {
|
|
301
|
+
if (this.state === t)
|
|
302
|
+
throw new o(
|
|
303
|
+
`Cannot call \`${e}()\` in state "${t}".`
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
// ─── Private: Helpers ────────────────────────────────────────────────────
|
|
307
|
+
resolveContainer() {
|
|
308
|
+
const { container: t } = this.config;
|
|
309
|
+
return t ? typeof t == "string" ? document.querySelector(t) : t : null;
|
|
310
|
+
}
|
|
311
|
+
buildWidgetUrl() {
|
|
312
|
+
const t = new URL(y);
|
|
313
|
+
if (t.searchParams.set("tnt", this.config.tenant), typeof this.config.theme == "object") {
|
|
314
|
+
const e = this.config.theme;
|
|
315
|
+
e.primaryColor && t.searchParams.set("color", e.primaryColor), e.textColor && t.searchParams.set("textColor", e.textColor), e.backgroundColor && t.searchParams.set("backgroundColor", e.backgroundColor), e.fontFamily && t.searchParams.set("fontFamily", e.fontFamily), e.borderRadius !== void 0 && t.searchParams.set("borderRadius", String(e.borderRadius));
|
|
316
|
+
}
|
|
317
|
+
return t.toString();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
export {
|
|
321
|
+
C as ChatSDK,
|
|
322
|
+
o as ChatSDKError
|
|
323
|
+
};
|
|
324
|
+
//# sourceMappingURL=chat-sdk.es.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
(function(a,r){typeof exports=="object"&&typeof module<"u"?r(exports):typeof define=="function"&&define.amd?define(["exports"],r):(a=typeof globalThis<"u"?globalThis:a||self,r(a.VerbaChatSDK={}))})(this,(function(a){"use strict";const r="verba-chat-sdk-styles",n="verba-widget-container",l="verba-widget-iframe",s="verba-widget-bubble",c="verba-widget-bubble--open";function f(){if(document.getElementById(r))return;const i=`
|
|
2
|
+
/* ── Verba Chat SDK ── */
|
|
3
|
+
|
|
4
|
+
.${n} {
|
|
5
|
+
position: fixed;
|
|
6
|
+
bottom: 24px;
|
|
7
|
+
right: 24px;
|
|
8
|
+
z-index: 9999;
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
align-items: flex-end;
|
|
12
|
+
gap: 12px;
|
|
13
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.${n}[data-position="bottom-left"] {
|
|
17
|
+
right: unset;
|
|
18
|
+
left: 24px;
|
|
19
|
+
align-items: flex-start;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.${n}.verba-widget--inline {
|
|
23
|
+
position: static;
|
|
24
|
+
bottom: unset;
|
|
25
|
+
right: unset;
|
|
26
|
+
left: unset;
|
|
27
|
+
width: 100%;
|
|
28
|
+
height: 100%;
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: stretch;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.${l} {
|
|
34
|
+
border: none;
|
|
35
|
+
border-radius: 16px;
|
|
36
|
+
width: 380px;
|
|
37
|
+
height: 600px;
|
|
38
|
+
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.18), 0 8px 24px rgba(0, 0, 0, 0.12);
|
|
39
|
+
opacity: 0;
|
|
40
|
+
transform: translateY(12px) scale(0.97);
|
|
41
|
+
transform-origin: bottom right;
|
|
42
|
+
transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1),
|
|
43
|
+
transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
44
|
+
pointer-events: none;
|
|
45
|
+
background: transparent;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.${n}[data-position="bottom-left"] .${l} {
|
|
49
|
+
transform-origin: bottom left;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.${l}.verba-iframe--visible {
|
|
53
|
+
opacity: 1;
|
|
54
|
+
transform: translateY(0) scale(1);
|
|
55
|
+
pointer-events: all;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.${n}.verba-widget--inline .${l} {
|
|
59
|
+
width: 100%;
|
|
60
|
+
height: 100%;
|
|
61
|
+
border-radius: 0;
|
|
62
|
+
box-shadow: none;
|
|
63
|
+
opacity: 1;
|
|
64
|
+
transform: none;
|
|
65
|
+
pointer-events: all;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* ── Bubble Button ── */
|
|
69
|
+
|
|
70
|
+
.${s} {
|
|
71
|
+
width: 56px;
|
|
72
|
+
height: 56px;
|
|
73
|
+
border-radius: 50%;
|
|
74
|
+
border: none;
|
|
75
|
+
cursor: pointer;
|
|
76
|
+
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
|
77
|
+
box-shadow: 0 4px 20px rgba(99, 102, 241, 0.45), 0 2px 6px rgba(0,0,0,0.15);
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
justify-content: center;
|
|
81
|
+
transition: transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1),
|
|
82
|
+
box-shadow 0.22s ease;
|
|
83
|
+
flex-shrink: 0;
|
|
84
|
+
outline: none;
|
|
85
|
+
overflow: hidden;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.${s}:hover {
|
|
89
|
+
transform: scale(1.1);
|
|
90
|
+
box-shadow: 0 6px 28px rgba(99, 102, 241, 0.55), 0 2px 10px rgba(0,0,0,0.18);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.${s}:active {
|
|
94
|
+
transform: scale(0.95);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.${s} .verba-bubble-icon {
|
|
98
|
+
position: absolute;
|
|
99
|
+
transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.${s} .verba-bubble-icon--chat { opacity: 1; transform: scale(1) rotate(0deg); }
|
|
103
|
+
.${s} .verba-bubble-icon--close { opacity: 0; transform: scale(0.5) rotate(-90deg); }
|
|
104
|
+
|
|
105
|
+
.${s}.${c} .verba-bubble-icon--chat {
|
|
106
|
+
opacity: 0;
|
|
107
|
+
transform: scale(0.5) rotate(90deg);
|
|
108
|
+
}
|
|
109
|
+
.${s}.${c} .verba-bubble-icon--close {
|
|
110
|
+
opacity: 1;
|
|
111
|
+
transform: scale(1) rotate(0deg);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* ── Ripple pulse on load ── */
|
|
115
|
+
@keyframes verba-pulse {
|
|
116
|
+
0% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 0 rgba(99,102,241,0.4); }
|
|
117
|
+
70% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 14px rgba(99,102,241,0); }
|
|
118
|
+
100% { box-shadow: 0 4px 20px rgba(99,102,241,0.45), 0 0 0 0 rgba(99,102,241,0); }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.${s}.verba-bubble--pulse {
|
|
122
|
+
animation: verba-pulse 1.8s ease-out 0.4s 2;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* ── Mobile ── */
|
|
126
|
+
@media (max-width: 480px) {
|
|
127
|
+
.${l} {
|
|
128
|
+
width: calc(100vw - 24px);
|
|
129
|
+
height: 75vh;
|
|
130
|
+
max-height: 600px;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.${n} {
|
|
134
|
+
right: 12px;
|
|
135
|
+
bottom: 12px;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.${n}[data-position="bottom-left"] {
|
|
139
|
+
left: 12px;
|
|
140
|
+
right: unset;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
`,t=document.createElement("style");t.id=r,t.textContent=i,document.head.appendChild(t)}function p(){document.getElementById(r)?.remove()}function m(i){const t=document.createElement("div");return t.className=n,t.dataset.position=i,t}function g(){const i=document.createElement("div");return i.className=`${n} verba-widget--inline`,i.style.width="100%",i.style.height="100%",i}function h(i){const t=document.createElement("iframe");return t.className=l,t.src=i,t.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups"),t.setAttribute("allow","microphone; camera"),t.setAttribute("loading","lazy"),t.setAttribute("title","Verba AI Chat Widget"),t.setAttribute("aria-label","Chat support widget"),t}function x(){const i=document.createElement("button");i.className=`${s} verba-bubble--pulse`,i.setAttribute("aria-label","Open chat"),i.setAttribute("aria-expanded","false"),i.setAttribute("type","button");const t=document.createElementNS("http://www.w3.org/2000/svg","svg");t.setAttribute("class","verba-bubble-icon verba-bubble-icon--chat"),t.setAttribute("width","26"),t.setAttribute("height","26"),t.setAttribute("viewBox","0 0 24 24"),t.setAttribute("fill","none"),t.setAttribute("stroke","#ffffff"),t.setAttribute("stroke-width","2"),t.setAttribute("stroke-linecap","round"),t.setAttribute("stroke-linejoin","round"),t.innerHTML=`
|
|
144
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
|
145
|
+
`;const e=document.createElementNS("http://www.w3.org/2000/svg","svg");return e.setAttribute("class","verba-bubble-icon verba-bubble-icon--close"),e.setAttribute("width","22"),e.setAttribute("height","22"),e.setAttribute("viewBox","0 0 24 24"),e.setAttribute("fill","none"),e.setAttribute("stroke","#ffffff"),e.setAttribute("stroke-width","2.5"),e.setAttribute("stroke-linecap","round"),e.innerHTML=`
|
|
146
|
+
<line x1="18" y1="6" x2="6" y2="18"/>
|
|
147
|
+
<line x1="6" y1="6" x2="18" y2="18"/>
|
|
148
|
+
`,i.appendChild(t),i.appendChild(e),i}function w(i){requestAnimationFrame(()=>{i.classList.add("verba-iframe--visible")})}function v(i){i.classList.remove("verba-iframe--visible")}function u(i,t){i.classList.toggle(c,t),i.setAttribute("aria-label",t?"Close chat":"Open chat"),i.setAttribute("aria-expanded",String(t))}const y="https://embed.verba.chat/embeddable.html";class b extends Error{constructor(t){super(`[VerbaChatSDK] ${t}`),this.name="ChatSDKError"}}class C{config;state="uninitialized";isVisible=!1;isInline=!1;container=null;iframe=null;bubble=null;cleanupCallbacks=[];constructor(t){const e=t.tenant;if(!e?.trim())throw new b("`tenant` is required and must not be empty.");this.config={tenant:e.trim(),theme:t.theme??"light",position:t.position??"bottom-right",container:t.container,userMetadata:t.userMetadata}}init(){return this.assertState("uninitialized","init"),f(),this.state="ready",this.isInline=!!this.config.container,this.isInline?this.mountInline():this.mountFloating(),this}show(){return this.assertNotState("uninitialized","show"),this.assertNotState("destroyed","show"),this.isVisible||!this.iframe?this:(this.isVisible=!0,this.isInline||(w(this.iframe),this.bubble&&u(this.bubble,!0)),this)}hide(){return this.assertNotState("uninitialized","hide"),this.assertNotState("destroyed","hide"),!this.isVisible||!this.iframe?this:(this.isVisible=!1,this.isInline||(v(this.iframe),this.bubble&&u(this.bubble,!1)),this)}destroy(){if(this.state!=="destroyed"){this.container?.remove(),this.container=null,this.iframe=null,this.bubble=null;for(const t of this.cleanupCallbacks)t();this.cleanupCallbacks.length=0,p(),this.state="destroyed",this.isVisible=!1}}get currentState(){return this.state}mountInline(){const t=this.resolveContainer();if(!t){console.error("[VerbaChatSDK] Container element not found. Falling back to floating mode."),this.isInline=!1,this.mountFloating();return}const e=g(),o=h(this.buildWidgetUrl());e.appendChild(o),t.appendChild(e),this.container=e,this.iframe=o,this.isVisible=!0}mountFloating(){const t=m(this.config.position??"bottom-right"),e=h(this.buildWidgetUrl()),o=x(),d=()=>{this.isVisible?this.hide():this.show()};o.addEventListener("click",d),this.cleanupCallbacks.push(()=>o.removeEventListener("click",d)),t.appendChild(e),t.appendChild(o),document.body.appendChild(t),this.container=t,this.iframe=e,this.bubble=o,this.isVisible=!1}assertState(t,e){if(this.state!==t)throw new b(`Cannot call \`${e}()\` in state "${this.state}". Expected "${t}".`)}assertNotState(t,e){if(this.state===t)throw new b(`Cannot call \`${e}()\` in state "${t}".`)}resolveContainer(){const{container:t}=this.config;return t?typeof t=="string"?document.querySelector(t):t:null}buildWidgetUrl(){const t=new URL(y);if(t.searchParams.set("tnt",this.config.tenant),typeof this.config.theme=="object"){const e=this.config.theme;e.primaryColor&&t.searchParams.set("color",e.primaryColor),e.textColor&&t.searchParams.set("textColor",e.textColor),e.backgroundColor&&t.searchParams.set("backgroundColor",e.backgroundColor),e.fontFamily&&t.searchParams.set("fontFamily",e.fontFamily),e.borderRadius!==void 0&&t.searchParams.set("borderRadius",String(e.borderRadius))}return t.toString()}}a.ChatSDK=C,a.ChatSDKError=b,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
|
|
149
|
+
//# sourceMappingURL=chat-sdk.umd.cjs.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ChatSDKConfig, WidgetState } from './types.ts';
|
|
2
|
+
/** Thrown when an SDK method is called in an invalid lifecycle state. */
|
|
3
|
+
export declare class ChatSDKError extends Error {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Main class for the Verba Chat Widget SDK.
|
|
8
|
+
*
|
|
9
|
+
* ### Lifecycle
|
|
10
|
+
* ```
|
|
11
|
+
* new ChatSDK(config) → .init() → .show() / .hide() → .destroy()
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* ### Floating mode (default)
|
|
15
|
+
* When no `container` is provided a floating bubble button is created in the
|
|
16
|
+
* bottom-right corner of the viewport. Clicking the bubble opens/closes the
|
|
17
|
+
* iframe with a smooth CSS animation.
|
|
18
|
+
*
|
|
19
|
+
* ### Inline mode
|
|
20
|
+
* When `container` is a CSS selector or `HTMLElement`, the iframe is mounted
|
|
21
|
+
* directly inside that element and the bubble button is omitted.
|
|
22
|
+
*/
|
|
23
|
+
export declare class ChatSDK {
|
|
24
|
+
private readonly config;
|
|
25
|
+
private state;
|
|
26
|
+
private isVisible;
|
|
27
|
+
private isInline;
|
|
28
|
+
private container;
|
|
29
|
+
private iframe;
|
|
30
|
+
private bubble;
|
|
31
|
+
private readonly cleanupCallbacks;
|
|
32
|
+
/**
|
|
33
|
+
* Create a new `ChatSDK` instance.
|
|
34
|
+
*
|
|
35
|
+
* @param config - SDK configuration. Only `tenant` is required.
|
|
36
|
+
*
|
|
37
|
+
* @throws {ChatSDKError} if `tenant` is empty.
|
|
38
|
+
*/
|
|
39
|
+
constructor(config: ChatSDKConfig);
|
|
40
|
+
/**
|
|
41
|
+
* Inject the widget into the DOM and establish the postMessage bridge.
|
|
42
|
+
*
|
|
43
|
+
* In **floating mode** a chat bubble is rendered in the viewport corner.
|
|
44
|
+
* In **inline mode** the iframe is mounted directly into the configured container.
|
|
45
|
+
*
|
|
46
|
+
* @throws {ChatSDKError} if called more than once or after `destroy()`.
|
|
47
|
+
*/
|
|
48
|
+
init(): this;
|
|
49
|
+
/**
|
|
50
|
+
* Make the widget visible.
|
|
51
|
+
* In floating mode this opens the iframe above the bubble.
|
|
52
|
+
*
|
|
53
|
+
* @throws {ChatSDKError} if `init()` has not been called.
|
|
54
|
+
*/
|
|
55
|
+
show(): this;
|
|
56
|
+
/**
|
|
57
|
+
* Hide the widget (does not destroy it — all state is preserved).
|
|
58
|
+
*
|
|
59
|
+
* @throws {ChatSDKError} if `init()` has not been called.
|
|
60
|
+
*/
|
|
61
|
+
hide(): this;
|
|
62
|
+
/**
|
|
63
|
+
* Completely tear down the SDK:
|
|
64
|
+
* - Removes all DOM elements it created.
|
|
65
|
+
* - Removes all event listeners.
|
|
66
|
+
* - Resets state to `'destroyed'`.
|
|
67
|
+
*
|
|
68
|
+
* After calling `destroy()` this instance cannot be reused — create a new one.
|
|
69
|
+
*/
|
|
70
|
+
destroy(): void;
|
|
71
|
+
/** Current lifecycle state of this SDK instance. */
|
|
72
|
+
get currentState(): WidgetState;
|
|
73
|
+
private mountInline;
|
|
74
|
+
private mountFloating;
|
|
75
|
+
private assertState;
|
|
76
|
+
private assertNotState;
|
|
77
|
+
private resolveContainer;
|
|
78
|
+
private buildWidgetUrl;
|
|
79
|
+
}
|
|
80
|
+
export type { ChatSDKConfig, Theme, ThemeConfig, BubblePosition, WidgetState, } from './types.ts';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @verba/chat-sdk/types
|
|
3
|
+
* All public-facing types and interfaces for the Verba Chat Widget SDK.
|
|
4
|
+
*/
|
|
5
|
+
/** Visual theme passed to the embedded widget. */
|
|
6
|
+
export type Theme = 'light' | 'dark';
|
|
7
|
+
/** Advanced visual theme configuration passed via URL parameters. */
|
|
8
|
+
export interface ThemeConfig {
|
|
9
|
+
primaryColor?: string;
|
|
10
|
+
textColor?: string;
|
|
11
|
+
backgroundColor?: string;
|
|
12
|
+
fontFamily?: string;
|
|
13
|
+
borderRadius?: string | number;
|
|
14
|
+
}
|
|
15
|
+
/** Position of the floating chat bubble (only relevant when no container is provided). */
|
|
16
|
+
export type BubblePosition = 'bottom-right' | 'bottom-left';
|
|
17
|
+
/**
|
|
18
|
+
* Configuration object accepted by the `ChatSDK` constructor.
|
|
19
|
+
*/
|
|
20
|
+
export interface ChatSDKConfig {
|
|
21
|
+
/**
|
|
22
|
+
* Your Verba tenant ID — passed via the `tnt` URL parameter.
|
|
23
|
+
*/
|
|
24
|
+
tenant: string;
|
|
25
|
+
/**
|
|
26
|
+
* Visual theme of the widget.
|
|
27
|
+
* Can be a simple string ('light' | 'dark') or an object for detailed customisation.
|
|
28
|
+
* @default 'light'
|
|
29
|
+
*/
|
|
30
|
+
theme?: Theme | ThemeConfig;
|
|
31
|
+
/**
|
|
32
|
+
* Where to mount the widget iframe.
|
|
33
|
+
* - A CSS selector string (e.g. `'#chat-root'`)
|
|
34
|
+
* - An `HTMLElement` reference
|
|
35
|
+
* - Omit for a **floating bubble** fixed to the viewport corner.
|
|
36
|
+
*/
|
|
37
|
+
container?: string | HTMLElement;
|
|
38
|
+
/**
|
|
39
|
+
* Corner position of the floating bubble.
|
|
40
|
+
* Has no effect when `container` is provided.
|
|
41
|
+
* @default 'bottom-right'
|
|
42
|
+
*/
|
|
43
|
+
position?: BubblePosition;
|
|
44
|
+
/**
|
|
45
|
+
* Arbitrary key/value metadata forwarded to the widget in the INIT_WIDGET message.
|
|
46
|
+
* Use this to pass user profile data, locale, etc.
|
|
47
|
+
*/
|
|
48
|
+
userMetadata?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Lifecycle state of the SDK instance.
|
|
52
|
+
* Follows the state machine:
|
|
53
|
+
* uninitialized → ready
|
|
54
|
+
* ↓
|
|
55
|
+
* destroyed
|
|
56
|
+
*/
|
|
57
|
+
export type WidgetState = 'uninitialized' | 'ready' | 'destroyed';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { BubblePosition } from './types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Injects a `<style>` tag into the document `<head>` with all widget styles.
|
|
4
|
+
* Idempotent — safe to call multiple times.
|
|
5
|
+
*/
|
|
6
|
+
export declare function injectStyles(): void;
|
|
7
|
+
/** Removes the injected `<style>` tag from the document. */
|
|
8
|
+
export declare function removeInjectedStyles(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Creates the fixed-position outer container div used in floating mode.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createFloatingContainer(position: BubblePosition): HTMLDivElement;
|
|
13
|
+
/**
|
|
14
|
+
* Creates an inline container — wrapper inside a user-supplied element.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createInlineContainer(): HTMLDivElement;
|
|
17
|
+
/**
|
|
18
|
+
* Creates the `<iframe>` element with correct sandbox + security attributes.
|
|
19
|
+
* The iframe starts **hidden** — call `showIframe()` to animate it in.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createIframeElement(src: string): HTMLIFrameElement;
|
|
22
|
+
/**
|
|
23
|
+
* Creates the floating bubble toggle button with animated chat / close icons (pure SVG).
|
|
24
|
+
*/
|
|
25
|
+
export declare function createBubbleButton(): HTMLButtonElement;
|
|
26
|
+
/** Animate the iframe into view. */
|
|
27
|
+
export declare function showIframe(iframe: HTMLIFrameElement): void;
|
|
28
|
+
/** Animate the iframe out of view (hides it). */
|
|
29
|
+
export declare function hideIframe(iframe: HTMLIFrameElement): void;
|
|
30
|
+
/** Toggle bubble button's open/close icon state. */
|
|
31
|
+
export declare function setBubbleOpen(button: HTMLButtonElement, isOpen: boolean): void;
|
package/dist/vite.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@verba-ai/chat-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Lightweight TypeScript SDK for embedding the Verba AI chat widget via iframe.",
|
|
5
|
+
"keywords": ["chat", "widget", "sdk", "iframe", "verba"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/chat-sdk.umd.cjs",
|
|
9
|
+
"module": "./dist/chat-sdk.es.js",
|
|
10
|
+
"types": "./dist/types/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/chat-sdk.es.js",
|
|
14
|
+
"require": "./dist/chat-sdk.umd.cjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": ["dist"],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "vite",
|
|
20
|
+
"build:lib": "tsc --noEmit && vite build --config vite.config.ts",
|
|
21
|
+
"build": "vite build --config vite.config.ts",
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"preview": "vite preview"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@eslint/js": "^9.39.1",
|
|
27
|
+
"@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
|
+
"eslint": "^9.39.1",
|
|
32
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
33
|
+
"eslint-plugin-react-refresh": "^0.4.24",
|
|
34
|
+
"globals": "^16.5.0",
|
|
35
|
+
"typescript": "^5.7.0",
|
|
36
|
+
"vite": "^7.3.1",
|
|
37
|
+
"vite-plugin-dts": "^4.5.4"
|
|
38
|
+
}
|
|
39
|
+
}
|