avin-ai 0.1.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.
@@ -0,0 +1,575 @@
1
+ var u = Object.defineProperty;
2
+ var b = (a, e, t) => e in a ? u(a, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[e] = t;
3
+ var r = (a, e, t) => b(a, typeof e != "symbol" ? e + "" : e, t);
4
+ class f {
5
+ constructor(e) {
6
+ r(this, "serverUrl");
7
+ r(this, "agentId");
8
+ r(this, "callId");
9
+ r(this, "pc", null);
10
+ r(this, "dataChannel", null);
11
+ r(this, "audioElement", null);
12
+ r(this, "connected", !1);
13
+ r(this, "onConnected");
14
+ r(this, "onDisconnected");
15
+ r(this, "onError");
16
+ r(this, "onTranscription");
17
+ if (!e.serverUrl) throw new Error("serverUrl is required");
18
+ if (!e.agentId) throw new Error("agentId is required");
19
+ this.serverUrl = e.serverUrl.replace(/\/$/, ""), this.agentId = e.agentId, this.callId = this.generateCallId(), this.onConnected = e.onConnected || (() => {
20
+ }), this.onDisconnected = e.onDisconnected || (() => {
21
+ }), this.onError = e.onError || ((t) => console.error("[WebRTC]", t)), this.onTranscription = e.onTranscription || (() => {
22
+ });
23
+ }
24
+ async connect() {
25
+ console.log("đŸ”ĩ [WebRTC] Starting connection...");
26
+ try {
27
+ this.pc = new RTCPeerConnection({
28
+ iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
29
+ }), this.pc.addTransceiver("video", { direction: "recvonly" });
30
+ const e = await navigator.mediaDevices.getUserMedia({
31
+ audio: {
32
+ echoCancellation: !0,
33
+ noiseSuppression: !0,
34
+ autoGainControl: !0,
35
+ sampleRate: { ideal: 16e3 },
36
+ channelCount: 1
37
+ }
38
+ });
39
+ console.log("🎤 [WebRTC] Microphone access granted"), e.getTracks().forEach((i) => {
40
+ this.pc && this.pc.addTrack(i, e);
41
+ }), this.dataChannel = this.pc.createDataChannel("control"), this.dataChannel.onopen = () => console.log("📡 [WebRTC] DataChannel opened"), this.dataChannel.onmessage = (i) => this.handleControlEvent(JSON.parse(i.data)), this.dataChannel.onerror = (i) => console.error("❌ [WebRTC] DataChannel error:", i), this.pc.ontrack = (i) => {
42
+ const s = i.track, c = i.streams[0];
43
+ console.log(`đŸ“Ĩ [WebRTC] Received ${s.kind} track`), s.kind === "audio" && (console.log("🔊 [WebRTC] Setting up audio playback"), this.audioElement = new Audio(), this.audioElement.srcObject = c, this.audioElement.play().catch((l) => console.warn("Audio autoplay blocked:", l)), this.audioElement.onended = () => {
44
+ this.sendEvent("playedStream");
45
+ });
46
+ }, this.pc.onconnectionstatechange = () => {
47
+ var i, s, c, l, h;
48
+ console.log("🔄 [WebRTC] State:", (i = this.pc) == null ? void 0 : i.connectionState), ((s = this.pc) == null ? void 0 : s.connectionState) === "connected" ? (this.connected = !0, this.onConnected()) : (((c = this.pc) == null ? void 0 : c.connectionState) === "failed" || ((l = this.pc) == null ? void 0 : l.connectionState) === "disconnected" || ((h = this.pc) == null ? void 0 : h.connectionState) === "closed") && (this.connected = !1, this.onDisconnected());
49
+ };
50
+ const t = await this.pc.createOffer();
51
+ await this.pc.setLocalDescription(t), console.log("📝 [WebRTC] Created offer, waiting for ICE gathering..."), await this.waitForIceGathering(), console.log("🧊 [WebRTC] ICE gathering complete"), console.log("📤 [WebRTC] Sending offer via HTTP...");
52
+ const n = await fetch(`${this.serverUrl}/webrtc?agent=${this.agentId}_${this.callId}`, {
53
+ method: "POST",
54
+ headers: { "Content-Type": "application/json" },
55
+ body: JSON.stringify({
56
+ offer: this.pc.localDescription,
57
+ agentId: this.agentId,
58
+ callId: this.callId
59
+ })
60
+ });
61
+ if (!n.ok) {
62
+ const i = await n.json();
63
+ throw new Error(i.error || `HTTP ${n.status}`);
64
+ }
65
+ const { answer: o } = await n.json();
66
+ console.log("đŸ“Ĩ [WebRTC] Received answer from server"), await this.pc.setRemoteDescription(o), console.log("✅ [WebRTC] Connection established!");
67
+ } catch (e) {
68
+ throw console.error("❌ [WebRTC] Connection failed:", e), this.onError(e.message || e), e;
69
+ }
70
+ }
71
+ waitForIceGathering() {
72
+ return new Promise((e) => {
73
+ if (!this.pc) return e();
74
+ if (this.pc.iceGatheringState === "complete")
75
+ e();
76
+ else {
77
+ const t = () => {
78
+ var n;
79
+ ((n = this.pc) == null ? void 0 : n.iceGatheringState) === "complete" && (this.pc.removeEventListener("icegatheringstatechange", t), e());
80
+ };
81
+ this.pc.addEventListener("icegatheringstatechange", t), setTimeout(() => {
82
+ var n;
83
+ (n = this.pc) == null || n.removeEventListener("icegatheringstatechange", t), console.warn("âš ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"), e();
84
+ }, 5e3);
85
+ }
86
+ });
87
+ }
88
+ sendEvent(e, t = {}) {
89
+ var n;
90
+ ((n = this.dataChannel) == null ? void 0 : n.readyState) === "open" && this.dataChannel.send(JSON.stringify({ event: e, ...t }));
91
+ }
92
+ handleControlEvent(e) {
93
+ switch (e.event) {
94
+ case "clearAudio":
95
+ console.log("🛑 [WebRTC] Interrupt: stopping audio"), this.audioElement && (this.audioElement.pause(), this.audioElement.currentTime = 0);
96
+ break;
97
+ case "transcription":
98
+ console.log("📝 [WebRTC] Transcription:", e.text), e.text && this.onTranscription(e.text, !!e.isFinal);
99
+ break;
100
+ case "mark":
101
+ console.log("đŸˇī¸ [WebRTC] Mark:", e.name);
102
+ break;
103
+ default:
104
+ console.log("â„šī¸ [WebRTC] Unknown event:", e.event);
105
+ }
106
+ }
107
+ disconnect() {
108
+ console.log("🔴 [WebRTC] Disconnecting..."), this.audioElement && (this.audioElement.pause(), this.audioElement.srcObject = null), this.dataChannel && this.dataChannel.close(), this.pc && this.pc.close(), this.connected = !1, this.onDisconnected();
109
+ }
110
+ generateCallId() {
111
+ return "web_" + Date.now() + "_" + Math.random().toString(36).substr(2, 8);
112
+ }
113
+ }
114
+ const w = "https://coredb.travelr.club/v1/graphql";
115
+ async function v(a) {
116
+ var t;
117
+ const e = `
118
+ query GetWidgetConfig($id: uuid!) {
119
+ app_widget_by_pk(id: $id) {
120
+ agent_id
121
+ client_id
122
+ agent {
123
+ tts_voice {
124
+ provider {
125
+ root_name
126
+ provider_country
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+ `;
133
+ try {
134
+ const i = (t = (await (await fetch(w, {
135
+ method: "POST",
136
+ headers: {
137
+ "Content-Type": "application/json"
138
+ },
139
+ body: JSON.stringify({
140
+ query: e,
141
+ variables: { id: a }
142
+ })
143
+ })).json()).data) == null ? void 0 : t.app_widget_by_pk;
144
+ if (!i)
145
+ throw new Error(`Widget configuration not found for ID: ${a}`);
146
+ return i;
147
+ } catch (n) {
148
+ throw console.error("[AvinAI] Failed to fetch widget config:", n), n;
149
+ }
150
+ }
151
+ const d = {
152
+ phone: `
153
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
154
+ <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/>
155
+ </svg>
156
+ `,
157
+ close: `
158
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
159
+ <line x1="18" y1="6" x2="6" y2="18"/>
160
+ <line x1="6" y1="6" x2="18" y2="18"/>
161
+ </svg>
162
+ `,
163
+ mic: `
164
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
165
+ <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3z"/>
166
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
167
+ <line x1="12" y1="19" x2="12" y2="22"/>
168
+ </svg>
169
+ `,
170
+ micOff: `
171
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
172
+ <line x1="1" y1="1" x2="23" y2="23"/>
173
+ <path d="M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6"/>
174
+ <path d="M17 16.95A7 7 0 0 1 5 12v-2m14 0v2a7 7 0 0 1-.11 1.23"/>
175
+ <line x1="12" y1="19" x2="12" y2="23"/>
176
+ </svg>
177
+ `,
178
+ waves: `
179
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
180
+ <path d="M2 6c.6.5 1.2 1 2.5 1C7 7 7 5 9.5 5c2.6 0 2.4 2 5 2 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"/>
181
+ <path d="M2 12c.6.5 1.2 1 2.5 1 2.5 0 2.5-2 5-2 2.6 0 2.4 2 5 2 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"/>
182
+ <path d="M2 18c.6.5 1.2 1 2.5 1 2.5 0 2.5-2 5-2 2.6 0 2.4 2 5 2 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"/>
183
+ </svg>
184
+ `
185
+ }, m = `
186
+ * {
187
+ box-sizing: border-box;
188
+ }
189
+
190
+ :host {
191
+ --primary: #6366f1;
192
+ --primary-hover: #4f46e5;
193
+ --bg: #ffffff;
194
+ --text: #1f2937;
195
+ --text-muted: #6b7280;
196
+ --border: #e5e7eb;
197
+ --shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
198
+ --shadow-lg: 0 20px 60px rgba(0, 0, 0, 0.15);
199
+ }
200
+
201
+ .widget-fab {
202
+ position: fixed;
203
+ z-index: 9999;
204
+ width: 64px;
205
+ height: 64px;
206
+ border-radius: 50%;
207
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-hover) 100%);
208
+ border: none;
209
+ cursor: pointer;
210
+ display: flex;
211
+ align-items: center;
212
+ justify-content: center;
213
+ box-shadow: var(--shadow);
214
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
215
+ animation: fadeInScale 0.4s ease-out;
216
+ }
217
+
218
+ .widget-fab:hover {
219
+ transform: scale(1.1);
220
+ box-shadow: var(--shadow-lg);
221
+ }
222
+
223
+ .widget-fab:active {
224
+ transform: scale(0.95);
225
+ }
226
+
227
+ .widget-fab svg {
228
+ width: 28px;
229
+ height: 28px;
230
+ color: white;
231
+ }
232
+
233
+ .widget-panel {
234
+ position: fixed;
235
+ z-index: 9998;
236
+ background: var(--bg);
237
+ border-radius: 20px;
238
+ box-shadow: var(--shadow-lg);
239
+ overflow: hidden;
240
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
241
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
242
+ }
243
+
244
+ .widget-panel.hidden {
245
+ opacity: 0;
246
+ pointer-events: none;
247
+ transform: scale(0.8);
248
+ }
249
+
250
+ .widget-header {
251
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-hover) 100%);
252
+ color: white;
253
+ padding: 20px;
254
+ display: flex;
255
+ justify-content: space-between;
256
+ align-items: center;
257
+ }
258
+
259
+ .widget-title {
260
+ font-size: 18px;
261
+ font-weight: 600;
262
+ margin: 0;
263
+ }
264
+
265
+ .close-btn {
266
+ background: rgba(255, 255, 255, 0.2);
267
+ border: none;
268
+ width: 32px;
269
+ height: 32px;
270
+ border-radius: 50%;
271
+ display: flex;
272
+ align-items: center;
273
+ justify-content: center;
274
+ cursor: pointer;
275
+ transition: background 0.2s;
276
+ }
277
+
278
+ .close-btn:hover {
279
+ background: rgba(255, 255, 255, 0.3);
280
+ }
281
+
282
+ .close-btn svg {
283
+ width: 18px;
284
+ height: 18px;
285
+ color: white;
286
+ }
287
+
288
+ .widget-body {
289
+ padding: 24px;
290
+ }
291
+
292
+ .status-indicator {
293
+ display: flex;
294
+ align-items: center;
295
+ gap: 12px;
296
+ padding: 16px;
297
+ background: #f9fafb;
298
+ border-radius: 12px;
299
+ margin-bottom: 20px;
300
+ }
301
+
302
+ .status-dot {
303
+ width: 10px;
304
+ height: 10px;
305
+ border-radius: 50%;
306
+ background: #10b981;
307
+ animation: pulse 2s infinite;
308
+ }
309
+
310
+ .status-text {
311
+ font-size: 14px;
312
+ color: var(--text-muted);
313
+ margin: 0;
314
+ }
315
+
316
+ .transcript-box {
317
+ min-height: 100px;
318
+ max-height: 200px;
319
+ overflow-y: auto;
320
+ background: #f9fafb;
321
+ border: 1px solid var(--border);
322
+ border-radius: 12px;
323
+ padding: 16px;
324
+ margin-bottom: 20px;
325
+ font-size: 14px;
326
+ line-height: 1.6;
327
+ color: var(--text);
328
+ }
329
+
330
+ .transcript-box::-webkit-scrollbar {
331
+ width: 6px;
332
+ }
333
+
334
+ .transcript-box::-webkit-scrollbar-thumb {
335
+ background: #cbd5e1;
336
+ border-radius: 3px;
337
+ }
338
+
339
+ .controls {
340
+ display: flex;
341
+ gap: 12px;
342
+ justify-content: center;
343
+ }
344
+
345
+ .control-btn {
346
+ flex: 1;
347
+ padding: 14px 20px;
348
+ border: none;
349
+ border-radius: 12px;
350
+ font-size: 14px;
351
+ font-weight: 600;
352
+ cursor: pointer;
353
+ transition: all 0.2s;
354
+ display: flex;
355
+ align-items: center;
356
+ justify-content: center;
357
+ gap: 8px;
358
+ }
359
+
360
+ .control-btn svg {
361
+ width: 18px;
362
+ height: 18px;
363
+ }
364
+
365
+ .control-btn.primary {
366
+ background: var(--primary);
367
+ color: white;
368
+ }
369
+
370
+ .control-btn.primary:hover {
371
+ background: var(--primary-hover);
372
+ }
373
+
374
+ .control-btn.danger {
375
+ background: #ef4444;
376
+ color: white;
377
+ }
378
+
379
+ .control-btn.danger:hover {
380
+ background: #dc2626;
381
+ }
382
+
383
+ .control-btn.secondary {
384
+ background: #f3f4f6;
385
+ color: var(--text);
386
+ }
387
+
388
+ .control-btn.secondary:hover {
389
+ background: #e5e7eb;
390
+ }
391
+
392
+ .control-btn:disabled {
393
+ opacity: 0.5;
394
+ cursor: not-allowed;
395
+ }
396
+
397
+ .wave-animation {
398
+ display: inline-block;
399
+ animation: wave 1.5s ease-in-out infinite;
400
+ }
401
+
402
+ @keyframes fadeInScale {
403
+ from {
404
+ opacity: 0;
405
+ transform: scale(0.8);
406
+ }
407
+ to {
408
+ opacity: 1;
409
+ transform: scale(1);
410
+ }
411
+ }
412
+
413
+ @keyframes pulse {
414
+ 0%, 100% {
415
+ opacity: 1;
416
+ }
417
+ 50% {
418
+ opacity: 0.5;
419
+ }
420
+ }
421
+
422
+ @keyframes wave {
423
+ 0%, 100% {
424
+ transform: translateY(0);
425
+ }
426
+ 50% {
427
+ transform: translateY(-4px);
428
+ }
429
+ }
430
+
431
+ @media (max-width: 640px) {
432
+ .widget-panel {
433
+ width: 100vw !important;
434
+ height: 100vh !important;
435
+ top: 0 !important;
436
+ left: 0 !important;
437
+ right: 0 !important;
438
+ bottom: 0 !important;
439
+ border-radius: 0;
440
+ }
441
+ }
442
+ `;
443
+ class y extends HTMLElement {
444
+ constructor() {
445
+ super();
446
+ r(this, "isPanelOpen", !1);
447
+ r(this, "callActive", !1);
448
+ r(this, "transcript", "");
449
+ r(this, "client", null);
450
+ r(this, "shadow");
451
+ // Configuration
452
+ r(this, "widgetId", "");
453
+ r(this, "serverUrl", "https://api.travelr.club");
454
+ // Default
455
+ r(this, "agentId", "");
456
+ this.shadow = this.attachShadow({ mode: "open" });
457
+ }
458
+ static get observedAttributes() {
459
+ return ["widget-id", "agent-id", "position", "primary-color", "server-url"];
460
+ }
461
+ connectedCallback() {
462
+ console.log("[AvinAI Widget] Initializing..."), this.widgetId = this.getAttribute("widget-id") || "", this.agentId = this.getAttribute("agent-id") || "", this.serverUrl = this.getAttribute("server-url") || "https://api.travelr.club";
463
+ const t = this.getAttribute("position") || "bottom-right", n = this.getAttribute("primary-color") || "#6366f1";
464
+ this.render(t, n), this.attachEventListeners(), this.widgetId && !this.agentId && this.initializeConfig();
465
+ }
466
+ async initializeConfig() {
467
+ var t, n, o, i;
468
+ try {
469
+ console.log(`[AvinAI Widget] Fetching config for widget: ${this.widgetId}`);
470
+ const s = await v(this.widgetId);
471
+ this.agentId = s.agent_id, ((i = (o = (n = (t = s.agent) == null ? void 0 : t.tts_voice) == null ? void 0 : n.provider) == null ? void 0 : o.root_name) == null ? void 0 : i.toLowerCase()) === "premium" ? this.serverUrl = "https://godspeed.travelr.club" : this.serverUrl = "https://in-godspeed.travelr.club", console.log(`[AvinAI Widget] Configured for Agent ID: ${this.agentId}`), console.log(`[AvinAI Widget] Using Server URL: ${this.serverUrl}`);
472
+ } catch (s) {
473
+ console.error("[AvinAI Widget] Configuration failed:", s), this.updateStatus("Configuration Failed", "error");
474
+ }
475
+ }
476
+ render(t, n) {
477
+ const o = {
478
+ "bottom-right": { bottom: "24px", right: "24px" },
479
+ "bottom-left": { bottom: "24px", left: "24px" },
480
+ "top-right": { top: "24px", right: "24px" },
481
+ "top-left": { top: "24px", left: "24px" }
482
+ }, i = o[t] || o["bottom-right"], s = Object.entries(i).map(([p, g]) => `${p}: ${g}`).join("; "), c = t.includes("bottom") ? "bottom: 100px;" : "top: 100px;", l = t.includes("right") ? "right: 24px;" : "left: 24px;", h = m.replace("--primary: #6366f1", `--primary: ${n}`);
483
+ this.shadow.innerHTML = `
484
+ <style>${h}</style>
485
+
486
+ <!-- Floating Action Button -->
487
+ <button class="widget-fab" style="${s}">
488
+ ${d.phone}
489
+ </button>
490
+
491
+ <!-- Voice Panel -->
492
+ <div class="widget-panel hidden" style="width: 380px; height: 480px; ${c} ${l}">
493
+ <div class="widget-header">
494
+ <h3 class="widget-title">Voice Assistant</h3>
495
+ <button class="close-btn" id="close-panel">
496
+ ${d.close}
497
+ </button>
498
+ </div>
499
+
500
+ <div class="widget-body">
501
+ <div class="status-indicator">
502
+ <div class="status-dot" id="status-dot" style="background: #ef4444;"></div>
503
+ <p class="status-text" id="status-text">Click Start to begin conversation</p>
504
+ </div>
505
+
506
+ <div class="transcript-box" id="transcript">
507
+ <p style="color: #9ca3af; font-style: italic;">Transcript will appear here...</p>
508
+ </div>
509
+
510
+ <div class="controls" id="controls">
511
+ <button class="control-btn primary" id="start-btn">
512
+ ${d.mic}
513
+ <span>Start Call</span>
514
+ </button>
515
+ </div>
516
+ </div>
517
+ </div>
518
+ `;
519
+ }
520
+ attachEventListeners() {
521
+ const t = this.shadow.querySelector(".widget-fab"), n = this.shadow.querySelector(".widget-panel"), o = this.shadow.querySelector("#close-panel"), i = this.shadow.querySelector("#start-btn");
522
+ t == null || t.addEventListener("click", () => {
523
+ this.isPanelOpen = !this.isPanelOpen, n == null || n.classList.toggle("hidden", !this.isPanelOpen);
524
+ }), o == null || o.addEventListener("click", () => {
525
+ this.isPanelOpen = !1, n == null || n.classList.add("hidden");
526
+ }), i == null || i.addEventListener("click", () => {
527
+ this.callActive ? this.stopCall() : this.startCall();
528
+ });
529
+ }
530
+ updateStatus(t, n) {
531
+ const o = this.shadow.querySelector("#status-text"), i = this.shadow.querySelector("#status-dot");
532
+ o && (o.textContent = t), i && (n === "idle" && (i.style.background = "#ef4444"), n === "connecting" && (i.style.background = "#f59e0b"), n === "connected" && (i.style.background = "#10b981"), n === "error" && (i.style.background = "#ef4444"));
533
+ }
534
+ async startCall() {
535
+ if (!this.agentId) {
536
+ this.updateStatus("Error: Agent ID not configured", "error");
537
+ return;
538
+ }
539
+ const t = this.shadow.querySelector("#start-btn"), n = this.shadow.querySelector("#transcript");
540
+ try {
541
+ this.updateStatus("Connecting...", "connecting"), t && (t.disabled = !0), this.client = new f({
542
+ serverUrl: this.serverUrl,
543
+ agentId: this.agentId,
544
+ onConnected: () => {
545
+ this.callActive = !0, this.updateStatus("Connected - Listening...", "connected"), t && (t.innerHTML = `${d.close}<span>End Call</span>`, t.className = "control-btn danger", t.disabled = !1);
546
+ },
547
+ onDisconnected: () => {
548
+ this.stopCall();
549
+ },
550
+ onError: (o) => {
551
+ this.updateStatus(`Error: ${o.message || "Connection failed"}`, "error"), t && (t.disabled = !1);
552
+ },
553
+ onTranscription: (o, i) => {
554
+ i && (this.transcript += (this.transcript ? `
555
+ ` : "") + o, n && (n.innerHTML = this.transcript, n.scrollTop = n.scrollHeight));
556
+ }
557
+ }), await this.client.connect();
558
+ } catch (o) {
559
+ console.error("[AvinAI Widget] Start call failed:", o), this.updateStatus("Connection failed", "error"), t && (t.disabled = !1);
560
+ }
561
+ }
562
+ stopCall() {
563
+ this.client && (this.client.disconnect(), this.client = null), this.callActive = !1, this.updateStatus("Click Start to begin conversation", "idle");
564
+ const t = this.shadow.querySelector("#start-btn");
565
+ t && (t.innerHTML = `${d.mic}<span>Start Call</span>`, t.className = "control-btn primary", t.disabled = !1);
566
+ }
567
+ disconnectedCallback() {
568
+ this.client && this.client.disconnect();
569
+ }
570
+ }
571
+ export {
572
+ y as AvinWidget,
573
+ f as WebRTCClient,
574
+ v as fetchWidgetConfig
575
+ };