ttp-agent-sdk 2.36.0 → 2.38.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.
@@ -8349,8 +8349,18 @@ function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new T
8349
8349
  function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
8350
8350
  /**
8351
8351
  * AudioRecorder - Handles audio recording using AudioWorklet
8352
+ *
8353
+ * iOS FIX: Uses a module-level shared AudioContext to avoid the iOS WebKit bug
8354
+ * where closing and immediately recreating an AudioContext causes the new one
8355
+ * to fail silently (getUserMedia returns a "live" stream that produces no audio).
8356
+ * iOS shares a single audio session for all input/output, and AudioContext.close()
8357
+ * doesn't release the hardware synchronously.
8352
8358
  */
8353
8359
 
8360
+ var _sharedRecorderContext = null;
8361
+ var _sharedRecorderSampleRate = null;
8362
+ var _sharedRecorderWorkletLoaded = false;
8363
+ var _sharedRecorderWorkletPath = null;
8354
8364
  var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8355
8365
  function AudioRecorder(config) {
8356
8366
  var _this;
@@ -8361,6 +8371,7 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8361
8371
  _this.audioWorkletNode = null;
8362
8372
  _this.mediaStream = null;
8363
8373
  _this.isRecording = false;
8374
+ _this._starting = false;
8364
8375
  return _this;
8365
8376
  }
8366
8377
 
