@staylift-tech/conv-widget 0.0.1

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 (49) hide show
  1. package/README.md +220 -0
  2. package/dist/cjs/app-globals-V2Kpy_OQ.js +5 -0
  3. package/dist/cjs/index-BDscJkkF.js +1464 -0
  4. package/dist/cjs/index.cjs.js +10 -0
  5. package/dist/cjs/loader.cjs.js +13 -0
  6. package/dist/cjs/staylift-orb.cjs.entry.js +117 -0
  7. package/dist/cjs/staylift-widget.cjs.entry.js +26479 -0
  8. package/dist/cjs/staylift-widget.cjs.js +25 -0
  9. package/dist/collection/collection-manifest.json +13 -0
  10. package/dist/collection/components/staylift-orb/staylift-orb.css +15 -0
  11. package/dist/collection/components/staylift-orb/staylift-orb.js +229 -0
  12. package/dist/collection/components/staylift-widget/staylift-widget.css +578 -0
  13. package/dist/collection/components/staylift-widget/staylift-widget.js +898 -0
  14. package/dist/collection/index.js +3 -0
  15. package/dist/collection/types.js +1 -0
  16. package/dist/components/index.d.ts +35 -0
  17. package/dist/components/index.js +1 -0
  18. package/dist/components/p-DnOZjbwA.js +1 -0
  19. package/dist/components/staylift-orb.d.ts +11 -0
  20. package/dist/components/staylift-orb.js +1 -0
  21. package/dist/components/staylift-widget.d.ts +11 -0
  22. package/dist/components/staylift-widget.js +1 -0
  23. package/dist/esm/app-globals-DQuL1Twl.js +3 -0
  24. package/dist/esm/index-DAtfDq5B.js +1456 -0
  25. package/dist/esm/index.js +3 -0
  26. package/dist/esm/loader.js +11 -0
  27. package/dist/esm/staylift-orb.entry.js +115 -0
  28. package/dist/esm/staylift-widget.entry.js +26477 -0
  29. package/dist/esm/staylift-widget.js +21 -0
  30. package/dist/index.cjs.js +1 -0
  31. package/dist/index.js +1 -0
  32. package/dist/staylift-widget/index.esm.js +1 -0
  33. package/dist/staylift-widget/p-37510f2e.entry.js +1 -0
  34. package/dist/staylift-widget/p-94923740.entry.js +1 -0
  35. package/dist/staylift-widget/p-DAtfDq5B.js +2 -0
  36. package/dist/staylift-widget/p-DQuL1Twl.js +1 -0
  37. package/dist/staylift-widget/staylift-widget.esm.js +1 -0
  38. package/dist/types/components/staylift-orb/staylift-orb.d.ts +19 -0
  39. package/dist/types/components/staylift-widget/staylift-widget.d.ts +80 -0
  40. package/dist/types/components.d.ts +212 -0
  41. package/dist/types/index.d.ts +3 -0
  42. package/dist/types/stencil-public-runtime.d.ts +1839 -0
  43. package/dist/types/types.d.ts +1 -0
  44. package/loader/cdn.js +1 -0
  45. package/loader/index.cjs.js +1 -0
  46. package/loader/index.d.ts +24 -0
  47. package/loader/index.es2017.js +1 -0
  48. package/loader/index.js +2 -0
  49. package/package.json +58 -0
