@myned-ai/avatar-chat-widget 0.6.0 → 0.7.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.
@@ -41,6 +41,7 @@ declare class AvatarChatElement extends HTMLElement {
41
41
  private _isMounted;
42
42
  private _isConnected;
43
43
  private _isCollapsed;
44
+ private visualViewportHandler;
44
45
  constructor();
45
46
  /**
46
47
  * Configure the widget (call before mount)
@@ -75,10 +76,23 @@ declare class AvatarChatElement extends HTMLElement {
75
76
  * Setup UI event listeners
76
77
  */
77
78
  private setupUIEvents;
79
+ /**
80
+ * Handle mobile keyboard appearance using VisualViewport API
81
+ * Resizes the avatar to fit when keyboard opens
82
+ */
83
+ private setupMobileKeyboardHandling;
78
84
  /**
79
85
  * Escape HTML to prevent XSS in user-provided suggestions
80
86
  */
81
87
  private escapeHtml;
88
+ /**
89
+ * Generate CSS overrides for primaryColor and secondaryColor config options
90
+ */
91
+ private generateColorOverrides;
92
+ /**
93
+ * Darken a hex color by a percentage
94
+ */
95
+ private darkenColor;
82
96
  /**
83
97
  * Mark that conversation has messages (shows chat area instead of suggestions)
84
98
  */
@@ -3566,8 +3566,6 @@ class VoiceInputController {
3566
3566
  }
3567
3567
  try {
3568
3568
  log$5.info("Starting recording (PCM16 24kHz)");
3569
- this.protocolClient.sendAudioStreamStart();
3570
- log$5.info("Sent audio_stream_start to server");
3571
3569
  let chunkCount = 0;
3572
3570
  await this.audioInput.startRecording((audioData) => {
3573
3571
  chunkCount++;
@@ -3576,15 +3574,22 @@ class VoiceInputController {
3576
3574
  }
3577
3575
  this.protocolClient.sendAudioData(audioData);
3578
3576
  }, "pcm16");
3579
- log$5.info("Recording started successfully");
3577
+ this.protocolClient.sendAudioStreamStart();
3578
+ log$5.info("Recording started successfully, sent audio_stream_start to server");
3580
3579
  this.setRecordingState(true);
3581
3580
  } catch (error2) {
3582
3581
  log$5.error("Failed to start recording:", error2);
3583
3582
  errorBoundary.handleError(error2, "audio-input");
3584
3583
  this.options.onError?.(error2);
3584
+ const errorName = error2.name || "";
3585
3585
  const errorMsg = error2.message || "";
3586
- if (errorMsg.includes("Permission denied") || errorMsg.includes("NotAllowedError")) {
3586
+ const errorStr = `${errorName} ${errorMsg}`.toLowerCase();
3587
+ if (errorStr.includes("policy") || errorStr.includes("not allowed in this document")) {
3588
+ alert("Microphone is blocked by this website's security settings. The site owner needs to enable microphone access.");
3589
+ } else if (errorName === "NotAllowedError" || errorStr.includes("permission denied") || errorStr.includes("denied")) {
3587
3590
  alert("Microphone access was denied. Please allow microphone access in your browser settings to use voice input.");
3591
+ } else {
3592
+ alert("Could not access microphone. Please check your browser settings and try again.");
3588
3593
  }
3589
3594
  }
3590
3595
  }
@@ -4249,6 +4254,7 @@ const WIDGET_STYLES = `
4249
4254
  box-sizing: border-box;
4250
4255
  --primary-color: #4B4ACF;
4251
4256
  --primary-gradient: linear-gradient(135deg, #4B4ACF 0%, #2E3A87 100%);
4257
+ --secondary-color: #1F2937;
4252
4258
  --bg-color: #ffffff;
4253
4259
  --text-color: #1F2937;
4254
4260
  --input-bg: #f5f5f7;
@@ -4414,7 +4420,7 @@ const WIDGET_STYLES = `
4414
4420
  margin: 0;
4415
4421
  font-size: 16px;
4416
4422
  font-weight: 700;
4417
- color: var(--text-color);
4423
+ color: var(--secondary-color);
4418
4424
  letter-spacing: -0.01em;
4419
4425
  }