@@ -8527,20 +8538,28 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8527
8538
  value: (function () {
8528
8539
  var _start = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
8529
8540
  var _this2 = this;
8530
- var permissionState, sampleRate, defaultConstraints, audioConstraints, isConnected, error, _isConnected, _error, processorPath, _isConnected2, _error2, source, userMessage, _permissionState, enhancedError, _t3;
8541
+ var permissionState, sampleRate, defaultConstraints, audioConstraints, isConnected, error, canReuseContext, _isConnected, _error, processorPath, _isConnected2, _error2, source, userMessage, _permissionState, enhancedError, _t3, _t4;
8531
8542
  return _regenerator().w(function (_context3) {
8532
8543
  while (1) switch (_context3.p = _context3.n) {
8533
8544
  case 0:
8534
- _context3.p = 0;
8545
+ if (!this._starting) {
8546
+ _context3.n = 1;
8547
+ break;
8548
+ }
8549
+ console.warn('⚠️ AudioRecorder: start() already in progress, ignoring duplicate call');
8550
+ return _context3.a(2);
8551
+ case 1:
8552
+ this._starting = true;
8553
+ _context3.p = 2;
8535
8554
  // Always try to request permission - we call getUserMedia every time
8536
8555
  // This ensures the prompt appears if the permission state allows it
8537
8556
  // Even if previously denied, the user might have changed settings, so we always try
8538
8557
  console.log('🎤 AudioRecorder: Requesting microphone access...');
8539
8558
 
8540
8559
  // Check permission state for logging/debugging (but don't block the request)
8541
- _context3.n = 1;
8560
+ _context3.n = 3;
8542
8561
  return this.checkPermissionState();
8543
- case 1:
8562
+ case 3:
8544
8563
  permissionState = _context3.v;
8545
8564
  if (permissionState) {
8546
8565
  console.log("\uD83D\uDCCB AudioRecorder: Current permission state: ".concat(permissionState));
@@ -8558,22 +8577,22 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8558
8577
  autoGainControl: true
8559
8578
  };
8560
8579
  audioConstraints = this.config.audioConstraints ? _objectSpread(_objectSpread({}, defaultConstraints), this.config.audioConstraints) : defaultConstraints;
8561
- _context3.n = 2;
8580
+ _context3.n = 4;
8562
8581
  return navigator.mediaDevices.getUserMedia({
8563
8582
  audio: _objectSpread({
8564
8583
  sampleRate: sampleRate,
8565
8584
  channelCount: 1
8566
8585
  }, audioConstraints)
8567
8586
  });
8568
- case 2:
8587
+ case 4:
8569
8588
  this.mediaStream = _context3.v;
8570
8589
  if (!(this.config.checkConnection && typeof this.config.checkConnection === 'function')) {
8571
- _context3.n = 3;
8590
+ _context3.n = 5;
8572
8591
  break;
8573
8592
  }
8574
8593
  isConnected = this.config.checkConnection();
8575
8594
  if (isConnected) {
8576
- _context3.n = 3;
8595
+ _context3.n = 5;
8577
8596
  break;
8578
8597
  }
8579
8598
  // Silent cleanup - server rejected, this is expected
@@ -8586,81 +8605,121 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8586
8605
  error.name = 'ServerRejected';
8587
8606
  error.isServerRejection = true;
8588
8607
  throw error;
8589
- case 3:
8590
- // Create AudioContext with the same sample rate
8608
+ case 5:
8609
+ // Reuse shared AudioContext if available and compatible (iOS fix)
8610
+ // iOS WebKit has a bug where closing and immediately recreating an AudioContext
8611
+ // causes the new one to fail silently - the audio hardware isn't released in time.
8612
+ // By reusing the same AudioContext across calls, we avoid this race condition entirely.
8613
+ canReuseContext = _sharedRecorderContext && _sharedRecorderContext.state !== 'closed' && _sharedRecorderSampleRate === sampleRate;
8614
+ if (!canReuseContext) {
8615
+ _context3.n = 7;
8616
+ break;
8617
+ }
8618
+ console.log('♻️ AudioRecorder: Reusing shared AudioContext (iOS-safe)');
8619
+ this.audioContext = _sharedRecorderContext;
8620
+ if (!(this.audioContext.state === 'suspended')) {
8621
+ _context3.n = 6;
8622
+ break;
8623
+ }
8624
+ _context3.n = 6;
8625
+ return this.audioContext.resume();
8626
+ case 6:
8627
+ _context3.n = 13;
8628
+ break;
8629
+ case 7:
8630
+ if (!(_sharedRecorderContext && _sharedRecorderContext.state !== 'closed')) {
8631
+ _context3.n = 12;
8632
+ break;
8633
+ }
8634
+ console.log('🔄 AudioRecorder: Sample rate changed, closing old shared AudioContext');
8635
+ _context3.p = 8;
8636
+ _context3.n = 9;
8637
+ return _sharedRecorderContext.close();
8638
+ case 9:
8639
+ _context3.n = 11;
8640
+ break;
8641
+ case 10:
8642
+ _context3.p = 10;
8643
+ _t3 = _context3.v;
8644
+ console.warn('⚠️ AudioRecorder: Error closing old shared context:', _t3);
8645
+ case 11:
8646
+ _sharedRecorderWorkletLoaded = false;
8647
+ _sharedRecorderWorkletPath = null;
8648
+ case 12:
8591
8649
  this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
8592
8650
  sampleRate: sampleRate
8593
8651
  });
8594
-
8595
- // Resume AudioContext if suspended
8652
+ _sharedRecorderContext = this.audioContext;
8653
+ _sharedRecorderSampleRate = sampleRate;
8654
+ _sharedRecorderWorkletLoaded = false;
8655
+ console.log('🎵 AudioRecorder: Created new shared AudioContext at', sampleRate, 'Hz');
8656
+ case 13:
8596
8657
  if (!(this.audioContext.state === 'suspended')) {
8597
- _context3.n = 4;
8658
+ _context3.n = 14;
8598
8659
  break;
8599
8660
  }
8600
- _context3.n = 4;
8661
+ _context3.n = 14;
8601
8662
  return this.audioContext.resume();
8602
- case 4:
8663
+ case 14:
8603
8664
  if (!(this.config.checkConnection && typeof this.config.checkConnection === 'function')) {
8604
- _context3.n = 6;
8665
+ _context3.n = 15;
8605
8666
  break;
8606
8667
  }
8607
8668
  _isConnected = this.config.checkConnection();
8608
8669
  if (_isConnected) {
8609
- _context3.n = 6;
8670
+ _context3.n = 15;
8610
8671
  break;
8611
8672
  }
8612
8673
  // Silent cleanup - server rejected, this is expected
8613
- // Clean up media stream and AudioContext
8674
+ // Stop media stream but DON'T close the shared AudioContext
8614
8675
  this.mediaStream.getTracks().forEach(function (track) {
8615
8676
  return track.stop();
8616
8677
  });
8617
8678
  this.mediaStream = null;
8618
- if (!(this.audioContext && this.audioContext.state !== 'closed')) {
8619
- _context3.n = 5;
8620
- break;
8621
- }
8622
- _context3.n = 5;
8623
- return this.audioContext.close();
8624
- case 5:
8625
8679
  this.audioContext = null;
8626
8680
  _error = new Error('Connection lost - server may have rejected the call');
8627
8681
  _error.name = 'ServerRejected';
8628
8682
  _error.isServerRejection = true;
8629
8683
  throw _error;
8630
- case 6:
8684
+ case 15:
8631
8685
  // Get the audio processor path
8632
- processorPath = this.getAudioProcessorPath(); // Load AudioWorklet module
8633
- _context3.n = 7;
8686
+ processorPath = this.getAudioProcessorPath(); // Load AudioWorklet module (skip if already loaded on this context)
8687
+ if (!(!_sharedRecorderWorkletLoaded || _sharedRecorderWorkletPath !== processorPath)) {
8688
+ _context3.n = 17;
8689
+ break;
8690
+ }
8691
+ _context3.n = 16;
8634
8692
  return this.audioContext.audioWorklet.addModule(processorPath);
8635
- case 7:
8693
+ case 16:
8694
+ _sharedRecorderWorkletLoaded = true;
8695
+ _sharedRecorderWorkletPath = processorPath;
8696
+ console.log('✅ AudioRecorder: AudioWorklet module loaded');
8697
+ _context3.n = 18;
8698
+ break;
8699
+ case 17:
8700
+ console.log('♻️ AudioRecorder: AudioWorklet module already loaded, skipping');
8701
+ case 18:
8636
8702
  if (!(this.config.checkConnection && typeof this.config.checkConnection === 'function')) {
8637
- _context3.n = 9;
8703
+ _context3.n = 19;
8638
8704
  break;
8639
8705
  }
8640
8706
  _isConnected2 = this.config.checkConnection();
8641
8707
  if (_isConnected2) {
8642
- _context3.n = 9;
8708
+ _context3.n = 19;
8643
8709
  break;
8644
8710
  }
8645
8711
  // Silent cleanup - server rejected, this is expected
8646
- // Clean up everything
8712
+ // Stop media stream but DON'T close the shared AudioContext
8647
8713
  this.mediaStream.getTracks().forEach(function (track) {
8648
8714
  return track.stop();
8649
8715
  });
8650
8716
  this.mediaStream = null;
8651
- if (!(this.audioContext && this.audioContext.state !== 'closed')) {
8652
- _context3.n = 8;
8653
- break;
8654
- }
8655
- _context3.n = 8;
8656
- return this.audioContext.close();
8657
- case 8:
8658
8717
  this.audioContext = null;
8659
8718
  _error2 = new Error('Connection lost - server may have rejected the call');
8660
8719
  _error2.name = 'ServerRejected';
8661
8720
  _error2.isServerRejection = true;
8662
8721
  throw _error2;
8663
- case 9:
8722
+ case 19:
8664
8723
  // Create AudioWorklet node with processor options (sampleRate will be used by processor)
8665
8724
  this.audioWorkletNode = new AudioWorkletNode(this.audioContext, 'audio-processor', {
8666
8725
  processorOptions: {
@@ -8696,26 +8755,26 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8696
8755
  console.log('✅ AudioRecorder: Microphone access granted');
8697
8756
  this.isRecording = true;
8698
8757
  this.emit('recordingStarted');
8699
- _context3.n = 15;
8758
+ _context3.n = 25;
8700
8759
  break;
8701
- case 10:
8702
- _context3.p = 10;
8703
- _t3 = _context3.v;
8704
- if (!(_t3.isServerRejection || _t3.name === 'ServerRejected')) {
8705
- _context3.n = 11;
8760
+ case 20:
8761
+ _context3.p = 20;
8762
+ _t4 = _context3.v;
8763
+ if (!(_t4.isServerRejection || _t4.name === 'ServerRejected')) {
8764
+ _context3.n = 21;
8706
8765
  break;
8707
8766
  }
8708
- throw _t3;
8709
- case 11:
8767
+ throw _t4;
8768
+ case 21:
8710
8769
  // Provide helpful error messages for permission issues
8711
- userMessage = _t3.message;
8712
- if (!(_t3.name === 'NotAllowedError' || _t3.name === 'PermissionDeniedError')) {
8713
- _context3.n = 13;
8770
+ userMessage = _t4.message;
8771
+ if (!(_t4.name === 'NotAllowedError' || _t4.name === 'PermissionDeniedError')) {
8772
+ _context3.n = 23;
8714
8773
  break;
8715
8774
  }
8716
- _context3.n = 12;
8775
+ _context3.n = 22;
8717
8776
  return this.checkPermissionState();
8718
- case 12:
8777
+ case 22:
8719
8778
  _permissionState = _context3.v;
8720
8779
  if (_permissionState === 'denied') {
8721
8780
  // Permission was explicitly blocked - browser won't show prompt again
@@ -8730,29 +8789,33 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8730
8789
  userMessage = 'Microphone permission was denied. Click the button again to retry, or enable microphone access in your browser settings (lock icon 🔒 in address bar).';
8731
8790
  console.error('❌ AudioRecorder: Permission denied. User can try again or change in browser settings.');
8732
8791
  }
8733
- _context3.n = 14;
8792
+ _context3.n = 24;
8734
8793
  break;
8735
- case 13:
8736
- if (_t3.name === 'NotFoundError' || _t3.name === 'DevicesNotFoundError') {
8794
+ case 23:
8795
+ if (_t4.name === 'NotFoundError' || _t4.name === 'DevicesNotFoundError') {
8737
8796
  userMessage = 'No microphone found. Please connect a microphone and try again.';
8738
8797
  console.error('❌ AudioRecorder: No microphone device found.');
8739
- } else if (_t3.name === 'NotReadableError' || _t3.name === 'TrackStartError') {
8798
+ } else if (_t4.name === 'NotReadableError' || _t4.name === 'TrackStartError') {
8740
8799
  userMessage = 'Microphone is already in use by another application. Please close other apps using the microphone and try again.';
8741
8800
  console.error('❌ AudioRecorder: Microphone is busy or not readable.');
8742
- } else if (_t3.name === 'SecurityError' || _t3.name === 'PermissionDeniedError') {
8801
+ } else if (_t4.name === 'SecurityError' || _t4.name === 'PermissionDeniedError') {
8743
8802
  userMessage = 'Microphone access blocked. On mobile devices, please tap the recording button directly (not from an iframe or popup).';
8744
8803
  console.error('❌ AudioRecorder: User agent blocked microphone access. This often happens on mobile when not called from direct user interaction.');
8745
8804
  }
8746
- case 14:
8805
+ case 24:
8747
8806
  enhancedError = new Error(userMessage);
8748
- enhancedError.name = _t3.name;
8749
- enhancedError.originalError = _t3;
8807
+ enhancedError.name = _t4.name;
8808
+ enhancedError.originalError = _t4;
8750
8809
  this.emit('error', enhancedError);
8751
8810
  throw enhancedError;
8752
- case 15:
8811
+ case 25:
8812
+ _context3.p = 25;
8813
+ this._starting = false;
8814
+ return _context3.f(25);
8815
+ case 26:
8753
8816
  return _context3.a(2);
8754
8817
  }
8755
- }, _callee3, this, [[0, 10]]);
8818
+ }, _callee3, this, [[8, 10], [2, 20, 25, 26]]);
8756
8819
  }));
8757
8820
  function start() {
8758
8821
  return _start.apply(this, arguments);
@@ -8767,7 +8830,7 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8767
8830
  key: "stop",
8768
8831
  value: (function () {
8769
8832
  var _stop = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
8770
- var _t4;
8833
+ var _t5;
8771
8834
  return _regenerator().w(function (_context4) {
8772
8835
  while (1) switch (_context4.p = _context4.n) {
8773
8836
  case 0:
@@ -8833,18 +8896,22 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8833
8896
  this.mediaStream = null;
8834
8897
  }
8835
8898
 
8836
- // Close AudioContext (this fully stops all audio processing)
8837
- if (!(this.audioContext && this.audioContext.state !== 'closed')) {
8899
+ // iOS FIX: Suspend AudioContext instead of closing it.
8900
+ // Closing and recreating AudioContexts on iOS causes the new one to fail silently
8901
+ // because iOS doesn't release the audio hardware synchronously.
8902
+ // The shared AudioContext will be reused on the next start() call.
8903
+ if (!(this.audioContext && this.audioContext.state === 'running')) {
8838
8904
  _context4.n = 6;
8839
8905
  break;
8840
8906
  }
8841
- console.log('🛑 AudioRecorder: Closing AudioContext...');
8907
+ console.log('🛑 AudioRecorder: Suspending AudioContext (kept alive for reuse)...');
8842
8908
  _context4.n = 5;
8843
- return this.audioContext.close();
8909
+ return this.audioContext.suspend();
8844
8910
  case 5:
8845
- this.audioContext = null;
8846
- console.log('✅ AudioRecorder: AudioContext closed');
8911
+ console.log('✅ AudioRecorder: AudioContext suspended');
8847
8912
  case 6:
8913
+ // Release instance reference but keep the shared context alive
8914
+ this.audioContext = null;
8848
8915
  this.audioWorkletNode = null;
8849
8916
  this.isRecording = false;
8850
8917
  this.emit('recordingStopped');
@@ -8853,9 +8920,9 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8853
8920
  break;
8854
8921
  case 7:
8855
8922
  _context4.p = 7;
8856
- _t4 = _context4.v;
8857
- this.emit('error', _t4);
8858
- throw _t4;
8923
+ _t5 = _context4.v;
8924
+ this.emit('error', _t5);
8925
+ throw _t5;
8859
8926
  case 8:
8860
8927
  return _context4.a(2);
8861
8928
  }
@@ -8875,7 +8942,7 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8875
8942
  key: "resumeAudioContext",
8876
8943
  value: (function () {
8877
8944
  var _resumeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
8878
- var audioTracks, activeTracks, _t5;
8945
+ var audioTracks, activeTracks, _t6;
8879
8946
  return _regenerator().w(function (_context5) {
8880
8947
  while (1) switch (_context5.p = _context5.n) {
8881
8948
  case 0:
@@ -8913,9 +8980,9 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8913
8980
  break;
8914
8981
  case 4:
8915
8982
  _context5.p = 4;
8916
- _t5 = _context5.v;
8917
- console.error('❌ AudioRecorder: Failed to resume AudioContext:', _t5);
8918
- this.emit('error', _t5);
8983
+ _t6 = _context5.v;
8984
+ console.error('❌ AudioRecorder: Failed to resume AudioContext:', _t6);
8985
+ this.emit('error', _t6);
8919
8986
  case 5:
8920
8987
  return _context5.a(2);
8921
8988
  }
@@ -8940,13 +9007,32 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
8940
9007
  }
8941
9008
 
8942
9009
  /**
8943
- * Cleanup resources
9010
+ * Cleanup resources.
9011
+ * Note: Does NOT close the shared AudioContext - it's reused across instances
9012
+ * to avoid the iOS AudioContext close/create race condition.
8944
9013
  */
8945
9014
  }, {
8946
9015
  key: "destroy",
8947
9016
  value: function destroy() {
8948
9017
  this.stop();
8949
9018
  }
9019
+
9020
+ /**
9021
+ * Fully close the shared AudioContext.
9022
+ * Only call this when the widget is being completely removed from the page.
9023
+ */
9024
+ }], [{
9025
+ key: "closeSharedContext",
9026
+ value: function closeSharedContext() {
9027
+ if (_sharedRecorderContext && _sharedRecorderContext.state !== 'closed') {
9028
+ console.log('🗑️ AudioRecorder: Closing shared AudioContext');
9029
+ _sharedRecorderContext.close();
9030
+ }
9031
+ _sharedRecorderContext = null;
9032
+ _sharedRecorderSampleRate = null;
9033
+ _sharedRecorderWorkletLoaded = false;
9034
+ _sharedRecorderWorkletPath = null;
9035
+ }
8950
9036
  }]);
8951
9037
  }(_EventEmitter_js__WEBPACK_IMPORTED_MODULE_0__["default"]);
8952
9038
 
@@ -10332,6 +10418,7 @@ __webpack_require__.r(__webpack_exports__);
10332
10418
  /* harmony import */ var _CartToast_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./CartToast.js */ "./src/ecommerce/CartToast.js");
10333
10419
  /* harmony import */ var _CartSummary_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./CartSummary.js */ "./src/ecommerce/CartSummary.js");
10334
10420
  /* harmony import */ var _ecommerceStyles_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./ecommerceStyles.js */ "./src/ecommerce/ecommerceStyles.js");
10421
+ /* harmony import */ var _MobileProductCarousel_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./MobileProductCarousel.js */ "./src/ecommerce/MobileProductCarousel.js");
10335
10422
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
10336
10423
  function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
10337
10424
  function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
@@ -10351,14 +10438,17 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
10351
10438
 
10352
10439
 
10353
10440
 
10441
+
10354
10442
  var EcommerceManager = /*#__PURE__*/function () {
10355
10443
  function EcommerceManager() {
10356
10444
  var _this = this;
10357
10445
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10358
10446
  _classCallCheck(this, EcommerceManager);
10359
10447
  this.shadowRoot = config.shadowRoot;
10448
+ this.primaryColor = config.primaryColor || '#3b82f6';
10360
10449
  this.onProductSelected = config.onProductSelected || function () {};
10361
10450
  this.onProductDeselected = config.onProductDeselected || function () {};
10451
+ this.mobileCarousel = null;
10362
10452
  this.productPicker = new _ProductPicker_js__WEBPACK_IMPORTED_MODULE_0__.ProductPicker({
10363
10453
  onProductAdd: function onProductAdd(product) {
10364
10454
  return _this._handleAdd(product);
@@ -10445,7 +10535,6 @@ var EcommerceManager = /*#__PURE__*/function () {
10445
10535
  }, {
10446
10536
  key: "handleShowProducts",
10447
10537
  value: function handleShowProducts(message) {
10448
- this._ensureReady();
10449
10538
  var products = message.products,
10450
10539
  title = message.title,
10451
10540
  layout = message.layout;
@@ -10453,6 +10542,12 @@ var EcommerceManager = /*#__PURE__*/function () {
10453
10542
  console.warn('[EcommerceManager] show_products with no products');
10454
10543
  return;
10455
10544
  }
10545
+ this._currencySymbol = products[0].currency === 'ILS' ? '₪' : '$';
10546
+ if (this._isMobileCall()) {
10547
+ this._showMobileCarousel(products, title);
10548
+ return;
10549
+ }
10550
+ this._ensureReady();
10456
10551
  this._switchToCompactMode();
10457
10552
  this._setContainerMode('products');
10458
10553
  this.productPicker.hide();
@@ -10461,6 +10556,67 @@ var EcommerceManager = /*#__PURE__*/function () {
10461
10556
  this.container.insertBefore(pickerEl, this.container.firstChild);
10462
10557
  this._expandPanelToFit(products.length, effectiveLayout);
10463
10558
  }
10559
+
10560
+ /**
10561
+ * Detect if we're in a mobile voice call (minimized bar is present on body).
10562
+ */
10563
+ }, {
10564
+ key: "_isMobileCall",
10565
+ value: function _isMobileCall() {
10566
+ return !!document.getElementById('mobile-voice-call-bar-container');
10567
+ }
10568
+
10569
+ /**
10570
+ * Show 3D product carousel above the mobile minimized voice bar.
10571
+ */
10572
+ }, {
10573
+ key: "_showMobileCarousel",
10574
+ value: function _showMobileCarousel(products, title) {
10575
+ var _this2 = this;
10576
+ if (!this.mobileCarousel) {
10577
+ this.mobileCarousel = new _MobileProductCarousel_js__WEBPACK_IMPORTED_MODULE_4__.MobileProductCarousel({
10578
+ primaryColor: this.primaryColor,
10579
+ onProductAdd: function onProductAdd(product) {
10580
+ return _this2._handleMobileAdd(product);
10581
+ },
10582
+ onClose: function onClose() {}
10583
+ });
10584
+ }
10585
+ this.mobileCarousel.show(products, title);
10586
+ }
10587
+ }, {
10588
+ key: "_handleMobileAdd",
10589
+ value: function _handleMobileAdd(product) {
10590
+ var price = product.price || 0;
10591
+ var qty = product.selectedAmount || 1;
10592
+ var currency = product.currency === 'ILS' ? '₪' : '$';
10593
+ this.cartSummary.update(this.cartSummary.itemCount + 1, this.cartSummary.total + price * qty, currency);
10594
+ var cartInfo = {
10595
+ itemCount: this.cartSummary.itemCount,
10596
+ total: this.cartSummary.total,
10597
+ currency: this.cartSummary.currency
10598
+ };
10599
+ this.mobileCarousel.showToast(product.name, cartInfo);
10600
+ this._updateMobileCartRow(cartInfo);
10601
+ this.onProductSelected(product);
10602
+ }
10603
+ }, {
10604
+ key: "_updateMobileCartRow",
10605
+ value: function _updateMobileCartRow(_ref) {
10606
+ var itemCount = _ref.itemCount,
10607
+ total = _ref.total,
10608
+ currency = _ref.currency;
10609
+ var row = document.getElementById('mobileCartRow');
10610
+ var textEl = document.getElementById('mobileCartText');
10611
+ if (!row || !textEl) return;
10612
+ if (itemCount <= 0) {
10613
+ row.style.display = 'none';
10614
+ return;
10615
+ }
10616
+ var itemText = "".concat(itemCount, " item").concat(itemCount !== 1 ? 's' : '');
10617
+ textEl.textContent = "".concat(itemText, " \xB7 ").concat(currency).concat(total.toFixed(2));
10618
+ row.style.display = 'flex';
10619
+ }
10464
10620
  }, {
10465
10621
  key: "handleCartUpdated",
10466
10622
  value: function handleCartUpdated(message) {
@@ -10474,7 +10630,7 @@ var EcommerceManager = /*#__PURE__*/function () {
10474
10630
  this.container.insertBefore(toastEl, this.container.firstChild);
10475
10631
  }
10476
10632
  if (cartTotal !== undefined && cartItemCount !== undefined) {
10477
- this.cartSummary.update(cartItemCount, cartTotal);
10633
+ this.cartSummary.update(cartItemCount, cartTotal, this._currencySymbol);
10478
10634
  }
10479
10635
  }
10480
10636
 
@@ -10529,15 +10685,20 @@ var EcommerceManager = /*#__PURE__*/function () {
10529
10685
  var chrome = 62 + 40 + 20 + 150;
10530
10686
  var productsHeight;
10531
10687
  if (layout === 'cards') {
10532
- // Card: image 85 + details ~195 + border 2 = ~282px
10533
- // Container padding: 24px (12px * 2) + breathing room
10534
10688
  productsHeight = 360;
10535
10689
  } else {
10536
- // List item: ~58px each, container padding 20px
10537
- productsHeight = Math.min(productCount * 58 + 20, 400);
10690
+ // Each list item ~58px + 6px gap between items + container padding
10691
+ productsHeight = Math.min(productCount * 64 + 20, 500);
10692
+ }
10693
+
10694
+ // For 3+ products, ensure enough height for at least 3 full items + 4th peeking
10695
+ if (productCount > 2) {
10696
+ var minForThreeAndHalf = Math.ceil(3.5 * 64) + 20;
10697
+ productsHeight = Math.max(productsHeight, minForThreeAndHalf);
10538
10698
  }
10539
10699
  var totalHeight = chrome + productsHeight;
10540
- var clampedHeight = Math.min(Math.max(totalHeight, 450), 800);
10700
+ var minHeight = productCount > 2 ? 700 : 450;
10701
+ var clampedHeight = Math.min(Math.max(totalHeight, minHeight), 850);
10541
10702
  panel.classList.add('ttp-panel-expanded');
10542
10703
  panel.style.height = "".concat(clampedHeight, "px");
10543
10704
  }
@@ -10590,17 +10751,18 @@ var EcommerceManager = /*#__PURE__*/function () {
10590
10751
  }, {
10591
10752
  key: "_handleAdd",
10592
10753
  value: function _handleAdd(product) {
10593
- var _this2 = this;
10754
+ var _this3 = this;
10594
10755
  var price = product.price || 0;
10595
10756
  var qty = product.selectedAmount || 1;
10596
- this.cartSummary.update(this.cartSummary.itemCount + 1, this.cartSummary.total + price * qty);
10757
+ var currency = this._currencySymbol || (product.currency === 'ILS' ? '₪' : '$');
10758
+ this.cartSummary.update(this.cartSummary.itemCount + 1, this.cartSummary.total + price * qty, currency);
10597
10759
  var toastEl = this.cartToast.show(product, 'added');
10598
10760
  this.container.insertBefore(toastEl, this.container.firstChild);
10599
10761
  setTimeout(function () {
10600
- _this2.productPicker.hide();
10601
- _this2._shrinkPanel();
10602
- _this2._restoreFullVoiceMode();
10603
- _this2._setContainerMode('summary');
10762
+ _this3.productPicker.hide();
10763
+ _this3._shrinkPanel();
10764
+ _this3._restoreFullVoiceMode();
10765
+ _this3._setContainerMode('summary');
10604
10766
  }, 500);
10605
10767
  this.onProductSelected(product);
10606
10768
  }
@@ -10616,6 +10778,10 @@ var EcommerceManager = /*#__PURE__*/function () {
10616
10778
  }, {
10617
10779
  key: "destroy",
10618
10780
  value: function destroy() {
10781
+ if (this.mobileCarousel) {
10782
+ this.mobileCarousel.destroy();
10783
+ this.mobileCarousel = null;
10784
+ }
10619
10785
  this.productPicker.hide();
10620
10786
  this.cartToast.hide();
10621
10787
  this._shrinkPanel();
@@ -10633,6 +10799,539 @@ var EcommerceManager = /*#__PURE__*/function () {
10633
10799
 
10634
10800
  /***/ }),
10635
10801
 
10802
+ /***/ "./src/ecommerce/MobileProductCarousel.js":
10803
+ /*!************************************************!*\
10804
+ !*** ./src/ecommerce/MobileProductCarousel.js ***!
10805
+ \************************************************/
10806
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
10807
+
10808
+ "use strict";
10809
+ __webpack_require__.r(__webpack_exports__);
10810
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
10811
+ /* harmony export */ MobileProductCarousel: () => (/* binding */ MobileProductCarousel)
10812
+ /* harmony export */ });
10813
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
10814
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
10815
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
10816
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
10817
+ function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
10818
+ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
10819
+ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
10820
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
10821
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
10822
+ /**
10823
+ * MobileProductCarousel — 3D carousel for product display on mobile during voice calls.
10824
+ *
10825
+ * Floats above the minimized voice bar (appended to document.body).
10826
+ * Uses CSS 3D perspective transforms for a rotating wheel effect.
10827
+ * Supports swipe/drag navigation, quantity/weight steppers, out-of-stock states.
10828
+ *
10829
+ * Options:
10830
+ * primaryColor: hex string (e.g. '#3b82f6')
10831
+ * onProductAdd(product): fired when user taps Add
10832
+ * onClose(): fired when user taps close button
10833
+ */
10834
+ var MobileProductCarousel = /*#__PURE__*/function () {
10835
+ function MobileProductCarousel() {
10836
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10837
+ _classCallCheck(this, MobileProductCarousel);
10838
+ this.onProductAdd = options.onProductAdd || function () {};
10839
+ this.onClose = options.onClose || function () {};
10840
+ this.primaryColor = options.primaryColor || '#3b82f6';
10841
+ this.products = [];
10842
+ this.current = 0;
10843
+ this.amounts = [];
10844
+ this.sellByModes = [];
10845
+ this.container = null;
10846
+ this.wheel = null;
10847
+ this.dotsEl = null;
10848
+ this.anglePerCard = 42;
10849
+ this.radius = 240;
10850
+ this.dragging = false;
10851
+ this.startX = 0;
10852
+ this.swiped = false;
10853
+ this.dragThreshold = 40;
10854
+ this._boundTouchMove = this._onTouchMove.bind(this);
10855
+ this._boundTouchEnd = this._onTouchEnd.bind(this);
10856
+ this._boundMouseMove = this._onMouseMove.bind(this);
10857
+ this._boundMouseUp = this._onMouseUp.bind(this);
10858
+ }
10859
+
10860
+ // ═══════════════════════════════════════════
10861
+ // PUBLIC API
10862
+ // ═══════════════════════════════════════════
10863
+ return _createClass(MobileProductCarousel, [{
10864
+ key: "show",
10865
+ value: function show(products, title) {
10866
+ this.hide();
10867
+ if (!products || products.length === 0) return;
10868
+ this._normalizeProducts(products);
10869
+ this.current = 0;
10870
+ this._injectStyles();
10871
+ this._buildDOM(title);
10872
+ this._buildCards();
10873
+ this._buildDots();
10874
+ this._setupDragEvents();
10875
+ this._updateWheel();
10876
+ document.body.appendChild(this.container);
10877
+ this._positionAboveMobileBar();
10878
+ }
10879
+ }, {
10880
+ key: "hide",
10881
+ value: function hide() {
10882
+ this._removeEventListeners();
10883
+ if (this.container && this.container.parentNode) {
10884
+ this.container.classList.add('ttp-carousel--hiding');
10885
+ var c = this.container;
10886
+ setTimeout(function () {
10887
+ if (c.parentNode) c.parentNode.removeChild(c);
10888
+ }, 280);
10889
+ }
10890
+ this.container = null;
10891
+ this.wheel = null;
10892
+ this.dotsEl = null;
10893
+ }
10894
+ }, {
10895
+ key: "destroy",
10896
+ value: function destroy() {
10897
+ this._removeEventListeners();
10898
+ if (this.container && this.container.parentNode) {
10899
+ this.container.parentNode.removeChild(this.container);
10900
+ }
10901
+ this.container = null;
10902
+ var toast = document.getElementById('ttpCarouselToast');
10903
+ if (toast) toast.remove();
10904
+ var style = document.getElementById('ttp-mobile-carousel-styles');
10905
+ if (style) style.remove();
10906
+ }
10907
+ }, {
10908
+ key: "showToast",
10909
+ value: function showToast(productName, cartInfo) {
10910
+ var existing = document.getElementById('ttpCarouselToast');
10911
+ if (existing) existing.remove();
10912
+ var toast = document.createElement('div');
10913
+ toast.id = 'ttpCarouselToast';
10914
+ toast.className = 'ttp-carousel-toast';
10915
+ var cartLine = cartInfo ? "<div class=\"ttp-carousel-toast__cart\">".concat(cartInfo.itemCount, " item").concat(cartInfo.itemCount !== 1 ? 's' : '', " \xB7 ").concat(cartInfo.currency).concat(cartInfo.total.toFixed(2), "</div>") : '';
10916
+ toast.innerHTML = "\n <div class=\"ttp-carousel-toast__icon\">\u2713</div>\n <div class=\"ttp-carousel-toast__text\">\n <strong>".concat(productName, "</strong> added to cart\n ").concat(cartLine, "\n </div>\n ");
10917
+ toast.style.setProperty('--ttp-carousel-primary', this.primaryColor);
10918
+ document.body.appendChild(toast);
10919
+ var mobileBar = document.getElementById('mobile-voice-call-bar-container');
10920
+ var cartPill = document.getElementById('ttpMobileCartSummary');
10921
+ if (cartPill) {
10922
+ toast.style.bottom = "".concat(parseInt(cartPill.style.bottom) + cartPill.offsetHeight + 8, "px");
10923
+ } else if (mobileBar) {
10924
+ toast.style.bottom = "".concat(mobileBar.offsetHeight + 12, "px");
10925
+ } else {
10926
+ toast.style.bottom = '100px';
10927
+ }
10928
+ setTimeout(function () {
10929
+ toast.classList.add('ttp-carousel-toast--hiding');
10930
+ setTimeout(function () {
10931
+ return toast.remove();
10932
+ }, 300);
10933
+ }, 3000);
10934
+ }
10935
+
10936
+ // ═══════════════════════════════════════════
10937
+ // DOM CONSTRUCTION
10938
+ // ═══════════════════════════════════════════
10939
+ }, {
10940
+ key: "_normalizeProducts",
10941
+ value: function _normalizeProducts(products) {
10942
+ this.products = products.map(function (p) {
10943
+ return _objectSpread(_objectSpread({}, p), {}, {
10944
+ imageUrl: p.imageUrl || p.image_url || '',
10945
+ inStock: p.inStock !== undefined ? p.inStock : p.in_stock !== undefined ? p.in_stock : true,
10946
+ sellBy: p.sellBy || p.sell_by || 'quantity',
10947
+ cartQuantity: p.cartQuantity || p.cart_quantity || 0,
10948
+ brand: p.brand || '',
10949
+ unit: p.unit || '',
10950
+ currency: p.currency || 'USD'
10951
+ });
10952
+ });
10953
+ this.amounts = this.products.map(function (p) {
10954
+ var min = p.sellBy === 'weight' ? 0.5 : 1;
10955
+ return p.cartQuantity > 0 ? p.cartQuantity : min;
10956
+ });
10957
+ this.sellByModes = this.products.map(function (p) {
10958
+ return p.sellBy;
10959
+ });
10960
+ }
10961
+ }, {
10962
+ key: "_buildDOM",
10963
+ value: function _buildDOM(title) {
10964
+ var _this = this;
10965
+ this.container = document.createElement('div');
10966
+ this.container.className = 'ttp-mobile-carousel';
10967
+ this.container.id = 'ttpMobileCarousel';
10968
+ this.container.style.setProperty('--ttp-carousel-primary', this.primaryColor);
10969
+ this.container.style.setProperty('--ttp-carousel-primary-light', this._hexToRgba(this.primaryColor, 0.12));
10970
+ this.container.style.setProperty('--ttp-carousel-primary-dark', this._darkenHex(this.primaryColor, 30));
10971
+ this.container.style.setProperty('--ttp-carousel-primary-shadow', this._hexToRgba(this.primaryColor, 0.3));
10972
+ var header = document.createElement('div');
10973
+ header.className = 'ttp-carousel-header';
10974
+ header.innerHTML = "\n <span class=\"ttp-carousel-title\">".concat(title || 'Products', "</span>\n <button class=\"ttp-carousel-close\" aria-label=\"Close\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n ");
10975
+ header.querySelector('.ttp-carousel-close').addEventListener('click', function () {
10976
+ _this.hide();
10977
+ _this.onClose();
10978
+ });
10979
+ var area = document.createElement('div');
10980
+ area.className = 'ttp-carousel-area';
10981
+ this.wheel = document.createElement('div');
10982
+ this.wheel.className = 'ttp-carousel-wheel';
10983
+ area.appendChild(this.wheel);
10984
+ this.dotsEl = document.createElement('div');
10985
+ this.dotsEl.className = 'ttp-carousel-dots';
10986
+ this.container.appendChild(header);
10987
+ this.container.appendChild(area);
10988
+ this.container.appendChild(this.dotsEl);
10989
+ }
10990
+ }, {
10991
+ key: "_buildCards",
10992
+ value: function _buildCards() {
10993
+ var _this2 = this;
10994
+ this.products.forEach(function (p, i) {
10995
+ var card = document.createElement('div');
10996
+ card.className = 'ttp-carousel-card' + (!p.inStock ? ' ttp-carousel-card--oos' : '');
10997
+ card.dataset.index = i;
10998
+ var sym = p.currency === 'ILS' ? '₪' : '$';
10999
+ var sellBy = _this2.sellByModes[i];
11000
+ var displayVal = sellBy === 'weight' ? "".concat(_this2.amounts[i].toFixed(1), " kg") : "".concat(_this2.amounts[i]);
11001
+ var inCart = p.cartQuantity > 0;
11002
+ card.innerHTML = "\n <div class=\"ttp-carousel-card__image\">\n <img src=\"".concat(p.imageUrl, "\" alt=\"").concat(p.name, "\" onerror=\"this.style.display='none'\" />\n ").concat(!p.inStock ? '<div class="ttp-carousel-card__badge">Out of Stock</div>' : '', "\n </div>\n <div class=\"ttp-carousel-card__info\">\n ").concat(p.brand ? "<div class=\"ttp-carousel-card__brand\">".concat(p.brand, "</div>") : '', "\n <div class=\"ttp-carousel-card__name\">").concat(p.name, "</div>\n ").concat(p.unit ? "<div class=\"ttp-carousel-card__unit\">".concat(p.unit, "</div>") : '', "\n <div class=\"ttp-carousel-card__price\">").concat(sym).concat(p.price.toFixed(2), "</div>\n </div>\n <div class=\"ttp-carousel-card__actions\">\n <div class=\"ttp-carousel-card__stepper\">\n <button class=\"ttp-carousel-card__qty-btn ttp-carousel-minus\" data-idx=\"").concat(i, "\" ").concat(!p.inStock ? 'disabled' : '', ">\u2212</button>\n <span class=\"ttp-carousel-card__qty-val\" id=\"ttpCQty").concat(i, "\">").concat(displayVal, "</span>\n <button class=\"ttp-carousel-card__qty-btn ttp-carousel-plus\" data-idx=\"").concat(i, "\" ").concat(!p.inStock ? 'disabled' : '', ">+</button>\n </div>\n <button class=\"ttp-carousel-card__add-btn").concat(inCart ? ' ttp-carousel-card__add-btn--update' : '', "\" data-idx=\"").concat(i, "\" ").concat(!p.inStock ? 'disabled' : '', ">\n ").concat(p.inStock ? inCart ? 'Update' : 'Add to Cart' : 'Unavailable', "\n </button>\n </div>\n ");
11003
+ card.style.transform = "rotateY(".concat(i * _this2.anglePerCard, "deg) translateZ(").concat(_this2.radius, "px)");
11004
+ _this2.wheel.appendChild(card);
11005
+ });
11006
+ this._setupCardHandlers();
11007
+ }
11008
+
11009
+ /**
11010
+ * Area-level tap detection — bypasses browser 3D hit-testing entirely.
11011
+ *
11012
+ * Instead of attaching handlers to individual buttons (which requires
11013
+ * the browser to correctly identify which 3D-transformed button was
11014
+ * tapped), we capture ALL touches at the carousel-area level and
11015
+ * manually match the touch coordinates against the current card's
11016
+ * buttons using getBoundingClientRect.
11017
+ */
11018
+ }, {
11019
+ key: "_setupCardHandlers",
11020
+ value: function _setupCardHandlers() {
11021
+ var _this3 = this;
11022
+ var area = this.container.querySelector('.ttp-carousel-area');
11023
+ if (!area) return;
11024
+ var tapOrigin = null;
11025
+ area.addEventListener('touchstart', function (e) {
11026
+ tapOrigin = {
11027
+ x: e.touches[0].clientX,
11028
+ y: e.touches[0].clientY
11029
+ };
11030
+ }, {
11031
+ passive: true
11032
+ });
11033
+ area.addEventListener('touchend', function (e) {
11034
+ if (!tapOrigin || _this3.swiped) {
11035
+ tapOrigin = null;
11036
+ return;
11037
+ }
11038
+ var touch = e.changedTouches[0];
11039
+ var dx = Math.abs(touch.clientX - tapOrigin.x);
11040
+ var dy = Math.abs(touch.clientY - tapOrigin.y);
11041
+ tapOrigin = null;
11042
+ if (dx > 15 || dy > 15) return;
11043
+ e.preventDefault();
11044
+ _this3._handleTapAt(touch.clientX, touch.clientY);
11045
+ });
11046
+ area.addEventListener('click', function (e) {
11047
+ var target = e.target;
11048
+ var card = target.closest('.ttp-carousel-card');
11049
+ if (!card || parseInt(card.dataset.index) !== _this3.current) return;
11050
+ var minus = target.closest('.ttp-carousel-minus');
11051
+ var plus = target.closest('.ttp-carousel-plus');
11052
+ var addBtn = target.closest('.ttp-carousel-card__add-btn');
11053
+ if (minus && !minus.disabled) {
11054
+ e.stopPropagation();
11055
+ _this3._handleMinus();
11056
+ } else if (plus && !plus.disabled) {
11057
+ e.stopPropagation();
11058
+ _this3._handlePlus();
11059
+ } else if (addBtn && !addBtn.disabled) {
11060
+ e.stopPropagation();
11061
+ _this3._handleAdd();
11062
+ }
11063
+ });
11064
+ }
11065
+ }, {
11066
+ key: "_handleTapAt",
11067
+ value: function _handleTapAt(x, y) {
11068
+ var cards = this.wheel.querySelectorAll('.ttp-carousel-card');
11069
+ var currentCard = cards[this.current];
11070
+ if (!currentCard) return;
11071
+ var minus = currentCard.querySelector('.ttp-carousel-minus');
11072
+ var plus = currentCard.querySelector('.ttp-carousel-plus');
11073
+ var addBtn = currentCard.querySelector('.ttp-carousel-card__add-btn');
11074
+ if (this._hitTest(x, y, minus) && !(minus !== null && minus !== void 0 && minus.disabled)) {
11075
+ this._handleMinus();
11076
+ } else if (this._hitTest(x, y, plus) && !(plus !== null && plus !== void 0 && plus.disabled)) {
11077
+ this._handlePlus();
11078
+ } else if (this._hitTest(x, y, addBtn) && !(addBtn !== null && addBtn !== void 0 && addBtn.disabled)) {
11079
+ this._handleAdd();
11080
+ }
11081
+ }
11082
+ }, {
11083
+ key: "_hitTest",
11084
+ value: function _hitTest(x, y, el) {
11085
+ if (!el) return false;
11086
+ var r = el.getBoundingClientRect();
11087
+ return x >= r.left && x <= r.right && y >= r.top && y <= r.bottom;
11088
+ }
11089
+ }, {
11090
+ key: "_handleMinus",
11091
+ value: function _handleMinus() {
11092
+ var idx = this.current;
11093
+ var sellBy = this.sellByModes[idx];
11094
+ var min = sellBy === 'weight' ? 0.5 : 1;
11095
+ var step = sellBy === 'weight' ? 0.5 : 1;
11096
+ if (this.amounts[idx] > min) {
11097
+ this.amounts[idx] = Math.round((this.amounts[idx] - step) * 10) / 10;
11098
+ this._refreshStepper(idx);
11099
+ }
11100
+ }
11101
+ }, {
11102
+ key: "_handlePlus",
11103
+ value: function _handlePlus() {
11104
+ var idx = this.current;
11105
+ var step = this.sellByModes[idx] === 'weight' ? 0.5 : 1;
11106
+ this.amounts[idx] = Math.round((this.amounts[idx] + step) * 10) / 10;
11107
+ this._refreshStepper(idx);
11108
+ }
11109
+ }, {
11110
+ key: "_handleAdd",
11111
+ value: function _handleAdd() {
11112
+ var _cards$idx,
11113
+ _this4 = this;
11114
+ var idx = this.current;
11115
+ var product = this.products[idx];
11116
+ if (!product.inStock) return;
11117
+ var cards = this.wheel.querySelectorAll('.ttp-carousel-card');
11118
+ var btn = (_cards$idx = cards[idx]) === null || _cards$idx === void 0 ? void 0 : _cards$idx.querySelector('.ttp-carousel-card__add-btn');
11119
+ if (btn) {
11120
+ btn.textContent = '✓ Added!';
11121
+ btn.classList.add('ttp-carousel-card__add-btn--added');
11122
+ btn.disabled = true;
11123
+ }
11124
+ this.onProductAdd(_objectSpread(_objectSpread({}, product), {}, {
11125
+ selectedAmount: this.amounts[idx],
11126
+ sellBy: this.sellByModes[idx]
11127
+ }));
11128
+ setTimeout(function () {
11129
+ return _this4.hide();
11130
+ }, 800);
11131
+ }
11132
+ }, {
11133
+ key: "_refreshStepper",
11134
+ value: function _refreshStepper(idx) {
11135
+ var sellBy = this.sellByModes[idx];
11136
+ var min = sellBy === 'weight' ? 0.5 : 1;
11137
+ var el = this.container.querySelector("#ttpCQty".concat(idx));
11138
+ var minusBtn = this.container.querySelector(".ttp-carousel-minus[data-idx=\"".concat(idx, "\"]"));
11139
+ if (el) {
11140
+ el.textContent = sellBy === 'weight' ? "".concat(this.amounts[idx].toFixed(1), " kg") : "".concat(this.amounts[idx]);
11141
+ }
11142
+ if (minusBtn) {
11143
+ minusBtn.disabled = this.amounts[idx] <= min;
11144
+ }
11145
+ }
11146
+ }, {
11147
+ key: "_buildDots",
11148
+ value: function _buildDots() {
11149
+ var _this5 = this;
11150
+ if (this.products.length <= 1) return;
11151
+ this.products.forEach(function (_, i) {
11152
+ var dot = document.createElement('div');
11153
+ dot.className = 'ttp-carousel-dot';
11154
+ dot.addEventListener('click', function () {
11155
+ return _this5.goTo(i);
11156
+ });
11157
+ _this5.dotsEl.appendChild(dot);
11158
+ });
11159
+ }
11160
+
11161
+ // ═══════════════════════════════════════════
11162
+ // 3D WHEEL
11163
+ // ═══════════════════════════════════════════
11164
+ }, {
11165
+ key: "_updateWheel",
11166
+ value: function _updateWheel() {
11167
+ var _this6 = this;
11168
+ var rotation = -(this.current * this.anglePerCard);
11169
+ this.wheel.style.transform = "translateZ(-".concat(this.radius, "px) rotateY(").concat(rotation, "deg)");
11170
+ var cards = this.wheel.querySelectorAll('.ttp-carousel-card');
11171
+ cards.forEach(function (card, i) {
11172
+ var eff = i * _this6.anglePerCard + rotation;
11173
+ while (eff > 180) eff -= 360;
11174
+ while (eff < -180) eff += 360;
11175
+ if (Math.abs(eff) > 90) {
11176
+ card.style.visibility = 'hidden';
11177
+ card.style.opacity = '0';
11178
+ } else {
11179
+ card.style.visibility = 'visible';
11180
+ card.style.opacity = _this6.products[i].inStock ? '1' : '0.7';
11181
+ }
11182
+ });
11183
+ var dots = this.dotsEl.querySelectorAll('.ttp-carousel-dot');
11184
+ dots.forEach(function (dot, i) {
11185
+ return dot.classList.toggle('ttp-carousel-dot--active', i === _this6.current);
11186
+ });
11187
+ }
11188
+ }, {
11189
+ key: "goTo",
11190
+ value: function goTo(i) {
11191
+ this.current = Math.max(0, Math.min(this.products.length - 1, i));
11192
+ this._updateWheel();
11193
+ }
11194
+
11195
+ // ═══════════════════════════════════════════
11196
+ // DRAG / SWIPE
11197
+ // ═══════════════════════════════════════════
11198
+ }, {
11199
+ key: "_setupDragEvents",
11200
+ value: function _setupDragEvents() {
11201
+ var _this7 = this;
11202
+ var area = this.container.querySelector('.ttp-carousel-area');
11203
+ if (!area) return;
11204
+ area.addEventListener('touchstart', function (e) {
11205
+ _this7.dragging = true;
11206
+ _this7.startX = e.touches[0].clientX;
11207
+ _this7.swiped = false;
11208
+ }, {
11209
+ passive: true
11210
+ });
11211
+ area.addEventListener('mousedown', function (e) {
11212
+ _this7.dragging = true;
11213
+ _this7.startX = e.clientX;
11214
+ _this7.swiped = false;
11215
+ });
11216
+ document.addEventListener('touchmove', this._boundTouchMove, {
11217
+ passive: true
11218
+ });
11219
+ document.addEventListener('touchend', this._boundTouchEnd);
11220
+ document.addEventListener('mousemove', this._boundMouseMove);
11221
+ document.addEventListener('mouseup', this._boundMouseUp);
11222
+ }
11223
+ }, {
11224
+ key: "_onTouchMove",
11225
+ value: function _onTouchMove(e) {
11226
+ if (!this.dragging) return;
11227
+ var diff = e.touches[0].clientX - this.startX;
11228
+ if (Math.abs(diff) > this.dragThreshold) {
11229
+ this.goTo(this.current + (diff > 0 ? -1 : 1));
11230
+ this.startX = e.touches[0].clientX;
11231
+ this.swiped = true;
11232
+ }
11233
+ }
11234
+ }, {
11235
+ key: "_onTouchEnd",
11236
+ value: function _onTouchEnd() {
11237
+ var _this8 = this;
11238
+ this.dragging = false;
11239
+ if (this.swiped) {
11240
+ setTimeout(function () {
11241
+ _this8.swiped = false;
11242
+ }, 400);
11243
+ }
11244
+ }
11245
+ }, {
11246
+ key: "_onMouseMove",
11247
+ value: function _onMouseMove(e) {
11248
+ if (!this.dragging) return;
11249
+ var diff = e.clientX - this.startX;
11250
+ if (Math.abs(diff) > this.dragThreshold) {
11251
+ this.goTo(this.current + (diff > 0 ? -1 : 1));
11252
+ this.startX = e.clientX;
11253
+ this.swiped = true;
11254
+ }
11255
+ }
11256
+ }, {
11257
+ key: "_onMouseUp",
11258
+ value: function _onMouseUp() {
11259
+ var _this9 = this;
11260
+ this.dragging = false;
11261
+ if (this.swiped) {
11262
+ setTimeout(function () {
11263
+ _this9.swiped = false;
11264
+ }, 400);
11265
+ }
11266
+ }
11267
+ }, {
11268
+ key: "_removeEventListeners",
11269
+ value: function _removeEventListeners() {
11270
+ document.removeEventListener('touchmove', this._boundTouchMove);
11271
+ document.removeEventListener('touchend', this._boundTouchEnd);
11272
+ document.removeEventListener('mousemove', this._boundMouseMove);
11273
+ document.removeEventListener('mouseup', this._boundMouseUp);
11274
+ }
11275
+
11276
+ // ═══════════════════════════════════════════
11277
+ // POSITIONING
11278
+ // ═══════════════════════════════════════════
11279
+ }, {
11280
+ key: "_positionAboveMobileBar",
11281
+ value: function _positionAboveMobileBar() {
11282
+ var _this0 = this;
11283
+ requestAnimationFrame(function () {
11284
+ if (!_this0.container) return;
11285
+ var bar = document.getElementById('mobile-voice-call-bar-container');
11286
+ if (bar) {
11287
+ var barRect = bar.getBoundingClientRect();
11288
+ _this0.container.style.bottom = "".concat(window.innerHeight - barRect.top + 8, "px");
11289
+ }
11290
+ });
11291
+ }
11292
+
11293
+ // ═══════════════════════════════════════════
11294
+ // COLOR HELPERS
11295
+ // ═══════════════════════════════════════════
11296
+ }, {
11297
+ key: "_hexToRgba",
11298
+ value: function _hexToRgba(hex, alpha) {
11299
+ var r = parseInt(hex.slice(1, 3), 16);
11300
+ var g = parseInt(hex.slice(3, 5), 16);
11301
+ var b = parseInt(hex.slice(5, 7), 16);
11302
+ return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alpha, ")");
11303
+ }
11304
+ }, {
11305
+ key: "_darkenHex",
11306
+ value: function _darkenHex(hex, amount) {
11307
+ var r = Math.max(0, parseInt(hex.slice(1, 3), 16) - amount);
11308
+ var g = Math.max(0, parseInt(hex.slice(3, 5), 16) - amount);
11309
+ var b = Math.max(0, parseInt(hex.slice(5, 7), 16) - amount);
11310
+ return "#".concat(r.toString(16).padStart(2, '0')).concat(g.toString(16).padStart(2, '0')).concat(b.toString(16).padStart(2, '0'));
11311
+ }
11312
+
11313
+ // ═══════════════════════════════════════════
11314
+ // STYLES
11315
+ // ═══════════════════════════════════════════
11316
+ }, {
11317
+ key: "_injectStyles",
11318
+ value: function _injectStyles() {
11319
+ if (document.getElementById('ttp-mobile-carousel-styles')) return;
11320
+ var s = document.createElement('style');
11321
+ s.id = 'ttp-mobile-carousel-styles';
11322
+ s.textContent = this._generateCSS();
11323
+ document.head.appendChild(s);
11324
+ }
11325
+ }, {
11326
+ key: "_generateCSS",
11327
+ value: function _generateCSS() {
11328
+ return "\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n MOBILE PRODUCT CAROUSEL\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-mobile-carousel {\n position: fixed;\n bottom: 120px;\n left: 0;\n right: 0;\n top: 0;\n z-index: 100000;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n animation: ttpCarouselIn 0.35s cubic-bezier(0.23, 1, 0.32, 1);\n -webkit-tap-highlight-color: transparent;\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n padding-bottom: 16px;\n}\n\n.ttp-mobile-carousel > .ttp-carousel-header,\n.ttp-mobile-carousel > .ttp-carousel-area,\n.ttp-mobile-carousel > .ttp-carousel-dots {\n position: relative;\n z-index: 1;\n}\n\n.ttp-mobile-carousel::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 420px;\n background: rgba(0, 0, 0, 0.2);\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n border-radius: 24px 24px 0 0;\n pointer-events: none;\n z-index: 0;\n}\n\n.ttp-mobile-carousel.ttp-carousel--hiding {\n animation: ttpCarouselOut 0.28s ease forwards;\n pointer-events: none;\n}\n\n@keyframes ttpCarouselIn {\n from { opacity: 0; transform: translateY(30px); }\n to { opacity: 1; transform: translateY(0); }\n}\n@keyframes ttpCarouselOut {\n from { opacity: 1; transform: translateY(0); }\n to { opacity: 0; transform: translateY(30px); }\n}\n\n/* \u2500\u2500 Header \u2500\u2500 */\n\n.ttp-carousel-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0 24px 8px;\n flex-shrink: 0;\n}\n\n.ttp-carousel-title {\n font-size: 13px;\n font-weight: 600;\n color: #fff;\n text-shadow: 0 1px 4px rgba(0,0,0,0.4);\n}\n\n.ttp-carousel-close {\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 0;\n background: none;\n color: #fff;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: auto;\n padding: 0;\n opacity: 0.85;\n}\n.ttp-carousel-close:active {\n opacity: 1;\n}\n\n/* \u2500\u2500 3D Carousel Area \u2500\u2500 */\n\n.ttp-carousel-area {\n position: relative;\n height: 300px;\n flex-shrink: 0;\n perspective: 800px;\n perspective-origin: 50% 55%;\n cursor: grab;\n user-select: none;\n -webkit-user-select: none;\n}\n.ttp-carousel-area:active {\n cursor: grabbing;\n}\n\n.ttp-carousel-wheel {\n position: absolute;\n width: 100%;\n height: 100%;\n transform-style: preserve-3d;\n transition: transform 0.5s cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* \u2500\u2500 Card \u2500\u2500 */\n\n.ttp-carousel-card {\n position: absolute;\n width: 150px;\n height: 285px;\n left: 50%;\n top: 50%;\n margin-left: -75px;\n margin-top: -142px;\n background: #fff;\n border-radius: 18px;\n overflow: hidden;\n box-shadow: 0 8px 32px rgba(0,0,0,0.14);\n transform-style: preserve-3d;\n transition: visibility 0.3s, opacity 0.3s;\n display: flex;\n flex-direction: column;\n}\n\n.ttp-carousel-card--oos {\n opacity: 0.7;\n}\n\n/* Card Image */\n.ttp-carousel-card__image {\n width: 100%;\n height: 100px;\n background: linear-gradient(145deg, #f0f4ff, #e8ecf8);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n position: relative;\n overflow: hidden;\n}\n\n.ttp-carousel-card__image img {\n max-width: 80%;\n max-height: 80%;\n object-fit: contain;\n}\n\n.ttp-carousel-card__badge {\n position: absolute;\n top: 6px;\n right: 6px;\n background: #ef4444;\n color: #fff;\n font-size: 8px;\n font-weight: 700;\n padding: 2px 6px;\n border-radius: 4px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n/* Card Info */\n.ttp-carousel-card__info {\n padding: 8px 10px 4px;\n flex-shrink: 0;\n}\n\n.ttp-carousel-card__brand {\n font-size: 9px;\n font-weight: 500;\n color: #94a3b8;\n margin-bottom: 1px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.ttp-carousel-card__name {\n font-size: 12px;\n font-weight: 600;\n color: #1e1b4b;\n line-height: 1.25;\n margin-bottom: 1px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.ttp-carousel-card__unit {\n font-size: 9px;\n color: #64748b;\n margin-bottom: 3px;\n}\n\n.ttp-carousel-card__price {\n font-size: 16px;\n font-weight: 700;\n color: var(--ttp-carousel-primary, #3b82f6);\n}\n\n/* Card Actions */\n.ttp-carousel-card__actions {\n padding: 6px 10px 10px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: auto;\n}\n\n/* Stepper */\n.ttp-carousel-card__stepper {\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--ttp-carousel-primary-light, rgba(59,130,246,0.12));\n border-radius: 10px;\n padding: 3px;\n}\n\n.ttp-carousel-card__qty-btn {\n width: 30px;\n height: 30px;\n border: none;\n background: transparent;\n color: var(--ttp-carousel-primary, #3b82f6);\n font-size: 17px;\n font-weight: 600;\n cursor: pointer;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.15s;\n -webkit-tap-highlight-color: transparent;\n position: relative;\n z-index: 10;\n touch-action: manipulation;\n}\n.ttp-carousel-card__qty-btn:active:not(:disabled) {\n background: var(--ttp-carousel-primary-light, rgba(59,130,246,0.12));\n}\n.ttp-carousel-card__qty-btn:disabled {\n opacity: 0.3;\n cursor: default;\n}\n\n.ttp-carousel-card__qty-val {\n min-width: 40px;\n text-align: center;\n font-size: 14px;\n font-weight: 600;\n color: #1e1b4b;\n user-select: none;\n}\n\n/* Add Button */\n.ttp-carousel-card__add-btn {\n width: 100%;\n padding: 9px 12px;\n background: linear-gradient(135deg, var(--ttp-carousel-primary, #3b82f6), var(--ttp-carousel-primary-dark, #2563eb));\n color: #fff;\n border: none;\n border-radius: 10px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n box-shadow: 0 4px 12px var(--ttp-carousel-primary-shadow, rgba(59,130,246,0.3));\n transition: transform 0.15s, box-shadow 0.15s;\n -webkit-tap-highlight-color: transparent;\n position: relative;\n z-index: 10;\n touch-action: manipulation;\n}\n.ttp-carousel-card__add-btn:active:not(:disabled) {\n transform: scale(0.97);\n}\n.ttp-carousel-card__add-btn:disabled {\n background: #94a3b8;\n cursor: default;\n box-shadow: none;\n}\n\n.ttp-carousel-card__add-btn--added {\n background: linear-gradient(135deg, #10b981, #059669) !important;\n box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3) !important;\n}\n\n.ttp-carousel-card__add-btn--update {\n background: linear-gradient(135deg, #f59e0b, #d97706);\n box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);\n}\n\n/* \u2500\u2500 Dots \u2500\u2500 */\n\n.ttp-carousel-dots {\n display: flex;\n justify-content: center;\n gap: 8px;\n padding: 8px 0 0;\n flex-shrink: 0;\n}\n\n.ttp-carousel-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: rgba(255,255,255,0.35);\n cursor: pointer;\n transition: all 0.3s;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ttp-carousel-dot--active {\n width: 24px;\n border-radius: 4px;\n background: #fff;\n}\n\n/* \u2500\u2500 Toast \u2500\u2500 */\n\n.ttp-carousel-toast {\n position: fixed;\n left: 16px;\n right: 16px;\n bottom: 100px;\n z-index: 100001;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n background: #fff;\n border-radius: 14px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.15);\n animation: ttpCarouselToastIn 0.3s ease;\n pointer-events: auto;\n}\n\n.ttp-carousel-toast--hiding {\n animation: ttpCarouselOut 0.3s ease forwards;\n}\n\n@keyframes ttpCarouselToastIn {\n from { opacity: 0; transform: translateY(12px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.ttp-carousel-toast__icon {\n width: 28px;\n height: 28px;\n border-radius: 50%;\n background: #10b981;\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n font-weight: 700;\n flex-shrink: 0;\n}\n\n.ttp-carousel-toast__text {\n font-size: 13px;\n color: #1e293b;\n line-height: 1.3;\n}\n.ttp-carousel-toast__text strong {\n font-weight: 600;\n}\n.ttp-carousel-toast__cart {\n font-size: 11px;\n color: #64748b;\n margin-top: 2px;\n font-weight: 500;\n}\n";
11329
+ }
11330
+ }]);
11331
+ }();
11332
+
11333
+ /***/ }),
11334
+
10636
11335
  /***/ "./src/ecommerce/ProductCard.js":
10637
11336
  /*!**************************************!*\
10638
11337
  !*** ./src/ecommerce/ProductCard.js ***!
@@ -10897,7 +11596,9 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
10897
11596
 
10898
11597
  var TTPEcommerceWidget = /*#__PURE__*/function () {
10899
11598
  function TTPEcommerceWidget() {
10900
- var _this = this;
11599
+ var _this$widget$config,
11600
+ _config$colors,
11601
+ _this = this;
10901
11602
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10902
11603
  _classCallCheck(this, TTPEcommerceWidget);
10903
11604
  console.log('[TTPEcommerceWidget] v2.35 initialized');
@@ -10906,6 +11607,7 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
10906
11607
  this.widget = new _widget_TTPChatWidget_js__WEBPACK_IMPORTED_MODULE_0__.TTPChatWidget(config);
10907
11608
  this.ecommerce = new _EcommerceManager_js__WEBPACK_IMPORTED_MODULE_1__.EcommerceManager({
10908
11609
  shadowRoot: this.widget.shadowRoot,
11610
+ primaryColor: ((_this$widget$config = this.widget.config) === null || _this$widget$config === void 0 || (_this$widget$config = _this$widget$config.colors) === null || _this$widget$config === void 0 ? void 0 : _this$widget$config.primary) || ((_config$colors = config.colors) === null || _config$colors === void 0 ? void 0 : _config$colors.primary) || '#3b82f6',
10909
11611
  onProductSelected: function onProductSelected(product) {
10910
11612
  return _this._handleProductSelected(product);
10911
11613
  },
@@ -10931,6 +11633,9 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
10931
11633
  console.warn('[TTPEcommerceWidget] AgentSDK not available yet');
10932
11634
  return;
10933
11635
  }
11636
+
11637
+ // Disable the unsupported-action warning — this widget handles e-commerce actions
11638
+ agentSDK.onUnsupportedAction = null;
10934
11639
  var self = this;
10935
11640
  var originalConnect = agentSDK.connect.bind(agentSDK);
10936
11641
  agentSDK.connect = /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
@@ -10973,6 +11678,9 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
10973
11678
  var originalOnCallEnd = vi.config.onCallEnd;
10974
11679
  var ecommerce = this.ecommerce;
10975
11680
  vi.config.onCallEnd = function () {
11681
+ if (ecommerce.mobileCarousel) {
11682
+ ecommerce.mobileCarousel.hide();
11683
+ }
10976
11684
  ecommerce.productPicker.hide();
10977
11685
  ecommerce._setContainerMode('summary');
10978
11686
  ecommerce._shrinkPanel();
@@ -11269,6 +11977,13 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
11269
11977
  key: "_handleProductSelected",
11270
11978
  value: function _handleProductSelected(product) {
11271
11979
  var quantity = product.selectedAmount || 1;
11980
+ try {
11981
+ var _this$widget$voiceInt2;
11982
+ var voiceSDK = (_this$widget$voiceInt2 = this.widget.voiceInterface) === null || _this$widget$voiceInt2 === void 0 || (_this$widget$voiceInt2 = _this$widget$voiceInt2.sdk) === null || _this$widget$voiceInt2 === void 0 ? void 0 : _this$widget$voiceInt2.voiceSDK;
11983
+ if (voiceSDK !== null && voiceSDK !== void 0 && voiceSDK.stopAudioPlayback) {
11984
+ voiceSDK.stopAudioPlayback();
11985
+ }
11986
+ } catch (e) {/* best-effort */}
11272
11987
  this._sendToVoiceSDK({
11273
11988
  t: 'product_selected',
11274
11989
  productId: product.id,
@@ -11296,8 +12011,8 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
11296
12011
  key: "_sendToVoiceSDK",
11297
12012
  value: function _sendToVoiceSDK(message) {
11298
12013
  try {
11299
- var _this$widget$voiceInt2;
11300
- var voiceSDK = (_this$widget$voiceInt2 = this.widget.voiceInterface) === null || _this$widget$voiceInt2 === void 0 || (_this$widget$voiceInt2 = _this$widget$voiceInt2.sdk) === null || _this$widget$voiceInt2 === void 0 ? void 0 : _this$widget$voiceInt2.voiceSDK;
12014
+ var _this$widget$voiceInt3;
12015
+ var voiceSDK = (_this$widget$voiceInt3 = this.widget.voiceInterface) === null || _this$widget$voiceInt3 === void 0 || (_this$widget$voiceInt3 = _this$widget$voiceInt3.sdk) === null || _this$widget$voiceInt3 === void 0 ? void 0 : _this$widget$voiceInt3.voiceSDK;
11301
12016
  if (voiceSDK && voiceSDK.sendMessage) {
11302
12017
  voiceSDK.sendMessage(message);
11303
12018
  }
@@ -11467,7 +12182,7 @@ __webpack_require__.r(__webpack_exports__);
11467
12182
  * Follows the same pattern as Styles.generateCSS().
11468
12183
  */
11469
12184
  function generateEcommerceCSS() {
11470
- return "\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n ECOMMERCE AREA\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/*\n * When products are visible, expand the panel dynamically.\n * Actual height is set via inline style by EcommerceManager._expandPanelToFit().\n * When the class is removed, the panel returns to its CSS-defined default.\n */\n#text-chat-panel.ttp-panel-expanded {\n transition: height 0.25s ease;\n}\n\n/*\n * Layout overrides when ecommerce layer is active.\n * voiceActiveState is a flex column: [voice-controls] [ecommerce] [conversation].\n * Ecommerce area fills the middle and scrolls; conversation panel stays at bottom.\n */\n#voiceActiveState {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow: hidden !important;\n}\n\n.desktop-voice-section {\n flex-shrink: 1 !important;\n}\n\n.desktop-conversation-panel {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n display: flex !important;\n flex-direction: column !important;\n}\n\n.desktop-conversation-panel .live-transcript-collapsed {\n flex: 1 1 0 !important;\n min-height: 40px !important;\n overflow: hidden !important;\n}\n\n.desktop-conversation-panel .voice-text-input-area {\n margin-top: auto !important;\n padding: 3px 16px !important;\n}\n\n.desktop-conversation-panel .voice-input-hint {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .desktop-voice-section {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .compact-voice-section {\n display: flex !important;\n}\n\n#voiceActiveState.ttp-products-visible .desktop-conversation-panel {\n flex: 0 0 auto !important;\n}\n\n#voiceActiveState.ttp-products-visible .conversation-history {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .live-transcript-collapsed {\n flex: 0 0 auto !important;\n}\n\n.voice-text-input-area {\n flex-shrink: 0 !important;\n}\n\n.conversation-history {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow-y: auto !important;\n}\n\n.ttp-ecommerce-area {\n display: flex;\n flex-direction: column;\n width: 100%;\n flex: 1 1 0;\n min-height: 0;\n overflow-x: hidden;\n overflow-y: auto;\n position: relative;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT PICKER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-picker {\n background: #fff;\n border-radius: 14px;\n border: 1px solid #e5e7eb;\n box-shadow: 0 6px 24px rgba(0,0,0,0.08);\n margin: 8px 12px;\n overflow: hidden;\n animation: ttpSlideUp 0.25s ease;\n display: flex;\n flex-direction: column;\n}\n\n@keyframes ttpSlideUp {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.ttp-picker-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n border-bottom: 1px solid #f1f5f9;\n flex-shrink: 0;\n}\n\n.ttp-picker-title {\n font-size: 12px;\n font-weight: 600;\n color: #64748b;\n}\n\n.ttp-picker-close {\n background: none;\n border: none;\n cursor: pointer;\n color: #94a3b8;\n font-size: 14px;\n padding: 2px 6px;\n border-radius: 4px;\n}\n.ttp-picker-close:hover {\n background: #f1f5f9;\n color: #475569;\n}\n\n/* Horizontal card layout */\n.ttp-products-horizontal {\n display: flex;\n gap: 10px;\n padding: 12px;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: thin;\n}\n.ttp-products-horizontal::-webkit-scrollbar { height: 4px; }\n.ttp-products-horizontal::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 2px; }\n\n/* Vertical list layout */\n.ttp-products-vertical {\n padding: 8px 12px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex: 1;\n min-height: 0;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT CARD (horizontal)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-card {\n width: 140px;\n flex-shrink: 0;\n background: #fff;\n border-radius: 12px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n transition: border-color 0.15s;\n}\n.ttp-product-card:hover { border-color: #3b82f6; }\n\n.ttp-product-image {\n height: 85px;\n background: #f8fafc;\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n overflow: hidden;\n}\n.ttp-product-image img {\n max-width: 90%;\n max-height: 75px;\n object-fit: contain;\n}\n\n.ttp-stock-badge {\n position: absolute;\n top: 4px; right: 4px;\n background: #fef2f2;\n color: #ef4444;\n font-size: 9px;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: 4px;\n}\n\n.ttp-product-details {\n padding: 8px 10px 10px;\n}\n\n.ttp-product-name {\n font-size: 12px;\n font-weight: 600;\n color: #1e293b;\n line-height: 1.3;\n min-height: 30px;\n}\n\n.ttp-product-meta {\n font-size: 10px;\n color: #94a3b8;\n margin-top: 2px;\n}\n\n.ttp-product-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-top: 6px;\n}\n\n.ttp-product-price {\n font-size: 14px;\n font-weight: 700;\n color: #1e293b;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT COMPACT (list view)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-compact {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n background: #fff;\n border-radius: 10px;\n border: 1px solid #e5e7eb;\n transition: border-color 0.15s;\n}\n.ttp-product-compact:hover { border-color: #3b82f6; }\n\n.ttp-product-image-sm {\n width: 40px; height: 40px;\n border-radius: 8px;\n background: #f8fafc;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: hidden;\n}\n.ttp-product-image-sm img {\n max-width: 36px;\n max-height: 36px;\n object-fit: contain;\n}\n\n.ttp-product-compact .ttp-product-info {\n flex: 1;\n min-width: 0;\n}\n.ttp-product-compact .ttp-product-name {\n min-height: auto;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ttp-product-compact .ttp-product-action {\n text-align: right;\n flex-shrink: 0;\n}\n.ttp-product-compact .ttp-product-price {\n font-size: 13px;\n}\n\n.ttp-out-of-stock {\n font-size: 10px;\n color: #ef4444;\n font-weight: 500;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n ADD BUTTON\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-add-btn {\n padding: 4px 14px;\n border-radius: 8px;\n border: none;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n background: #3b82f6;\n color: #fff;\n transition: all 0.15s;\n}\n.ttp-add-btn:hover { background: #2563eb; }\n.ttp-add-btn:active { transform: scale(0.95); }\n.ttp-add-btn.ttp-added { background: #10b981 !important; }\n.ttp-add-btn:disabled { cursor: default; }\n.ttp-add-btn.ttp-update-btn { background: #f59e0b; }\n.ttp-add-btn.ttp-update-btn:hover { background: #d97706; }\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n CART TOAST\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-cart-toast {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n margin: 0 12px;\n background: #f0fdf4;\n border-radius: 12px;\n border: 1px solid #bbf7d0;\n animation: ttpToastIn 0.25s ease;\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n z-index: 10;\n box-shadow: 0 4px 12px rgba(0,0,0,0.1);\n}\n\n@keyframes ttpToastIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ttp-cart-toast.ttp-toast-hiding {\n animation: ttpSlideDown 0.3s ease forwards;\n}\n\n@keyframes ttpSlideDown {\n to { opacity: 0; transform: translateY(-8px); height: 0; padding: 0; margin: 0; overflow: hidden; }\n}\n\n.ttp-toast-icon {\n width: 24px; height: 24px;\n border-radius: 50%;\n background: #10b981;\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 700;\n flex-shrink: 0;\n}\n\n.ttp-toast-content { flex: 1; min-width: 0; }\n.ttp-toast-title { font-size: 12px; font-weight: 600; color: #166534; }\n.ttp-toast-product {\n font-size: 11px;\n color: #15803d;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ttp-toast-undo {\n background: none;\n border: 1px solid #86efac;\n border-radius: 6px;\n padding: 3px 8px;\n font-size: 10px;\n color: #15803d;\n cursor: pointer;\n font-weight: 600;\n flex-shrink: 0;\n}\n.ttp-toast-undo:hover { background: #dcfce7; }\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n CART SUMMARY BAR\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-cart-summary {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 14px;\n margin: 4px 12px 8px;\n background: #f8fafc;\n border-radius: 10px;\n border: 1px solid #e2e8f0;\n}\n\n.ttp-cart-info {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.ttp-cart-icon { font-size: 14px; }\n.ttp-cart-count { font-size: 12px; font-weight: 600; color: #475569; }\n.ttp-cart-separator { font-size: 12px; color: #94a3b8; }\n.ttp-cart-total { font-size: 13px; font-weight: 700; color: #1e293b; }\n.ttp-cart-view {\n font-size: 12px;\n font-weight: 600;\n color: #6366f1;\n text-decoration: none;\n cursor: pointer;\n white-space: nowrap;\n}\n.ttp-cart-view:hover {\n color: #4f46e5;\n text-decoration: underline;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n QUANTITY/WEIGHT STEPPER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-stepper {\n display: flex;\n align-items: center;\n gap: 2px;\n margin: 4px 0;\n}\n\n.ttp-stepper-btn {\n width: 24px;\n height: 24px;\n border-radius: 6px;\n border: 1px solid #e2e8f0;\n background: #f8fafc;\n color: #475569;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n line-height: 1;\n transition: all 0.15s;\n flex-shrink: 0;\n}\n\n.ttp-stepper-btn:hover:not(:disabled) {\n background: #e2e8f0;\n border-color: #cbd5e1;\n}\n\n.ttp-stepper-btn:active:not(:disabled) {\n transform: scale(0.9);\n}\n\n.ttp-stepper-btn:disabled,\n.ttp-stepper-disabled {\n opacity: 0.35;\n cursor: default;\n}\n\n.ttp-stepper-value {\n font-size: 12px;\n font-weight: 600;\n color: #1e293b;\n min-width: 36px;\n text-align: center;\n user-select: none;\n}\n\n.ttp-product-compact .ttp-stepper {\n justify-content: flex-end;\n}\n\n.ttp-product-compact .ttp-product-action {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 2px;\n}\n\n.ttp-product-card .ttp-stepper {\n justify-content: center;\n margin: 4px 0;\n}\n\n.ttp-product-card .ttp-product-footer {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n}\n\n ";
12185
+ return "\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n ECOMMERCE AREA\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/*\n * When products are visible, expand the panel dynamically.\n * Actual height is set via inline style by EcommerceManager._expandPanelToFit().\n * When the class is removed, the panel returns to its CSS-defined default.\n */\n#text-chat-panel.ttp-panel-expanded {\n transition: height 0.25s ease;\n}\n\n/*\n * Layout overrides when ecommerce layer is active.\n * voiceActiveState is a flex column: [voice-controls] [ecommerce] [conversation].\n * Ecommerce area fills the middle and scrolls; conversation panel stays at bottom.\n */\n#voiceActiveState {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow: hidden !important;\n}\n\n.desktop-voice-section {\n flex-shrink: 1 !important;\n}\n\n.desktop-conversation-panel {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n display: flex !important;\n flex-direction: column !important;\n}\n\n.desktop-conversation-panel .live-transcript-collapsed {\n flex: 1 1 0 !important;\n min-height: 40px !important;\n overflow: hidden !important;\n}\n\n.desktop-conversation-panel .voice-text-input-area {\n margin-top: auto !important;\n padding: 3px 16px !important;\n}\n\n.desktop-conversation-panel .voice-input-hint {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .desktop-voice-section {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .compact-voice-section {\n display: flex !important;\n}\n\n#voiceActiveState.ttp-products-visible .desktop-conversation-panel {\n flex: 0 0 auto !important;\n}\n\n#voiceActiveState.ttp-products-visible .conversation-history {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .live-transcript-collapsed {\n flex: 0 0 auto !important;\n}\n\n.voice-text-input-area {\n flex-shrink: 0 !important;\n}\n\n.conversation-history {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow-y: auto !important;\n}\n\n.ttp-ecommerce-area {\n display: flex;\n flex-direction: column;\n width: 100%;\n flex: 1 1 0;\n min-height: 0;\n overflow-x: hidden;\n overflow-y: auto;\n position: relative;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT PICKER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-picker {\n background: #fff;\n border-radius: 14px;\n border: 1px solid #e5e7eb;\n box-shadow: 0 6px 24px rgba(0,0,0,0.08);\n margin: 8px 12px;\n overflow: hidden;\n animation: ttpSlideUp 0.25s ease;\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n}\n\n@keyframes ttpSlideUp {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.ttp-picker-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n border-bottom: 1px solid #f1f5f9;\n flex-shrink: 0;\n}\n\n.ttp-picker-title {\n font-size: 12px;\n font-weight: 600;\n color: #64748b;\n}\n\n.ttp-picker-close {\n background: none;\n border: none;\n cursor: pointer;\n color: #94a3b8;\n font-size: 14px;\n padding: 2px 6px;\n border-radius: 4px;\n}\n.ttp-picker-close:hover {\n background: #f1f5f9;\n color: #475569;\n}\n\n/* Horizontal card layout */\n.ttp-products-horizontal {\n display: flex;\n gap: 10px;\n padding: 12px;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: thin;\n}\n.ttp-products-horizontal::-webkit-scrollbar { height: 4px; }\n.ttp-products-horizontal::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 2px; }\n\n/* Vertical list layout */\n.ttp-products-vertical {\n padding: 8px 12px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex: 1;\n min-height: 0;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT CARD (horizontal)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-card {\n width: 140px;\n flex-shrink: 0;\n background: #fff;\n border-radius: 12px;\n border: 1px solid #e5e7eb;\n overflow: hidden;\n transition: border-color 0.15s;\n}\n.ttp-product-card:hover { border-color: #3b82f6; }\n\n.ttp-product-image {\n height: 85px;\n background: #f8fafc;\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n overflow: hidden;\n}\n.ttp-product-image img {\n max-width: 90%;\n max-height: 75px;\n object-fit: contain;\n}\n\n.ttp-stock-badge {\n position: absolute;\n top: 4px; right: 4px;\n background: #fef2f2;\n color: #ef4444;\n font-size: 9px;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: 4px;\n}\n\n.ttp-product-details {\n padding: 8px 10px 14px;\n}\n\n.ttp-product-name {\n font-size: 12px;\n font-weight: 600;\n color: #1e293b;\n line-height: 1.3;\n min-height: 30px;\n}\n\n.ttp-product-meta {\n font-size: 10px;\n color: #94a3b8;\n margin-top: 2px;\n}\n\n.ttp-product-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-top: 6px;\n}\n\n.ttp-product-price {\n font-size: 14px;\n font-weight: 700;\n color: #1e293b;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT COMPACT (list view)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-compact {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n background: #fff;\n border-radius: 10px;\n border: 1px solid #e5e7eb;\n transition: border-color 0.15s;\n}\n.ttp-product-compact:hover { border-color: #3b82f6; }\n\n.ttp-product-image-sm {\n width: 40px; height: 40px;\n border-radius: 8px;\n background: #f8fafc;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: hidden;\n}\n.ttp-product-image-sm img {\n max-width: 36px;\n max-height: 36px;\n object-fit: contain;\n}\n\n.ttp-product-compact .ttp-product-info {\n flex: 1;\n min-width: 0;\n}\n.ttp-product-compact .ttp-product-name {\n min-height: auto;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ttp-product-compact .ttp-product-action {\n text-align: right;\n flex-shrink: 0;\n}\n.ttp-product-compact .ttp-product-price {\n font-size: 13px;\n}\n\n.ttp-out-of-stock {\n font-size: 10px;\n color: #ef4444;\n font-weight: 500;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n ADD BUTTON\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-add-btn {\n padding: 4px 14px;\n border-radius: 8px;\n border: none;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n background: #3b82f6;\n color: #fff;\n transition: all 0.15s;\n}\n.ttp-add-btn:hover { background: #2563eb; }\n.ttp-add-btn:active { transform: scale(0.95); }\n.ttp-add-btn.ttp-added { background: #10b981 !important; }\n.ttp-add-btn:disabled { cursor: default; }\n.ttp-add-btn.ttp-update-btn { background: #f59e0b; }\n.ttp-add-btn.ttp-update-btn:hover { background: #d97706; }\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n CART TOAST\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-cart-toast {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n margin: 0 12px;\n background: #f0fdf4;\n border-radius: 12px;\n border: 1px solid #bbf7d0;\n animation: ttpToastIn 0.25s ease;\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n z-index: 10;\n box-shadow: 0 4px 12px rgba(0,0,0,0.1);\n}\n\n@keyframes ttpToastIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ttp-cart-toast.ttp-toast-hiding {\n animation: ttpSlideDown 0.3s ease forwards;\n}\n\n@keyframes ttpSlideDown {\n to { opacity: 0; transform: translateY(-8px); height: 0; padding: 0; margin: 0; overflow: hidden; }\n}\n\n.ttp-toast-icon {\n width: 24px; height: 24px;\n border-radius: 50%;\n background: #10b981;\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 700;\n flex-shrink: 0;\n}\n\n.ttp-toast-content { flex: 1; min-width: 0; }\n.ttp-toast-title { font-size: 12px; font-weight: 600; color: #166534; }\n.ttp-toast-product {\n font-size: 11px;\n color: #15803d;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ttp-toast-undo {\n background: none;\n border: 1px solid #86efac;\n border-radius: 6px;\n padding: 3px 8px;\n font-size: 10px;\n color: #15803d;\n cursor: pointer;\n font-weight: 600;\n flex-shrink: 0;\n}\n.ttp-toast-undo:hover { background: #dcfce7; }\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n CART SUMMARY BAR\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-cart-summary {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 14px;\n margin: 4px 12px 8px;\n background: #f8fafc;\n border-radius: 10px;\n border: 1px solid #e2e8f0;\n}\n\n.ttp-cart-info {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.ttp-cart-icon { font-size: 14px; }\n.ttp-cart-count { font-size: 12px; font-weight: 600; color: #475569; }\n.ttp-cart-separator { font-size: 12px; color: #94a3b8; }\n.ttp-cart-total { font-size: 13px; font-weight: 700; color: #1e293b; }\n.ttp-cart-view {\n font-size: 12px;\n font-weight: 600;\n color: #6366f1;\n text-decoration: none;\n cursor: pointer;\n white-space: nowrap;\n}\n.ttp-cart-view:hover {\n color: #4f46e5;\n text-decoration: underline;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n QUANTITY/WEIGHT STEPPER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-stepper {\n display: flex;\n align-items: center;\n gap: 2px;\n margin: 4px 0;\n}\n\n.ttp-stepper-btn {\n width: 24px;\n height: 24px;\n border-radius: 6px;\n border: 1px solid #e2e8f0;\n background: #f8fafc;\n color: #475569;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n line-height: 1;\n transition: all 0.15s;\n flex-shrink: 0;\n}\n\n.ttp-stepper-btn:hover:not(:disabled) {\n background: #e2e8f0;\n border-color: #cbd5e1;\n}\n\n.ttp-stepper-btn:active:not(:disabled) {\n transform: scale(0.9);\n}\n\n.ttp-stepper-btn:disabled,\n.ttp-stepper-disabled {\n opacity: 0.35;\n cursor: default;\n}\n\n.ttp-stepper-value {\n font-size: 12px;\n font-weight: 600;\n color: #1e293b;\n min-width: 36px;\n text-align: center;\n user-select: none;\n}\n\n.ttp-product-compact .ttp-stepper {\n justify-content: flex-end;\n}\n\n.ttp-product-compact .ttp-product-action {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 2px;\n}\n\n.ttp-product-card .ttp-stepper {\n justify-content: center;\n margin: 4px 0;\n}\n\n.ttp-product-card .ttp-product-footer {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n}\n\n ";
11471
12186
  }
11472
12187
 
11473
12188
  /***/ }),
@@ -11857,19 +12572,23 @@ var MicPermissionModal = /*#__PURE__*/function () {
11857
12572
  }();
11858
12573
  /**
11859
12574
  * Pre-prompt modal - shown before requesting browser permission
12575
+ * Uses a transparent overlay (no blur) so the call view is visible behind it
11860
12576
  */
11861
12577
  function showPrePromptModal(onAccept, onCancel) {
11862
12578
  var modal = new MicPermissionModal();
11863
12579
  var overlay = modal.createOverlay();
11864
12580
  var deviceInfo = (0,_MicPermissionUtils_js__WEBPACK_IMPORTED_MODULE_0__.getDeviceInfo)();
12581
+
12582
+ // Remove blur and darken so the call view is visible behind
12583
+ overlay.style.background = 'rgba(0, 0, 0, 0.3)';
12584
+ overlay.style.backdropFilter = 'none';
12585
+ overlay.style.webkitBackdropFilter = 'none';
11865
12586
  var modalDiv = document.createElement('div');
11866
12587
  modalDiv.className = 'mic-permission-modal';
11867
12588
  modalDiv.innerHTML = "\n <div class=\"mic-permission-modal__icon\">\uD83C\uDFA4</div>\n <h2 class=\"mic-permission-modal__title\">Enable Your Microphone</h2>\n <p class=\"mic-permission-modal__description\">\n To start a voice call with our agent, we need access to your microphone.\n </p>\n <div class=\"mic-permission-modal__highlight\">\n <span class=\"mic-permission-modal__highlight-icon\">\uD83D\uDC46</span>\n <span>Please click <strong>\"Allow\"</strong> on the next popup</span>\n </div>\n ".concat(deviceInfo.isIOS ? "\n <div class=\"mic-permission-modal__note\">\n <span>\u2139\uFE0F</span>\n <span>On iPhone/iPad, you may need to allow microphone access in your device Settings.</span>\n </div>\n " : '', "\n <div class=\"mic-permission-modal__actions\">\n <button class=\"mic-permission-modal__btn mic-permission-modal__btn--primary\" data-action=\"accept\">\n Continue\n </button>\n <button class=\"mic-permission-modal__btn mic-permission-modal__btn--secondary\" data-action=\"cancel\">\n Not Now\n </button>\n </div>\n ");
11868
12589
  overlay.appendChild(modalDiv);
11869
12590
  modal.overlay = overlay;
11870
12591
  modal.modal = modalDiv;
11871
-
11872
- // Event handlers
11873
12592
  modalDiv.querySelector('[data-action="accept"]').addEventListener('click', function () {
11874
12593
  modal.close();
11875
12594
  onAccept();
@@ -11878,8 +12597,6 @@ function showPrePromptModal(onAccept, onCancel) {
11878
12597
  modal.close();
11879
12598
  onCancel();
11880
12599
  });
11881
-
11882
- // Close on overlay click
11883
12600
  overlay.addEventListener('click', function (e) {
11884
12601
  if (e.target === overlay) {
11885
12602
  modal.close();
@@ -14458,6 +15175,12 @@ function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf
14458
15175
 
14459
15176
 
14460
15177
 
15178
+ // iOS FIX: Shared AudioContext for playback that persists across AudioPlayer instances.
15179
+ // iOS WebKit doesn't release audio hardware synchronously when AudioContext.close() is called,
15180
+ // so creating a new AudioContext immediately after closing the old one can fail silently.
15181
+ var _sharedPlayerContext = null;
15182
+ var _sharedPlayerSampleRate = null;
15183
+
14461
15184
  /**
14462
15185
 
14463
15186
  * AudioPlayer v2 - Handles multiple audio formats with codec support
@@ -14494,8 +15217,6 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
14494
15217
 
14495
15218
  // Version identification log
14496
15219
  console.log('%c🔊 AUDIO PLAYER - MIC/SPEAKER RESET ON NEW CALL ENABLED', 'background: #10b981; color: white; font-size: 14px; font-weight: bold; padding: 4px 8px; border-radius: 4px;');
14497
- console.log('%cFix: Mic/speaker mute state + UI fully reset on call end and call start', 'background: #d1fae5; color: #065f46; font-size: 12px; padding: 2px 6px; border-radius: 3px;');
14498
- console.log('%cBuild: 2026-02-26-MIC-SPEAKER-RESET', 'background: #e0e7ff; color: #1e40af; font-size: 11px; padding: 2px 6px; border-radius: 3px;');
14499
15220
  console.log('');
14500
15221
  _this.config = config;
14501
15222
  _this.audioContext = null;
@@ -15829,43 +16550,84 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
15829
16550
  }, {
15830
16551
  key: "initializeAudioContext",
15831
16552
  value: function initializeAudioContext() {
15832
- var _this$outputFormat5, _this$outputFormat6;
16553
+ var _this$outputFormat5,
16554
+ _this7 = this,
16555
+ _this$outputFormat6;
15833
16556
  // Use negotiated sample rate, default to 44100Hz to match server output
15834
- // This ensures AudioContext matches server output before format negotiation completes
15835
16557
  var desiredSampleRate = ((_this$outputFormat5 = this.outputFormat) === null || _this$outputFormat5 === void 0 ? void 0 : _this$outputFormat5.sampleRate) || 44100;
15836
-
15837
- // If backend sends non-standard rate (like 22050Hz), check if browser supports it
15838
- // If not, prefer 44100Hz (default) or 48000Hz to avoid resampling issues
15839
16558
  if (desiredSampleRate !== 48000 && desiredSampleRate !== 44100) {
15840
- // For non-standard rates, try to use browser's native rate to avoid resampling
15841
- // This prevents tiny cuts caused by browser resampling
15842
16559
  console.log("\u2139\uFE0F AudioPlayer: Backend requested ".concat(desiredSampleRate, "Hz, but browser may resample"));
15843
16560
  console.log(" Consider requesting 44100Hz or 48000Hz from backend to avoid resampling and improve quality");
15844
16561
  }
15845
16562
 
15846
- // Check if AudioContext exists and if it matches the desired sample rate
16563
+ // Check if current instance AudioContext exists and matches
15847
16564
  if (this.audioContext) {
15848
16565
  var currentSampleRate = this.audioContext.sampleRate;
15849
- // If sample rate differs significantly, recreate the AudioContext
15850
16566
  if (Math.abs(currentSampleRate - desiredSampleRate) > 100) {
15851
16567
  console.warn("\u26A0\uFE0F AudioPlayer: AudioContext sample rate (".concat(currentSampleRate, "Hz) doesn't match format (").concat(desiredSampleRate, "Hz), recreating..."));
15852
16568
  this.stopImmediate();
15853
16569
  this._cleanupAudioContext();
15854
16570
  } else {
15855
- // Sample rate matches (or close enough), no need to recreate
15856
16571
  return;
15857
16572
  }
15858
16573
  }
16574
+
16575
+ // iOS FIX: Reuse shared AudioContext if available and compatible.
16576
+ // iOS WebKit doesn't release audio hardware synchronously on AudioContext.close(),
16577
+ // causing newly created AudioContexts to fail silently.
16578
+ var canReuseShared = _sharedPlayerContext && _sharedPlayerContext.state !== 'closed' && _sharedPlayerSampleRate === desiredSampleRate;
16579
+ if (canReuseShared) {
16580
+ console.log("\u267B\uFE0F AudioPlayer: Reusing shared AudioContext at ".concat(desiredSampleRate, "Hz (iOS-safe)"));
16581
+ this.audioContext = _sharedPlayerContext;
16582
+ var setupAfterResume = function setupAfterResume() {
16583
+ _this7.setupAudioContextStateMonitoring();
16584
+ if (_this7.gainNode) {
16585
+ try {
16586
+ _this7.gainNode.disconnect();
16587
+ } catch (e) {
16588
+ console.warn('⚠️ AudioPlayer: Error disconnecting old GainNode:', e);
16589
+ }
16590
+ }
16591
+ _this7.gainNode = _this7.audioContext.createGain();
16592
+ _this7.gainNode.gain.value = 1.0;
16593
+ _this7.gainNode.connect(_this7.audioContext.destination);
16594
+ if (!_this7._audioContextPrimed) {
16595
+ _this7._primeAudioContext();
16596
+ }
16597
+ };
16598
+ if (this.audioContext.state === 'suspended') {
16599
+ this.audioContext.resume().then(function () {
16600
+ return setupAfterResume();
16601
+ }).catch(function (e) {
16602
+ console.warn('⚠️ AudioPlayer: Error resuming shared context:', e);
16603
+ setupAfterResume();
16604
+ });
16605
+ } else {
16606
+ setupAfterResume();
16607
+ }
16608
+ return;
16609
+ }
15859
16610
  console.log("\uD83C\uDFB5 AudioPlayer: Creating AudioContext at ".concat(desiredSampleRate, "Hz (from outputFormat: ").concat(((_this$outputFormat6 = this.outputFormat) === null || _this$outputFormat6 === void 0 ? void 0 : _this$outputFormat6.sampleRate) || 'not set', ")"));
16611
+
16612
+ // Close old shared context if sample rate changed
16613
+ if (_sharedPlayerContext && _sharedPlayerContext.state !== 'closed') {
16614
+ console.log('🔄 AudioPlayer: Closing old shared AudioContext (sample rate changed)');
16615
+ try {
16616
+ _sharedPlayerContext.close();
16617
+ } catch (e) {
16618
+ console.warn('⚠️ AudioPlayer: Error closing old shared context:', e);
16619
+ }
16620
+ }
15860
16621
  try {
15861
- // Try to create with specific sample rate
15862
16622
  this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
15863
16623
  sampleRate: desiredSampleRate,
15864
- latencyHint: 'playback' // ✅ Better for streaming audio - prioritizes smooth playback over low latency
16624
+ latencyHint: 'playback'
15865
16625
  });
15866
- console.log("\u2705 AudioContext created at ".concat(this.audioContext.sampleRate, "Hz (requested: ").concat(desiredSampleRate, "Hz)"));
15867
16626
 
15868
- // CRITICAL: If browser can't create requested sample rate, we have a problem
16627
+ // Store as shared context
16628
+ _sharedPlayerContext = this.audioContext;
16629
+ _sharedPlayerSampleRate = desiredSampleRate;
16630
+ console.log("\u2705 AudioContext created at ".concat(this.audioContext.sampleRate, "Hz (requested: ").concat(desiredSampleRate, "Hz)"));
15869
16631
  if (Math.abs(this.audioContext.sampleRate - desiredSampleRate) > 100) {
15870
16632
  console.error("\u274C CRITICAL: Browser sample rate mismatch!");
15871
16633
  console.error(" Requested: ".concat(desiredSampleRate, "Hz"));
@@ -15876,34 +16638,23 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
15876
16638
  console.warn("\u26A0\uFE0F Browser adjusted sample rate: ".concat(desiredSampleRate, "Hz \u2192 ").concat(this.audioContext.sampleRate, "Hz"));
15877
16639
  console.warn(" Browser will automatically resample audio.");
15878
16640
  }
15879
-
15880
- // ✅ NEW: Monitor AudioContext state changes (handles mic permission, tab switching, etc.)
15881
16641
  this.setupAudioContextStateMonitoring();
15882
-
15883
- // Create GainNode for volume/mute control
15884
16642
  this.gainNode = this.audioContext.createGain();
15885
- this.gainNode.gain.value = 1.0; // Default to full volume
16643
+ this.gainNode.gain.value = 1.0;
15886
16644
  this.gainNode.connect(this.audioContext.destination);
15887
16645
  console.log('✅ AudioPlayer: GainNode created for volume control');
15888
-
15889
- // Prime audio hardware for Android
15890
16646
  this._primeAudioContext();
15891
16647
  } catch (error) {
15892
- // Fallback to default if browser doesn't support custom sample rate
15893
16648
  console.error("\u274C Failed to create AudioContext:", error);
15894
16649
  this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
16650
+ _sharedPlayerContext = this.audioContext;
16651
+ _sharedPlayerSampleRate = this.audioContext.sampleRate;
15895
16652
  console.log("\u2139\uFE0F Using browser default: ".concat(this.audioContext.sampleRate, "Hz"));
15896
-
15897
- // Setup monitoring for fallback AudioContext too
15898
16653
  this.setupAudioContextStateMonitoring();
15899
-
15900
- // Create GainNode for volume/mute control
15901
16654
  this.gainNode = this.audioContext.createGain();
15902
- this.gainNode.gain.value = 1.0; // Default to full volume
16655
+ this.gainNode.gain.value = 1.0;
15903
16656
  this.gainNode.connect(this.audioContext.destination);
15904
16657
  console.log('✅ AudioPlayer: GainNode created for volume control (fallback)');
15905
-
15906
- // Prime audio hardware for Android
15907
16658
  this._primeAudioContext();
15908
16659
  }
15909
16660
  }
@@ -15964,7 +16715,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
15964
16715
  }, {
15965
16716
  key: "_cleanupAudioContext",
15966
16717
  value: function _cleanupAudioContext() {
15967
- console.log('[TTP AudioPlayer] 🧹 Cleaning up AudioContext (refactored method)');
16718
+ console.log('[TTP AudioPlayer] 🧹 Cleaning up AudioContext (iOS-safe: keeping shared context alive)');
15968
16719
 
15969
16720
  // Disconnect and cleanup gainNode
15970
16721
  if (this.gainNode) {
@@ -15988,24 +16739,34 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
15988
16739
  this._waitForReadyStateHandler = null;
15989
16740
  }
15990
16741
 
15991
- // Close context if open
15992
- if (this.audioContext && this.audioContext.state !== 'closed') {
15993
- this.audioContext.close();
16742
+ // iOS FIX: Suspend instead of closing the AudioContext.
16743
+ // The shared context will be reused by the next AudioPlayer instance.
16744
+ // Closing it would cause the next instance to create a new one,
16745
+ // which fails on iOS due to the audio hardware not being released in time.
16746
+ if (this.audioContext && this.audioContext.state === 'running') {
16747
+ this.audioContext.suspend().catch(function (e) {
16748
+ console.warn('⚠️ AudioPlayer: Error suspending AudioContext during cleanup:', e);
16749
+ });
15994
16750
  }
15995
16751
 
15996
- // Set to null after cleanup
16752
+ // Release instance reference but keep shared context alive
15997
16753
  this.audioContext = null;
15998
16754
  this._audioContextPrimed = false;
15999
16755
  }
16000
16756
 
16001
16757
  /**
16002
- * Setup AudioContext state change monitoring
16003
- * Handles mic permission grants, tab switching, browser suspension, etc.
16758
+ * Fully close the shared AudioContext.
16759
+ * Only call this when the widget is being completely removed from the page.
16004
16760
  */
16005
16761
  }, {
16006
16762
  key: "setupAudioContextStateMonitoring",
16007
- value: function setupAudioContextStateMonitoring() {
16008
- var _this7 = this;
16763
+ value:
16764
+ /**
16765
+ * Setup AudioContext state change monitoring
16766
+ * Handles mic permission grants, tab switching, browser suspension, etc.
16767
+ */
16768
+ function setupAudioContextStateMonitoring() {
16769
+ var _this8 = this;
16009
16770
  if (!this.audioContext) {
16010
16771
  return;
16011
16772
  }
@@ -16018,35 +16779,35 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16018
16779
  // Create handler that references this.audioContext dynamically
16019
16780
  this._audioContextStateChangeHandler = function () {
16020
16781
  // Null check required because audioContext may be cleaned up while handler is queued
16021
- if (!_this7.audioContext) {
16782
+ if (!_this8.audioContext) {
16022
16783
  console.warn('⚠️ AudioPlayer: State change handler fired but AudioContext is null');
16023
16784
  return;
16024
16785
  }
16025
- console.log("\uD83C\uDFB5 AudioPlayer: AudioContext state changed to: ".concat(_this7.audioContext.state));
16026
- if (_this7.audioContext.state === 'suspended' && _this7.isPlaying) {
16786
+ console.log("\uD83C\uDFB5 AudioPlayer: AudioContext state changed to: ".concat(_this8.audioContext.state));
16787
+ if (_this8.audioContext.state === 'suspended' && _this8.isPlaying) {
16027
16788
  // AudioContext was suspended during playback (tab switch, mic permission, etc.)
16028
16789
  console.warn('⚠️ AudioPlayer: AudioContext suspended during playback');
16029
16790
  // Note: Playback will pause automatically, but we should handle queue processing
16030
16791
  // The state change will be handled when we try to process next frame
16031
- } else if (_this7.audioContext.state === 'running' && !_this7.isPlaying && (_this7.audioQueue.length > 0 || _this7.pcmChunkQueue.length > 0 || _this7.preparedBuffer.length > 0)) {
16792
+ } else if (_this8.audioContext.state === 'running' && !_this8.isPlaying && (_this8.audioQueue.length > 0 || _this8.pcmChunkQueue.length > 0 || _this8.preparedBuffer.length > 0)) {
16032
16793
  // AudioContext resumed and we have queued frames
16033
16794
  // This handles: mic permission grant, tab switching back, browser resume, etc.
16034
16795
  console.log('✅ AudioPlayer: AudioContext resumed - resuming queue processing');
16035
16796
 
16036
16797
  // Resume queue processing if we have frames
16037
- if (_this7.audioQueue.length > 0 && !_this7.isProcessingQueue) {
16798
+ if (_this8.audioQueue.length > 0 && !_this8.isProcessingQueue) {
16038
16799
  setTimeout(function () {
16039
- return _this7.processQueue();
16800
+ return _this8.processQueue();
16040
16801
  }, 50);
16041
16802
  }
16042
- if (_this7.pcmChunkQueue.length > 0 && !_this7.isProcessingPcmQueue) {
16803
+ if (_this8.pcmChunkQueue.length > 0 && !_this8.isProcessingPcmQueue) {
16043
16804
  setTimeout(function () {
16044
- return _this7.processPcmQueue();
16805
+ return _this8.processPcmQueue();
16045
16806
  }, 50);
16046
16807
  }
16047
- if (_this7.preparedBuffer.length > 0 && !_this7.isSchedulingFrames) {
16808
+ if (_this8.preparedBuffer.length > 0 && !_this8.isSchedulingFrames) {
16048
16809
  setTimeout(function () {
16049
- return _this7.scheduleFrames();
16810
+ return _this8.scheduleFrames();
16050
16811
  }, 50);
16051
16812
  }
16052
16813
  }
@@ -16065,7 +16826,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16065
16826
  key: "processQueue",
16066
16827
  value: (function () {
16067
16828
  var _processQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
16068
- var _this8 = this;
16829
+ var _this9 = this;
16069
16830
  var audioBlob, wasFirstPlay, audioContext, arrayBuffer, audioBuffer, shouldEmitStart, source, _t4;
16070
16831
  return _regenerator().w(function (_context7) {
16071
16832
  while (1) switch (_context7.p = _context7.n) {
@@ -16131,22 +16892,22 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16131
16892
  // Handle end
16132
16893
 
16133
16894
  source.onended = function () {
16134
- _this8.currentSource = null;
16135
- _this8.isProcessingQueue = false;
16895
+ _this9.currentSource = null;
16896
+ _this9.isProcessingQueue = false;
16136
16897
 
16137
16898
  // Process next chunk
16138
16899
 
16139
- if (_this8.audioQueue.length > 0) {
16900
+ if (_this9.audioQueue.length > 0) {
16140
16901
  setTimeout(function () {
16141
- return _this8.processQueue();
16902
+ return _this9.processQueue();
16142
16903
  }, 50);
16143
16904
  } else {
16144
16905
  // No more chunks - stop after delay
16145
16906
 
16146
16907
  setTimeout(function () {
16147
- if (_this8.audioQueue.length === 0 && !_this8.currentSource) {
16148
- _this8.isPlaying = false;
16149
- _this8.emit('playbackStopped');
16908
+ if (_this9.audioQueue.length === 0 && !_this9.currentSource) {
16909
+ _this9.isPlaying = false;
16910
+ _this9.emit('playbackStopped');
16150
16911
  }
16151
16912
  }, 100);
16152
16913
  }
@@ -16169,7 +16930,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16169
16930
  if (this.audioQueue.length > 0) {
16170
16931
  this.isProcessingQueue = false;
16171
16932
  setTimeout(function () {
16172
- return _this8.processQueue();
16933
+ return _this9.processQueue();
16173
16934
  }, 100);
16174
16935
  } else {
16175
16936
  this.isPlaying = false;
@@ -16318,7 +17079,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16318
17079
  }, {
16319
17080
  key: "markNewSentence",
16320
17081
  value: function markNewSentence(text) {
16321
- var _this9 = this;
17082
+ var _this0 = this;
16322
17083
  var wasStopped = this._isStopped;
16323
17084
  var isCurrentlyPlaying = this.isPlaying || this.scheduledSources.size > 0;
16324
17085
 
@@ -16374,20 +17135,20 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16374
17135
  var sentenceText = text; // Capture for timeout callback
16375
17136
  this._emptySentenceTimeout = setTimeout(function () {
16376
17137
  // Check if this sentence still has no chunks after timeout
16377
- if (_this9.pendingSentenceText === sentenceText && _this9.scheduledBuffers === 0 && _this9.preparedBuffer.length === 0 && _this9.pcmChunkQueue.length === 0 && !_this9._isStopped) {
17138
+ if (_this0.pendingSentenceText === sentenceText && _this0.scheduledBuffers === 0 && _this0.preparedBuffer.length === 0 && _this0.pcmChunkQueue.length === 0 && !_this0._isStopped) {
16378
17139
  console.warn("\u26A0\uFE0F AudioPlayer: Empty sentence detected after 5s timeout - no chunks received for: \"".concat(sentenceText.substring(0, 40), "...\""));
16379
17140
  // Clear pending sentence to unblock next sentence
16380
- if (_this9.pendingSentenceText === sentenceText) {
16381
- _this9.pendingSentenceText = null;
17141
+ if (_this0.pendingSentenceText === sentenceText) {
17142
+ _this0.pendingSentenceText = null;
16382
17143
  }
16383
17144
  // Emit playbackStopped to allow next sentence to start
16384
17145
  // Only if we're not currently playing (to avoid interrupting real playback)
16385
- if (!_this9.isPlaying && _this9.scheduledSources.size === 0) {
17146
+ if (!_this0.isPlaying && _this0.scheduledSources.size === 0) {
16386
17147
  console.log('🛑 AudioPlayer: Emitting playbackStopped for empty sentence timeout');
16387
- _this9.emit('playbackStopped');
17148
+ _this0.emit('playbackStopped');
16388
17149
  }
16389
17150
  }
16390
- _this9._emptySentenceTimeout = null;
17151
+ _this0._emptySentenceTimeout = null;
16391
17152
  }, 5000); // 5 second timeout - adjust based on expected chunk arrival rate
16392
17153
  }
16393
17154
 
@@ -16397,14 +17158,14 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16397
17158
  }, {
16398
17159
  key: "startTranscriptChecker",
16399
17160
  value: function startTranscriptChecker() {
16400
- var _this0 = this;
17161
+ var _this1 = this;
16401
17162
  if (this.isCheckingTranscripts) return;
16402
17163
  this.isCheckingTranscripts = true;
16403
17164
  console.log('📝 AudioPlayer: Transcript checker started');
16404
17165
  var _checkLoop = function checkLoop() {
16405
- if (!_this0.isCheckingTranscripts || !_this0.audioContext) return;
16406
- var currentTime = _this0.audioContext.currentTime;
16407
- var _iterator = _createForOfIteratorHelper(_this0.sentenceTimings),
17166
+ if (!_this1.isCheckingTranscripts || !_this1.audioContext) return;
17167
+ var currentTime = _this1.audioContext.currentTime;
17168
+ var _iterator = _createForOfIteratorHelper(_this1.sentenceTimings),
16408
17169
  _step;
16409
17170
  try {
16410
17171
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
@@ -16412,7 +17173,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16412
17173
  if (!timing.displayed && currentTime >= timing.startTime) {
16413
17174
  timing.displayed = true;
16414
17175
  console.log("\uD83D\uDCDD AudioPlayer: Display transcript at ".concat(currentTime.toFixed(3), "s: \"").concat(timing.text.substring(0, 40), "...\""));
16415
- _this0.emit('transcriptDisplay', {
17176
+ _this1.emit('transcriptDisplay', {
16416
17177
  text: timing.text
16417
17178
  });
16418
17179
  }
@@ -16422,10 +17183,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16422
17183
  } finally {
16423
17184
  _iterator.f();
16424
17185
  }
16425
- if (_this0.isPlaying || _this0.scheduledBuffers > 0) {
17186
+ if (_this1.isPlaying || _this1.scheduledBuffers > 0) {
16426
17187
  requestAnimationFrame(_checkLoop);
16427
17188
  } else {
16428
- _this0.isCheckingTranscripts = false;
17189
+ _this1.isCheckingTranscripts = false;
16429
17190
  console.log('📝 AudioPlayer: Transcript checker stopped');
16430
17191
  }
16431
17192
  };
@@ -16546,6 +17307,16 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
16546
17307
  this._cleanupAudioContext();
16547
17308
  this.removeAllListeners();
16548
17309
  }
17310
+ }], [{
17311
+ key: "closeSharedContext",
17312
+ value: function closeSharedContext() {
17313
+ if (_sharedPlayerContext && _sharedPlayerContext.state !== 'closed') {
17314
+ console.log('🗑️ AudioPlayer: Closing shared AudioContext');
17315
+ _sharedPlayerContext.close();
17316
+ }
17317
+ _sharedPlayerContext = null;
17318
+ _sharedPlayerSampleRate = null;
17319
+ }
16549
17320
  }]);
16550
17321
  }(_shared_EventEmitter_js__WEBPACK_IMPORTED_MODULE_0__["default"]);
16551
17322
  /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (AudioPlayer);
@@ -19249,6 +20020,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
19249
20020
  this.audioRecorder.destroy();
19250
20021
  }
19251
20022
 
20023
+ // Close shared AudioContexts so they don't leak on SPA route changes
20024
+ _AudioPlayer_js__WEBPACK_IMPORTED_MODULE_1__["default"].closeSharedContext();
20025
+ _core_AudioRecorder_js__WEBPACK_IMPORTED_MODULE_2__["default"].closeSharedContext();
20026
+
19252
20027
  // Cleanup client tools registry
19253
20028
  if (this.clientToolsRegistry) {
19254
20029
  var tools = this.clientToolsRegistry.getRegisteredTools();
@@ -20562,6 +21337,7 @@ var AgentSDK = /*#__PURE__*/function () {
20562
21337
  this.onAudioStartPlaying = config.onAudioStartPlaying || function () {};
20563
21338
  this.onAudioStoppedPlaying = config.onAudioStoppedPlaying || function () {};
20564
21339
  this.onSubtitleDisplay = config.onSubtitleDisplay || function () {};
21340
+ this.onUnsupportedAction = null;
20565
21341
  }
20566
21342
 
20567
21343
  /**
@@ -20833,6 +21609,13 @@ var AgentSDK = /*#__PURE__*/function () {
20833
21609
  case 'error':
20834
21610
  this.onError(new Error(message.message || message.error || 'Unknown error'));
20835
21611
  break;
21612
+ case 'show_products':
21613
+ case 'cart_updated':
21614
+ case 'add_to_store_cart':
21615
+ if (typeof this.onUnsupportedAction === 'function') {
21616
+ this.onUnsupportedAction(messageType, message);
21617
+ }
21618
+ break;
20836
21619
  default:
20837
21620
  // Handle other message types
20838
21621
  break;
@@ -21273,6 +22056,17 @@ var TTPChatWidget = /*#__PURE__*/function () {
21273
22056
  }
21274
22057
  var widgetMode = ((_this$config$behavior = _this.config.behavior) === null || _this$config$behavior === void 0 ? void 0 : _this$config$behavior.mode) || 'unified';
21275
22058
  if (widgetMode === 'unified') {
22059
+ if (window.innerWidth <= 768) {
22060
+ var _this$shadowRoot, _this$shadowRoot2;
22061
+ // Mobile: close panel and show the FAB so user returns to normal state
22062
+ var panel = (_this$shadowRoot = _this.shadowRoot) === null || _this$shadowRoot === void 0 ? void 0 : _this$shadowRoot.getElementById('text-chat-panel');
22063
+ if (panel) {
22064
+ panel.classList.remove('open');
22065
+ _this.isOpen = false;
22066
+ }
22067
+ var fab = (_this$shadowRoot2 = _this.shadowRoot) === null || _this$shadowRoot2 === void 0 ? void 0 : _this$shadowRoot2.querySelector('.ttp-mobile-fab');
22068
+ if (fab) fab.style.display = '';
22069
+ }
21276
22070
  _this.showLanding();
21277
22071
  }
21278
22072
  },
@@ -21316,13 +22110,25 @@ var TTPChatWidget = /*#__PURE__*/function () {
21316
22110
  this.createWidget();
21317
22111
  this.setupEventHandlers();
21318
22112
 
21319
- // Start open if configured (immediately)
22113
+ // Start open if configured
21320
22114
  if (this.config.behavior.startOpen || this.config.behavior.autoOpen) {
21321
- var _this$shadowRoot;
21322
- var panel = (_this$shadowRoot = this.shadowRoot) === null || _this$shadowRoot === void 0 ? void 0 : _this$shadowRoot.getElementById('text-chat-panel');
21323
- if (panel) {
21324
- this.isOpen = true;
21325
- panel.classList.add('open');
22115
+ if (window.innerWidth <= 768) {
22116
+ var _this$shadowRoot3, _this$shadowRoot4;
22117
+ // Mobile: show the mobile landing overlay instead of the desktop panel
22118
+ var mobileFab = (_this$shadowRoot3 = this.shadowRoot) === null || _this$shadowRoot3 === void 0 ? void 0 : _this$shadowRoot3.querySelector('.ttp-mobile-fab');
22119
+ var mobileLanding = (_this$shadowRoot4 = this.shadowRoot) === null || _this$shadowRoot4 === void 0 ? void 0 : _this$shadowRoot4.getElementById('ttpMobileLanding');
22120
+ if (mobileFab && mobileLanding) {
22121
+ mobileFab.style.display = 'none';
22122
+ mobileLanding.classList.add('active');
22123
+ this.isMobileLandingOpen = true;
22124
+ }
22125
+ } else {
22126
+ var _this$shadowRoot5;
22127
+ var panel = (_this$shadowRoot5 = this.shadowRoot) === null || _this$shadowRoot5 === void 0 ? void 0 : _this$shadowRoot5.getElementById('text-chat-panel');
22128
+ if (panel) {
22129
+ this.isOpen = true;
22130
+ panel.classList.add('open');
22131
+ }
21326
22132
  }
21327
22133
  }
21328
22134
 
@@ -21376,9 +22182,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
21376
22182
  }, {
21377
22183
  key: "open",
21378
22184
  value: function open() {
21379
- var _this$shadowRoot2;
22185
+ var _this$shadowRoot6;
21380
22186
  if (this.isOpen) return;
21381
- var panel = (_this$shadowRoot2 = this.shadowRoot) === null || _this$shadowRoot2 === void 0 ? void 0 : _this$shadowRoot2.getElementById('text-chat-panel');
22187
+ var panel = (_this$shadowRoot6 = this.shadowRoot) === null || _this$shadowRoot6 === void 0 ? void 0 : _this$shadowRoot6.getElementById('text-chat-panel');
21382
22188
  if (panel) {
21383
22189
  this.isOpen = true;
21384
22190
  panel.classList.add('open');
@@ -21843,10 +22649,22 @@ var TTPChatWidget = /*#__PURE__*/function () {
21843
22649
  var panel = this.shadowRoot.getElementById('text-chat-panel');
21844
22650
  if (panel) {
21845
22651
  if (this.config.behavior.startOpen || this.config.behavior.autoOpen) {
21846
- this.isOpen = true;
21847
- panel.classList.add('open');
21848
- // Hide prompt if widget starts open
21849
- this.hidePrompt();
22652
+ if (window.innerWidth <= 768) {
22653
+ var _this$shadowRoot7, _this$shadowRoot8;
22654
+ // Mobile: show mobile landing overlay
22655
+ var mobileFab = (_this$shadowRoot7 = this.shadowRoot) === null || _this$shadowRoot7 === void 0 ? void 0 : _this$shadowRoot7.querySelector('.ttp-mobile-fab');
22656
+ var mobileLanding = (_this$shadowRoot8 = this.shadowRoot) === null || _this$shadowRoot8 === void 0 ? void 0 : _this$shadowRoot8.getElementById('ttpMobileLanding');
22657
+ if (mobileFab && mobileLanding) {
22658
+ mobileFab.style.display = 'none';
22659
+ mobileLanding.classList.add('active');
22660
+ this.isMobileLandingOpen = true;
22661
+ }
22662
+ this.hidePrompt();
22663
+ } else {
22664
+ this.isOpen = true;
22665
+ panel.classList.add('open');
22666
+ this.hidePrompt();
22667
+ }
21850
22668
  } else {
21851
22669
  // Widget starts closed - show prompt
21852
22670
  this.showPrompt();
@@ -22181,8 +22999,18 @@ var TTPChatWidget = /*#__PURE__*/function () {
22181
22999
  if ((_this6$voiceInterface3 = _this6.voiceInterface) !== null && _this6$voiceInterface3 !== void 0 && _this6$voiceInterface3.isActive) {
22182
23000
  // If call is active, show message inside widget instead of modal
22183
23001
  _this6.showBackButtonWarning();
23002
+ } else if (window.innerWidth <= 768) {
23003
+ // On mobile, close panel and return to FAB
23004
+ var panel = _this6.shadowRoot.getElementById('text-chat-panel');
23005
+ if (panel) {
23006
+ panel.classList.remove('open');
23007
+ _this6.isOpen = false;
23008
+ }
23009
+ var fab = _this6.shadowRoot.querySelector('.ttp-mobile-fab');
23010
+ if (fab) fab.style.display = '';
23011
+ _this6.showLanding();
22184
23012
  } else {
22185
- // If not active, just show landing
23013
+ // Desktop: show landing inside panel
22186
23014
  _this6.showLanding();
22187
23015
  }
22188
23016
  };
@@ -22585,12 +23413,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
22585
23413
  if (mobileChatBtn) {
22586
23414
  mobileChatBtn.onclick = function () {
22587
23415
  closeMobileLanding();
22588
- var desktopTextCard = _this7.shadowRoot.getElementById('mode-card-text');
22589
- if (desktopTextCard) {
22590
- desktopTextCard.click();
22591
- } else {
22592
- _this7.showText();
23416
+ var panel = _this7.shadowRoot.getElementById('text-chat-panel');
23417
+ if (panel) {
23418
+ panel.classList.add('open');
23419
+ _this7.isOpen = true;
22593
23420
  }
23421
+ mobileFab.style.display = 'none';
23422
+ _this7.showText();
22594
23423
  };
22595
23424
  }
22596
23425
 
@@ -22835,6 +23664,11 @@ var TTPChatWidget = /*#__PURE__*/function () {
22835
23664
  if (input) input.focus();
22836
23665
  }, 100);
22837
23666
  } else {
23667
+ // Panel is closing — restore mobile FAB if on mobile
23668
+ if (window.innerWidth <= 768) {
23669
+ var fab = this.shadowRoot.querySelector('.ttp-mobile-fab');
23670
+ if (fab) fab.style.display = '';
23671
+ }
22838
23672
  // Panel is closing - show prompt (if enabled)
22839
23673
  // Call showPrompt immediately, then also retry after a delay to ensure it's visible
22840
23674
  this.showPrompt();
@@ -24456,6 +25290,18 @@ var VoiceInterface = /*#__PURE__*/function () {
24456
25290
  return _ref2.apply(this, arguments);
24457
25291
  };
24458
25292
  }();
25293
+
25294
+ // Handle e-commerce actions received on a non-ecommerce widget
25295
+ this.sdk.onUnsupportedAction = function (actionType) {
25296
+ console.warn("\u26A0\uFE0F Received \"".concat(actionType, "\" but this is not an E-Commerce widget. Use TTPEcommerceWidget instead."));
25297
+ var msg = 'This widget does not support e-commerce features. Please use TTPEcommerceWidget to display products.';
25298
+ if (_this.config.onErrorToast) {
25299
+ _this.config.onErrorToast(msg, 'warning');
25300
+ }
25301
+ if (_this.isMobile && _this.config.onMobileError) {
25302
+ _this.config.onMobileError(msg);
25303
+ }
25304
+ };
24459
25305
  }
24460
25306
 
24461
25307
  /**
@@ -24708,7 +25554,7 @@ var VoiceInterface = /*#__PURE__*/function () {
24708
25554
  value: (function () {
24709
25555
  var _startVoiceCall = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
24710
25556
  var _this2 = this;
24711
- var permissionState, deviceInfo;
25557
+ var permissionState, deviceInfo, idleState, activeState;
24712
25558
  return _regenerator().w(function (_context4) {
24713
25559
  while (1) switch (_context4.n) {
24714
25560
  case 0:
@@ -24751,14 +25597,19 @@ var VoiceInterface = /*#__PURE__*/function () {
24751
25597
  _context4.n = 4;
24752
25598
  break;
24753
25599
  }
24754
- console.log('📋 Permission is in prompt state, showing pre-prompt modal...');
25600
+ console.log('📋 Permission is in prompt state, showing pre-prompt modal with call view...');
25601
+
25602
+ // Switch UI to active call view so it's visible behind the modal
25603
+ idleState = this.shadowRoot.getElementById('voiceIdleState');
25604
+ activeState = this.shadowRoot.getElementById('voiceActiveState');
25605
+ if (idleState) idleState.style.display = 'none';
25606
+ if (activeState) activeState.style.display = 'flex';
24755
25607
  return _context4.a(2, new Promise(function (resolve, reject) {
24756
25608
  (0,_shared_MicPermissionModals_js__WEBPACK_IMPORTED_MODULE_2__.showPrePromptModal)(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
24757
25609
  var _t;
24758
25610
  return _regenerator().w(function (_context3) {
24759
25611
  while (1) switch (_context3.p = _context3.n) {
24760
25612
  case 0:
24761
- // User accepted pre-prompt - proceed with voice call
24762
25613
  console.log('✅ Pre-prompt accepted, proceeding with voice call...');
24763
25614
  _context3.p = 1;
24764
25615
  _context3.n = 2;
@@ -24776,10 +25627,11 @@ var VoiceInterface = /*#__PURE__*/function () {
24776
25627
  }
24777
25628
  }, _callee3, null, [[1, 3]]);
24778
25629
  })), function () {
24779
- // User cancelled pre-prompt - reset connecting state
24780
25630
  console.log('❌ Pre-prompt cancelled');
25631
+ // Revert UI back to idle state
25632
+ if (idleState) idleState.style.display = 'flex';
25633
+ if (activeState) activeState.style.display = 'none';
24781
25634
  _this2.resetConnectingState();
24782
- // Reject so caller knows call was cancelled
24783
25635
  reject(new Error('Call cancelled by user'));
24784
25636
  });
24785
25637
  }));
@@ -25899,13 +26751,15 @@ var VoiceInterface = /*#__PURE__*/function () {
25899
26751
  this.removeMobileMinimizedBar();
25900
26752
  this.messages = [];
25901
26753
 
25902
- // Show widget panel again (restore it)
25903
- // Restore widget panel (use shadowRoot if in Shadow DOM)
26754
+ // Restore mobile FAB visibility (it was hidden when call started)
26755
+ var mobileFab = this.shadowRoot.querySelector('.ttp-mobile-fab');
26756
+ if (mobileFab) mobileFab.style.display = '';
26757
+
26758
+ // Restore widget panel (remove inline display:none)
25904
26759
  var panel = this.shadowRoot.getElementById('text-chat-panel');
25905
26760
  if (panel) {
25906
- panel.style.display = ''; // Remove inline display:none
26761
+ panel.style.display = '';
25907
26762
  } else {
25908
- // Fallback: try document if not in Shadow DOM
25909
26763
  var fallbackPanel = document.getElementById('text-chat-panel');
25910
26764
  if (fallbackPanel) {
25911
26765
  fallbackPanel.style.display = '';
@@ -27314,7 +28168,7 @@ var Mobile = /*#__PURE__*/function () {
27314
28168
  container.id = 'mobile-voice-call-bar-container';
27315
28169
  container.innerHTML = "\n <!-- Duration Badge -->\n <div class=\"mobile-duration-badge\" id=\"mobileDurationBadge\">\n <div class=\"mobile-duration-dot\"></div>\n <span id=\"mobileDurationText\">00:00</span>\n </div>\n \n <!-- Main Bar -->\n <div class=\"mobile-voice-bar\">\n <!-- Top Row: Status, Waveform, Controls -->\n <div class=\"mobile-top-row\">\n <!-- Status Indicator -->\n <div class=\"mobile-status-indicator\">\n <div class=\"mobile-status-dot\" id=\"mobileStatusDot\"></div>\n <span class=\"mobile-status-text\" id=\"mobileStatusText\">".concat(this.t('listening'), "</span>\n </div>\n \n <!-- Centered Waveform -->\n <div class=\"mobile-waveform-center\" id=\"mobileWaveform\">\n ").concat(Array(8).fill(0).map(function (_, i) {
27316
28170
  return "<div class=\"mobile-waveform-bar\" data-index=\"".concat(i, "\"></div>");
27317
- }).join(''), "\n </div>\n \n <!-- Controls -->\n <div class=\"mobile-controls\">\n <button class=\"mobile-control-btn\" id=\"mobileMuteBtn\" aria-label=\"Mute microphone\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-icon\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#ef4444\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-off-icon\" style=\"display: none;\">\n <line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\"/>\n <path d=\"M18.89 13.23A7.12 7.12 0 0 0 19 12v-2\"/>\n <path d=\"M5 10v2a7 7 0 0 0 12 5\"/>\n <path d=\"M15 9.34V5a3 3 0 0 0-5.68-1.33\"/>\n <path d=\"M9 9v3a3 3 0 0 0 5.12 2.12\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n </button>\n \n <button class=\"mobile-control-btn\" id=\"mobileSpeakerBtn\" aria-label=\"Toggle speaker\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#ef4444\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-off-icon\" style=\"display: none;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <line x1=\"22\" x2=\"16\" y1=\"9\" y2=\"15\"/>\n <line x1=\"16\" x2=\"22\" y1=\"9\" y2=\"15\"/>\n </svg>\n </button>\n \n <button class=\"mobile-end-call-btn\" id=\"mobileEndCallBtn\" aria-label=\"End call\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <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\"/>\n <line x1=\"22\" x2=\"2\" y1=\"2\" y2=\"22\"/>\n </svg>\n </button>\n </div>\n </div>\n \n <!-- Bottom Row: Transcript (expandable) -->\n <button class=\"mobile-transcript-row\" id=\"mobileTranscriptRow\">\n <p class=\"mobile-transcript-text\" id=\"mobileTranscriptText\">").concat(this.t('transcriptWillAppear'), "</p>\n <div class=\"mobile-transcript-footer\">\n <span class=\"mobile-expand-hint\">tap to expand & type</span>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"opacity: 0.6;\">\n <path d=\"M7 17L17 7M17 7H7M17 7V17\"/>\n </svg>\n </div>\n </button>\n </div>\n \n <!-- Expanded Transcript Overlay -->\n <div class=\"mobile-transcript-overlay\" id=\"mobileTranscriptOverlay\" style=\"display: none;\">\n <div class=\"mobile-expanded-transcript\" id=\"mobileExpandedTranscript\">\n <!-- Header -->\n <div class=\"mobile-transcript-header\">\n <div class=\"mobile-header-left\">\n <div class=\"mobile-header-status\">\n <div class=\"mobile-header-status-dot\"></div>\n <span class=\"mobile-header-status-text\" id=\"mobileHeaderStatusText\">").concat(this.t('listening'), "</span>\n <span class=\"mobile-header-duration\" id=\"mobileHeaderDuration\">00:00</span>\n </div>\n <span class=\"mobile-transcript-label\">CONVERSATION</span>\n </div>\n <button class=\"mobile-close-transcript-btn\" id=\"mobileCloseTranscriptBtn\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n \n <!-- Messages Container -->\n <div class=\"mobile-messages-container\" id=\"mobileMessagesContainer\">\n <!-- Messages will be dynamically added here -->\n </div>\n \n <!-- Text Input Area -->\n <div class=\"mobile-input-area\">\n <div class=\"mobile-input-wrapper\">\n <input\n type=\"text\"\n placeholder=\"Type a message...\"\n id=\"mobileTextInput\"\n class=\"mobile-text-input\"\n />\n <button class=\"mobile-send-btn\" id=\"mobileSendBtn\" disabled>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z\"/>\n </svg>\n </button>\n </div>\n <p class=\"mobile-input-hint\">\uD83C\uDF99\uFE0F ").concat(this.t('voiceActive') || 'Voice is still active', " \u2022 ").concat(this.t('typeOrSpeak') || 'Type or speak', "</p>\n </div>\n </div>\n </div>\n ");
28171
+ }).join(''), "\n </div>\n \n <!-- Controls -->\n <div class=\"mobile-controls\">\n <button class=\"mobile-control-btn\" id=\"mobileMuteBtn\" aria-label=\"Mute microphone\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-icon\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#ef4444\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-off-icon\" style=\"display: none;\">\n <line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\"/>\n <path d=\"M18.89 13.23A7.12 7.12 0 0 0 19 12v-2\"/>\n <path d=\"M5 10v2a7 7 0 0 0 12 5\"/>\n <path d=\"M15 9.34V5a3 3 0 0 0-5.68-1.33\"/>\n <path d=\"M9 9v3a3 3 0 0 0 5.12 2.12\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n </button>\n \n <button class=\"mobile-control-btn\" id=\"mobileSpeakerBtn\" aria-label=\"Toggle speaker\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#ef4444\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-off-icon\" style=\"display: none;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <line x1=\"22\" x2=\"16\" y1=\"9\" y2=\"15\"/>\n <line x1=\"16\" x2=\"22\" y1=\"9\" y2=\"15\"/>\n </svg>\n </button>\n \n <button class=\"mobile-end-call-btn\" id=\"mobileEndCallBtn\" aria-label=\"End call\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <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\"/>\n <line x1=\"22\" x2=\"2\" y1=\"2\" y2=\"22\"/>\n </svg>\n </button>\n </div>\n </div>\n \n <!-- Bottom Row: Transcript (expandable) -->\n <button class=\"mobile-transcript-row\" id=\"mobileTranscriptRow\">\n <p class=\"mobile-transcript-text\" id=\"mobileTranscriptText\">").concat(this.t('transcriptWillAppear'), "</p>\n <div class=\"mobile-transcript-footer\">\n <span class=\"mobile-expand-hint\">tap to expand & type</span>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"opacity: 0.6;\">\n <path d=\"M7 17L17 7M17 7H7M17 7V17\"/>\n </svg>\n </div>\n </button>\n\n <!-- Cart Row - shown when items are in cart -->\n <div class=\"mobile-cart-row\" id=\"mobileCartRow\" style=\"display: none;\">\n <div class=\"mobile-cart-row__content\">\n <span class=\"mobile-cart-row__icon\">\uD83D\uDED2</span>\n <span class=\"mobile-cart-row__text\" id=\"mobileCartText\">0 items \xB7 \u20AA0.00</span>\n </div>\n </div>\n </div>\n \n <!-- Expanded Transcript Overlay -->\n <div class=\"mobile-transcript-overlay\" id=\"mobileTranscriptOverlay\" style=\"display: none;\">\n <div class=\"mobile-expanded-transcript\" id=\"mobileExpandedTranscript\">\n <!-- Header -->\n <div class=\"mobile-transcript-header\">\n <div class=\"mobile-header-left\">\n <div class=\"mobile-header-status\">\n <div class=\"mobile-header-status-dot\"></div>\n <span class=\"mobile-header-status-text\" id=\"mobileHeaderStatusText\">").concat(this.t('listening'), "</span>\n <span class=\"mobile-header-duration\" id=\"mobileHeaderDuration\">00:00</span>\n </div>\n <span class=\"mobile-transcript-label\">CONVERSATION</span>\n </div>\n <button class=\"mobile-close-transcript-btn\" id=\"mobileCloseTranscriptBtn\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n \n <!-- Messages Container -->\n <div class=\"mobile-messages-container\" id=\"mobileMessagesContainer\">\n <!-- Messages will be dynamically added here -->\n </div>\n \n <!-- Text Input Area -->\n <div class=\"mobile-input-area\">\n <div class=\"mobile-input-wrapper\">\n <input\n type=\"text\"\n placeholder=\"Type a message...\"\n id=\"mobileTextInput\"\n class=\"mobile-text-input\"\n />\n <button class=\"mobile-send-btn\" id=\"mobileSendBtn\" disabled>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z\"/>\n </svg>\n </button>\n </div>\n <p class=\"mobile-input-hint\">\uD83C\uDF99\uFE0F ").concat(this.t('voiceActive') || 'Voice is still active', " \u2022 ").concat(this.t('typeOrSpeak') || 'Type or speak', "</p>\n </div>\n </div>\n </div>\n ");
27318
28172
 
27319
28173
  // Append to body
27320
28174
  document.body.appendChild(container);
@@ -27613,7 +28467,7 @@ var Styles = /*#__PURE__*/function () {
27613
28467
  }, {
27614
28468
  key: "_getSharedMobileCSS",
27615
28469
  value: function _getSharedMobileCSS(important) {
27616
- return "\n .mobile-duration-dot {\ndisplay: block !important;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background-color: #ef4444;\n animation: mobilePulse 1.5s ease-in-out infinite;\n }\n\n @keyframes mobilePulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(0.95); }\n }\n\n .mobile-voice-bar {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 50%, #c084fc 100%);\n border-radius: 24px 24px 0 0;\n box-shadow: 0 -8px 32px rgba(124, 58, 237, 0.5), 0 -2px 8px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.2);\n width: 100%;\n max-width: 100%;\n pointer-events: auto;\n }\n\n .mobile-top-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n position: relative;\n }\n\n .mobile-status-indicator {\n display: flex;\n align-items: center;\n gap: 5px;\n min-width: 85px;\n }\n\n .mobile-status-dot {\ndisplay: block !important;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background-color: #22c55e;\n box-shadow: 0 0 8px #22c55e;\n transition: all 0.3s ease;\n }\n\n .mobile-status-text {\n font-size: 11px;\n font-weight: 700;\n color: rgba(255,255,255,0.95);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n }\n\n .mobile-waveform-center {\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 3px;\n height: 24px;\n }\n\n .mobile-waveform-bar {\ndisplay: block !important;\n width: 4px;\n height: 4px;\n background-color: #fff;\n border-radius: 2px;\n transition: height 0.15s ease;\n box-shadow: 0 0 8px rgba(255,255,255,0.5);\n }\n\n .mobile-controls {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .mobile-control-btn {\n display: flex".concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n width: 32px").concat(important, ";\n height: 32px").concat(important, ";\n min-width: 32px").concat(important, ";\n min-height: 32px").concat(important, ";\n max-width: 32px").concat(important, ";\n max-height: 32px").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n transition: all 0.2s ease;\n background-color: rgba(255,255,255,0.15)").concat(important, ";\n position: relative").concat(important, ";\n overflow: hidden").concat(important, ";\n padding: 0").concat(important, ";\n margin: 0").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n\n .mobile-control-btn:hover {\n background-color: rgba(255,255,255,0.25)").concat(important, ";\n }\n\n .mobile-control-btn.muted {\n background-color: rgba(239, 68, 68, 0.3)").concat(important, ";\n }\n\n .mobile-control-btn svg {\n position: absolute").concat(important, ";\n top: 50%").concat(important, ";\n left: 50%").concat(important, ";\n transform: translate(-50%, -50%)").concat(important, ";\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n width: 15px").concat(important, ";\n height: 15px").concat(important, ";\n margin: 0").concat(important, ";\n padding: 0").concat(important, ";\n }\n\n .mobile-mic-off-icon,\n .mobile-speaker-off-icon {\n display: none !important;\n }\n\n .mobile-control-btn.muted .mobile-mic-icon,\n .mobile-control-btn.muted .mobile-speaker-icon {\n display: none !important;\n }\n\n .mobile-control-btn.muted .mobile-mic-off-icon,\n .mobile-control-btn.muted .mobile-speaker-off-icon {\n display: block !important;\n }\n\n .mobile-end-call-btn {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n width: 36px").concat(important, ";\n height: 36px").concat(important, ";\n min-width: 36px").concat(important, ";\n min-height: 36px").concat(important, ";\n max-width: 36px").concat(important, ";\n max-height: 36px").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n background-color: #ef4444").concat(important, ";\n box-shadow: 0 4px 14px rgba(239, 68, 68, 0.5);\n transition: all 0.2s ease;\n overflow: hidden").concat(important, ";\n padding: 0").concat(important, ";\n margin: 0").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n\n .mobile-end-call-btn svg {\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n width: 15px").concat(important, ";\n height: 15px").concat(important, ";\n margin: 0").concat(important, ";\n padding: 0").concat(important, ";\n position: relative").concat(important, ";\n }\n\n /* ID-specific selectors for WordPress compatibility */\n #mobileMuteBtn.mobile-control-btn,\n #mobileSpeakerBtn.mobile-control-btn,\n #mobileEndCallBtn.mobile-end-call-btn {\n border-radius: 50% !important;\n overflow: hidden !important;\n }\n\n #mobileMuteBtn.mobile-control-btn,\n #mobileSpeakerBtn.mobile-control-btn {\n width: 32px !important;\n height: 32px !important;\n min-width: 32px !important;\n min-height: 32px !important;\n max-width: 32px !important;\n max-height: 32px !important;\n }\n\n #mobileEndCallBtn.mobile-end-call-btn {\n width: 36px !important;\n height: 36px !important;\n min-width: 36px !important;\n min-height: 36px !important;\n max-width: 36px !important;\n max-height: 36px !important;\n }\n\n #mobileMuteBtn.mobile-control-btn svg,\n #mobileSpeakerBtn.mobile-control-btn svg,\n #mobileEndCallBtn.mobile-end-call-btn svg {\n display: block !important;\n visibility: visible !important;\n opacity: 1 !important;\n width: 15px !important;\n height: 15px !important;\n margin: 0 !important;\n padding: 0 !important;\n }\n\n .mobile-end-call-btn:hover {\n transform: scale(1.05);\n }\n\n .mobile-transcript-row {\n width: 100%;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 12px;\n background: rgba(255,255,255,0.18);\n border-radius: 14px;\n border: none;\n cursor: pointer;\n text-align: left;\n backdrop-filter: blur(4px);\n transition: background 0.2s ease;\n }\n\n .mobile-transcript-row:hover {\n background: rgba(255,255,255,0.25);\n }\n\n .mobile-transcript-text {\n margin: 0;\n font-size: 13px;\n color: #fff;\n line-height: 1.4;\n font-weight: 500;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .mobile-transcript-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .mobile-expand-hint {\n font-size: 10px;\n color: rgba(255,255,255,0.6);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n font-weight: 600;\n }\n\n .mobile-transcript-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0,0,0,0.6);\n display: flex;\n align-items: flex-end;\n padding: 12px;\n z-index: 100000;\n pointer-events: auto;\n }\n\n .mobile-expanded-transcript {\n width: 100%;\n max-height: 75%;\n background: #fff;\n border-radius: 24px 24px 16px 16px;\n overflow: hidden;\n box-shadow: 0 -8px 40px rgba(0,0,0,0.3);\n display: flex;\n flex-direction: column;\n }\n\n .mobile-transcript-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 14px 16px;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n flex-shrink: 0;\n }\n\n .mobile-header-left {\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n\n .mobile-header-status {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .mobile-header-status-dot {\ndisplay: block !important;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background-color: #22c55e;\n box-shadow: 0 0 6px #22c55e;\n }\n\n .mobile-header-status-text {\n font-size: 10px;\n font-weight: 700;\n color: rgba(255,255,255,0.9);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .mobile-header-duration {\n font-size: 10px;\n font-weight: 600;\n color: rgba(255,255,255,0.7);\n margin-left: 8px;\n }\n\n .mobile-transcript-label {\n font-size: 11px;\n font-weight: 800;\n color: rgba(255,255,255,0.7);\n letter-spacing: 1px;\n }\n\n .mobile-close-transcript-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n border: none;\n background: rgba(255,255,255,0.2);\n color: #fff;\n cursor: pointer;\n }\n\n .mobile-close-transcript-btn:hover {\n background: rgba(255,255,255,0.3);\n }\n\n .mobile-messages-container {\n flex: 1;\n padding: 16px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .mobile-message-bubble {\n max-width: 85%;\n padding: 12px 16px;\n border-radius: 18px;\n font-size: 14px;\n line-height: 1.5;\n word-wrap: break-word;\n }\n\n .mobile-message-bubble.assistant {\n align-self: flex-start;\n background-color: #f3f4f6;\n color: #374151;\n border-bottom-left-radius: 4px;\n }\n\n .mobile-message-bubble.user {\n align-self: flex-end;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n color: #fff;\n border-bottom-right-radius: 4px;\n }\n\n .mobile-input-area {\n padding: 12px 16px 16px;\n border-top: 1px solid #e5e7eb;\n background: #f9fafb;\n flex-shrink: 0;\n }\n\n .mobile-input-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 4px 4px 16px;\n background: #fff;\n border-radius: 24px;\n border: 2px solid #e5e7eb;\n }\n\n .mobile-text-input {\n flex: 1;\n border: none;\n outline: none;\n font-size: 14px;\n color: #374151;\n background: transparent;\n padding: 10px 0;\n }\n\n .mobile-text-input::placeholder {\n color: #9ca3af;\n }\n\n .mobile-send-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n border: none;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n color: #fff;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .mobile-send-btn:hover:not(:disabled) {\n transform: scale(1.05);\n }\n\n .mobile-send-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .mobile-input-hint {\n margin: 8px 0 0;\n font-size: 11px;\n color: #9ca3af;\n text-align: center;\n transition: color 0.2s ease;\n }\n\n /* Mobile text input status states */\n .mobile-input-hint--sending {\n color: #666;\n }\n\n .mobile-input-hint--success {\n color: #28a745;\n }\n\n .mobile-input-hint--queued {\n color: #ffc107;\n }\n\n .mobile-input-hint--error {\n color: #dc3545;\n }\n\n /* Disabled mobile input styling */\n #mobileTextInput:disabled {\n background-color: #f5f5f5;\n cursor: not-allowed;\n }\n\n #mobileSendBtn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n ");
28470
+ return "\n .mobile-duration-dot {\ndisplay: block !important;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background-color: #ef4444;\n animation: mobilePulse 1.5s ease-in-out infinite;\n }\n\n @keyframes mobilePulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(0.95); }\n }\n\n .mobile-voice-bar {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 50%, #c084fc 100%);\n border-radius: 24px 24px 0 0;\n box-shadow: 0 -8px 32px rgba(124, 58, 237, 0.5), 0 -2px 8px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.2);\n width: 100% !important;\n max-width: 100% !important;\n pointer-events: auto;\n overflow: hidden !important;\n margin: 0 !important;\n }\n\n .mobile-top-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n position: relative;\n }\n\n .mobile-status-indicator {\n display: flex;\n align-items: center;\n gap: 5px;\n min-width: 85px;\n }\n\n .mobile-status-dot {\ndisplay: block !important;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background-color: #22c55e;\n box-shadow: 0 0 8px #22c55e;\n transition: all 0.3s ease;\n }\n\n .mobile-status-text {\n font-size: 11px;\n font-weight: 700;\n color: rgba(255,255,255,0.95);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n }\n\n .mobile-waveform-center {\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 3px;\n height: 24px;\n }\n\n .mobile-waveform-bar {\ndisplay: block !important;\n width: 4px;\n height: 4px;\n background-color: #fff;\n border-radius: 2px;\n transition: height 0.15s ease;\n box-shadow: 0 0 8px rgba(255,255,255,0.5);\n }\n\n .mobile-controls {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .mobile-control-btn {\n display: flex".concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n width: 32px").concat(important, ";\n height: 32px").concat(important, ";\n min-width: 32px").concat(important, ";\n min-height: 32px").concat(important, ";\n max-width: 32px").concat(important, ";\n max-height: 32px").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n transition: all 0.2s ease;\n background-color: rgba(255,255,255,0.15)").concat(important, ";\n position: relative").concat(important, ";\n overflow: hidden").concat(important, ";\n padding: 0").concat(important, ";\n margin: 0").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n\n .mobile-control-btn:hover {\n background-color: rgba(255,255,255,0.25)").concat(important, ";\n }\n\n .mobile-control-btn.muted {\n background-color: rgba(239, 68, 68, 0.3)").concat(important, ";\n }\n\n .mobile-control-btn svg {\n position: absolute").concat(important, ";\n top: 50%").concat(important, ";\n left: 50%").concat(important, ";\n transform: translate(-50%, -50%)").concat(important, ";\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n width: 15px").concat(important, ";\n height: 15px").concat(important, ";\n margin: 0").concat(important, ";\n padding: 0").concat(important, ";\n }\n\n .mobile-mic-off-icon,\n .mobile-speaker-off-icon {\n display: none !important;\n }\n\n .mobile-control-btn.muted .mobile-mic-icon,\n .mobile-control-btn.muted .mobile-speaker-icon {\n display: none !important;\n }\n\n .mobile-control-btn.muted .mobile-mic-off-icon,\n .mobile-control-btn.muted .mobile-speaker-off-icon {\n display: block !important;\n }\n\n .mobile-end-call-btn {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n width: 36px").concat(important, ";\n height: 36px").concat(important, ";\n min-width: 36px").concat(important, ";\n min-height: 36px").concat(important, ";\n max-width: 36px").concat(important, ";\n max-height: 36px").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n background-color: #ef4444").concat(important, ";\n box-shadow: 0 4px 14px rgba(239, 68, 68, 0.5);\n transition: all 0.2s ease;\n overflow: hidden").concat(important, ";\n padding: 0").concat(important, ";\n margin: 0").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n\n .mobile-end-call-btn svg {\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n width: 15px").concat(important, ";\n height: 15px").concat(important, ";\n margin: 0").concat(important, ";\n padding: 0").concat(important, ";\n position: relative").concat(important, ";\n }\n\n /* ID-specific selectors for WordPress compatibility */\n #mobileMuteBtn.mobile-control-btn,\n #mobileSpeakerBtn.mobile-control-btn,\n #mobileEndCallBtn.mobile-end-call-btn {\n border-radius: 50% !important;\n overflow: hidden !important;\n }\n\n #mobileMuteBtn.mobile-control-btn,\n #mobileSpeakerBtn.mobile-control-btn {\n width: 32px !important;\n height: 32px !important;\n min-width: 32px !important;\n min-height: 32px !important;\n max-width: 32px !important;\n max-height: 32px !important;\n }\n\n #mobileEndCallBtn.mobile-end-call-btn {\n width: 36px !important;\n height: 36px !important;\n min-width: 36px !important;\n min-height: 36px !important;\n max-width: 36px !important;\n max-height: 36px !important;\n }\n\n #mobileMuteBtn.mobile-control-btn svg,\n #mobileSpeakerBtn.mobile-control-btn svg,\n #mobileEndCallBtn.mobile-end-call-btn svg {\n display: block !important;\n visibility: visible !important;\n opacity: 1 !important;\n width: 15px !important;\n height: 15px !important;\n margin: 0 !important;\n padding: 0 !important;\n }\n\n .mobile-end-call-btn:hover {\n transform: scale(1.05);\n }\n\n .mobile-transcript-row {\n width: 100%;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 12px;\n background: rgba(255,255,255,0.18);\n border-radius: 14px;\n border: none;\n cursor: pointer;\n text-align: left;\n backdrop-filter: blur(4px);\n transition: background 0.2s ease;\n }\n\n .mobile-transcript-row:hover {\n background: rgba(255,255,255,0.25);\n }\n\n .mobile-transcript-text {\n margin: 0;\n font-size: 13px;\n color: #fff;\n line-height: 1.4;\n font-weight: 500;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .mobile-transcript-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .mobile-expand-hint {\n font-size: 10px;\n color: rgba(255,255,255,0.6);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n font-weight: 600;\n }\n\n .mobile-transcript-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0,0,0,0.6);\n display: flex;\n align-items: flex-end;\n padding: 12px;\n z-index: 100000;\n pointer-events: auto;\n }\n\n .mobile-expanded-transcript {\n width: 100%;\n max-height: 75%;\n background: #fff;\n border-radius: 24px 24px 16px 16px;\n overflow: hidden;\n box-shadow: 0 -8px 40px rgba(0,0,0,0.3);\n display: flex;\n flex-direction: column;\n }\n\n .mobile-transcript-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 14px 16px;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n flex-shrink: 0;\n }\n\n .mobile-header-left {\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n\n .mobile-header-status {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .mobile-header-status-dot {\ndisplay: block !important;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background-color: #22c55e;\n box-shadow: 0 0 6px #22c55e;\n }\n\n .mobile-header-status-text {\n font-size: 10px;\n font-weight: 700;\n color: rgba(255,255,255,0.9);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .mobile-header-duration {\n font-size: 10px;\n font-weight: 600;\n color: rgba(255,255,255,0.7);\n margin-left: 8px;\n }\n\n .mobile-transcript-label {\n font-size: 11px;\n font-weight: 800;\n color: rgba(255,255,255,0.7);\n letter-spacing: 1px;\n }\n\n .mobile-close-transcript-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n border: none;\n background: rgba(255,255,255,0.2);\n color: #fff;\n cursor: pointer;\n }\n\n .mobile-close-transcript-btn:hover {\n background: rgba(255,255,255,0.3);\n }\n\n .mobile-messages-container {\n flex: 1;\n padding: 16px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .mobile-message-bubble {\n max-width: 85%;\n padding: 12px 16px;\n border-radius: 18px;\n font-size: 14px;\n line-height: 1.5;\n word-wrap: break-word;\n }\n\n .mobile-message-bubble.assistant {\n align-self: flex-start;\n background-color: #f3f4f6;\n color: #374151;\n border-bottom-left-radius: 4px;\n }\n\n .mobile-message-bubble.user {\n align-self: flex-end;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n color: #fff;\n border-bottom-right-radius: 4px;\n }\n\n .mobile-input-area {\n padding: 12px 16px 16px;\n border-top: 1px solid #e5e7eb;\n background: #f9fafb;\n flex-shrink: 0;\n }\n\n .mobile-input-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 4px 4px 16px;\n background: #fff;\n border-radius: 24px;\n border: 2px solid #e5e7eb;\n }\n\n .mobile-text-input {\n flex: 1;\n border: none;\n outline: none;\n font-size: 14px;\n color: #374151;\n background: transparent;\n padding: 10px 0;\n }\n\n .mobile-text-input::placeholder {\n color: #9ca3af;\n }\n\n .mobile-send-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n border: none;\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n color: #fff;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .mobile-send-btn:hover:not(:disabled) {\n transform: scale(1.05);\n }\n\n .mobile-send-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .mobile-input-hint {\n margin: 8px 0 0;\n font-size: 11px;\n color: #9ca3af;\n text-align: center;\n transition: color 0.2s ease;\n }\n\n /* Mobile text input status states */\n .mobile-input-hint--sending {\n color: #666;\n }\n\n .mobile-input-hint--success {\n color: #28a745;\n }\n\n .mobile-input-hint--queued {\n color: #ffc107;\n }\n\n .mobile-input-hint--error {\n color: #dc3545;\n }\n\n /* Disabled mobile input styling */\n #mobileTextInput:disabled {\n background-color: #f5f5f5;\n cursor: not-allowed;\n }\n\n #mobileSendBtn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n ");
27617
28471
  }
27618
28472
 
27619
28473
  /**
@@ -27623,7 +28477,7 @@ var Styles = /*#__PURE__*/function () {
27623
28477
  key: "generateMobileCSS",
27624
28478
  value: function generateMobileCSS() {
27625
28479
  var important = this.config.useShadowDOM === false ? ' !important' : '';
27626
- return "\n /* Mobile Voice Call UI - Container positioning */\n #mobile-voice-call-bar-container {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 99999;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n padding: 0 12px 12px;\n pointer-events: none;\n }\n\n .mobile-duration-badge {\n position: absolute;\n top: -28px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 14px;\n background: rgba(0,0,0,0.75);\n border-radius: 20px;\n font-size: 12px;\n font-weight: 700;\n color: #fff;\n backdrop-filter: blur(8px);\n z-index: 10;\n pointer-events: auto;\n }\n\n /* Shared mobile styles */\n ".concat(this._getSharedMobileCSS(important), "\n ");
28480
+ return "\n /* Mobile Voice Call UI - Container positioning */\n #mobile-voice-call-bar-container {\n position: fixed !important;\n bottom: 0 !important;\n left: 0 !important;\n right: 0 !important;\n z-index: 99999 !important;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;\n padding: 0 12px 12px !important;\n pointer-events: none;\n box-sizing: border-box !important;\n width: 100% !important;\n max-width: 100vw !important;\n margin: 0 !important;\n overflow: visible !important;\n }\n\n #mobile-voice-call-bar-container *,\n #mobile-voice-call-bar-container *::before,\n #mobile-voice-call-bar-container *::after {\n box-sizing: border-box !important;\n }\n\n .mobile-duration-badge {\n position: absolute;\n top: -28px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 14px;\n background: rgba(0,0,0,0.75);\n border-radius: 20px;\n font-size: 12px;\n font-weight: 700;\n color: #fff;\n backdrop-filter: blur(8px);\n z-index: 10;\n pointer-events: auto;\n }\n\n /* \u2500\u2500 Mobile Cart Row \u2500\u2500 */\n\n .mobile-cart-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 7px 16px;\n background: rgba(255, 255, 255, 0.08);\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 0 0 16px 16px;\n animation: mobileCartRowIn 0.25s ease-out;\n pointer-events: auto;\n }\n\n @keyframes mobileCartRowIn {\n from { opacity: 0; max-height: 0; padding-top: 0; padding-bottom: 0; }\n to { opacity: 1; max-height: 50px; padding-top: 7px; padding-bottom: 7px; }\n }\n\n .mobile-cart-row__content {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .mobile-cart-row__icon {\n font-size: 13px;\n }\n\n .mobile-cart-row__text {\n font-size: 12px;\n font-weight: 600;\n color: rgba(255, 255, 255, 0.9);\n letter-spacing: 0.2px;\n }\n\n /* Shared mobile styles */\n ".concat(this._getSharedMobileCSS(important), "\n ");
27627
28481
  }
27628
28482
  }]);
27629
28483
  }();
@@ -27951,6 +28805,14 @@ var TextHandlers = /*#__PURE__*/function () {
27951
28805
  input.value = '';
27952
28806
  if (sendBtn) sendBtn.disabled = true;
27953
28807
 
28808
+ // Stop any currently playing audio and clear the queue before sending
28809
+ try {
28810
+ var voiceSDK = this.voiceInterface.sdk.voiceSDK;
28811
+ if (voiceSDK !== null && voiceSDK !== void 0 && voiceSDK.stopAudioPlayback) {
28812
+ voiceSDK.stopAudioPlayback();
28813
+ }
28814
+ } catch (e) {/* best-effort */}
28815
+
27954
28816
  // Show sending status
27955
28817
  this.showTextInputStatus(isMobile ? 'mobile' : 'desktop', 'sending', 'Sending...');
27956
28818