@staylift-tech/conv-widget 0.0.1 → 0.0.3

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.
@@ -28,13 +28,13 @@
28
28
  }
29
29
 
30
30
  /* Horizontal positioning */
31
- .sl-x-left { left: 20px; right: auto; }
31
+ .sl-x-left { left: 40px; right: auto; }
32
32
  .sl-x-center { left: 50%; right: auto; transform: translateX(-50%); }
33
- .sl-x-right { right: 20px; left: auto; }
33
+ .sl-x-right { right: 40px; left: auto; }
34
34
 
35
35
  /* Vertical positioning */
36
- .sl-y-top { top: 20px; bottom: auto; }
37
- .sl-y-bottom { bottom: 20px; top: auto; }
36
+ .sl-y-top { top: 40px; bottom: auto; }
37
+ .sl-y-bottom { bottom: 40px; top: auto; }
38
38
 
39
39
  /* Old circular FAB (kept for reference) */
40
40
  .sl-fab {
@@ -99,6 +99,9 @@
99
99
  }
100
100
 
101
101
  .sl-fab-btn {
102
+ display: flex;
103
+ align-items: center;
104
+ gap: 6px;
102
105
  padding: 8px 20px;
103
106
  border: none;
104
107
  border-radius: 20px;
@@ -118,8 +121,8 @@
118
121
  /* ============ CARD ============ */
119
122
 
120
123
  .sl-card {
121
- width: 400px;
122
- height: 380px;
124
+ width: 440px;
125
+ height: 440px;
123
126
  max-height: calc(100vh - 100px);
124
127
  background: var(--sl-bg);
125
128
  border-radius: 16px;
@@ -133,8 +136,8 @@
133
136
 
134
137
  .sl-inline {
135
138
  width: 100%;
136
- max-width: 500px;
137
- height: 380px;
139
+ max-width: 540px;
140
+ height: 440px;
138
141
  background: var(--sl-bg);
139
142
  border-radius: 16px;
140
143
  border: 1px solid var(--sl-border);
@@ -278,18 +281,16 @@
278
281
  .sl-content {
279
282
  flex: 1;
280
283
  overflow-y: auto;
281
- padding: 24px;
284
+ padding: 12px 24px 24px;
282
285
  display: flex;
283
286
  flex-direction: column;
284
287
  gap: 8px;
285
288
  }
286
289
 
287
290
  .sl-empty {
288
- flex: 1;
289
291
  display: flex;
290
292
  flex-direction: column;
291
293
  align-items: center;
292
- justify-content: center;
293
294
  text-align: center;
294
295
  gap: 12px;
295
296
  }
@@ -307,6 +308,84 @@
307
308
  color: var(--sl-muted);
308
309
  }
309
310
 
311
+ /* ============ MODE TOGGLE ============ */
312
+
313
+ .sl-mode-toggle {
314
+ display: flex;
315
+ gap: 8px;
316
+ margin-bottom: 12px;
317
+ padding: 4px;
318
+ background: var(--sl-surface);
319
+ border-radius: 10px;
320
+ }
321
+
322
+ .sl-mode-btn {
323
+ display: flex;
324
+ align-items: center;
325
+ gap: 6px;
326
+ padding: 8px 16px;
327
+ border: none;
328
+ border-radius: 8px;
329
+ background: transparent;
330
+ color: var(--sl-muted);
331
+ font-size: 13px;
332
+ font-weight: 500;
333
+ cursor: pointer;
334
+ transition: all 0.2s;
335
+ }
336
+
337
+ .sl-mode-btn:hover {
338
+ color: var(--sl-text);
339
+ }
340
+
341
+ .sl-mode-btn--active {
342
+ background: var(--sl-bg);
343
+ color: var(--sl-text);
344
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
345
+ }
346
+
347
+ /* ============ VOICE CONTROLS ============ */
348
+
349
+ .sl-voice-controls {
350
+ display: flex;
351
+ justify-content: center;
352
+ }
353
+
354
+ .sl-voice-btn {
355
+ display: flex;
356
+ align-items: center;
357
+ justify-content: center;
358
+ gap: 10px;
359
+ width: 100%;
360
+ padding: 14px 24px;
361
+ border: none;
362
+ border-radius: 12px;
363
+ background: var(--sl-primary);
364
+ color: white;
365
+ font-size: 14px;
366
+ font-weight: 500;
367
+ cursor: pointer;
368
+ transition: all 0.2s;
369
+ }
370
+
371
+ .sl-voice-btn:hover:not(:disabled) {
372
+ filter: brightness(1.1);
373
+ transform: translateY(-1px);
374
+ }
375
+
376
+ .sl-voice-btn:disabled {
377
+ opacity: 0.6;
378
+ cursor: not-allowed;
379
+ }
380
+
381
+ .sl-voice-btn--end {
382
+ background: var(--sl-danger);
383
+ }
384
+
385
+ .sl-voice-btn--end:hover:not(:disabled) {
386
+ filter: brightness(1.1);
387
+ }
388
+
310
389
  /* ============ MESSAGES ============ */
311
390
 
312
391
  .sl-msg {
@@ -351,15 +430,6 @@
351
430
  border-bottom-left-radius: 4px;
352
431
  }
353
432
 
354
- .sl-msg-orb {
355
- width: 24px;
356
- height: 24px;
357
- border-radius: 50%;
358
- border: 1px solid var(--sl-border);
359
- overflow: hidden;
360
- flex-shrink: 0;
361
- }
362
-
363
433
  .sl-msg-actions {
364
434
  display: flex;
365
435
  gap: 4px;
@@ -395,10 +465,14 @@
395
465
 
396
466
  .sl-branding {
397
467
  text-align: center;
398
- margin-bottom: 12px;
468
+ margin-top: 8px;
469
+ padding-top: 8px;
399
470
  }
400
471
 
401
472
  .sl-branding a {
473
+ display: inline-flex;
474
+ align-items: center;
475
+ gap: 6px;
402
476
  font-size: 11px;
403
477
  color: var(--sl-muted);
404
478
  text-decoration: none;
@@ -410,6 +484,13 @@
410
484
  opacity: 1;
411
485
  }
412
486
 
487
+ .sl-branding-logo {
488
+ width: 16px;
489
+ height: 16px;
490
+ border-radius: 3px;
491
+ object-fit: cover;
492
+ }
493
+
413
494
  .sl-input-row {
414
495
  display: flex;
415
496
  align-items: center;
@@ -1,5 +1,5 @@
1
1
  import { h } from "@stencil/core";
2
- import { Conversation } from "@elevenlabs/client";
2
+ import { TextConversation, VoiceConversation } from "@elevenlabs/client";
3
3
  export class StayliftWidget {
4
4
  constructor() {
5
5
  this.positionX = 'right';
@@ -23,6 +23,7 @@ export class StayliftWidget {
23
23
  this.messages = [];
24
24
  this.inputText = '';
25
25
  this.copiedIndex = null;
26
+ this.selectedMode = 'text';
26
27
  // ============ PRIVATE ============
27
28
  this.conversation = null;
28
29
  this.volumeInterval = null;
@@ -126,12 +127,17 @@ export class StayliftWidget {
126
127
  const timeoutId = setTimeout(() => {
127
128
  rejectConnected(new Error('Connection timeout'));
128
129
  }, CONNECTION_TIMEOUT);
129
- this.conversation = await Conversation.startSession({
130
+ // Use TextConversation for text-only mode (no mic access), VoiceConversation for voice
131
+ const ConversationClass = textOnly ? TextConversation : VoiceConversation;
132
+ this.conversation = await ConversationClass.startSession({
130
133
  agentId: this.agentId,
131
134
  connectionType: textOnly ? 'websocket' : 'webrtc',
132
135
  overrides: {
133
- conversation: { textOnly },
134
- agent: { firstMessage: textOnly ? '' : undefined },
136
+ conversation: {
137
+ textOnly,
138
+ // Enable streaming text responses for text mode
139
+ client_events: textOnly ? ['agent_response', 'agent_chat_response_part', 'user_transcript'] : undefined,
140
+ },
135
141
  },
136
142
  onStatusChange: (statusEvent) => {
137
143
  const newStatus = statusEvent.status;
@@ -219,15 +225,16 @@ export class StayliftWidget {
219
225
  const msg = text || this.inputText.trim();
220
226
  if (!msg)
221
227
  return;
222
- // If disconnected, start text-only session
228
+ // If disconnected, start session based on selected mode
223
229
  if (this.status === 'disconnected') {
224
230
  const userMessage = { role: 'user', content: msg };
225
231
  this.inputText = '';
226
232
  this.pendingMessage = msg; // Store message to send after connection
227
233
  this.messages = [userMessage];
228
234
  this.scrollToBottom();
235
+ const textOnly = this.selectedMode === 'text';
229
236
  try {
230
- await this.handleStartConversation(true, true);
237
+ await this.handleStartConversation(textOnly, true);
231
238
  // Message will be sent in onConnect callback
232
239
  }
233
240
  catch {
@@ -255,6 +262,14 @@ export class StayliftWidget {
255
262
  await this.handleEndConversation();
256
263
  }
257
264
  }
265
+ async handleTextButton() {
266
+ if (this.status === 'disconnected' || this.status === null) {
267
+ await this.handleStartConversation(true);
268
+ }
269
+ else if (this.status === 'connected') {
270
+ await this.handleEndConversation();
271
+ }
272
+ }
258
273
  startVolumeMonitoring() {
259
274
  this.volumeInterval = setInterval(() => {
260
275
  if (this.conversation && this.status === 'connected') {
@@ -304,6 +319,12 @@ export class StayliftWidget {
304
319
  termsWarning: 'If you do not wish to have your conversations recorded, please refrain from using this service.',
305
320
  termsAgree: 'Agree',
306
321
  termsDecline: 'Decline',
322
+ modeText: 'Text',
323
+ modeVoice: 'Voice',
324
+ startVoice: 'Start Voice Call',
325
+ endVoice: 'End Call',
326
+ startText: 'Start Text Chat',
327
+ endText: 'End Chat',
307
328
  },
308
329
  pl: {
309
330
  microphoneError: 'Proszę włączyć uprawnienia mikrofonu.',
@@ -323,6 +344,12 @@ export class StayliftWidget {
323
344
  termsWarning: 'Jeśli nie chcesz, aby Twoje rozmowy były nagrywane, prosimy o nieużywanie tej usługi.',
324
345
  termsAgree: 'Zgadzam się',
325
346
  termsDecline: 'Odrzuć',
347
+ modeText: 'Tekst',
348
+ modeVoice: 'Głos',
349
+ startVoice: 'Rozpocznij rozmowę głosową',
350
+ endVoice: 'Zakończ',
351
+ startText: 'Rozpocznij czat',
352
+ endText: 'Zakończ czat',
326
353
  },
327
354
  de: {
328
355
  microphoneError: 'Bitte aktivieren Sie die Mikrofonberechtigung in Ihrem Browser.',
@@ -342,6 +369,12 @@ export class StayliftWidget {
342
369
  termsWarning: 'Wenn Sie nicht möchten, dass Ihre Gespräche aufgezeichnet werden, verwenden Sie diesen Dienst bitte nicht.',
343
370
  termsAgree: 'Zustimmen',
344
371
  termsDecline: 'Ablehnen',
372
+ modeText: 'Text',
373
+ modeVoice: 'Stimme',
374
+ startVoice: 'Sprachanruf starten',
375
+ endVoice: 'Beenden',
376
+ startText: 'Text-Chat starten',
377
+ endText: 'Chat beenden',
345
378
  },
346
379
  };
347
380
  return translations[this.language]?.[key] || translations['en'][key] || key;
@@ -381,18 +414,18 @@ export class StayliftWidget {
381
414
  '--sl-surface': theme.surface,
382
415
  };
383
416
  if (this.variant === 'inline') {
384
- return (h("div", { class: "sl-widget sl-inline", style: cssVars }, this.renderCard(isCallActive, isTransitioning)));
417
+ return (h("div", { class: "sl-widget sl-inline", style: cssVars }, this.renderCard(isTransitioning)));
385
418
  }
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))))));
419
+ return (h("div", { class: `sl-widget sl-floating ${this.getPositionClasses()}`, style: cssVars }, this.isExpanded ? (h("div", { class: "sl-card" }, this.renderCard(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 }, h("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("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" })), this.fabButtonText))))));
387
420
  }
388
- renderCard(isCallActive, isTransitioning) {
421
+ renderCard(isTransitioning) {
389
422
  if (!this.termsAccepted) {
390
423
  return this.renderTerms();
391
424
  }
392
425
  return [
393
426
  this.renderHeader(isTransitioning),
394
427
  this.renderContent(),
395
- this.renderFooter(isCallActive, isTransitioning),
428
+ this.renderFooter(isTransitioning),
396
429
  ];
397
430
  }
398
431
  renderTerms() {
@@ -404,10 +437,15 @@ export class StayliftWidget {
404
437
  renderContent() {
405
438
  const isConnecting = this.status === 'connecting';
406
439
  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" }))))))))))));
440
+ 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-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
441
  }
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" })))))));
442
+ renderFooter(isTransitioning) {
443
+ const isDisconnected = this.status === 'disconnected';
444
+ const isConnectedText = this.status === 'connected' && this.isTextOnlyMode;
445
+ const isConnectedVoice = this.status === 'connected' && !this.isTextOnlyMode;
446
+ // Only show text input when connected in text mode
447
+ const showTextInput = isConnectedText;
448
+ return (h("div", { class: "sl-footer" }, isDisconnected && (h("div", { class: "sl-mode-toggle" }, h("button", { class: `sl-mode-btn ${this.selectedMode === 'text' ? 'sl-mode-btn--active' : ''}`, onClick: () => this.selectedMode = 'text' }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })), this.t('modeText')), h("button", { class: `sl-mode-btn ${this.selectedMode === 'voice' ? 'sl-mode-btn--active' : ''}`, onClick: () => this.selectedMode = 'voice' }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }), h("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }), h("line", { x1: "12", y1: "19", x2: "12", y2: "23" }), h("line", { x1: "8", y1: "23", x2: "16", y2: "23" })), this.t('modeVoice')))), showTextInput && (h("div", { class: "sl-input-row" }, h("button", { class: "sl-btn sl-btn--end", onClick: () => this.handleTextButton(), disabled: isTransitioning, title: this.t('endText') }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), h("line", { x1: "6", y1: "6", x2: "18", y2: "18" }))), 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" }))))), this.selectedMode === 'text' && isDisconnected && (h("div", { class: "sl-voice-controls" }, h("button", { class: "sl-voice-btn", onClick: () => this.handleTextButton(), disabled: isTransitioning }, h("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })), isTransitioning ? this.t('connecting') : this.t('startText')))), this.selectedMode === 'voice' && isDisconnected && (h("div", { class: "sl-voice-controls" }, h("button", { class: "sl-voice-btn", onClick: () => this.handleVoiceButton(), disabled: isTransitioning }, h("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, h("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }), h("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }), h("line", { x1: "12", y1: "19", x2: "12", y2: "23" }), h("line", { x1: "8", y1: "23", x2: "16", y2: "23" })), isTransitioning ? this.t('connecting') : this.t('startVoice')))), isConnectedVoice && (h("div", { class: "sl-voice-controls" }, h("button", { class: "sl-voice-btn sl-voice-btn--end", onClick: () => this.handleVoiceButton(), disabled: isTransitioning }, h("svg", { width: "20", height: "20", 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" })), this.t('endVoice')))), this.showBranding && (h("div", { class: "sl-branding" }, h("a", { href: "https://staylift.com", target: "_blank", rel: "noopener noreferrer" }, h("img", { src: "data:image/png;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBMRXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8IAEQgAIAAgAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAMCBAEFAAYHCAkKC//EAMMQAAEDAwIEAwQGBAcGBAgGcwECAAMRBBIhBTETIhAGQVEyFGFxIweBIJFCFaFSM7EkYjAWwXLRQ5I0ggjhU0AlYxc18JNzolBEsoPxJlQ2ZJR0wmDShKMYcOInRTdls1V1pJXDhfLTRnaA40dWZrQJChkaKCkqODk6SElKV1hZWmdoaWp3eHl6hoeIiYqQlpeYmZqgpaanqKmqsLW2t7i5usDExcbHyMnK0NTV1tfY2drg5OXm5+jp6vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAQIAAwQFBgcICQoL/8QAwxEAAgIBAwMDAgMFAgUCBASHAQACEQMQEiEEIDFBEwUwIjJRFEAGMyNhQhVxUjSBUCSRoUOxFgdiNVPw0SVgwUThcvEXgmM2cCZFVJInotIICQoYGRooKSo3ODk6RkdISUpVVldYWVpkZWZnaGlqc3R1dnd4eXqAg4SFhoeIiYqQk5SVlpeYmZqgo6SlpqeoqaqwsrO0tba3uLm6wMLDxMXGx8jJytDT1NXW19jZ2uDi4+Tl5ufo6ery8/T19vf4+fr/2wBDAAICAgICAgMCAgMFAwMDBQYFBQUFBggGBgYGBggKCAgICAgICgoKCgoKCgoMDAwMDAwODg4ODg8PDw8PDw8PDw//2wBDAQICAgQEBAcEBAcQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/2gAMAwEAAhEDEQAAAfiPo6T9gTw/lbxH1R8xAWn0XwXsuXwv2J+cvsHnGXj/AP/aAAgBAQABBQJ2O0btujuvDXiGygZS/B6bCDwr9Yvii93+QRKL5RL8C7HvO8yb3t22f0cO3xxpg2+r22+3Pbre83K/u0XUoD//2gAIAQMRAT8BwnczyRDk/enDhl9/j+j8l+8kutyx9kUA/wD/2gAIAQIRAT8B0OVnO3//2gAIAQEABj8CajttnLc48eWgqo1XV5t08MMftLWggDvtxssUw8hKlH+VTrr9rTFZoWnZ41URJQhMyx+avw8nw7SWKbmWDaf+BASqiVfyR8S7mwljSi1jhISnyTQdNPtfB8GLe1uVxRjXFLwurhcqR5E6dv/EADMQAQADAAICAgICAwEBAAACCwERACExQVFhcYGRobHB8NEQ4fEgMEBQYHCAkKCwwNDg/9oACAEBAAE/IZsYHlgL2hFFykgNxq/8i4rZQAQHMl53M1Hu+FPRCE0H7+OCoZU37nAvVx5U656pS9EIT8wQj3YdyrMaSOzIAl5fmjVxMtl8cUFL/9oADAMBAAIRAxEAABD2kov/xAAzEQEBAQADAAECBQUBAQABAQkBABEhMRBBUWEgcfCRgaGx0cHh8TBAUGBwgJCgsMDQ4P/aAAgBAxEBPxDT3M5sJdI+Ma/bs4t0zZy8v7fT4v/aAAgBAhEBPxBPA2RcX//aAAgBAQABPxCXMUdNxPzQSDHSzRnYYmQIwJUDyoU5qBhvVIgVuSPBo61iI8V5sIdq5DxIPbJQrnR+qCA5rryHQleAAiIk2UhJuwnSp6JKa/KjgYm/NRmPignYSnymSrtfVNewxg4eEjzFnxEX/9k=", alt: "Staylift", class: "sl-branding-logo" }), this.t('poweredBy'))))));
411
449
  }
412
450
  static get is() { return "staylift-widget"; }
413
451
  static get encapsulation() { return "shadow"; }
@@ -717,7 +755,8 @@ export class StayliftWidget {
717
755
  "outputVolume": {},
718
756
  "messages": {},
719
757
  "inputText": {},
720
- "copiedIndex": {}
758
+ "copiedIndex": {},
759
+ "selectedMode": {}
721
760
  };
722
761
  }
723
762
  static get events() {