avin-ai 0.1.0 â 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avin-ai.es.js +109 -65
- package/dist/avin-ai.js +10 -10
- package/dist/avin-ai.umd.js +7 -7
- package/dist/index.d.ts +27 -6
- package/package.json +2 -2
package/dist/avin-ai.es.js
CHANGED
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
var u = Object.defineProperty;
|
|
2
|
-
var b = (
|
|
3
|
-
var
|
|
2
|
+
var b = (s, e, t) => e in s ? u(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t;
|
|
3
|
+
var a = (s, e, t) => b(s, typeof e != "symbol" ? e + "" : e, t);
|
|
4
4
|
class f {
|
|
5
5
|
constructor(e) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
a(this, "serverUrl");
|
|
7
|
+
a(this, "agentId");
|
|
8
|
+
a(this, "callId");
|
|
9
|
+
a(this, "onConnected");
|
|
10
|
+
a(this, "onDisconnected");
|
|
11
|
+
a(this, "onError");
|
|
12
|
+
a(this, "onTranscription");
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
a(this, "onRemoteTrack");
|
|
15
|
+
a(this, "pc", null);
|
|
16
|
+
a(this, "dataChannel", null);
|
|
17
|
+
a(this, "audioElement", null);
|
|
18
|
+
a(this, "connected", !1);
|
|
17
19
|
if (!e.serverUrl) throw new Error("serverUrl is required");
|
|
18
20
|
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 || (() => {
|
|
21
|
+
this.serverUrl = e.serverUrl.replace(/\/$/, ""), this.agentId = e.agentId, this.callId = e.callId || this.generateCallId(), this.onConnected = e.onConnected || (() => {
|
|
20
22
|
}), this.onDisconnected = e.onDisconnected || (() => {
|
|
21
23
|
}), this.onError = e.onError || ((t) => console.error("[WebRTC]", t)), this.onTranscription = e.onTranscription || (() => {
|
|
24
|
+
}), this.onRemoteTrack = e.onRemoteTrack || (() => {
|
|
22
25
|
});
|
|
23
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Connect via HTTP signaling (no WebSocket)
|
|
29
|
+
*/
|
|
24
30
|
async connect() {
|
|
25
31
|
console.log("đĩ [WebRTC] Starting connection...");
|
|
26
32
|
try {
|
|
@@ -36,16 +42,37 @@ class f {
|
|
|
36
42
|
channelCount: 1
|
|
37
43
|
}
|
|
38
44
|
});
|
|
39
|
-
console.log("đ¤ [WebRTC] Microphone access granted"),
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
if (console.log("đ¤ [WebRTC] Microphone access granted"), !this.pc) {
|
|
46
|
+
console.log("[WebRTC] Connection aborted: peer connection closed during setup"), e.getTracks().forEach((o) => o.stop());
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (e.getTracks().forEach((o) => {
|
|
50
|
+
var r;
|
|
51
|
+
(r = this.pc) == null || r.addTrack(o, e);
|
|
52
|
+
}), !this.pc)
|
|
53
|
+
throw new Error("RTCPeerConnection was closed unexpectedly");
|
|
54
|
+
this.dataChannel = this.pc.createDataChannel("control"), this.dataChannel.onopen = () => console.log("đĄ [WebRTC] DataChannel opened"), this.dataChannel.onmessage = (o) => {
|
|
55
|
+
if (typeof o.data == "string")
|
|
56
|
+
try {
|
|
57
|
+
this.handleControlEvent(JSON.parse(o.data));
|
|
58
|
+
} catch {
|
|
59
|
+
console.warn("[WebRTC] Failed to parse message:", o.data);
|
|
60
|
+
}
|
|
61
|
+
else o.data instanceof ArrayBuffer ? console.log("[WebRTC] Received binary data:", o.data.byteLength, "bytes") : o.data instanceof Blob && o.data.text().then((r) => {
|
|
62
|
+
try {
|
|
63
|
+
this.handleControlEvent(JSON.parse(r));
|
|
64
|
+
} catch {
|
|
65
|
+
console.warn("[WebRTC] Failed to parse blob data:", r);
|
|
66
|
+
}
|
|
45
67
|
});
|
|
68
|
+
}, this.dataChannel.onerror = (o) => console.error("â [WebRTC] DataChannel error:", o), this.pc.ontrack = (o) => {
|
|
69
|
+
const r = o.track, c = o.streams[0];
|
|
70
|
+
console.log(`đĨ [WebRTC] Received ${r.kind} track`), r.kind === "audio" ? (console.log("đ [WebRTC] Received audio track from server"), this.audioElement = new Audio(), this.audioElement.srcObject = c, this.audioElement.play().catch((l) => console.warn("Audio autoplay blocked:", l)), this.audioElement.onended = () => {
|
|
71
|
+
this.sendEvent("playedStream"), console.log("â
[WebRTC] TTS playback complete");
|
|
72
|
+
}) : r.kind === "video" && (console.log("đš [WebRTC] Video track received"), this.onRemoteTrack(r, c));
|
|
46
73
|
}, this.pc.onconnectionstatechange = () => {
|
|
47
|
-
var
|
|
48
|
-
console.log("đ [WebRTC] State:", (
|
|
74
|
+
var o, r, c, l, h;
|
|
75
|
+
console.log("đ [WebRTC] State:", (o = this.pc) == null ? void 0 : o.connectionState), ((r = this.pc) == null ? void 0 : r.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
76
|
};
|
|
50
77
|
const t = await this.pc.createOffer();
|
|
51
78
|
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...");
|
|
@@ -59,15 +86,18 @@ class f {
|
|
|
59
86
|
})
|
|
60
87
|
});
|
|
61
88
|
if (!n.ok) {
|
|
62
|
-
const
|
|
63
|
-
throw new Error(
|
|
89
|
+
const o = await n.json();
|
|
90
|
+
throw new Error(o.error || `HTTP ${n.status}`);
|
|
64
91
|
}
|
|
65
|
-
const { answer:
|
|
66
|
-
console.log("đĨ [WebRTC] Received answer from server"), await this.pc.setRemoteDescription(
|
|
92
|
+
const { answer: i } = await n.json();
|
|
93
|
+
console.log("đĨ [WebRTC] Received answer from server"), await this.pc.setRemoteDescription(i), console.log("â
[WebRTC] Connection established!");
|
|
67
94
|
} catch (e) {
|
|
68
|
-
throw console.error("â [WebRTC] Connection failed:", e), this.onError(e.message || e), e;
|
|
95
|
+
throw console.error("â [WebRTC] Connection failed:", e), this.disconnect(), this.onError(e.message || e), e;
|
|
69
96
|
}
|
|
70
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Wait for ICE gathering to complete
|
|
100
|
+
*/
|
|
71
101
|
waitForIceGathering() {
|
|
72
102
|
return new Promise((e) => {
|
|
73
103
|
if (!this.pc) return e();
|
|
@@ -75,8 +105,8 @@ class f {
|
|
|
75
105
|
e();
|
|
76
106
|
else {
|
|
77
107
|
const t = () => {
|
|
78
|
-
var n;
|
|
79
|
-
((n = this.pc) == null ? void 0 : n.iceGatheringState) === "complete" && (this.pc.removeEventListener("icegatheringstatechange", t), e());
|
|
108
|
+
var n, i;
|
|
109
|
+
((n = this.pc) == null ? void 0 : n.iceGatheringState) === "complete" && ((i = this.pc) == null || i.removeEventListener("icegatheringstatechange", t), e());
|
|
80
110
|
};
|
|
81
111
|
this.pc.addEventListener("icegatheringstatechange", t), setTimeout(() => {
|
|
82
112
|
var n;
|
|
@@ -85,17 +115,23 @@ class f {
|
|
|
85
115
|
}
|
|
86
116
|
});
|
|
87
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Send control event via DataChannel
|
|
120
|
+
*/
|
|
88
121
|
sendEvent(e, t = {}) {
|
|
89
122
|
var n;
|
|
90
123
|
((n = this.dataChannel) == null ? void 0 : n.readyState) === "open" && this.dataChannel.send(JSON.stringify({ event: e, ...t }));
|
|
91
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Handle control events from server
|
|
127
|
+
*/
|
|
92
128
|
handleControlEvent(e) {
|
|
93
129
|
switch (e.event) {
|
|
94
130
|
case "clearAudio":
|
|
95
131
|
console.log("đ [WebRTC] Interrupt: stopping audio"), this.audioElement && (this.audioElement.pause(), this.audioElement.currentTime = 0);
|
|
96
132
|
break;
|
|
97
133
|
case "transcription":
|
|
98
|
-
console.log("đ [WebRTC] Transcription:", e.text),
|
|
134
|
+
console.log("đ [WebRTC] Transcription:", e.text), this.onTranscription(e.text, e.isFinal);
|
|
99
135
|
break;
|
|
100
136
|
case "mark":
|
|
101
137
|
console.log("đˇī¸ [WebRTC] Mark:", e.name);
|
|
@@ -104,15 +140,23 @@ class f {
|
|
|
104
140
|
console.log("âšī¸ [WebRTC] Unknown event:", e.event);
|
|
105
141
|
}
|
|
106
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Disconnect and cleanup
|
|
145
|
+
*/
|
|
107
146
|
disconnect() {
|
|
108
|
-
console.log("đ´ [WebRTC] Disconnecting..."), this.audioElement && (this.audioElement.pause(), this.audioElement.srcObject = null), this.dataChannel && this.dataChannel.close(), this.pc && this.pc.
|
|
147
|
+
console.log("đ´ [WebRTC] Disconnecting..."), this.audioElement && (this.audioElement.pause(), this.audioElement.srcObject = null), this.dataChannel && (this.dataChannel.close(), this.dataChannel = null), this.pc && (this.pc.getSenders().forEach((e) => {
|
|
148
|
+
e.track && e.track.stop();
|
|
149
|
+
}), this.pc.close(), this.pc = null), this.connected = !1, this.onDisconnected();
|
|
109
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Generate unique call ID
|
|
153
|
+
*/
|
|
110
154
|
generateCallId() {
|
|
111
155
|
return "web_" + Date.now() + "_" + Math.random().toString(36).substr(2, 8);
|
|
112
156
|
}
|
|
113
157
|
}
|
|
114
158
|
const w = "https://coredb.travelr.club/v1/graphql";
|
|
115
|
-
async function v(
|
|
159
|
+
async function v(s) {
|
|
116
160
|
var t;
|
|
117
161
|
const e = `
|
|
118
162
|
query GetWidgetConfig($id: uuid!) {
|
|
@@ -131,19 +175,19 @@ async function v(a) {
|
|
|
131
175
|
}
|
|
132
176
|
`;
|
|
133
177
|
try {
|
|
134
|
-
const
|
|
178
|
+
const o = (t = (await (await fetch(w, {
|
|
135
179
|
method: "POST",
|
|
136
180
|
headers: {
|
|
137
181
|
"Content-Type": "application/json"
|
|
138
182
|
},
|
|
139
183
|
body: JSON.stringify({
|
|
140
184
|
query: e,
|
|
141
|
-
variables: { id:
|
|
185
|
+
variables: { id: s }
|
|
142
186
|
})
|
|
143
187
|
})).json()).data) == null ? void 0 : t.app_widget_by_pk;
|
|
144
|
-
if (!
|
|
145
|
-
throw new Error(`Widget configuration not found for ID: ${
|
|
146
|
-
return
|
|
188
|
+
if (!o)
|
|
189
|
+
throw new Error(`Widget configuration not found for ID: ${s}`);
|
|
190
|
+
return o;
|
|
147
191
|
} catch (n) {
|
|
148
192
|
throw console.error("[AvinAI] Failed to fetch widget config:", n), n;
|
|
149
193
|
}
|
|
@@ -443,16 +487,16 @@ const d = {
|
|
|
443
487
|
class y extends HTMLElement {
|
|
444
488
|
constructor() {
|
|
445
489
|
super();
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
490
|
+
a(this, "isPanelOpen", !1);
|
|
491
|
+
a(this, "callActive", !1);
|
|
492
|
+
a(this, "transcript", "");
|
|
493
|
+
a(this, "client", null);
|
|
494
|
+
a(this, "shadow");
|
|
451
495
|
// Configuration
|
|
452
|
-
|
|
453
|
-
|
|
496
|
+
a(this, "widgetId", "");
|
|
497
|
+
a(this, "serverUrl", "https://api.travelr.club");
|
|
454
498
|
// Default
|
|
455
|
-
|
|
499
|
+
a(this, "agentId", "");
|
|
456
500
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
457
501
|
}
|
|
458
502
|
static get observedAttributes() {
|
|
@@ -464,27 +508,27 @@ class y extends HTMLElement {
|
|
|
464
508
|
this.render(t, n), this.attachEventListeners(), this.widgetId && !this.agentId && this.initializeConfig();
|
|
465
509
|
}
|
|
466
510
|
async initializeConfig() {
|
|
467
|
-
var t, n,
|
|
511
|
+
var t, n, i, o;
|
|
468
512
|
try {
|
|
469
513
|
console.log(`[AvinAI Widget] Fetching config for widget: ${this.widgetId}`);
|
|
470
|
-
const
|
|
471
|
-
this.agentId =
|
|
472
|
-
} catch (
|
|
473
|
-
console.error("[AvinAI Widget] Configuration failed:",
|
|
514
|
+
const r = await v(this.widgetId);
|
|
515
|
+
this.agentId = r.agent_id, ((o = (i = (n = (t = r.agent) == null ? void 0 : t.tts_voice) == null ? void 0 : n.provider) == null ? void 0 : i.root_name) == null ? void 0 : o.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}`);
|
|
516
|
+
} catch (r) {
|
|
517
|
+
console.error("[AvinAI Widget] Configuration failed:", r), this.updateStatus("Configuration Failed", "error");
|
|
474
518
|
}
|
|
475
519
|
}
|
|
476
520
|
render(t, n) {
|
|
477
|
-
const
|
|
521
|
+
const i = {
|
|
478
522
|
"bottom-right": { bottom: "24px", right: "24px" },
|
|
479
523
|
"bottom-left": { bottom: "24px", left: "24px" },
|
|
480
524
|
"top-right": { top: "24px", right: "24px" },
|
|
481
525
|
"top-left": { top: "24px", left: "24px" }
|
|
482
|
-
},
|
|
526
|
+
}, o = i[t] || i["bottom-right"], r = Object.entries(o).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
527
|
this.shadow.innerHTML = `
|
|
484
528
|
<style>${h}</style>
|
|
485
529
|
|
|
486
530
|
<!-- Floating Action Button -->
|
|
487
|
-
<button class="widget-fab" style="${
|
|
531
|
+
<button class="widget-fab" style="${r}">
|
|
488
532
|
${d.phone}
|
|
489
533
|
</button>
|
|
490
534
|
|
|
@@ -518,18 +562,18 @@ class y extends HTMLElement {
|
|
|
518
562
|
`;
|
|
519
563
|
}
|
|
520
564
|
attachEventListeners() {
|
|
521
|
-
const t = this.shadow.querySelector(".widget-fab"), n = this.shadow.querySelector(".widget-panel"),
|
|
565
|
+
const t = this.shadow.querySelector(".widget-fab"), n = this.shadow.querySelector(".widget-panel"), i = this.shadow.querySelector("#close-panel"), o = this.shadow.querySelector("#start-btn");
|
|
522
566
|
t == null || t.addEventListener("click", () => {
|
|
523
567
|
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
568
|
}), i == null || i.addEventListener("click", () => {
|
|
569
|
+
this.isPanelOpen = !1, n == null || n.classList.add("hidden");
|
|
570
|
+
}), o == null || o.addEventListener("click", () => {
|
|
527
571
|
this.callActive ? this.stopCall() : this.startCall();
|
|
528
572
|
});
|
|
529
573
|
}
|
|
530
574
|
updateStatus(t, n) {
|
|
531
|
-
const
|
|
532
|
-
|
|
575
|
+
const i = this.shadow.querySelector("#status-text"), o = this.shadow.querySelector("#status-dot");
|
|
576
|
+
i && (i.textContent = t), o && (n === "idle" && (o.style.background = "#ef4444"), n === "connecting" && (o.style.background = "#f59e0b"), n === "connected" && (o.style.background = "#10b981"), n === "error" && (o.style.background = "#ef4444"));
|
|
533
577
|
}
|
|
534
578
|
async startCall() {
|
|
535
579
|
if (!this.agentId) {
|
|
@@ -547,16 +591,16 @@ class y extends HTMLElement {
|
|
|
547
591
|
onDisconnected: () => {
|
|
548
592
|
this.stopCall();
|
|
549
593
|
},
|
|
550
|
-
onError: (
|
|
551
|
-
this.updateStatus(`Error: ${
|
|
594
|
+
onError: (i) => {
|
|
595
|
+
this.updateStatus(`Error: ${i.message || "Connection failed"}`, "error"), t && (t.disabled = !1);
|
|
552
596
|
},
|
|
553
|
-
onTranscription: (
|
|
554
|
-
|
|
555
|
-
` : "") +
|
|
597
|
+
onTranscription: (i, o) => {
|
|
598
|
+
o && (this.transcript += (this.transcript ? `
|
|
599
|
+
` : "") + i, n && (n.innerHTML = this.transcript, n.scrollTop = n.scrollHeight));
|
|
556
600
|
}
|
|
557
601
|
}), await this.client.connect();
|
|
558
|
-
} catch (
|
|
559
|
-
console.error("[AvinAI Widget] Start call failed:",
|
|
602
|
+
} catch (i) {
|
|
603
|
+
console.error("[AvinAI Widget] Start call failed:", i), this.updateStatus("Connection failed", "error"), t && (t.disabled = !1);
|
|
560
604
|
}
|
|
561
605
|
}
|
|
562
606
|
stopCall() {
|
package/dist/avin-ai.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var y=Object.defineProperty;var x=(l,s,p)=>s in l?y(l,s,{enumerable:!0,configurable:!0,writable:!0,value:p}):l[s]=p;var a=(l,s,p)=>x(l,typeof s!="symbol"?s+"":s,p);(function(){"use strict";class l{constructor(e){a(this,"serverUrl");a(this,"agentId");a(this,"callId");a(this,"onConnected");a(this,"onDisconnected");a(this,"onError");a(this,"onTranscription");a(this,"onRemoteTrack");a(this,"pc",null);a(this,"dataChannel",null);a(this,"audioElement",null);a(this,"connected",!1);if(!e.serverUrl)throw new Error("serverUrl is required");if(!e.agentId)throw new Error("agentId is required");this.serverUrl=e.serverUrl.replace(/\/$/,""),this.agentId=e.agentId,this.callId=e.callId||this.generateCallId(),this.onConnected=e.onConnected||(()=>{}),this.onDisconnected=e.onDisconnected||(()=>{}),this.onError=e.onError||(t=>console.error("[WebRTC]",t)),this.onTranscription=e.onTranscription||(()=>{}),this.onRemoteTrack=e.onRemoteTrack||(()=>{})}async connect(){console.log("đĩ [WebRTC] Starting connection...");try{this.pc=new RTCPeerConnection({iceServers:[{urls:"stun:stun.l.google.com:19302"}]}),this.pc.addTransceiver("video",{direction:"recvonly"});const e=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:{ideal:16e3},channelCount:1}});if(console.log("đ¤ [WebRTC] Microphone access granted"),!this.pc){console.log("[WebRTC] Connection aborted: peer connection closed during setup"),e.getTracks().forEach(i=>i.stop());return}if(e.getTracks().forEach(i=>{var r;(r=this.pc)==null||r.addTrack(i,e)}),!this.pc)throw new Error("RTCPeerConnection was closed unexpectedly");this.dataChannel=this.pc.createDataChannel("control"),this.dataChannel.onopen=()=>console.log("đĄ [WebRTC] DataChannel opened"),this.dataChannel.onmessage=i=>{if(typeof i.data=="string")try{this.handleControlEvent(JSON.parse(i.data))}catch{console.warn("[WebRTC] Failed to parse message:",i.data)}else i.data instanceof ArrayBuffer?console.log("[WebRTC] Received binary data:",i.data.byteLength,"bytes"):i.data instanceof Blob&&i.data.text().then(r=>{try{this.handleControlEvent(JSON.parse(r))}catch{console.warn("[WebRTC] Failed to parse blob data:",r)}})},this.dataChannel.onerror=i=>console.error("â [WebRTC] DataChannel error:",i),this.pc.ontrack=i=>{const r=i.track,c=i.streams[0];console.log(`đĨ [WebRTC] Received ${r.kind} track`),r.kind==="audio"?(console.log("đ [WebRTC] Received audio track from server"),this.audioElement=new Audio,this.audioElement.srcObject=c,this.audioElement.play().catch(g=>console.warn("Audio autoplay blocked:",g)),this.audioElement.onended=()=>{this.sendEvent("playedStream"),console.log("â
[WebRTC] TTS playback complete")}):r.kind==="video"&&(console.log("đš [WebRTC] Video track received"),this.onRemoteTrack(r,c))},this.pc.onconnectionstatechange=()=>{var i,r,c,g,b;console.log("đ [WebRTC] State:",(i=this.pc)==null?void 0:i.connectionState),((r=this.pc)==null?void 0:r.connectionState)==="connected"?(this.connected=!0,this.onConnected()):(((c=this.pc)==null?void 0:c.connectionState)==="failed"||((g=this.pc)==null?void 0:g.connectionState)==="disconnected"||((b=this.pc)==null?void 0:b.connectionState)==="closed")&&(this.connected=!1,this.onDisconnected())};const t=await this.pc.createOffer();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...");const n=await fetch(`${this.serverUrl}/webrtc?agent=${this.agentId}_${this.callId}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:this.pc.localDescription,agentId:this.agentId,callId:this.callId})});if(!n.ok){const i=await n.json();throw new Error(i.error||`HTTP ${n.status}`)}const{answer:o}=await n.json();console.log("đĨ [WebRTC] Received answer from server"),await this.pc.setRemoteDescription(o),console.log("â
[WebRTC] Connection established!")}catch(e){throw console.error("â [WebRTC] Connection failed:",e),this.disconnect(),this.onError(e.message||e),e}}waitForIceGathering(){return new Promise(e=>{if(!this.pc)return e();if(this.pc.iceGatheringState==="complete")e();else{const t=()=>{var n,o;((n=this.pc)==null?void 0:n.iceGatheringState)==="complete"&&((o=this.pc)==null||o.removeEventListener("icegatheringstatechange",t),e())};this.pc.addEventListener("icegatheringstatechange",t),setTimeout(()=>{var n;(n=this.pc)==null||n.removeEventListener("icegatheringstatechange",t),console.warn("â ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"),e()},5e3)}})}sendEvent(e,t={}){var n;((n=this.dataChannel)==null?void 0:n.readyState)==="open"&&this.dataChannel.send(JSON.stringify({event:e,...t}))}handleControlEvent(e){switch(e.event){case"clearAudio":console.log("đ [WebRTC] Interrupt: stopping audio"),this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0);break;case"transcription":console.log("đ [WebRTC] Transcription:",e.text),this.onTranscription(e.text,e.isFinal);break;case"mark":console.log("đˇī¸ [WebRTC] Mark:",e.name);break;default:console.log("âšī¸ [WebRTC] Unknown event:",e.event)}}disconnect(){console.log("đ´ [WebRTC] Disconnecting..."),this.audioElement&&(this.audioElement.pause(),this.audioElement.srcObject=null),this.dataChannel&&(this.dataChannel.close(),this.dataChannel=null),this.pc&&(this.pc.getSenders().forEach(e=>{e.track&&e.track.stop()}),this.pc.close(),this.pc=null),this.connected=!1,this.onDisconnected()}generateCallId(){return"web_"+Date.now()+"_"+Math.random().toString(36).substr(2,8)}}const s="https://coredb.travelr.club/v1/graphql";async function p(d){var t;const e=`
|
|
2
2
|
query GetWidgetConfig($id: uuid!) {
|
|
3
3
|
app_widget_by_pk(id: $id) {
|
|
4
4
|
agent_id
|
|
@@ -13,7 +13,7 @@ var x=Object.defineProperty;var y=(l,a,h)=>a in l?x(l,a,{enumerable:!0,configura
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
`;try{const i=(t=(await(await fetch(
|
|
16
|
+
`;try{const i=(t=(await(await fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:e,variables:{id:d}})})).json()).data)==null?void 0:t.app_widget_by_pk;if(!i)throw new Error(`Widget configuration not found for ID: ${d}`);return i}catch(n){throw console.error("[AvinAI] Failed to fetch widget config:",n),n}}const u={phone:`
|
|
17
17
|
<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">
|
|
18
18
|
<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"/>
|
|
19
19
|
</svg>
|
|
@@ -298,20 +298,20 @@ var x=Object.defineProperty;var y=(l,a,h)=>a in l?x(l,a,{enumerable:!0,configura
|
|
|
298
298
|
border-radius: 0;
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
|
-
`;class f extends HTMLElement{constructor(){super();
|
|
301
|
+
`;class f extends HTMLElement{constructor(){super();a(this,"isPanelOpen",!1);a(this,"callActive",!1);a(this,"transcript","");a(this,"client",null);a(this,"shadow");a(this,"widgetId","");a(this,"serverUrl","https://api.travelr.club");a(this,"agentId","");this.shadow=this.attachShadow({mode:"open"})}static get observedAttributes(){return["widget-id","agent-id","position","primary-color","server-url"]}connectedCallback(){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";const t=this.getAttribute("position")||"bottom-right",n=this.getAttribute("primary-color")||"#6366f1";this.render(t,n),this.attachEventListeners(),this.widgetId&&!this.agentId&&this.initializeConfig()}async initializeConfig(){var t,n,o,i;try{console.log(`[AvinAI Widget] Fetching config for widget: ${this.widgetId}`);const r=await p(this.widgetId);this.agentId=r.agent_id,((i=(o=(n=(t=r.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}`)}catch(r){console.error("[AvinAI Widget] Configuration failed:",r),this.updateStatus("Configuration Failed","error")}}render(t,n){const o={"bottom-right":{bottom:"24px",right:"24px"},"bottom-left":{bottom:"24px",left:"24px"},"top-right":{top:"24px",right:"24px"},"top-left":{top:"24px",left:"24px"}},i=o[t]||o["bottom-right"],r=Object.entries(i).map(([v,m])=>`${v}: ${m}`).join("; "),c=t.includes("bottom")?"bottom: 100px;":"top: 100px;",g=t.includes("right")?"right: 24px;":"left: 24px;",b=w.replace("--primary: #6366f1",`--primary: ${n}`);this.shadow.innerHTML=`
|
|
302
302
|
<style>${b}</style>
|
|
303
303
|
|
|
304
304
|
<!-- Floating Action Button -->
|
|
305
|
-
<button class="widget-fab" style="${
|
|
306
|
-
${
|
|
305
|
+
<button class="widget-fab" style="${r}">
|
|
306
|
+
${u.phone}
|
|
307
307
|
</button>
|
|
308
308
|
|
|
309
309
|
<!-- Voice Panel -->
|
|
310
|
-
<div class="widget-panel hidden" style="width: 380px; height: 480px; ${
|
|
310
|
+
<div class="widget-panel hidden" style="width: 380px; height: 480px; ${c} ${g}">
|
|
311
311
|
<div class="widget-header">
|
|
312
312
|
<h3 class="widget-title">Voice Assistant</h3>
|
|
313
313
|
<button class="close-btn" id="close-panel">
|
|
314
|
-
${
|
|
314
|
+
${u.close}
|
|
315
315
|
</button>
|
|
316
316
|
</div>
|
|
317
317
|
|
|
@@ -327,11 +327,11 @@ var x=Object.defineProperty;var y=(l,a,h)=>a in l?x(l,a,{enumerable:!0,configura
|
|
|
327
327
|
|
|
328
328
|
<div class="controls" id="controls">
|
|
329
329
|
<button class="control-btn primary" id="start-btn">
|
|
330
|
-
${
|
|
330
|
+
${u.mic}
|
|
331
331
|
<span>Start Call</span>
|
|
332
332
|
</button>
|
|
333
333
|
</div>
|
|
334
334
|
</div>
|
|
335
335
|
</div>
|
|
336
|
-
`}attachEventListeners(){const t=this.shadow.querySelector(".widget-fab"),
|
|
337
|
-
`:"")+o,
|
|
336
|
+
`}attachEventListeners(){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");t==null||t.addEventListener("click",()=>{this.isPanelOpen=!this.isPanelOpen,n==null||n.classList.toggle("hidden",!this.isPanelOpen)}),o==null||o.addEventListener("click",()=>{this.isPanelOpen=!1,n==null||n.classList.add("hidden")}),i==null||i.addEventListener("click",()=>{this.callActive?this.stopCall():this.startCall()})}updateStatus(t,n){const o=this.shadow.querySelector("#status-text"),i=this.shadow.querySelector("#status-dot");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"))}async startCall(){if(!this.agentId){this.updateStatus("Error: Agent ID not configured","error");return}const t=this.shadow.querySelector("#start-btn"),n=this.shadow.querySelector("#transcript");try{this.updateStatus("Connecting...","connecting"),t&&(t.disabled=!0),this.client=new l({serverUrl:this.serverUrl,agentId:this.agentId,onConnected:()=>{this.callActive=!0,this.updateStatus("Connected - Listening...","connected"),t&&(t.innerHTML=`${u.close}<span>End Call</span>`,t.className="control-btn danger",t.disabled=!1)},onDisconnected:()=>{this.stopCall()},onError:o=>{this.updateStatus(`Error: ${o.message||"Connection failed"}`,"error"),t&&(t.disabled=!1)},onTranscription:(o,i)=>{i&&(this.transcript+=(this.transcript?`
|
|
337
|
+
`:"")+o,n&&(n.innerHTML=this.transcript,n.scrollTop=n.scrollHeight))}}),await this.client.connect()}catch(o){console.error("[AvinAI Widget] Start call failed:",o),this.updateStatus("Connection failed","error"),t&&(t.disabled=!1)}}stopCall(){this.client&&(this.client.disconnect(),this.client=null),this.callActive=!1,this.updateStatus("Click Start to begin conversation","idle");const t=this.shadow.querySelector("#start-btn");t&&(t.innerHTML=`${u.mic}<span>Start Call</span>`,t.className="control-btn primary",t.disabled=!1)}disconnectedCallback(){this.client&&this.client.disconnect()}}customElements.get("avin-convai")||customElements.define("avin-convai",f),typeof window<"u"&&(window.AvinConvAI=f);const h=document.currentScript;if(h){const d=h.getAttribute("data-widget-id")||h.getAttribute("widget-id"),e=h.getAttribute("data-agent-id")||h.getAttribute("agent-id");if(d||e){const t=h.getAttribute("data-position")||"bottom-right",n=h.getAttribute("data-primary-color")||"#6366f1",o=document.createElement("avin-convai");d&&o.setAttribute("widget-id",d),e&&o.setAttribute("agent-id",e),o.setAttribute("position",t),o.setAttribute("primary-color",n),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>document.body.appendChild(o)):document.body.appendChild(o)}}})();
|
package/dist/avin-ai.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(s,
|
|
1
|
+
(function(s,c){typeof exports=="object"&&typeof module<"u"?c(exports):typeof define=="function"&&define.amd?define(["exports"],c):(s=typeof globalThis<"u"?globalThis:s||self,c(s.AvinAI={}))})(this,function(s){"use strict";var y=Object.defineProperty;var x=(s,c,d)=>c in s?y(s,c,{enumerable:!0,configurable:!0,writable:!0,value:d}):s[c]=d;var a=(s,c,d)=>x(s,typeof c!="symbol"?c+"":c,d);class c{constructor(o){a(this,"serverUrl");a(this,"agentId");a(this,"callId");a(this,"onConnected");a(this,"onDisconnected");a(this,"onError");a(this,"onTranscription");a(this,"onRemoteTrack");a(this,"pc",null);a(this,"dataChannel",null);a(this,"audioElement",null);a(this,"connected",!1);if(!o.serverUrl)throw new Error("serverUrl is required");if(!o.agentId)throw new Error("agentId is required");this.serverUrl=o.serverUrl.replace(/\/$/,""),this.agentId=o.agentId,this.callId=o.callId||this.generateCallId(),this.onConnected=o.onConnected||(()=>{}),this.onDisconnected=o.onDisconnected||(()=>{}),this.onError=o.onError||(t=>console.error("[WebRTC]",t)),this.onTranscription=o.onTranscription||(()=>{}),this.onRemoteTrack=o.onRemoteTrack||(()=>{})}async connect(){console.log("đĩ [WebRTC] Starting connection...");try{this.pc=new RTCPeerConnection({iceServers:[{urls:"stun:stun.l.google.com:19302"}]}),this.pc.addTransceiver("video",{direction:"recvonly"});const o=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:{ideal:16e3},channelCount:1}});if(console.log("đ¤ [WebRTC] Microphone access granted"),!this.pc){console.log("[WebRTC] Connection aborted: peer connection closed during setup"),o.getTracks().forEach(n=>n.stop());return}if(o.getTracks().forEach(n=>{var r;(r=this.pc)==null||r.addTrack(n,o)}),!this.pc)throw new Error("RTCPeerConnection was closed unexpectedly");this.dataChannel=this.pc.createDataChannel("control"),this.dataChannel.onopen=()=>console.log("đĄ [WebRTC] DataChannel opened"),this.dataChannel.onmessage=n=>{if(typeof n.data=="string")try{this.handleControlEvent(JSON.parse(n.data))}catch{console.warn("[WebRTC] Failed to parse message:",n.data)}else n.data instanceof ArrayBuffer?console.log("[WebRTC] Received binary data:",n.data.byteLength,"bytes"):n.data instanceof Blob&&n.data.text().then(r=>{try{this.handleControlEvent(JSON.parse(r))}catch{console.warn("[WebRTC] Failed to parse blob data:",r)}})},this.dataChannel.onerror=n=>console.error("â [WebRTC] DataChannel error:",n),this.pc.ontrack=n=>{const r=n.track,l=n.streams[0];console.log(`đĨ [WebRTC] Received ${r.kind} track`),r.kind==="audio"?(console.log("đ [WebRTC] Received audio track from server"),this.audioElement=new Audio,this.audioElement.srcObject=l,this.audioElement.play().catch(h=>console.warn("Audio autoplay blocked:",h)),this.audioElement.onended=()=>{this.sendEvent("playedStream"),console.log("â
[WebRTC] TTS playback complete")}):r.kind==="video"&&(console.log("đš [WebRTC] Video track received"),this.onRemoteTrack(r,l))},this.pc.onconnectionstatechange=()=>{var n,r,l,h,u;console.log("đ [WebRTC] State:",(n=this.pc)==null?void 0:n.connectionState),((r=this.pc)==null?void 0:r.connectionState)==="connected"?(this.connected=!0,this.onConnected()):(((l=this.pc)==null?void 0:l.connectionState)==="failed"||((h=this.pc)==null?void 0:h.connectionState)==="disconnected"||((u=this.pc)==null?void 0:u.connectionState)==="closed")&&(this.connected=!1,this.onDisconnected())};const t=await this.pc.createOffer();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...");const e=await fetch(`${this.serverUrl}/webrtc?agent=${this.agentId}_${this.callId}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({offer:this.pc.localDescription,agentId:this.agentId,callId:this.callId})});if(!e.ok){const n=await e.json();throw new Error(n.error||`HTTP ${e.status}`)}const{answer:i}=await e.json();console.log("đĨ [WebRTC] Received answer from server"),await this.pc.setRemoteDescription(i),console.log("â
[WebRTC] Connection established!")}catch(o){throw console.error("â [WebRTC] Connection failed:",o),this.disconnect(),this.onError(o.message||o),o}}waitForIceGathering(){return new Promise(o=>{if(!this.pc)return o();if(this.pc.iceGatheringState==="complete")o();else{const t=()=>{var e,i;((e=this.pc)==null?void 0:e.iceGatheringState)==="complete"&&((i=this.pc)==null||i.removeEventListener("icegatheringstatechange",t),o())};this.pc.addEventListener("icegatheringstatechange",t),setTimeout(()=>{var e;(e=this.pc)==null||e.removeEventListener("icegatheringstatechange",t),console.warn("â ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"),o()},5e3)}})}sendEvent(o,t={}){var e;((e=this.dataChannel)==null?void 0:e.readyState)==="open"&&this.dataChannel.send(JSON.stringify({event:o,...t}))}handleControlEvent(o){switch(o.event){case"clearAudio":console.log("đ [WebRTC] Interrupt: stopping audio"),this.audioElement&&(this.audioElement.pause(),this.audioElement.currentTime=0);break;case"transcription":console.log("đ [WebRTC] Transcription:",o.text),this.onTranscription(o.text,o.isFinal);break;case"mark":console.log("đˇī¸ [WebRTC] Mark:",o.name);break;default:console.log("âšī¸ [WebRTC] Unknown event:",o.event)}}disconnect(){console.log("đ´ [WebRTC] Disconnecting..."),this.audioElement&&(this.audioElement.pause(),this.audioElement.srcObject=null),this.dataChannel&&(this.dataChannel.close(),this.dataChannel=null),this.pc&&(this.pc.getSenders().forEach(o=>{o.track&&o.track.stop()}),this.pc.close(),this.pc=null),this.connected=!1,this.onDisconnected()}generateCallId(){return"web_"+Date.now()+"_"+Math.random().toString(36).substr(2,8)}}const d="https://coredb.travelr.club/v1/graphql";async function b(g){var t;const o=`
|
|
2
2
|
query GetWidgetConfig($id: uuid!) {
|
|
3
3
|
app_widget_by_pk(id: $id) {
|
|
4
4
|
agent_id
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
`;try{const
|
|
16
|
+
`;try{const n=(t=(await(await fetch(d,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({query:o,variables:{id:g}})})).json()).data)==null?void 0:t.app_widget_by_pk;if(!n)throw new Error(`Widget configuration not found for ID: ${g}`);return n}catch(e){throw console.error("[AvinAI] Failed to fetch widget config:",e),e}}const p={phone:`
|
|
17
17
|
<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">
|
|
18
18
|
<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"/>
|
|
19
19
|
</svg>
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
<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"/>
|
|
42
42
|
<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"/>
|
|
43
43
|
</svg>
|
|
44
|
-
`},
|
|
44
|
+
`},f=`
|
|
45
45
|
* {
|
|
46
46
|
box-sizing: border-box;
|
|
47
47
|
}
|
|
@@ -298,11 +298,11 @@
|
|
|
298
298
|
border-radius: 0;
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
|
-
`;class
|
|
301
|
+
`;class v extends HTMLElement{constructor(){super();a(this,"isPanelOpen",!1);a(this,"callActive",!1);a(this,"transcript","");a(this,"client",null);a(this,"shadow");a(this,"widgetId","");a(this,"serverUrl","https://api.travelr.club");a(this,"agentId","");this.shadow=this.attachShadow({mode:"open"})}static get observedAttributes(){return["widget-id","agent-id","position","primary-color","server-url"]}connectedCallback(){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";const t=this.getAttribute("position")||"bottom-right",e=this.getAttribute("primary-color")||"#6366f1";this.render(t,e),this.attachEventListeners(),this.widgetId&&!this.agentId&&this.initializeConfig()}async initializeConfig(){var t,e,i,n;try{console.log(`[AvinAI Widget] Fetching config for widget: ${this.widgetId}`);const r=await b(this.widgetId);this.agentId=r.agent_id,((n=(i=(e=(t=r.agent)==null?void 0:t.tts_voice)==null?void 0:e.provider)==null?void 0:i.root_name)==null?void 0:n.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}`)}catch(r){console.error("[AvinAI Widget] Configuration failed:",r),this.updateStatus("Configuration Failed","error")}}render(t,e){const i={"bottom-right":{bottom:"24px",right:"24px"},"bottom-left":{bottom:"24px",left:"24px"},"top-right":{top:"24px",right:"24px"},"top-left":{top:"24px",left:"24px"}},n=i[t]||i["bottom-right"],r=Object.entries(n).map(([w,m])=>`${w}: ${m}`).join("; "),l=t.includes("bottom")?"bottom: 100px;":"top: 100px;",h=t.includes("right")?"right: 24px;":"left: 24px;",u=f.replace("--primary: #6366f1",`--primary: ${e}`);this.shadow.innerHTML=`
|
|
302
302
|
<style>${u}</style>
|
|
303
303
|
|
|
304
304
|
<!-- Floating Action Button -->
|
|
305
|
-
<button class="widget-fab" style="${
|
|
305
|
+
<button class="widget-fab" style="${r}">
|
|
306
306
|
${p.phone}
|
|
307
307
|
</button>
|
|
308
308
|
|
|
@@ -333,5 +333,5 @@
|
|
|
333
333
|
</div>
|
|
334
334
|
</div>
|
|
335
335
|
</div>
|
|
336
|
-
`}attachEventListeners(){const t=this.shadow.querySelector(".widget-fab"),e=this.shadow.querySelector(".widget-panel"),
|
|
337
|
-
`:"")+
|
|
336
|
+
`}attachEventListeners(){const t=this.shadow.querySelector(".widget-fab"),e=this.shadow.querySelector(".widget-panel"),i=this.shadow.querySelector("#close-panel"),n=this.shadow.querySelector("#start-btn");t==null||t.addEventListener("click",()=>{this.isPanelOpen=!this.isPanelOpen,e==null||e.classList.toggle("hidden",!this.isPanelOpen)}),i==null||i.addEventListener("click",()=>{this.isPanelOpen=!1,e==null||e.classList.add("hidden")}),n==null||n.addEventListener("click",()=>{this.callActive?this.stopCall():this.startCall()})}updateStatus(t,e){const i=this.shadow.querySelector("#status-text"),n=this.shadow.querySelector("#status-dot");i&&(i.textContent=t),n&&(e==="idle"&&(n.style.background="#ef4444"),e==="connecting"&&(n.style.background="#f59e0b"),e==="connected"&&(n.style.background="#10b981"),e==="error"&&(n.style.background="#ef4444"))}async startCall(){if(!this.agentId){this.updateStatus("Error: Agent ID not configured","error");return}const t=this.shadow.querySelector("#start-btn"),e=this.shadow.querySelector("#transcript");try{this.updateStatus("Connecting...","connecting"),t&&(t.disabled=!0),this.client=new c({serverUrl:this.serverUrl,agentId:this.agentId,onConnected:()=>{this.callActive=!0,this.updateStatus("Connected - Listening...","connected"),t&&(t.innerHTML=`${p.close}<span>End Call</span>`,t.className="control-btn danger",t.disabled=!1)},onDisconnected:()=>{this.stopCall()},onError:i=>{this.updateStatus(`Error: ${i.message||"Connection failed"}`,"error"),t&&(t.disabled=!1)},onTranscription:(i,n)=>{n&&(this.transcript+=(this.transcript?`
|
|
337
|
+
`:"")+i,e&&(e.innerHTML=this.transcript,e.scrollTop=e.scrollHeight))}}),await this.client.connect()}catch(i){console.error("[AvinAI Widget] Start call failed:",i),this.updateStatus("Connection failed","error"),t&&(t.disabled=!1)}}stopCall(){this.client&&(this.client.disconnect(),this.client=null),this.callActive=!1,this.updateStatus("Click Start to begin conversation","idle");const t=this.shadow.querySelector("#start-btn");t&&(t.innerHTML=`${p.mic}<span>Start Call</span>`,t.className="control-btn primary",t.disabled=!1)}disconnectedCallback(){this.client&&this.client.disconnect()}}s.AvinWidget=v,s.WebRTCClient=c,s.fetchWidgetConfig=b,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})});
|
package/dist/index.d.ts
CHANGED
|
@@ -45,30 +45,51 @@ export declare class WebRTCClient {
|
|
|
45
45
|
private serverUrl;
|
|
46
46
|
private agentId;
|
|
47
47
|
private callId;
|
|
48
|
-
private pc;
|
|
49
|
-
private dataChannel;
|
|
50
|
-
private audioElement;
|
|
51
|
-
connected: boolean;
|
|
52
48
|
private onConnected;
|
|
53
49
|
private onDisconnected;
|
|
54
50
|
private onError;
|
|
55
51
|
private onTranscription;
|
|
52
|
+
private onRemoteTrack;
|
|
53
|
+
private pc;
|
|
54
|
+
private dataChannel;
|
|
55
|
+
private audioElement;
|
|
56
|
+
connected: boolean;
|
|
56
57
|
constructor(config: WebRTCClientConfig);
|
|
58
|
+
/**
|
|
59
|
+
* Connect via HTTP signaling (no WebSocket)
|
|
60
|
+
*/
|
|
57
61
|
connect(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Wait for ICE gathering to complete
|
|
64
|
+
*/
|
|
58
65
|
private waitForIceGathering;
|
|
59
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Send control event via DataChannel
|
|
68
|
+
*/
|
|
69
|
+
sendEvent(event: string, data?: {}): void;
|
|
70
|
+
/**
|
|
71
|
+
* Handle control events from server
|
|
72
|
+
*/
|
|
60
73
|
private handleControlEvent;
|
|
74
|
+
/**
|
|
75
|
+
* Disconnect and cleanup
|
|
76
|
+
*/
|
|
61
77
|
disconnect(): void;
|
|
62
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Generate unique call ID
|
|
80
|
+
*/
|
|
81
|
+
generateCallId(): string;
|
|
63
82
|
}
|
|
64
83
|
|
|
65
84
|
export declare interface WebRTCClientConfig {
|
|
66
85
|
serverUrl: string;
|
|
67
86
|
agentId: string;
|
|
87
|
+
callId?: string;
|
|
68
88
|
onConnected?: () => void;
|
|
69
89
|
onDisconnected?: () => void;
|
|
70
90
|
onError?: (error: any) => void;
|
|
71
91
|
onTranscription?: (text: string, isFinal: boolean) => void;
|
|
92
|
+
onRemoteTrack?: (track: MediaStreamTrack, stream: MediaStream) => void;
|
|
72
93
|
}
|
|
73
94
|
|
|
74
95
|
export declare interface WidgetConfig {
|
package/package.json
CHANGED