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.
- package/dist/agent-widget.dev.js +1064 -202
- package/dist/agent-widget.dev.js.map +1 -1
- package/dist/agent-widget.esm.js +1 -1
- package/dist/agent-widget.esm.js.map +1 -1
- package/dist/agent-widget.js +1 -1
- package/dist/agent-widget.js.map +1 -1
- package/dist/demos/test-ecommerce.html +1 -1
- package/dist/demos/test-widget.html +1 -1
- package/dist/demos/widget-customization.html +1 -1
- package/dist/examples/demo-v2.html +1 -1
- package/dist/examples/test-client-tools.html +1 -1
- package/dist/examples/test-ecommerce.html +1 -1
- package/dist/examples/test-text-chat.html +1 -1
- package/dist/examples/test-widget.html +1 -1
- package/dist/examples/widget-customization.html +1 -1
- package/examples/demo-v2.html +1 -1
- package/examples/test-client-tools.html +1 -1
- package/examples/test-ecommerce.html +1 -1
- package/examples/test-text-chat.html +1 -1
- package/examples/test-widget.html +1 -1
- package/examples/widget-customization.html +1 -1
- package/package.json +2 -2
package/dist/agent-widget.dev.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
8560
|
+
_context3.n = 3;
|
|
8542
8561
|
return this.checkPermissionState();
|
|
8543
|
-
case
|
|
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 =
|
|
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
|
|
8587
|
+
case 4:
|
|
8569
8588
|
this.mediaStream = _context3.v;
|
|
8570
8589
|
if (!(this.config.checkConnection && typeof this.config.checkConnection === 'function')) {
|
|
8571
|
-
_context3.n =
|
|
8590
|
+
_context3.n = 5;
|
|
8572
8591
|
break;
|
|
8573
8592
|
}
|
|
8574
8593
|
isConnected = this.config.checkConnection();
|
|
8575
8594
|
if (isConnected) {
|
|
8576
|
-
_context3.n =
|
|
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
|
|
8590
|
-
//
|
|
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
|
-
|
|
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 =
|
|
8658
|
+
_context3.n = 14;
|
|
8598
8659
|
break;
|
|
8599
8660
|
}
|
|
8600
|
-
_context3.n =
|
|
8661
|
+
_context3.n = 14;
|
|
8601
8662
|
return this.audioContext.resume();
|
|
8602
|
-
case
|
|
8663
|
+
case 14:
|
|
8603
8664
|
if (!(this.config.checkConnection && typeof this.config.checkConnection === 'function')) {
|
|
8604
|
-
_context3.n =
|
|
8665
|
+
_context3.n = 15;
|
|
8605
8666
|
break;
|
|
8606
8667
|
}
|
|
8607
8668
|
_isConnected = this.config.checkConnection();
|
|
8608
8669
|
if (_isConnected) {
|
|
8609
|
-
_context3.n =
|
|
8670
|
+
_context3.n = 15;
|
|
8610
8671
|
break;
|
|
8611
8672
|
}
|
|
8612
8673
|
// Silent cleanup - server rejected, this is expected
|
|
8613
|
-
//
|
|
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
|
|
8684
|
+
case 15:
|
|
8631
8685
|
// Get the audio processor path
|
|
8632
|
-
processorPath = this.getAudioProcessorPath(); // Load AudioWorklet module
|
|
8633
|
-
|
|
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
|
|
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 =
|
|
8703
|
+
_context3.n = 19;
|
|
8638
8704
|
break;
|
|
8639
8705
|
}
|
|
8640
8706
|
_isConnected2 = this.config.checkConnection();
|
|
8641
8707
|
if (_isConnected2) {
|
|
8642
|
-
_context3.n =
|
|
8708
|
+
_context3.n = 19;
|
|
8643
8709
|
break;
|
|
8644
8710
|
}
|
|
8645
8711
|
// Silent cleanup - server rejected, this is expected
|
|
8646
|
-
//
|
|
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
|
|
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 =
|
|
8758
|
+
_context3.n = 25;
|
|
8700
8759
|
break;
|
|
8701
|
-
case
|
|
8702
|
-
_context3.p =
|
|
8703
|
-
|
|
8704
|
-
if (!(
|
|
8705
|
-
_context3.n =
|
|
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
|
|
8709
|
-
case
|
|
8767
|
+
throw _t4;
|
|
8768
|
+
case 21:
|
|
8710
8769
|
// Provide helpful error messages for permission issues
|
|
8711
|
-
userMessage =
|
|
8712
|
-
if (!(
|
|
8713
|
-
_context3.n =
|
|
8770
|
+
userMessage = _t4.message;
|
|
8771
|
+
if (!(_t4.name === 'NotAllowedError' || _t4.name === 'PermissionDeniedError')) {
|
|
8772
|
+
_context3.n = 23;
|
|
8714
8773
|
break;
|
|
8715
8774
|
}
|
|
8716
|
-
_context3.n =
|
|
8775
|
+
_context3.n = 22;
|
|
8717
8776
|
return this.checkPermissionState();
|
|
8718
|
-
case
|
|
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 =
|
|
8792
|
+
_context3.n = 24;
|
|
8734
8793
|
break;
|
|
8735
|
-
case
|
|
8736
|
-
if (
|
|
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 (
|
|
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 (
|
|
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
|
|
8805
|
+
case 24:
|
|
8747
8806
|
enhancedError = new Error(userMessage);
|
|
8748
|
-
enhancedError.name =
|
|
8749
|
-
enhancedError.originalError =
|
|
8807
|
+
enhancedError.name = _t4.name;
|
|
8808
|
+
enhancedError.originalError = _t4;
|
|
8750
8809
|
this.emit('error', enhancedError);
|
|
8751
8810
|
throw enhancedError;
|
|
8752
|
-
case
|
|
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, [[
|
|
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
|
|
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
|
-
//
|
|
8837
|
-
|
|
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:
|
|
8907
|
+
console.log('🛑 AudioRecorder: Suspending AudioContext (kept alive for reuse)...');
|
|
8842
8908
|
_context4.n = 5;
|
|
8843
|
-
return this.audioContext.
|
|
8909
|
+
return this.audioContext.suspend();
|
|
8844
8910
|
case 5:
|
|
8845
|
-
|
|
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
|
-
|
|
8857
|
-
this.emit('error',
|
|
8858
|
-
throw
|
|
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,
|
|
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
|
-
|
|
8917
|
-
console.error('❌ AudioRecorder: Failed to resume AudioContext:',
|
|
8918
|
-
this.emit('error',
|
|
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
|
-
//
|
|
10537
|
-
productsHeight = Math.min(productCount *
|
|
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
|
|
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
|
|
10754
|
+
var _this3 = this;
|
|
10594
10755
|
var price = product.price || 0;
|
|
10595
10756
|
var qty = product.selectedAmount || 1;
|
|
10596
|
-
this.
|
|
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
|
-
|
|
10601
|
-
|
|
10602
|
-
|
|
10603
|
-
|
|
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
|
|
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$
|
|
11300
|
-
var voiceSDK = (_this$widget$
|
|
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,
|
|
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
|
|
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'
|
|
16624
|
+
latencyHint: 'playback'
|
|
15865
16625
|
});
|
|
15866
|
-
console.log("\u2705 AudioContext created at ".concat(this.audioContext.sampleRate, "Hz (requested: ").concat(desiredSampleRate, "Hz)"));
|
|
15867
16626
|
|
|
15868
|
-
//
|
|
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;
|
|
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;
|
|
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 (
|
|
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
|
-
//
|
|
15992
|
-
|
|
15993
|
-
|
|
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
|
-
//
|
|
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
|
-
*
|
|
16003
|
-
*
|
|
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:
|
|
16008
|
-
|
|
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 (!
|
|
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(
|
|
16026
|
-
if (
|
|
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 (
|
|
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 (
|
|
16798
|
+
if (_this8.audioQueue.length > 0 && !_this8.isProcessingQueue) {
|
|
16038
16799
|
setTimeout(function () {
|
|
16039
|
-
return
|
|
16800
|
+
return _this8.processQueue();
|
|
16040
16801
|
}, 50);
|
|
16041
16802
|
}
|
|
16042
|
-
if (
|
|
16803
|
+
if (_this8.pcmChunkQueue.length > 0 && !_this8.isProcessingPcmQueue) {
|
|
16043
16804
|
setTimeout(function () {
|
|
16044
|
-
return
|
|
16805
|
+
return _this8.processPcmQueue();
|
|
16045
16806
|
}, 50);
|
|
16046
16807
|
}
|
|
16047
|
-
if (
|
|
16808
|
+
if (_this8.preparedBuffer.length > 0 && !_this8.isSchedulingFrames) {
|
|
16048
16809
|
setTimeout(function () {
|
|
16049
|
-
return
|
|
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
|
|
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
|
-
|
|
16135
|
-
|
|
16895
|
+
_this9.currentSource = null;
|
|
16896
|
+
_this9.isProcessingQueue = false;
|
|
16136
16897
|
|
|
16137
16898
|
// Process next chunk
|
|
16138
16899
|
|
|
16139
|
-
if (
|
|
16900
|
+
if (_this9.audioQueue.length > 0) {
|
|
16140
16901
|
setTimeout(function () {
|
|
16141
|
-
return
|
|
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 (
|
|
16148
|
-
|
|
16149
|
-
|
|
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
|
|
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
|
|
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 (
|
|
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 (
|
|
16381
|
-
|
|
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 (!
|
|
17146
|
+
if (!_this0.isPlaying && _this0.scheduledSources.size === 0) {
|
|
16386
17147
|
console.log('🛑 AudioPlayer: Emitting playbackStopped for empty sentence timeout');
|
|
16387
|
-
|
|
17148
|
+
_this0.emit('playbackStopped');
|
|
16388
17149
|
}
|
|
16389
17150
|
}
|
|
16390
|
-
|
|
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
|
|
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 (!
|
|
16406
|
-
var currentTime =
|
|
16407
|
-
var _iterator = _createForOfIteratorHelper(
|
|
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
|
-
|
|
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 (
|
|
17186
|
+
if (_this1.isPlaying || _this1.scheduledBuffers > 0) {
|
|
16426
17187
|
requestAnimationFrame(_checkLoop);
|
|
16427
17188
|
} else {
|
|
16428
|
-
|
|
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
|
|
22113
|
+
// Start open if configured
|
|
21320
22114
|
if (this.config.behavior.startOpen || this.config.behavior.autoOpen) {
|
|
21321
|
-
|
|
21322
|
-
|
|
21323
|
-
|
|
21324
|
-
this.
|
|
21325
|
-
|
|
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$
|
|
22185
|
+
var _this$shadowRoot6;
|
|
21380
22186
|
if (this.isOpen) return;
|
|
21381
|
-
var panel = (_this$
|
|
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
|
-
|
|
21847
|
-
|
|
21848
|
-
|
|
21849
|
-
|
|
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
|
-
//
|
|
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
|
|
22589
|
-
if (
|
|
22590
|
-
|
|
22591
|
-
|
|
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
|
-
//
|
|
25903
|
-
|
|
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 = '';
|
|
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
|
|