@@ -0,0 +1,898 @@
1
+ import { h } from "@stencil/core";
2
+ import { Conversation } from "@elevenlabs/client";
3
+ export class StayliftWidget {
4
+ constructor() {
5
+ this.positionX = 'right';
6
+ this.positionY = 'bottom';
7
+ this.variant = 'floating';
8
+ this.mode = 'dark';
9
+ this.primaryColor = '#6366f1';
10
+ this.brandName = 'Customer Support';
11
+ this.language = 'en';
12
+ this.autoExpand = false;
13
+ this.showBranding = true;
14
+ this.fabPrompt = 'Do you need help?';
15
+ this.fabButtonText = 'Start';
16
+ // ============ STATE ============
17
+ this.status = 'disconnected';
18
+ this.isExpanded = false;
19
+ this.termsAccepted = false;
20
+ this.errorMessage = null;
21
+ this.inputVolume = 0;
22
+ this.outputVolume = 0;
23
+ this.messages = [];
24
+ this.inputText = '';
25
+ this.copiedIndex = null;
26
+ // ============ PRIVATE ============
27
+ this.conversation = null;
28
+ this.volumeInterval = null;
29
+ this.messagesContainer = null;
30
+ this.mediaStream = null;
31
+ this.isTextOnlyMode = true;
32
+ this.pendingMessage = null;
33
+ this.handleInputKeyDown = (e) => {
34
+ if (e.key === 'Enter' && !e.shiftKey) {
35
+ e.preventDefault();
36
+ this.handleSendText();
37
+ }
38
+ };
39
+ this.handleInputChange = (e) => {
40
+ this.inputText = e.target.value;
41
+ };
42
+ this.handleToggleExpand = () => {
43
+ this.isExpanded = !this.isExpanded;
44
+ };
45
+ this.copyToClipboard = (text, index) => {
46
+ navigator.clipboard.writeText(text);
47
+ this.copiedIndex = index;
48
+ setTimeout(() => { this.copiedIndex = null; }, 2000);
49
+ };
50
+ this.handleAcceptTerms = () => {
51
+ this.termsAccepted = true;
52
+ };
53
+ this.handleDeclineTerms = () => {
54
+ this.isExpanded = false;
55
+ };
56
+ }
57
+ // ============ LIFECYCLE ============
58
+ componentWillLoad() {
59
+ if (this.autoExpand)
60
+ this.isExpanded = true;
61
+ }
62
+ disconnectedCallback() {
63
+ this.cleanup();
64
+ }
65
+ // ============ PUBLIC METHODS ============
66
+ async startConversation(textOnly = true) {
67
+ await this.handleStartConversation(textOnly);
68
+ }
69
+ async endConversation() {
70
+ await this.handleEndConversation();
71
+ }
72
+ async getStatus() {
73
+ return this.status;
74
+ }
75
+ async sendMessage(text) {
76
+ await this.handleSendText(text);
77
+ }
78
+ // ============ PRIVATE METHODS ============
79
+ scrollToBottom() {
80
+ if (this.messagesContainer) {
81
+ const container = this.messagesContainer;
82
+ setTimeout(() => {
83
+ container.scrollTop = container.scrollHeight;
84
+ }, 50);
85
+ }
86
+ }
87
+ async getMicStream() {
88
+ if (this.mediaStream)
89
+ return this.mediaStream;
90
+ try {
91
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
92
+ this.mediaStream = stream;
93
+ this.errorMessage = null;
94
+ return stream;
95
+ }
96
+ catch (error) {
97
+ if (error instanceof DOMException && error.name === 'NotAllowedError') {
98
+ this.errorMessage = this.t('microphoneError');
99
+ }
100
+ throw error;
101
+ }
102
+ }
103
+ async handleStartConversation(textOnly = true, skipClear = false) {
104
+ if (this.status === 'connecting' || this.status === 'connected')
105
+ return;
106
+ try {
107
+ this.errorMessage = null;
108
+ this.status = 'connecting';
109
+ this.isTextOnlyMode = textOnly;
110
+ this.statusChanged.emit(this.status);
111
+ if (!skipClear)
112
+ this.messages = [];
113
+ if (!textOnly)
114
+ await this.getMicStream();
115
+ // Store pending message to send after connection
116
+ const messageToSend = this.pendingMessage;
117
+ // Create a promise that resolves when we're connected or rejects on failure
118
+ let resolveConnected;
119
+ let rejectConnected;
120
+ const connectedPromise = new Promise((resolve, reject) => {
121
+ resolveConnected = resolve;
122
+ rejectConnected = reject;
123
+ });
124
+ // Add connection timeout
125
+ const CONNECTION_TIMEOUT = 15000;
126
+ const timeoutId = setTimeout(() => {
127
+ rejectConnected(new Error('Connection timeout'));
128
+ }, CONNECTION_TIMEOUT);
129
+ this.conversation = await Conversation.startSession({
130
+ agentId: this.agentId,
131
+ connectionType: textOnly ? 'websocket' : 'webrtc',
132
+ overrides: {
133
+ conversation: { textOnly },
134
+ agent: { firstMessage: textOnly ? '' : undefined },
135
+ },
136
+ onStatusChange: (statusEvent) => {
137
+ const newStatus = statusEvent.status;
138
+ const previousStatus = this.status;
139
+ this.status = newStatus;
140
+ this.statusChanged.emit(newStatus);
141
+ // Handle connected state
142
+ if (newStatus === 'connected' && previousStatus !== 'connected') {
143
+ clearTimeout(timeoutId);
144
+ this.conversationStarted.emit();
145
+ if (!textOnly)
146
+ this.startVolumeMonitoring();
147
+ resolveConnected(); // Signal that we're connected
148
+ }
149
+ // Handle disconnected state
150
+ if (newStatus === 'disconnected') {
151
+ if (previousStatus === 'connected') {
152
+ this.conversationEnded.emit();
153
+ this.stopVolumeMonitoring();
154
+ }
155
+ else if (previousStatus === 'connecting') {
156
+ // Connection failed before establishing
157
+ clearTimeout(timeoutId);
158
+ rejectConnected(new Error('Connection failed'));
159
+ }
160
+ }
161
+ },
162
+ onMessage: (message) => {
163
+ if (message.message) {
164
+ // Use 'role' if available, fallback to deprecated 'source'
165
+ const messageRole = message.role || message.source;
166
+ const chatMessage = {
167
+ role: messageRole === 'user' ? 'user' : 'assistant',
168
+ content: message.message,
169
+ };
170
+ this.messages = [...this.messages, chatMessage];
171
+ this.messageReceived.emit(chatMessage);
172
+ this.scrollToBottom();
173
+ }
174
+ },
175
+ onError: (error) => {
176
+ this.errorMessage = this.t('connectionError');
177
+ this.status = 'disconnected';
178
+ this.statusChanged.emit(this.status);
179
+ this.widgetError.emit({ message: String(error) });
180
+ },
181
+ });
182
+ // Wait for connected status before sending message
183
+ await connectedPromise;
184
+ // Now send the pending message (status is guaranteed 'connected' after promise resolves)
185
+ if (messageToSend && this.conversation) {
186
+ this.conversation.sendUserMessage(messageToSend);
187
+ this.pendingMessage = null;
188
+ }
189
+ }
190
+ catch (error) {
191
+ this.status = 'disconnected';
192
+ this.statusChanged.emit(this.status);
193
+ // Don't clear messages - keep user's message visible
194
+ this.pendingMessage = null;
195
+ if (error instanceof DOMException && error.name === 'NotAllowedError') {
196
+ this.errorMessage = this.t('microphoneError');
197
+ }
198
+ else {
199
+ this.errorMessage = this.t('connectionError');
200
+ }
201
+ this.widgetError.emit({ message: this.errorMessage });
202
+ }
203
+ }
204
+ async handleEndConversation() {
205
+ if (!this.conversation)
206
+ return;
207
+ this.status = 'disconnecting';
208
+ this.statusChanged.emit(this.status);
209
+ await this.conversation.endSession();
210
+ this.conversation = null;
211
+ this.status = 'disconnected';
212
+ this.statusChanged.emit(this.status);
213
+ if (this.mediaStream) {
214
+ this.mediaStream.getTracks().forEach(t => t.stop());
215
+ this.mediaStream = null;
216
+ }
217
+ }
218
+ async handleSendText(text) {
219
+ const msg = text || this.inputText.trim();
220
+ if (!msg)
221
+ return;
222
+ // If disconnected, start text-only session
223
+ if (this.status === 'disconnected') {
224
+ const userMessage = { role: 'user', content: msg };
225
+ this.inputText = '';
226
+ this.pendingMessage = msg; // Store message to send after connection
227
+ this.messages = [userMessage];
228
+ this.scrollToBottom();
229
+ try {
230
+ await this.handleStartConversation(true, true);
231
+ // Message will be sent in onConnect callback
232
+ }
233
+ catch {
234
+ this.pendingMessage = null;
235
+ }
236
+ }
237
+ else if (this.status === 'connected') {
238
+ const userMessage = { role: 'user', content: msg };
239
+ this.messages = [...this.messages, userMessage];
240
+ this.inputText = '';
241
+ if (this.conversation && typeof this.conversation.sendUserMessage === 'function') {
242
+ this.conversation.sendUserMessage(msg);
243
+ }
244
+ else {
245
+ console.error('[Staylift] Cannot send message: no active conversation');
246
+ }
247
+ this.scrollToBottom();
248
+ }
249
+ }
250
+ async handleVoiceButton() {
251
+ if (this.status === 'disconnected' || this.status === null) {
252
+ await this.handleStartConversation(false);
253
+ }
254
+ else if (this.status === 'connected') {
255
+ await this.handleEndConversation();
256
+ }
257
+ }
258
+ startVolumeMonitoring() {
259
+ this.volumeInterval = setInterval(() => {
260
+ if (this.conversation && this.status === 'connected') {
261
+ const input = this.conversation.getInputVolume?.() ?? 0;
262
+ const output = this.conversation.getOutputVolume?.() ?? 0;
263
+ this.inputVolume = Math.min(1.0, Math.pow(input, 0.5) * 2.5);
264
+ this.outputVolume = Math.min(1.0, Math.pow(output, 0.5) * 2.5);
265
+ }
266
+ }, 50);
267
+ }
268
+ stopVolumeMonitoring() {
269
+ if (this.volumeInterval) {
270
+ clearInterval(this.volumeInterval);
271
+ this.volumeInterval = null;
272
+ }
273
+ this.inputVolume = 0;
274
+ this.outputVolume = 0;
275
+ }
276
+ cleanup() {
277
+ this.stopVolumeMonitoring();
278
+ if (this.conversation) {
279
+ this.conversation.endSession();
280
+ this.conversation = null;
281
+ }
282
+ if (this.mediaStream) {
283
+ this.mediaStream.getTracks().forEach(t => t.stop());
284
+ this.mediaStream = null;
285
+ }
286
+ }
287
+ t(key) {
288
+ const translations = {
289
+ en: {
290
+ microphoneError: 'Please enable microphone permissions in your browser.',
291
+ connectionError: 'Connection failed. Please try again.',
292
+ tapToStart: 'Tap to start voice chat',
293
+ connected: 'Connected',
294
+ placeholder: 'Type a message...',
295
+ emptyTitle: 'Start a conversation',
296
+ emptyDesc: 'Type a message or tap the voice button',
297
+ starting: 'Starting conversation',
298
+ connecting: 'Connecting...',
299
+ ready: 'Ready to chat',
300
+ talkOrType: 'Start talking or type',
301
+ poweredBy: 'Powered by Staylift',
302
+ termsTitle: 'Terms and Conditions',
303
+ termsText: 'By clicking "Agree," and each time I interact with this AI agent, I consent to the recording, storage, and sharing of my communications with third-party service providers, and as described in the Privacy Policy.',
304
+ termsWarning: 'If you do not wish to have your conversations recorded, please refrain from using this service.',
305
+ termsAgree: 'Agree',
306
+ termsDecline: 'Decline',
307
+ },
308
+ pl: {
309
+ microphoneError: 'Proszę włączyć uprawnienia mikrofonu.',
310
+ connectionError: 'Połączenie nieudane. Spróbuj ponownie.',
311
+ tapToStart: 'Dotknij, aby rozpocząć rozmowę',
312
+ connected: 'Połączono',
313
+ placeholder: 'Napisz wiadomość...',
314
+ emptyTitle: 'Rozpocznij rozmowę',
315
+ emptyDesc: 'Napisz wiadomość lub naciśnij przycisk głosowy',
316
+ starting: 'Rozpoczynanie rozmowy',
317
+ connecting: 'Łączenie...',
318
+ ready: 'Gotowe do czatu',
319
+ talkOrType: 'Mów lub pisz',
320
+ poweredBy: 'Powered by Staylift',
321
+ termsTitle: 'Regulamin',
322
+ termsText: 'Klikając „Zgadzam się" i za każdym razem, gdy wchodzę w interakcję z tym agentem AI, wyrażam zgodę na nagrywanie, przechowywanie i udostępnianie moich komunikatów zewnętrznym dostawcom usług, zgodnie z Polityką Prywatności.',
323
+ termsWarning: 'Jeśli nie chcesz, aby Twoje rozmowy były nagrywane, prosimy o nieużywanie tej usługi.',
324
+ termsAgree: 'Zgadzam się',
325
+ termsDecline: 'Odrzuć',
326
+ },
327
+ de: {
328
+ microphoneError: 'Bitte aktivieren Sie die Mikrofonberechtigung in Ihrem Browser.',
329
+ connectionError: 'Verbindung fehlgeschlagen. Bitte versuchen Sie es erneut.',
330
+ tapToStart: 'Tippen, um den Voice-Chat zu starten',
331
+ connected: 'Verbunden',
332
+ placeholder: 'Nachricht eingeben...',
333
+ emptyTitle: 'Gespräch starten',
334
+ emptyDesc: 'Nachricht eingeben oder Voice-Button drücken',
335
+ starting: 'Gespräch wird gestartet',
336
+ connecting: 'Verbindung wird hergestellt...',
337
+ ready: 'Bereit zum Chatten',
338
+ talkOrType: 'Sprechen oder tippen',
339
+ poweredBy: 'Powered by Staylift',
340
+ termsTitle: 'Nutzungsbedingungen',
341
+ termsText: 'Durch Klicken auf „Zustimmen" und bei jeder Interaktion mit diesem KI-Agenten stimme ich der Aufzeichnung, Speicherung und Weitergabe meiner Kommunikation an Drittanbieter zu, wie in der Datenschutzrichtlinie beschrieben.',
342
+ termsWarning: 'Wenn Sie nicht möchten, dass Ihre Gespräche aufgezeichnet werden, verwenden Sie diesen Dienst bitte nicht.',
343
+ termsAgree: 'Zustimmen',
344
+ termsDecline: 'Ablehnen',
345
+ },
346
+ };
347
+ return translations[this.language]?.[key] || translations['en'][key] || key;
348
+ }
349
+ getPositionClasses() {
350
+ return `sl-x-${this.positionX} sl-y-${this.positionY}`;
351
+ }
352
+ // ============ RENDER ============
353
+ getThemeColors() {
354
+ if (this.mode === 'light') {
355
+ return {
356
+ bg: '#ffffff',
357
+ text: '#18181b',
358
+ muted: '#71717a',
359
+ border: '#e4e4e7',
360
+ surface: '#f4f4f5',
361
+ };
362
+ }
363
+ return {
364
+ bg: '#18181b',
365
+ text: '#ffffff',
366
+ muted: '#a1a1aa',
367
+ border: '#27272a',
368
+ surface: '#27272a',
369
+ };
370
+ }
371
+ render() {
372
+ const isTransitioning = this.status === 'connecting' || this.status === 'disconnecting';
373
+ const isCallActive = this.status === 'connected' && !this.isTextOnlyMode;
374
+ const theme = this.getThemeColors();
375
+ const cssVars = {
376
+ '--sl-primary': this.primaryColor,
377
+ '--sl-bg': theme.bg,
378
+ '--sl-text': theme.text,
379
+ '--sl-muted': theme.muted,
380
+ '--sl-border': theme.border,
381
+ '--sl-surface': theme.surface,
382
+ };
383
+ if (this.variant === 'inline') {
384
+ return (h("div", { class: "sl-widget sl-inline", style: cssVars }, this.renderCard(isCallActive, isTransitioning)));
385
+ }
386
+ return (h("div", { class: `sl-widget sl-floating ${this.getPositionClasses()}`, style: cssVars }, this.isExpanded ? (h("div", { class: "sl-card" }, this.renderCard(isCallActive, isTransitioning))) : (h("div", { class: "sl-fab-pill" }, h("div", { class: "sl-fab-avatar" }, this.avatarUrl ? (h("img", { src: this.avatarUrl, alt: "", class: "sl-fab-avatar-img" })) : (h("staylift-orb", { size: 48, primaryColor: this.primaryColor, inputVolume: this.inputVolume, outputVolume: this.outputVolume, isActive: isCallActive }))), h("div", { class: "sl-fab-content" }, h("span", { class: "sl-fab-prompt" }, this.fabPrompt), h("button", { class: "sl-fab-btn", onClick: this.handleToggleExpand }, this.fabButtonText))))));
387
+ }
388
+ renderCard(isCallActive, isTransitioning) {
389
+ if (!this.termsAccepted) {
390
+ return this.renderTerms();
391
+ }
392
+ return [
393
+ this.renderHeader(isTransitioning),
394
+ this.renderContent(),
395
+ this.renderFooter(isCallActive, isTransitioning),
396
+ ];
397
+ }
398
+ renderTerms() {
399
+ return (h("div", { class: "sl-terms" }, h("div", { class: "sl-terms-content" }, h("h3", { class: "sl-terms-title" }, this.t('termsTitle')), h("p", { class: "sl-terms-text" }, this.t('termsText')), h("p", { class: "sl-terms-warning" }, this.t('termsWarning'))), h("div", { class: "sl-terms-actions" }, h("button", { class: "sl-terms-btn sl-terms-btn--decline", onClick: this.handleDeclineTerms }, this.t('termsDecline')), h("button", { class: "sl-terms-btn sl-terms-btn--agree", onClick: this.handleAcceptTerms }, this.t('termsAgree')))));
400
+ }
401
+ renderHeader(isTransitioning) {
402
+ return (h("div", { class: "sl-header" }, h("div", { class: "sl-header-left" }, h("div", { class: "sl-orb-ring" }, this.avatarUrl ? (h("img", { src: this.avatarUrl, alt: "", class: "sl-header-avatar-img" })) : (h("staylift-orb", { size: 40, primaryColor: this.primaryColor, inputVolume: this.inputVolume, outputVolume: this.outputVolume, isActive: this.status === 'connected' && !this.isTextOnlyMode }))), h("div", { class: "sl-header-text" }, h("span", { class: "sl-title" }, this.brandName), h("span", { class: "sl-subtitle" }, this.errorMessage ? (h("span", { class: "sl-error" }, this.errorMessage)) : this.status === 'disconnected' ? (this.t('tapToStart')) : this.status === 'connected' ? (h("span", { class: "sl-connected" }, this.t('connected'))) : isTransitioning ? (h("span", { class: "sl-shimmer" }, this.status)) : null))), h("div", { class: "sl-header-right" }, h("div", { class: `sl-dot ${this.status === 'connected' ? 'sl-dot--active' : ''} ${isTransitioning ? 'sl-dot--pulse' : ''}` }), this.variant === 'floating' && (h("button", { class: "sl-close", onClick: this.handleToggleExpand }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("path", { d: "M18 6L6 18M6 6l12 12" })))))));
403
+ }
404
+ renderContent() {
405
+ const isConnecting = this.status === 'connecting';
406
+ const isConnected = this.status === 'connected';
407
+ return (h("div", { class: "sl-content", ref: (el) => this.messagesContainer = el ?? null }, this.messages.length === 0 ? (h("div", { class: "sl-empty" }, h("staylift-orb", { size: 48, primaryColor: this.primaryColor, isActive: false }), h("h3", { class: "sl-empty-title" }, isConnecting ? this.t('starting') : isConnected ? this.t('talkOrType') : this.t('emptyTitle')), h("p", { class: "sl-empty-desc" }, isConnecting ? this.t('connecting') : isConnected ? this.t('ready') : this.t('emptyDesc')))) : (this.messages.map((message, index) => (h("div", { class: `sl-msg sl-msg--${message.role}`, key: index }, h("div", { class: "sl-msg-row" }, h("div", { class: "sl-msg-bubble" }, message.content), message.role === 'assistant' && (h("div", { class: "sl-msg-orb" }, h("staylift-orb", { size: 24, primaryColor: this.primaryColor, isActive: false })))), message.role === 'assistant' && (h("div", { class: "sl-msg-actions" }, h("button", { class: "sl-action", onClick: () => this.copyToClipboard(message.content, index) }, this.copiedIndex === index ? (h("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("polyline", { points: "20 6 9 17 4 12" }))) : (h("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }), h("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" }))))))))))));
408
+ }
409
+ renderFooter(isCallActive, isTransitioning) {
410
+ return (h("div", { class: "sl-footer" }, this.showBranding && (h("div", { class: "sl-branding" }, h("a", { href: "https://staylift.com", target: "_blank", rel: "noopener noreferrer" }, this.t('poweredBy')))), h("div", { class: "sl-input-row" }, h("input", { type: "text", class: "sl-input", placeholder: this.t('placeholder'), value: this.inputText, onInput: this.handleInputChange, onKeyDown: this.handleInputKeyDown, disabled: isTransitioning }), h("button", { class: "sl-btn", onClick: () => this.handleSendText(), disabled: !this.inputText.trim() || isTransitioning }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("line", { x1: "22", y1: "2", x2: "11", y2: "13" }), h("polygon", { points: "22 2 15 22 11 13 2 9 22 2" }))), !isCallActive ? (h("button", { class: "sl-btn", onClick: () => this.handleVoiceButton(), disabled: isTransitioning }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("path", { d: "M2 10v3" }), h("path", { d: "M6 6v11" }), h("path", { d: "M10 3v18" }), h("path", { d: "M14 8v7" }), h("path", { d: "M18 5v13" }), h("path", { d: "M22 10v3" })))) : (h("button", { class: "sl-btn sl-btn--end", onClick: () => this.handleVoiceButton(), disabled: isTransitioning }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("path", { d: "M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.63A2 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.91" }), h("line", { x1: "22", y1: "2", x2: "2", y2: "22" })))))));
411
+ }
412
+ static get is() { return "staylift-widget"; }
413
+ static get encapsulation() { return "shadow"; }
414
+ static get originalStyleUrls() {
415
+ return {
416
+ "$": ["staylift-widget.css"]
417
+ };
418
+ }
419
+ static get styleUrls() {
420
+ return {
421
+ "$": ["staylift-widget.css"]
422
+ };
423
+ }
424
+ static get properties() {
425
+ return {
426
+ "agentId": {
427
+ "type": "string",
428
+ "mutable": false,
429
+ "complexType": {
430
+ "original": "string",
431
+ "resolved": "string",
432
+ "references": {}
433
+ },
434
+ "required": true,
435
+ "optional": false,
436
+ "docs": {
437
+ "tags": [],
438
+ "text": ""
439
+ },
440
+ "getter": false,
441
+ "setter": false,
442
+ "reflect": false,
443
+ "attribute": "agent-id"
444
+ },
445
+ "positionX": {
446
+ "type": "string",
447
+ "mutable": false,
448
+ "complexType": {
449
+ "original": "WidgetPositionX",
450
+ "resolved": "\"center\" | \"left\" | \"right\"",
451
+ "references": {
452
+ "WidgetPositionX": {
453
+ "location": "local",
454
+ "path": "/Users/maciejkonczal/Documents/Projects/staylift-widget/src/components/staylift-widget/staylift-widget.tsx",
455
+ "id": "src/components/staylift-widget/staylift-widget.tsx::WidgetPositionX"
456
+ }
457
+ }
458
+ },
459
+ "required": false,
460
+ "optional": false,
461
+ "docs": {
462
+ "tags": [],
463
+ "text": ""
464
+ },
465
+ "getter": false,
466
+ "setter": false,
467
+ "reflect": false,
468
+ "attribute": "position-x",
469
+ "defaultValue": "'right'"
470
+ },
471
+ "positionY": {
472
+ "type": "string",
473
+ "mutable": false,
474
+ "complexType": {
475
+ "original": "WidgetPositionY",
476
+ "resolved": "\"bottom\" | \"top\"",
477
+ "references": {
478
+ "WidgetPositionY": {
479
+ "location": "local",
480
+ "path": "/Users/maciejkonczal/Documents/Projects/staylift-widget/src/components/staylift-widget/staylift-widget.tsx",
481
+ "id": "src/components/staylift-widget/staylift-widget.tsx::WidgetPositionY"
482
+ }
483
+ }
484
+ },
485
+ "required": false,
486
+ "optional": false,
487
+ "docs": {
488
+ "tags": [],
489
+ "text": ""
490
+ },
491
+ "getter": false,
492
+ "setter": false,
493
+ "reflect": false,
494
+ "attribute": "position-y",
495
+ "defaultValue": "'bottom'"
496
+ },
497
+ "variant": {
498
+ "type": "string",
499
+ "mutable": false,
500
+ "complexType": {
501
+ "original": "WidgetVariant",
502
+ "resolved": "\"floating\" | \"inline\"",
503
+ "references": {
504
+ "WidgetVariant": {
505
+ "location": "local",
506
+ "path": "/Users/maciejkonczal/Documents/Projects/staylift-widget/src/components/staylift-widget/staylift-widget.tsx",
507
+ "id": "src/components/staylift-widget/staylift-widget.tsx::WidgetVariant"
508
+ }
509
+ }
510
+ },
511
+ "required": false,
512
+ "optional": false,
513
+ "docs": {
514
+ "tags": [],
515
+ "text": ""
516
+ },
517
+ "getter": false,
518
+ "setter": false,
519
+ "reflect": false,
520
+ "attribute": "variant",
521
+ "defaultValue": "'floating'"
522
+ },
523
+ "mode": {
524
+ "type": "string",
525
+ "mutable": false,
526
+ "complexType": {
527
+ "original": "WidgetMode",
528
+ "resolved": "\"dark\" | \"light\"",
529
+ "references": {
530
+ "WidgetMode": {
531
+ "location": "local",
532
+ "path": "/Users/maciejkonczal/Documents/Projects/staylift-widget/src/components/staylift-widget/staylift-widget.tsx",
533
+ "id": "src/components/staylift-widget/staylift-widget.tsx::WidgetMode"
534
+ }
535
+ }
536
+ },
537
+ "required": false,
538
+ "optional": false,
539
+ "docs": {
540
+ "tags": [],
541
+ "text": ""
542
+ },
543
+ "getter": false,
544
+ "setter": false,
545
+ "reflect": false,
546
+ "attribute": "mode",
547
+ "defaultValue": "'dark'"
548
+ },
549
+ "primaryColor": {
550
+ "type": "string",
551
+ "mutable": false,
552
+ "complexType": {
553
+ "original": "string",
554
+ "resolved": "string",
555
+ "references": {}
556
+ },
557
+ "required": false,
558
+ "optional": false,
559
+ "docs": {
560
+ "tags": [],
561
+ "text": ""
562
+ },
563
+ "getter": false,
564
+ "setter": false,
565
+ "reflect": false,
566
+ "attribute": "primary-color",
567
+ "defaultValue": "'#6366f1'"
568
+ },
569
+ "brandName": {
570
+ "type": "string",
571
+ "mutable": false,
572
+ "complexType": {
573
+ "original": "string",
574
+ "resolved": "string",
575
+ "references": {}
576
+ },
577
+ "required": false,
578
+ "optional": false,
579
+ "docs": {
580
+ "tags": [],
581
+ "text": ""
582
+ },
583
+ "getter": false,
584
+ "setter": false,
585
+ "reflect": false,
586
+ "attribute": "brand-name",
587
+ "defaultValue": "'Customer Support'"
588
+ },
589
+ "language": {
590
+ "type": "string",
591
+ "mutable": false,
592
+ "complexType": {
593
+ "original": "string",
594
+ "resolved": "string",
595
+ "references": {}
596
+ },
597
+ "required": false,
598
+ "optional": false,
599
+ "docs": {
600
+ "tags": [],
601
+ "text": ""
602
+ },
603
+ "getter": false,
604
+ "setter": false,
605
+ "reflect": false,
606
+ "attribute": "language",
607
+ "defaultValue": "'en'"
608
+ },
609
+ "autoExpand": {
610
+ "type": "boolean",
611
+ "mutable": false,
612
+ "complexType": {
613
+ "original": "boolean",
614
+ "resolved": "boolean",
615
+ "references": {}
616
+ },
617
+ "required": false,
618
+ "optional": false,
619
+ "docs": {
620
+ "tags": [],
621
+ "text": ""
622
+ },
623
+ "getter": false,
624
+ "setter": false,
625
+ "reflect": false,
626
+ "attribute": "auto-expand",
627
+ "defaultValue": "false"
628
+ },
629
+ "showBranding": {
630
+ "type": "boolean",
631
+ "mutable": false,
632
+ "complexType": {
633
+ "original": "boolean",
634
+ "resolved": "boolean",
635
+ "references": {}
636
+ },
637
+ "required": false,
638
+ "optional": false,
639
+ "docs": {
640
+ "tags": [],
641
+ "text": ""
642
+ },
643
+ "getter": false,
644
+ "setter": false,
645
+ "reflect": false,
646
+ "attribute": "show-branding",
647
+ "defaultValue": "true"
648
+ },
649
+ "avatarUrl": {
650
+ "type": "string",
651
+ "mutable": false,
652
+ "complexType": {
653
+ "original": "string",
654
+ "resolved": "string | undefined",
655
+ "references": {}
656
+ },
657
+ "required": false,
658
+ "optional": true,
659
+ "docs": {
660
+ "tags": [],
661
+ "text": ""
662
+ },
663
+ "getter": false,
664
+ "setter": false,
665
+ "reflect": false,
666
+ "attribute": "avatar-url"
667
+ },
668
+ "fabPrompt": {
669
+ "type": "string",
670
+ "mutable": false,
671
+ "complexType": {
672
+ "original": "string",
673
+ "resolved": "string",
674
+ "references": {}
675
+ },
676
+ "required": false,
677
+ "optional": false,
678
+ "docs": {
679
+ "tags": [],
680
+ "text": ""
681
+ },
682
+ "getter": false,
683
+ "setter": false,
684
+ "reflect": false,
685
+ "attribute": "fab-prompt",
686
+ "defaultValue": "'Do you need help?'"
687
+ },
688
+ "fabButtonText": {
689
+ "type": "string",
690
+ "mutable": false,
691
+ "complexType": {
692
+ "original": "string",
693
+ "resolved": "string",
694
+ "references": {}
695
+ },
696
+ "required": false,
697
+ "optional": false,
698
+ "docs": {
699
+ "tags": [],
700
+ "text": ""
701
+ },
702
+ "getter": false,
703
+ "setter": false,
704
+ "reflect": false,
705
+ "attribute": "fab-button-text",
706
+ "defaultValue": "'Start'"
707
+ }
708
+ };
709
+ }
710
+ static get states() {
711
+ return {
712
+ "status": {},
713
+ "isExpanded": {},
714
+ "termsAccepted": {},
715
+ "errorMessage": {},
716
+ "inputVolume": {},
717
+ "outputVolume": {},
718
+ "messages": {},
719
+ "inputText": {},
720
+ "copiedIndex": {}
721
+ };
722
+ }
723
+ static get events() {
724
+ return [{
725
+ "method": "conversationStarted",
726
+ "name": "conversationStarted",
727
+ "bubbles": true,
728
+ "cancelable": true,
729
+ "composed": true,
730
+ "docs": {
731
+ "tags": [],
732
+ "text": ""
733
+ },
734
+ "complexType": {
735
+ "original": "void",
736
+ "resolved": "void",
737
+ "references": {}
738
+ }
739
+ }, {
740
+ "method": "conversationEnded",
741
+ "name": "conversationEnded",
742
+ "bubbles": true,
743
+ "cancelable": true,
744
+ "composed": true,
745
+ "docs": {
746
+ "tags": [],
747
+ "text": ""
748
+ },
749
+ "complexType": {
750
+ "original": "void",
751
+ "resolved": "void",
752
+ "references": {}
753
+ }
754
+ }, {
755
+ "method": "widgetError",
756
+ "name": "widgetError",
757
+ "bubbles": true,
758
+ "cancelable": true,
759
+ "composed": true,
760
+ "docs": {
761
+ "tags": [],
762
+ "text": ""
763
+ },
764
+ "complexType": {
765
+ "original": "{ message: string; code?: string }",
766
+ "resolved": "{ message: string; code?: string | undefined; }",
767
+ "references": {}
768
+ }
769
+ }, {
770
+ "method": "statusChanged",
771
+ "name": "statusChanged",
772
+ "bubbles": true,
773
+ "cancelable": true,
774
+ "composed": true,
775
+ "docs": {
776
+ "tags": [],
777
+ "text": ""
778
+ },
779
+ "complexType": {
780
+ "original": "WidgetStatus",
781
+ "resolved": "\"connected\" | \"connecting\" | \"disconnected\" | \"disconnecting\"",
782
+ "references": {
783
+ "WidgetStatus": {
784
+ "location": "local",
785
+ "path": "/Users/maciejkonczal/Documents/Projects/staylift-widget/src/components/staylift-widget/staylift-widget.tsx",
786
+ "id": "src/components/staylift-widget/staylift-widget.tsx::WidgetStatus"
787
+ }
788
+ }
789
+ }
790
+ }, {
791
+ "method": "messageReceived",
792
+ "name": "messageReceived",
793
+ "bubbles": true,
794
+ "cancelable": true,
795
+ "composed": true,
796
+ "docs": {
797
+ "tags": [],
798
+ "text": ""
799
+ },
800
+ "complexType": {
801
+ "original": "ChatMessage",
802
+ "resolved": "ChatMessage",
803
+ "references": {
804
+ "ChatMessage": {
805
+ "location": "global",
806
+ "id": "global::ChatMessage"
807
+ }
808
+ }
809
+ }
810
+ }];
811
+ }
812
+ static get methods() {
813
+ return {
814
+ "startConversation": {
815
+ "complexType": {
816
+ "signature": "(textOnly?: boolean) => Promise<void>",
817
+ "parameters": [{
818
+ "name": "textOnly",
819
+ "type": "boolean",
820
+ "docs": ""
821
+ }],
822
+ "references": {
823
+ "Promise": {
824
+ "location": "global",
825
+ "id": "global::Promise"
826
+ }
827
+ },
828
+ "return": "Promise<void>"
829
+ },
830
+ "docs": {
831
+ "text": "",
832
+ "tags": []
833
+ }
834
+ },
835
+ "endConversation": {
836
+ "complexType": {
837
+ "signature": "() => Promise<void>",
838
+ "parameters": [],
839
+ "references": {
840
+ "Promise": {
841
+ "location": "global",
842
+ "id": "global::Promise"
843
+ }
844
+ },
845
+ "return": "Promise<void>"
846
+ },
847
+ "docs": {
848
+ "text": "",
849
+ "tags": []
850
+ }
851
+ },
852
+ "getStatus": {
853
+ "complexType": {
854
+ "signature": "() => Promise<WidgetStatus>",
855
+ "parameters": [],
856
+ "references": {
857
+ "Promise": {
858
+ "location": "global",
859
+ "id": "global::Promise"
860
+ },
861
+ "WidgetStatus": {
862
+ "location": "local",
863
+ "path": "/Users/maciejkonczal/Documents/Projects/staylift-widget/src/components/staylift-widget/staylift-widget.tsx",
864
+ "id": "src/components/staylift-widget/staylift-widget.tsx::WidgetStatus"
865
+ }
866
+ },
867
+ "return": "Promise<WidgetStatus>"
868
+ },
869
+ "docs": {
870
+ "text": "",
871
+ "tags": []
872
+ }
873
+ },
874
+ "sendMessage": {
875
+ "complexType": {
876
+ "signature": "(text: string) => Promise<void>",
877
+ "parameters": [{
878
+ "name": "text",
879
+ "type": "string",
880
+ "docs": ""
881
+ }],
882
+ "references": {
883
+ "Promise": {
884
+ "location": "global",
885
+ "id": "global::Promise"
886
+ }
887
+ },
888
+ "return": "Promise<void>"
889
+ },
890
+ "docs": {
891
+ "text": "",
892
+ "tags": []
893
+ }
894
+ }
895
+ };
896
+ }
897
+ static get elementRef() { return "el"; }
898
+ }