convai-voice-widget 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.
Files changed (3) hide show
  1. package/README.md +150 -0
  2. package/dist/widget.js +327 -0
  3. package/package.json +34 -0
package/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # AvinAI ConvAI Widget Embed
2
+
3
+ Embeddable voice conversation widget for AvinAI agents. Add AI-powered voice conversations to any website with just 2 lines of code.
4
+
5
+ ## 🚀 Quick Start
6
+
7
+ Add the widget to your website:
8
+
9
+ ```html
10
+ <!-- Step 1: Add the widget element with your agent ID -->
11
+ <avin-convai id="YOUR_AGENT_ID"></avin-convai>
12
+
13
+ <!-- Step 2: Load the widget script -->
14
+ <script src="https://unpkg.com/@avinai/convai-widget-embed" async type="text/javascript"></script>
15
+ ```
16
+
17
+ That's it! The voice assistant will appear as a floating button on your website.
18
+
19
+ ## 📋 Features
20
+
21
+ - ✅ **Easy Integration** - Just 2 lines of code
22
+ - ✅ **Voice Conversations** - WebRTC-powered real-time voice
23
+ - ✅ **Customizable** - Multiple configuration options
24
+ - ✅ **Responsive** - Works on desktop and mobile
25
+ - ✅ **Zero Dependencies** - Self-contained web component
26
+
27
+ ## 🎨 Configuration
28
+
29
+ Customize the widget with attributes:
30
+
31
+ ```html
32
+ <avin-convai
33
+ id="YOUR_AGENT_ID"
34
+ position="bottom-right"
35
+ primary-color="#6366f1"
36
+ server-url="https://your-server.com"
37
+ ></avin-convai>
38
+ ```
39
+
40
+ ### Available Attributes
41
+
42
+ | Attribute | Type | Default | Description |
43
+ |-----------|------|---------|-------------|
44
+ | `id` | string | *required* | Your AvinAI agent ID |
45
+ | `position` | string | `bottom-right` | Widget position: `bottom-right`, `bottom-left`, `top-right`, `top-left` |
46
+ | `primary-color` | string | `#6366f1` | Primary color for the widget (hex format) |
47
+ | `server-url` | string | auto | Custom server URL for API calls |
48
+
49
+ ## 📖 Examples
50
+
51
+ ### Basic Usage
52
+ ```html
53
+ <avin-convai id="63c510f4-b98a-435c-ad22-aa1e042d1d92"></avin-convai>
54
+ <script src="https://unpkg.com/@avinai/convai-widget-embed" async type="text/javascript"></script>
55
+ ```
56
+
57
+ ### Custom Position and Color
58
+ ```html
59
+ <avin-convai
60
+ id="63c510f4-b98a-435c-ad22-aa1e042d1d92"
61
+ position="bottom-left"
62
+ primary-color="#10b981"
63
+ ></avin-convai>
64
+ <script src="https://unpkg.com/@avinai/convai-widget-embed" async type="text/javascript"></script>
65
+ ```
66
+
67
+ ### With Custom Server
68
+ ```html
69
+ <avin-convai
70
+ id="63c510f4-b98a-435c-ad22-aa1e042d1d92"
71
+ server-url="https://your-custom-server.com"
72
+ ></avin-convai>
73
+ <script src="https://unpkg.com/@avinai/convai-widget-embed" async type="text/javascript"></script>
74
+ ```
75
+
76
+ ## 🔧 Advanced Usage
77
+
78
+ ### Using Specific Version
79
+ ```html
80
+ <script src="https://unpkg.com/@avinai/convai-widget-embed@1.0.0" async type="text/javascript"></script>
81
+ ```
82
+
83
+ ### Self-Hosting
84
+ Download and host the widget yourself:
85
+
86
+ ```bash
87
+ npm install @avinai/convai-widget-embed
88
+ ```
89
+
90
+ Then include from your own server:
91
+ ```html
92
+ <script src="/path/to/widget.js" type="module"></script>
93
+ ```
94
+
95
+ ## 🌐 Browser Support
96
+
97
+ - Chrome / Edge (latest)
98
+ - Firefox (latest)
99
+ - Safari (latest)
100
+ - Mobile browsers (iOS Safari, Chrome Android)
101
+
102
+ **Requirements:**
103
+ - WebRTC support
104
+ - Microphone access
105
+
106
+ ## 📱 Mobile Support
107
+
108
+ The widget is fully responsive and works on mobile devices. Users will be prompted to grant microphone permission on first use.
109
+
110
+ ## 🔒 Security & Privacy
111
+
112
+ - HTTPS required for microphone access
113
+ - User must explicitly grant microphone permission
114
+ - All voice data is transmitted via secure WebRTC
115
+ - No data is stored by the widget
116
+
117
+ ## đŸ› ī¸ Development
118
+
119
+ ```bash
120
+ # Clone the repository
121
+ git clone https://github.com/Alok7268/hr-dashboard.git
122
+ cd sdk
123
+
124
+ # Install dependencies
125
+ npm install
126
+
127
+ # Start dev server
128
+ npm run dev
129
+
130
+ # Build for production
131
+ npm run build
132
+ ```
133
+
134
+ ## 📄 License
135
+
136
+ MIT
137
+
138
+ ## 🤝 Support
139
+
140
+ For support, please visit [AvinAI Dashboard](https://your-dashboard.com) or contact support@avinai.com
141
+
142
+ ## 🔗 Links
143
+
144
+ - [Documentation](https://docs.avinai.com)
145
+ - [Dashboard](https://your-dashboard.com)
146
+ - [GitHub](https://github.com/Alok7268/hr-dashboard)
147
+
148
+ ---
149
+
150
+ Made with â¤ī¸ by AvinAI
package/dist/widget.js ADDED
@@ -0,0 +1,327 @@
1
+ (function(){"use strict";const c={phone:`
2
+ <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">
3
+ <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"/>
4
+ </svg>
5
+ `,close:`
6
+ <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">
7
+ <line x1="18" y1="6" x2="6" y2="18"/>
8
+ <line x1="6" y1="6" x2="18" y2="18"/>
9
+ </svg>
10
+ `,mic:`
11
+ <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">
12
+ <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3z"/>
13
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
14
+ <line x1="12" y1="19" x2="12" y2="22"/>
15
+ </svg>
16
+ `,micOff:`
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
+ <line x1="1" y1="1" x2="23" y2="23"/>
19
+ <path d="M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6"/>
20
+ <path d="M17 16.95A7 7 0 0 1 5 12v-2m14 0v2a7 7 0 0 1-.11 1.23"/>
21
+ <line x1="12" y1="19" x2="12" y2="23"/>
22
+ </svg>
23
+ `,waves:`
24
+ <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">
25
+ <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"/>
26
+ <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"/>
27
+ <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"/>
28
+ </svg>
29
+ `},p=`
30
+ * {
31
+ box-sizing: border-box;
32
+ }
33
+
34
+ :host {
35
+ --primary: #6366f1;
36
+ --primary-hover: #4f46e5;
37
+ --bg: #ffffff;
38
+ --text: #1f2937;
39
+ --text-muted: #6b7280;
40
+ --border: #e5e7eb;
41
+ --shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
42
+ --shadow-lg: 0 20px 60px rgba(0, 0, 0, 0.15);
43
+ }
44
+
45
+ .widget-fab {
46
+ position: fixed;
47
+ z-index: 9999;
48
+ width: 64px;
49
+ height: 64px;
50
+ border-radius: 50%;
51
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-hover) 100%);
52
+ border: none;
53
+ cursor: pointer;
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ box-shadow: var(--shadow);
58
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
59
+ animation: fadeInScale 0.4s ease-out;
60
+ }
61
+
62
+ .widget-fab:hover {
63
+ transform: scale(1.1);
64
+ box-shadow: var(--shadow-lg);
65
+ }
66
+
67
+ .widget-fab:active {
68
+ transform: scale(0.95);
69
+ }
70
+
71
+ .widget-fab svg {
72
+ width: 28px;
73
+ height: 28px;
74
+ color: white;
75
+ }
76
+
77
+ .widget-panel {
78
+ position: fixed;
79
+ z-index: 9998;
80
+ background: var(--bg);
81
+ border-radius: 20px;
82
+ box-shadow: var(--shadow-lg);
83
+ overflow: hidden;
84
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
85
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
86
+ }
87
+
88
+ .widget-panel.hidden {
89
+ opacity: 0;
90
+ pointer-events: none;
91
+ transform: scale(0.8);
92
+ }
93
+
94
+ .widget-header {
95
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-hover) 100%);
96
+ color: white;
97
+ padding: 20px;
98
+ display: flex;
99
+ justify-content: space-between;
100
+ align-items: center;
101
+ }
102
+
103
+ .widget-title {
104
+ font-size: 18px;
105
+ font-weight: 600;
106
+ margin: 0;
107
+ }
108
+
109
+ .close-btn {
110
+ background: rgba(255, 255, 255, 0.2);
111
+ border: none;
112
+ width: 32px;
113
+ height: 32px;
114
+ border-radius: 50%;
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ cursor: pointer;
119
+ transition: background 0.2s;
120
+ }
121
+
122
+ .close-btn:hover {
123
+ background: rgba(255, 255, 255, 0.3);
124
+ }
125
+
126
+ .close-btn svg {
127
+ width: 18px;
128
+ height: 18px;
129
+ color: white;
130
+ }
131
+
132
+ .widget-body {
133
+ padding: 24px;
134
+ }
135
+
136
+ .status-indicator {
137
+ display: flex;
138
+ align-items: center;
139
+ gap: 12px;
140
+ padding: 16px;
141
+ background: #f9fafb;
142
+ border-radius: 12px;
143
+ margin-bottom: 20px;
144
+ }
145
+
146
+ .status-dot {
147
+ width: 10px;
148
+ height: 10px;
149
+ border-radius: 50%;
150
+ background: #10b981;
151
+ animation: pulse 2s infinite;
152
+ }
153
+
154
+ .status-text {
155
+ font-size: 14px;
156
+ color: var(--text-muted);
157
+ margin: 0;
158
+ }
159
+
160
+ .transcript-box {
161
+ min-height: 100px;
162
+ max-height: 200px;
163
+ overflow-y: auto;
164
+ background: #f9fafb;
165
+ border: 1px solid var(--border);
166
+ border-radius: 12px;
167
+ padding: 16px;
168
+ margin-bottom: 20px;
169
+ font-size: 14px;
170
+ line-height: 1.6;
171
+ color: var(--text);
172
+ }
173
+
174
+ .transcript-box::-webkit-scrollbar {
175
+ width: 6px;
176
+ }
177
+
178
+ .transcript-box::-webkit-scrollbar-thumb {
179
+ background: #cbd5e1;
180
+ border-radius: 3px;
181
+ }
182
+
183
+ .controls {
184
+ display: flex;
185
+ gap: 12px;
186
+ justify-content: center;
187
+ }
188
+
189
+ .control-btn {
190
+ flex: 1;
191
+ padding: 14px 20px;
192
+ border: none;
193
+ border-radius: 12px;
194
+ font-size: 14px;
195
+ font-weight: 600;
196
+ cursor: pointer;
197
+ transition: all 0.2s;
198
+ display: flex;
199
+ align-items: center;
200
+ justify-content: center;
201
+ gap: 8px;
202
+ }
203
+
204
+ .control-btn svg {
205
+ width: 18px;
206
+ height: 18px;
207
+ }
208
+
209
+ .control-btn.primary {
210
+ background: var(--primary);
211
+ color: white;
212
+ }
213
+
214
+ .control-btn.primary:hover {
215
+ background: var(--primary-hover);
216
+ }
217
+
218
+ .control-btn.danger {
219
+ background: #ef4444;
220
+ color: white;
221
+ }
222
+
223
+ .control-btn.danger:hover {
224
+ background: #dc2626;
225
+ }
226
+
227
+ .control-btn.secondary {
228
+ background: #f3f4f6;
229
+ color: var(--text);
230
+ }
231
+
232
+ .control-btn.secondary:hover {
233
+ background: #e5e7eb;
234
+ }
235
+
236
+ .control-btn:disabled {
237
+ opacity: 0.5;
238
+ cursor: not-allowed;
239
+ }
240
+
241
+ .wave-animation {
242
+ display: inline-block;
243
+ animation: wave 1.5s ease-in-out infinite;
244
+ }
245
+
246
+ @keyframes fadeInScale {
247
+ from {
248
+ opacity: 0;
249
+ transform: scale(0.8);
250
+ }
251
+ to {
252
+ opacity: 1;
253
+ transform: scale(1);
254
+ }
255
+ }
256
+
257
+ @keyframes pulse {
258
+ 0%, 100% {
259
+ opacity: 1;
260
+ }
261
+ 50% {
262
+ opacity: 0.5;
263
+ }
264
+ }
265
+
266
+ @keyframes wave {
267
+ 0%, 100% {
268
+ transform: translateY(0);
269
+ }
270
+ 50% {
271
+ transform: translateY(-4px);
272
+ }
273
+ }
274
+
275
+ @media (max-width: 640px) {
276
+ .widget-panel {
277
+ width: 100vw !important;
278
+ height: 100vh !important;
279
+ top: 0 !important;
280
+ left: 0 !important;
281
+ right: 0 !important;
282
+ bottom: 0 !important;
283
+ border-radius: 0;
284
+ }
285
+ }
286
+ `;class g{constructor(t){if(!t.serverUrl)throw new Error("serverUrl is required");if(!t.agentId)throw new Error("agentId is required");this.serverUrl=t.serverUrl.replace(/\/$/,""),this.agentId=t.agentId,this.callId=t.callId||this.generateCallId(),this.onConnected=t.onConnected||(()=>{}),this.onDisconnected=t.onDisconnected||(()=>{}),this.onError=t.onError||(o=>console.error("[WebRTC]",o)),this.onTranscription=t.onTranscription||(()=>{}),this.pc=null,this.dataChannel=null,this.audioElement=null,this.connected=!1}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 t=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,sampleRate:{ideal:16e3},channelCount:1}});console.log("🎤 [WebRTC] Microphone access granted"),t.getTracks().forEach(n=>{this.pc.addTrack(n,t)}),this.dataChannel=this.pc.createDataChannel("control"),this.dataChannel.onopen=()=>console.log("📡 [WebRTC] DataChannel opened"),this.dataChannel.onmessage=n=>this.handleControlEvent(JSON.parse(n.data)),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] Setting up audio playback"),this.audioElement=new Audio,this.audioElement.srcObject=l,this.audioElement.play().catch(v=>console.warn("Audio autoplay blocked:",v)),this.audioElement.onended=()=>{this.sendEvent("playedStream")})},this.pc.onconnectionstatechange=()=>{console.log("🔄 [WebRTC] State:",this.pc.connectionState),this.pc.connectionState==="connected"?(this.connected=!0,this.onConnected()):(this.pc.connectionState==="failed"||this.pc.connectionState==="disconnected"||this.pc.connectionState==="closed")&&(this.connected=!1,this.onDisconnected())};const o=await this.pc.createOffer();await this.pc.setLocalDescription(o),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:a}=await e.json();console.log("đŸ“Ĩ [WebRTC] Received answer from server"),await this.pc.setRemoteDescription(a),console.log("✅ [WebRTC] Connection established!")}catch(t){throw console.error("❌ [WebRTC] Connection failed:",t),this.onError(t.message||t),t}}waitForIceGathering(){return new Promise(t=>{if(this.pc.iceGatheringState==="complete")t();else{const o=()=>{this.pc.iceGatheringState==="complete"&&(this.pc.removeEventListener("icegatheringstatechange",o),t())};this.pc.addEventListener("icegatheringstatechange",o),setTimeout(()=>{this.pc.removeEventListener("icegatheringstatechange",o),console.warn("âš ī¸ [WebRTC] ICE gathering timeout, proceeding anyway"),t()},5e3)}})}sendEvent(t,o={}){var e;((e=this.dataChannel)==null?void 0:e.readyState)==="open"&&this.dataChannel.send(JSON.stringify({event:t,...o}))}handleControlEvent(t){switch(t.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:",t.text),this.onTranscription(t.text,t.isFinal);break;case"mark":console.log("đŸˇī¸ [WebRTC] Mark:",t.name);break;default:console.log("â„šī¸ [WebRTC] Unknown event:",t.event)}}disconnect(){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()}generateCallId(){return"web_"+Date.now()+"_"+Math.random().toString(36).substr(2,8)}}class d extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}),this.isPanelOpen=!1,this.callActive=!1,this.isMuted=!1,this.transcript="",this.client=null}connectedCallback(){console.log("[ConvAI Widget] connectedCallback triggered");const t=this.getAttribute("agent-id")||this.getAttribute("id"),o=this.getAttribute("position")||"bottom-right",e=this.getAttribute("primary-color")||"#6366f1",a=this.getAttribute("server-url")||"https://api.travelr.club";if(console.log("[ConvAI Widget] Configuration:",{agentId:t,position:o,primaryColor:e,serverUrl:a}),!t){console.error("[ConvAI Widget] agent-id attribute is required");return}this.agentId=t,this.serverUrl=a,console.log("[ConvAI Widget] Rendering widget..."),this.render(o,e),console.log("[ConvAI Widget] Attaching event listeners..."),this.attachEventListeners(),console.log("[ConvAI Widget] Widget initialization complete!")}render(t,o){const e={"bottom-right":{bottom:"24px",right:"24px"},"bottom-left":{bottom:"24px",left:"24px"},"top-right":{top:"24px",right:"24px"},"top-left":{top:"24px",left:"24px"}},a=e[t]||e["bottom-right"],n=p.replace("--primary: #6366f1",`--primary: ${o}`);this.shadowRoot.innerHTML=`
287
+ <style>${n}</style>
288
+
289
+ <!-- Floating Action Button -->
290
+ <button class="widget-fab" style="${Object.entries(a).map(([r,l])=>`${r}: ${l}`).join("; ")}">
291
+ ${c.phone}
292
+ </button>
293
+
294
+ <!-- Voice Panel (hidden by default) -->
295
+ <div class="widget-panel hidden" style="
296
+ width: 380px;
297
+ height: 480px;
298
+ ${t.includes("bottom")?"bottom: 100px":"top: 100px"};
299
+ ${t.includes("right")?"right: 24px":"left: 24px"};
300
+ ">
301
+ <div class="widget-header">
302
+ <h3 class="widget-title">Voice Assistant</h3>
303
+ <button class="close-btn" id="close-panel">
304
+ ${c.close}
305
+ </button>
306
+ </div>
307
+
308
+ <div class="widget-body">
309
+ <div class="status-indicator">
310
+ <div class="status-dot" id="status-dot" style="background: #ef4444;"></div>
311
+ <p class="status-text" id="status-text">Click Start to begin conversation</p>
312
+ </div>
313
+
314
+ <div class="transcript-box" id="transcript">
315
+ <p style="color: #9ca3af; font-style: italic;">Transcript will appear here...</p>
316
+ </div>
317
+
318
+ <div class="controls" id="controls">
319
+ <button class="control-btn primary" id="start-btn">
320
+ ${c.mic}
321
+ <span>Start Call</span>
322
+ </button>
323
+ </div>
324
+ </div>
325
+ </div>
326
+ `}attachEventListeners(){const t=this.shadowRoot.querySelector(".widget-fab"),o=this.shadowRoot.querySelector(".widget-panel"),e=this.shadowRoot.querySelector("#close-panel"),a=this.shadowRoot.querySelector("#start-btn");t.addEventListener("click",()=>{this.isPanelOpen=!this.isPanelOpen,o.classList.toggle("hidden",!this.isPanelOpen)}),e.addEventListener("click",()=>{this.isPanelOpen=!1,o.classList.add("hidden")}),a.addEventListener("click",()=>{this.callActive?this.stopCall():this.startCall()})}async startCall(){const t=this.shadowRoot.querySelector("#status-dot"),o=this.shadowRoot.querySelector("#status-text"),e=this.shadowRoot.querySelector("#start-btn"),a=this.shadowRoot.querySelector("#transcript");try{o.textContent="Connecting...",t.style.background="#f59e0b",e.disabled=!0,this.client=new g({serverUrl:this.serverUrl,agentId:this.agentId,onConnected:()=>{this.callActive=!0,o.textContent="Connected - Listening...",t.style.background="#10b981",e.innerHTML=`${c.close}<span>End Call</span>`,e.className="control-btn danger",e.disabled=!1},onDisconnected:()=>{this.stopCall()},onError:n=>{o.textContent=`Error: ${n}`,t.style.background="#ef4444",e.disabled=!1},onTranscription:(n,r)=>{r&&(this.transcript+=(this.transcript?`
327
+ `:"")+n,a.innerHTML=this.transcript||'<p style="color: #9ca3af; font-style: italic;">Transcript will appear here...</p>',a.scrollTop=a.scrollHeight)}}),await this.client.connect()}catch(n){console.error("[ConvAI Widget] Connection failed:",n),o.textContent="Connection failed. Please try again.",t.style.background="#ef4444",e.disabled=!1}}stopCall(){this.client&&(this.client.disconnect(),this.client=null);const t=this.shadowRoot.querySelector("#status-dot"),o=this.shadowRoot.querySelector("#status-text"),e=this.shadowRoot.querySelector("#start-btn");this.callActive=!1,o.textContent="Click Start to begin conversation",t.style.background="#ef4444",e.innerHTML=`${c.mic}<span>Start Call</span>`,e.className="control-btn primary",e.disabled=!1}disconnectedCallback(){this.client&&this.client.disconnect()}}const i=document.currentScript,h=i==null?void 0:i.getAttribute("data-agent-id"),u=(i==null?void 0:i.getAttribute("data-position"))||"bottom-right",b=(i==null?void 0:i.getAttribute("data-primary-color"))||"#6366f1",f=(i==null?void 0:i.getAttribute("data-server-url"))||"https://api.travelr.club";if(customElements.get("avin-convai")||customElements.define("avin-convai",d),h){const s=document.createElement("avin-convai");s.setAttribute("agent-id",h),s.setAttribute("position",u),s.setAttribute("primary-color",b),s.setAttribute("server-url",f),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{document.body.appendChild(s)}):document.body.appendChild(s)}window.AvinConvAI=d})();
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "convai-voice-widget",
3
+ "version": "1.0.0",
4
+ "description": "Embeddable voice conversation widget for AvinAI agents",
5
+ "main": "dist/widget.js",
6
+ "type": "module",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "dev": "vite",
12
+ "build": "vite build",
13
+ "preview": "vite preview",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "voice",
18
+ "ai",
19
+ "widget",
20
+ "webrtc",
21
+ "conversation",
22
+ "embed",
23
+ "avinai"
24
+ ],
25
+ "author": "AvinAI",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/Alok7268/hr-dashboard"
30
+ },
31
+ "devDependencies": {
32
+ "vite": "^5.4.2"
33
+ }
34
+ }