4420
4426
 
@@ -4467,7 +4473,7 @@ const WIDGET_STYLES = `
4467
4473
  .control-btn {
4468
4474
  background: transparent;
4469
4475
  border: none;
4470
- color: var(--text-color);
4476
+ color: var(--secondary-color);
4471
4477
  width: 32px;
4472
4478
  height: 32px;
4473
4479
  border-radius: 50%;
@@ -5233,7 +5239,7 @@ const WIDGET_STYLES = `
5233
5239
  display: flex;
5234
5240
  align-items: center;
5235
5241
  justify-content: center;
5236
- background: linear-gradient(135deg, #4B4ACF 0%, #2E3A87 100%);
5242
+ background: var(--primary-gradient);
5237
5243
  color: white;
5238
5244
  }
5239
5245
 
@@ -5503,6 +5509,47 @@ const WIDGET_STYLES = `
5503
5509
  white-space: nowrap;
5504
5510
  border-width: 0;
5505
5511
  }
5512
+
5513
+ /* ==========================================================================
5514
+ Mobile Keyboard Visible State
5515
+ Resize avatar when virtual keyboard appears on mobile
5516
+ ========================================================================== */
5517
+ @media (max-width: 480px) {
5518
+ /* When keyboard is visible, shrink the avatar section */
5519
+ .widget-root.keyboard-visible {
5520
+ --avatar-height: 180px;
5521
+ }
5522
+
5523
+ /* Scale down the avatar render and push it down */
5524
+ .widget-root.keyboard-visible .avatar-render-container {
5525
+ transform: translate(-50%, -35%) scale(0.65) !important;
5526
+ transition: transform 0.3s ease;
5527
+ }
5528
+
5529
+ /* Show subtitles but position them properly */
5530
+ .widget-root.keyboard-visible .avatar-subtitles {
5531
+ display: block !important;
5532
+ bottom: 100px !important;
5533
+ font-size: 14px;
5534
+ }
5535
+
5536
+ /* Hide mist to save space */
5537
+ .widget-root.keyboard-visible .avatar-mist-overlay {
5538
+ display: none !important;
5539
+ }
5540
+
5541
+ /* Move suggestions closer to input */
5542
+ .widget-root.keyboard-visible .avatar-suggestions {
5543
+ bottom: 95px !important;
5544
+ }
5545
+
5546
+ /* In text-focus mode with keyboard, ensure chat doesn't overflow */
5547
+ .widget-root.keyboard-visible[data-drawer-state="text-focus"] .chat-section {
5548
+ flex: 1 1 auto !important;
5549
+ min-height: 0 !important;
5550
+ height: auto !important;
5551
+ }
5552
+ }
5506
5553
  `;
5507
5554
  const LAYOUT = {
5508
5555
  /** Header bar height - contains title, status, minimize button */
@@ -5795,6 +5842,7 @@ class AvatarChatElement extends HTMLElement {
5795
5842
  this._isMounted = false;
5796
5843
  this._isConnected = false;
5797
5844
  this._isCollapsed = false;
5845
+ this.visualViewportHandler = null;
5798
5846
  this.shadow = this.attachShadow({ mode: "open" });
5799
5847
  }
5800
5848
  /**
@@ -5823,8 +5871,9 @@ class AvatarChatElement extends HTMLElement {
5823
5871
  return;
5824
5872
  }
5825
5873
  log$2.info("Mounting widget");
5874
+ const colorOverrides = this.generateColorOverrides();
5826
5875
  const styleEl = document.createElement("style");
5827
- styleEl.textContent = WIDGET_STYLES + (this.config.customStyles || "");
5876
+ styleEl.textContent = WIDGET_STYLES + colorOverrides + (this.config.customStyles || "");
5828
5877
  this.shadow.appendChild(styleEl);
5829
5878
  if (this.config.position && this.config.position !== "inline") {
5830
5879
  this.classList.add(`position-${this.config.position}`);
@@ -5858,6 +5907,7 @@ class AvatarChatElement extends HTMLElement {
5858
5907
  this.shadow.appendChild(root);
5859
5908
  this.initializeDrawer();
5860
5909
  this.setupUIEvents();
5910
+ this.setupMobileKeyboardHandling();
5861
5911
  if (!this.config.enableVoice) {
5862
5912
  const voiceBtn = this.shadow.getElementById("micBtn");
5863
5913
  if (voiceBtn) voiceBtn.style.display = "none";
@@ -6092,6 +6142,34 @@ class AvatarChatElement extends HTMLElement {
6092
6142
  });
6093
6143
  }
6094
6144
  }
6145
+ /**
6146
+ * Handle mobile keyboard appearance using VisualViewport API
6147
+ * Resizes the avatar to fit when keyboard opens
6148
+ */
6149
+ setupMobileKeyboardHandling() {
6150
+ if (!window.visualViewport) {
6151
+ return;
6152
+ }
6153
+ const widgetRoot = this.shadow.querySelector(".widget-root");
6154
+ if (!widgetRoot) {
6155
+ return;
6156
+ }
6157
+ let initialViewportHeight = window.visualViewport.height;
6158
+ const handleViewportChange = () => {
6159
+ const viewport = window.visualViewport;
6160
+ const currentHeight = viewport.height;
6161
+ const keyboardHeight = initialViewportHeight - currentHeight;
6162
+ if (keyboardHeight > 150) {
6163
+ widgetRoot.classList.add("keyboard-visible");
6164
+ } else {
6165
+ widgetRoot.classList.remove("keyboard-visible");
6166
+ initialViewportHeight = currentHeight;
6167
+ }
6168
+ };
6169
+ window.visualViewport.addEventListener("resize", handleViewportChange);
6170
+ window.visualViewport.addEventListener("scroll", handleViewportChange);
6171
+ this.visualViewportHandler = handleViewportChange;
6172
+ }
6095
6173
  /**
6096
6174
  * Escape HTML to prevent XSS in user-provided suggestions
6097
6175
  */
@@ -6100,6 +6178,36 @@ class AvatarChatElement extends HTMLElement {
6100
6178
  div.textContent = text;
6101
6179
  return div.innerHTML;
6102
6180
  }
6181
+ /**
6182
+ * Generate CSS overrides for primaryColor and secondaryColor config options
6183
+ */
6184
+ generateColorOverrides() {
6185
+ const overrides = [];
6186
+ if (this.config.primaryColor) {
6187
+ const primary = this.config.primaryColor;
6188
+ const darkerShade = this.darkenColor(primary, 0.2);
6189
+ overrides.push(`--primary-color: ${primary};`);
6190
+ overrides.push(`--primary-gradient: linear-gradient(135deg, ${primary} 0%, ${darkerShade} 100%);`);
6191
+ }
6192
+ if (this.config.secondaryColor) {
6193
+ overrides.push(`--secondary-color: ${this.config.secondaryColor};`);
6194
+ }
6195
+ if (overrides.length === 0) return "";
6196
+ return `:host { ${overrides.join(" ")} }`;
6197
+ }
6198
+ /**
6199
+ * Darken a hex color by a percentage
6200
+ */
6201
+ darkenColor(hex, percent) {
6202
+ hex = hex.replace(/^#/, "");
6203
+ let r = parseInt(hex.substring(0, 2), 16);
6204
+ let g = parseInt(hex.substring(2, 4), 16);
6205
+ let b = parseInt(hex.substring(4, 6), 16);
6206
+ r = Math.max(0, Math.floor(r * (1 - percent)));
6207
+ g = Math.max(0, Math.floor(g * (1 - percent)));
6208
+ b = Math.max(0, Math.floor(b * (1 - percent)));
6209
+ return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
6210
+ }
6103
6211
  /**
6104
6212
  * Mark that conversation has messages (shows chat area instead of suggestions)
6105
6213
  */