ttp-agent-sdk 2.35.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 +1440 -319
- 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
|
}
|
|
@@ -10585,48 +10746,586 @@ var EcommerceManager = /*#__PURE__*/function () {
|
|
|
10585
10746
|
}
|
|
10586
10747
|
|
|
10587
10748
|
// ═══════════════════════════════════════════
|
|
10588
|
-
// INTERNAL HANDLERS
|
|
10749
|
+
// INTERNAL HANDLERS
|
|
10750
|
+
// ═══════════════════════════════════════════
|
|
10751
|
+
}, {
|
|
10752
|
+
key: "_handleAdd",
|
|
10753
|
+
value: function _handleAdd(product) {
|
|
10754
|
+
var _this3 = this;
|
|
10755
|
+
var price = product.price || 0;
|
|
10756
|
+
var qty = product.selectedAmount || 1;
|
|
10757
|
+
var currency = this._currencySymbol || (product.currency === 'ILS' ? '₪' : '$');
|
|
10758
|
+
this.cartSummary.update(this.cartSummary.itemCount + 1, this.cartSummary.total + price * qty, currency);
|
|
10759
|
+
var toastEl = this.cartToast.show(product, 'added');
|
|
10760
|
+
this.container.insertBefore(toastEl, this.container.firstChild);
|
|
10761
|
+
setTimeout(function () {
|
|
10762
|
+
_this3.productPicker.hide();
|
|
10763
|
+
_this3._shrinkPanel();
|
|
10764
|
+
_this3._restoreFullVoiceMode();
|
|
10765
|
+
_this3._setContainerMode('summary');
|
|
10766
|
+
}, 500);
|
|
10767
|
+
this.onProductSelected(product);
|
|
10768
|
+
}
|
|
10769
|
+
}, {
|
|
10770
|
+
key: "_handleUndo",
|
|
10771
|
+
value: function _handleUndo(product) {
|
|
10772
|
+
this.onProductDeselected(product);
|
|
10773
|
+
}
|
|
10774
|
+
|
|
10775
|
+
// ═══════════════════════════════════════════
|
|
10776
|
+
// CLEANUP
|
|
10777
|
+
// ═══════════════════════════════════════════
|
|
10778
|
+
}, {
|
|
10779
|
+
key: "destroy",
|
|
10780
|
+
value: function destroy() {
|
|
10781
|
+
if (this.mobileCarousel) {
|
|
10782
|
+
this.mobileCarousel.destroy();
|
|
10783
|
+
this.mobileCarousel = null;
|
|
10784
|
+
}
|
|
10785
|
+
this.productPicker.hide();
|
|
10786
|
+
this.cartToast.hide();
|
|
10787
|
+
this._shrinkPanel();
|
|
10788
|
+
this._restoreFullVoiceMode();
|
|
10789
|
+
if (this.voiceActiveState) {
|
|
10790
|
+
this.voiceActiveState.classList.remove('ttp-products-visible');
|
|
10791
|
+
}
|
|
10792
|
+
if (this.container && this.container.parentNode) {
|
|
10793
|
+
this.container.parentNode.removeChild(this.container);
|
|
10794
|
+
}
|
|
10795
|
+
this.isAttached = false;
|
|
10796
|
+
}
|
|
10797
|
+
}]);
|
|
10798
|
+
}();
|
|
10799
|
+
|
|
10800
|
+
/***/ }),
|
|
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
|
|
10589
11295
|
// ═══════════════════════════════════════════
|
|
10590
11296
|
}, {
|
|
10591
|
-
key: "
|
|
10592
|
-
value: function
|
|
10593
|
-
var
|
|
10594
|
-
var
|
|
10595
|
-
var
|
|
10596
|
-
|
|
10597
|
-
var toastEl = this.cartToast.show(product, 'added');
|
|
10598
|
-
this.container.insertBefore(toastEl, this.container.firstChild);
|
|
10599
|
-
setTimeout(function () {
|
|
10600
|
-
_this2.productPicker.hide();
|
|
10601
|
-
_this2._shrinkPanel();
|
|
10602
|
-
_this2._restoreFullVoiceMode();
|
|
10603
|
-
_this2._setContainerMode('summary');
|
|
10604
|
-
}, 500);
|
|
10605
|
-
this.onProductSelected(product);
|
|
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, ")");
|
|
10606
11303
|
}
|
|
10607
11304
|
}, {
|
|
10608
|
-
key: "
|
|
10609
|
-
value: function
|
|
10610
|
-
|
|
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'));
|
|
10611
11311
|
}
|
|
10612
11312
|
|
|
10613
11313
|
// ═══════════════════════════════════════════
|
|
10614
|
-
//
|
|
11314
|
+
// STYLES
|
|
10615
11315
|
// ═══════════════════════════════════════════
|
|
10616
11316
|
}, {
|
|
10617
|
-
key: "
|
|
10618
|
-
value: function
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
|
|
10622
|
-
this.
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
}
|
|
10629
|
-
this.isAttached = false;
|
|
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";
|
|
10630
11329
|
}
|
|
10631
11330
|
}]);
|
|
10632
11331
|
}();
|
|
@@ -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() {
|
|
@@ -10949,6 +11654,8 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
|
|
|
10949
11654
|
self._handleCartUpdatedFromServer(message);
|
|
10950
11655
|
} else if (type === 'add_to_store_cart') {
|
|
10951
11656
|
self._handleAddToStoreCart(message);
|
|
11657
|
+
} else if (type === 'get_store_cart') {
|
|
11658
|
+
self._handleGetStoreCart();
|
|
10952
11659
|
}
|
|
10953
11660
|
});
|
|
10954
11661
|
console.log('[TTPEcommerceWidget] E-commerce message listener attached');
|
|
@@ -10971,6 +11678,9 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
|
|
|
10971
11678
|
var originalOnCallEnd = vi.config.onCallEnd;
|
|
10972
11679
|
var ecommerce = this.ecommerce;
|
|
10973
11680
|
vi.config.onCallEnd = function () {
|
|
11681
|
+
if (ecommerce.mobileCarousel) {
|
|
11682
|
+
ecommerce.mobileCarousel.hide();
|
|
11683
|
+
}
|
|
10974
11684
|
ecommerce.productPicker.hide();
|
|
10975
11685
|
ecommerce._setContainerMode('summary');
|
|
10976
11686
|
ecommerce._shrinkPanel();
|
|
@@ -11048,26 +11758,101 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
|
|
|
11048
11758
|
// ═══════════════════════════════════════════
|
|
11049
11759
|
|
|
11050
11760
|
/**
|
|
11051
|
-
*
|
|
11052
|
-
*
|
|
11053
|
-
* the store's
|
|
11054
|
-
*
|
|
11055
|
-
* We don't attempt to refresh the theme's cart UI (icon badge, drawer)
|
|
11056
|
-
* because there's no universal mechanism — every theme is different.
|
|
11057
|
-
* The user will see the updated cart when they navigate to /cart or refresh.
|
|
11058
|
-
* Our own CartToast + CartSummary provide immediate visual feedback.
|
|
11761
|
+
* Fetch cart contents from Shopify's AJAX Cart API and send back to backend.
|
|
11762
|
+
* Triggered when the LLM calls get_cart for a Shopify store.
|
|
11763
|
+
* Only the browser can read the store's cart because it requires the session cookie.
|
|
11059
11764
|
*/
|
|
11060
11765
|
}, {
|
|
11061
|
-
key: "
|
|
11766
|
+
key: "_handleGetStoreCart",
|
|
11062
11767
|
value: function () {
|
|
11063
|
-
var
|
|
11064
|
-
var
|
|
11768
|
+
var _handleGetStoreCart2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
|
|
11769
|
+
var response, cart, _t3;
|
|
11065
11770
|
return _regenerator().w(function (_context4) {
|
|
11066
11771
|
while (1) switch (_context4.p = _context4.n) {
|
|
11067
11772
|
case 0:
|
|
11068
|
-
|
|
11069
|
-
_context4.
|
|
11070
|
-
|
|
11773
|
+
_context4.p = 0;
|
|
11774
|
+
_context4.n = 1;
|
|
11775
|
+
return fetch('/cart.js', {
|
|
11776
|
+
method: 'GET',
|
|
11777
|
+
headers: {
|
|
11778
|
+
'Content-Type': 'application/json'
|
|
11779
|
+
}
|
|
11780
|
+
});
|
|
11781
|
+
case 1:
|
|
11782
|
+
response = _context4.v;
|
|
11783
|
+
if (response.ok) {
|
|
11784
|
+
_context4.n = 2;
|
|
11785
|
+
break;
|
|
11786
|
+
}
|
|
11787
|
+
console.error('[TTP Ecommerce] Failed to fetch /cart.js:', response.status);
|
|
11788
|
+
this._sendToVoiceSDK({
|
|
11789
|
+
t: 'cart_state_result',
|
|
11790
|
+
success: false,
|
|
11791
|
+
error: "Failed to fetch cart: HTTP ".concat(response.status)
|
|
11792
|
+
});
|
|
11793
|
+
return _context4.a(2);
|
|
11794
|
+
case 2:
|
|
11795
|
+
_context4.n = 3;
|
|
11796
|
+
return response.json();
|
|
11797
|
+
case 3:
|
|
11798
|
+
cart = _context4.v;
|
|
11799
|
+
this._sendToVoiceSDK({
|
|
11800
|
+
t: 'cart_state_result',
|
|
11801
|
+
success: true,
|
|
11802
|
+
items: (cart.items || []).map(function (item) {
|
|
11803
|
+
return {
|
|
11804
|
+
title: item.title,
|
|
11805
|
+
variant_title: item.variant_title,
|
|
11806
|
+
quantity: item.quantity,
|
|
11807
|
+
price: item.price,
|
|
11808
|
+
line_price: item.line_price,
|
|
11809
|
+
image: item.image,
|
|
11810
|
+
variant_id: item.variant_id,
|
|
11811
|
+
product_id: item.product_id
|
|
11812
|
+
};
|
|
11813
|
+
}),
|
|
11814
|
+
item_count: cart.item_count,
|
|
11815
|
+
total_price: cart.total_price,
|
|
11816
|
+
currency: cart.currency
|
|
11817
|
+
});
|
|
11818
|
+
console.log("[TTP Ecommerce] Cart state sent: ".concat(cart.item_count, " items, ").concat(cart.currency, " ").concat((cart.total_price / 100).toFixed(2)));
|
|
11819
|
+
_context4.n = 5;
|
|
11820
|
+
break;
|
|
11821
|
+
case 4:
|
|
11822
|
+
_context4.p = 4;
|
|
11823
|
+
_t3 = _context4.v;
|
|
11824
|
+
console.error('[TTP Ecommerce] Error fetching cart:', _t3);
|
|
11825
|
+
this._sendToVoiceSDK({
|
|
11826
|
+
t: 'cart_state_result',
|
|
11827
|
+
success: false,
|
|
11828
|
+
error: _t3.message || 'Failed to fetch cart'
|
|
11829
|
+
});
|
|
11830
|
+
case 5:
|
|
11831
|
+
return _context4.a(2);
|
|
11832
|
+
}
|
|
11833
|
+
}, _callee4, this, [[0, 4]]);
|
|
11834
|
+
}));
|
|
11835
|
+
function _handleGetStoreCart() {
|
|
11836
|
+
return _handleGetStoreCart2.apply(this, arguments);
|
|
11837
|
+
}
|
|
11838
|
+
return _handleGetStoreCart;
|
|
11839
|
+
}()
|
|
11840
|
+
/**
|
|
11841
|
+
* Add product to Shopify store cart via AJAX Cart API, then report result back to backend.
|
|
11842
|
+
* Must be done from the browser because only the browser has the store's session cookie.
|
|
11843
|
+
*/
|
|
11844
|
+
}, {
|
|
11845
|
+
key: "_handleAddToStoreCart",
|
|
11846
|
+
value: (function () {
|
|
11847
|
+
var _handleAddToStoreCart2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(data) {
|
|
11848
|
+
var variantId, quantity, productName, addResponse, errorText, addResult, cartResponse, cart, cartRefreshed, _t4;
|
|
11849
|
+
return _regenerator().w(function (_context5) {
|
|
11850
|
+
while (1) switch (_context5.p = _context5.n) {
|
|
11851
|
+
case 0:
|
|
11852
|
+
variantId = data.variantId, quantity = data.quantity;
|
|
11853
|
+
productName = '';
|
|
11854
|
+
_context5.p = 1;
|
|
11855
|
+
_context5.n = 2;
|
|
11071
11856
|
return fetch('/cart/add.js', {
|
|
11072
11857
|
method: 'POST',
|
|
11073
11858
|
headers: {
|
|
@@ -11075,63 +11860,130 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
|
|
|
11075
11860
|
},
|
|
11076
11861
|
body: JSON.stringify({
|
|
11077
11862
|
items: [{
|
|
11078
|
-
id: parseInt(variantId
|
|
11863
|
+
id: parseInt(variantId),
|
|
11079
11864
|
quantity: quantity || 1
|
|
11080
11865
|
}]
|
|
11081
11866
|
})
|
|
11082
11867
|
});
|
|
11083
11868
|
case 2:
|
|
11084
|
-
|
|
11085
|
-
if (
|
|
11086
|
-
|
|
11869
|
+
addResponse = _context5.v;
|
|
11870
|
+
if (addResponse.ok) {
|
|
11871
|
+
_context5.n = 4;
|
|
11087
11872
|
break;
|
|
11088
11873
|
}
|
|
11089
|
-
|
|
11090
|
-
return
|
|
11874
|
+
_context5.n = 3;
|
|
11875
|
+
return addResponse.text();
|
|
11091
11876
|
case 3:
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
11877
|
+
errorText = _context5.v;
|
|
11878
|
+
console.error('[TTP Ecommerce] /cart/add.js failed:', addResponse.status, errorText);
|
|
11879
|
+
this._sendToVoiceSDK({
|
|
11880
|
+
t: 'cart_add_result',
|
|
11881
|
+
success: false,
|
|
11882
|
+
error: "Cart add failed: HTTP ".concat(addResponse.status),
|
|
11883
|
+
productName: productName || String(variantId),
|
|
11884
|
+
quantity: quantity || 1
|
|
11100
11885
|
});
|
|
11886
|
+
return _context5.a(2);
|
|
11887
|
+
case 4:
|
|
11888
|
+
_context5.n = 5;
|
|
11889
|
+
return addResponse.json();
|
|
11101
11890
|
case 5:
|
|
11102
|
-
|
|
11891
|
+
addResult = _context5.v;
|
|
11892
|
+
if (addResult.items && addResult.items.length > 0) {
|
|
11893
|
+
productName = addResult.items[0].title || '';
|
|
11894
|
+
}
|
|
11895
|
+
_context5.n = 6;
|
|
11896
|
+
return fetch('/cart.js');
|
|
11897
|
+
case 6:
|
|
11898
|
+
cartResponse = _context5.v;
|
|
11899
|
+
_context5.n = 7;
|
|
11900
|
+
return cartResponse.json();
|
|
11901
|
+
case 7:
|
|
11902
|
+
cart = _context5.v;
|
|
11903
|
+
cartRefreshed = false;
|
|
11904
|
+
try {
|
|
11905
|
+
cartRefreshed = this._refreshCartCount(cart.item_count);
|
|
11906
|
+
} catch (e) {
|
|
11907
|
+
console.warn('[TTP Ecommerce] Cart count refresh failed (non-critical):', e);
|
|
11908
|
+
}
|
|
11103
11909
|
this._handleCartUpdatedFromServer({
|
|
11104
11910
|
action: 'added',
|
|
11105
11911
|
product: {
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
price: price || ((_cartResponse$items2 = cartResponse.items) === null || _cartResponse$items2 === void 0 || (_cartResponse$items2 = _cartResponse$items2[0]) === null || _cartResponse$items2 === void 0 ? void 0 : _cartResponse$items2.price) / 100 || 0
|
|
11912
|
+
name: productName,
|
|
11913
|
+
id: variantId
|
|
11109
11914
|
},
|
|
11110
|
-
cartTotal:
|
|
11111
|
-
cartItemCount:
|
|
11915
|
+
cartTotal: cart.total_price / 100,
|
|
11916
|
+
cartItemCount: cart.item_count,
|
|
11917
|
+
currency: cart.currency
|
|
11918
|
+
});
|
|
11919
|
+
this._sendToVoiceSDK({
|
|
11920
|
+
t: 'cart_add_result',
|
|
11921
|
+
success: true,
|
|
11922
|
+
cartRefreshed: cartRefreshed,
|
|
11923
|
+
productName: productName,
|
|
11924
|
+
quantity: quantity || 1,
|
|
11925
|
+
cartTotal: cart.total_price,
|
|
11926
|
+
cartItemCount: cart.item_count,
|
|
11927
|
+
currency: cart.currency
|
|
11112
11928
|
});
|
|
11113
|
-
|
|
11929
|
+
console.log("[TTP Ecommerce] Cart add result sent: ".concat(productName, ", refreshed=").concat(cartRefreshed, ", ").concat(cart.item_count, " items"));
|
|
11930
|
+
_context5.n = 9;
|
|
11114
11931
|
break;
|
|
11115
|
-
case
|
|
11116
|
-
|
|
11117
|
-
|
|
11118
|
-
console.error('[
|
|
11119
|
-
|
|
11120
|
-
|
|
11932
|
+
case 8:
|
|
11933
|
+
_context5.p = 8;
|
|
11934
|
+
_t4 = _context5.v;
|
|
11935
|
+
console.error('[TTP Ecommerce] Error in _handleAddToStoreCart:', _t4);
|
|
11936
|
+
this._sendToVoiceSDK({
|
|
11937
|
+
t: 'cart_add_result',
|
|
11938
|
+
success: false,
|
|
11939
|
+
error: _t4.message || 'Failed to add to cart',
|
|
11940
|
+
productName: productName || String(variantId),
|
|
11941
|
+
quantity: quantity || 1
|
|
11942
|
+
});
|
|
11943
|
+
case 9:
|
|
11944
|
+
return _context5.a(2);
|
|
11121
11945
|
}
|
|
11122
|
-
},
|
|
11946
|
+
}, _callee5, this, [[1, 8]]);
|
|
11123
11947
|
}));
|
|
11124
11948
|
function _handleAddToStoreCart(_x2) {
|
|
11125
11949
|
return _handleAddToStoreCart2.apply(this, arguments);
|
|
11126
11950
|
}
|
|
11127
11951
|
return _handleAddToStoreCart;
|
|
11128
|
-
}()
|
|
11952
|
+
}()
|
|
11953
|
+
/**
|
|
11954
|
+
* Simple cart count refresh — update common counter selectors.
|
|
11955
|
+
* Theme-specific refresh (Section Rendering API, etc.) can be added later per-theme.
|
|
11956
|
+
* @returns {boolean} Whether any element was updated
|
|
11957
|
+
*/
|
|
11958
|
+
)
|
|
11959
|
+
}, {
|
|
11960
|
+
key: "_refreshCartCount",
|
|
11961
|
+
value: function _refreshCartCount(itemCount) {
|
|
11962
|
+
var refreshed = false;
|
|
11963
|
+
var selectors = ['.cart-count', '.cart-count-bubble', '[data-cart-count]', '.js-cart-count', '.cart-item-count', '#CartCount', '.site-header__cart-count', '.cart-count-tag'];
|
|
11964
|
+
selectors.forEach(function (selector) {
|
|
11965
|
+
document.querySelectorAll(selector).forEach(function (el) {
|
|
11966
|
+
el.textContent = itemCount;
|
|
11967
|
+
refreshed = true;
|
|
11968
|
+
});
|
|
11969
|
+
});
|
|
11970
|
+
return refreshed;
|
|
11971
|
+
}
|
|
11972
|
+
|
|
11973
|
+
// ═══════════════════════════════════════════
|
|
11129
11974
|
// PRODUCT SELECTION (server manages cart)
|
|
11130
11975
|
// ═══════════════════════════════════════════
|
|
11131
11976
|
}, {
|
|
11132
11977
|
key: "_handleProductSelected",
|
|
11133
11978
|
value: function _handleProductSelected(product) {
|
|
11134
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 */}
|
|
11135
11987
|
this._sendToVoiceSDK({
|
|
11136
11988
|
t: 'product_selected',
|
|
11137
11989
|
productId: product.id,
|
|
@@ -11159,8 +12011,8 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
|
|
|
11159
12011
|
key: "_sendToVoiceSDK",
|
|
11160
12012
|
value: function _sendToVoiceSDK(message) {
|
|
11161
12013
|
try {
|
|
11162
|
-
var _this$widget$
|
|
11163
|
-
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;
|
|
11164
12016
|
if (voiceSDK && voiceSDK.sendMessage) {
|
|
11165
12017
|
voiceSDK.sendMessage(message);
|
|
11166
12018
|
}
|
|
@@ -11181,45 +12033,45 @@ var TTPEcommerceWidget = /*#__PURE__*/function () {
|
|
|
11181
12033
|
self.ecommerce.handleMessage(payload);
|
|
11182
12034
|
},
|
|
11183
12035
|
showProducts: function () {
|
|
11184
|
-
var _showProducts = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
12036
|
+
var _showProducts = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(query) {
|
|
11185
12037
|
var limit,
|
|
11186
12038
|
sessionId,
|
|
11187
12039
|
baseUrl,
|
|
11188
12040
|
response,
|
|
11189
12041
|
data,
|
|
11190
|
-
|
|
11191
|
-
|
|
11192
|
-
return _regenerator().w(function (
|
|
11193
|
-
while (1) switch (
|
|
12042
|
+
_args6 = arguments,
|
|
12043
|
+
_t5;
|
|
12044
|
+
return _regenerator().w(function (_context6) {
|
|
12045
|
+
while (1) switch (_context6.p = _context6.n) {
|
|
11194
12046
|
case 0:
|
|
11195
|
-
limit =
|
|
11196
|
-
sessionId =
|
|
12047
|
+
limit = _args6.length > 1 && _args6[1] !== undefined ? _args6[1] : 5;
|
|
12048
|
+
sessionId = _args6.length > 2 && _args6[2] !== undefined ? _args6[2] : 'test';
|
|
11197
12049
|
baseUrl = window.__TTP_MOCK_API__ || 'https://backend.talktopc.com';
|
|
11198
|
-
|
|
11199
|
-
|
|
12050
|
+
_context6.p = 1;
|
|
12051
|
+
_context6.n = 2;
|
|
11200
12052
|
return fetch("".concat(baseUrl, "/api/partner/mock-store/products/search?q=").concat(encodeURIComponent(query), "&limit=").concat(limit, "&sessionId=").concat(encodeURIComponent(sessionId)));
|
|
11201
12053
|
case 2:
|
|
11202
|
-
response =
|
|
11203
|
-
|
|
12054
|
+
response = _context6.v;
|
|
12055
|
+
_context6.n = 3;
|
|
11204
12056
|
return response.json();
|
|
11205
12057
|
case 3:
|
|
11206
|
-
data =
|
|
12058
|
+
data = _context6.v;
|
|
11207
12059
|
self.ecommerce.handleShowProducts({
|
|
11208
12060
|
t: 'show_products',
|
|
11209
12061
|
products: data.products,
|
|
11210
12062
|
title: "".concat(data.totalCount, " results for \"").concat(query, "\""),
|
|
11211
12063
|
layout: data.products.length <= 4 ? 'cards' : 'list'
|
|
11212
12064
|
});
|
|
11213
|
-
|
|
12065
|
+
_context6.n = 5;
|
|
11214
12066
|
break;
|
|
11215
12067
|
case 4:
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
console.error('[TTPEcommerceWidget] showProducts failed:',
|
|
12068
|
+
_context6.p = 4;
|
|
12069
|
+
_t5 = _context6.v;
|
|
12070
|
+
console.error('[TTPEcommerceWidget] showProducts failed:', _t5);
|
|
11219
12071
|
case 5:
|
|
11220
|
-
return
|
|
12072
|
+
return _context6.a(2);
|
|
11221
12073
|
}
|
|
11222
|
-
},
|
|
12074
|
+
}, _callee6, null, [[1, 4]]);
|
|
11223
12075
|
}));
|
|
11224
12076
|
function showProducts(_x3) {
|
|
11225
12077
|
return _showProducts.apply(this, arguments);
|
|
@@ -11330,7 +12182,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
11330
12182
|
* Follows the same pattern as Styles.generateCSS().
|
|
11331
12183
|
*/
|
|
11332
12184
|
function generateEcommerceCSS() {
|
|
11333
|
-
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 ";
|
|
11334
12186
|
}
|
|
11335
12187
|
|
|
11336
12188
|
/***/ }),
|
|
@@ -11720,19 +12572,23 @@ var MicPermissionModal = /*#__PURE__*/function () {
|
|
|
11720
12572
|
}();
|
|
11721
12573
|
/**
|
|
11722
12574
|
* Pre-prompt modal - shown before requesting browser permission
|
|
12575
|
+
* Uses a transparent overlay (no blur) so the call view is visible behind it
|
|
11723
12576
|
*/
|
|
11724
12577
|
function showPrePromptModal(onAccept, onCancel) {
|
|
11725
12578
|
var modal = new MicPermissionModal();
|
|
11726
12579
|
var overlay = modal.createOverlay();
|
|
11727
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';
|
|
11728
12586
|
var modalDiv = document.createElement('div');
|
|
11729
12587
|
modalDiv.className = 'mic-permission-modal';
|
|
11730
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 ");
|
|
11731
12589
|
overlay.appendChild(modalDiv);
|
|
11732
12590
|
modal.overlay = overlay;
|
|
11733
12591
|
modal.modal = modalDiv;
|
|
11734
|
-
|
|
11735
|
-
// Event handlers
|
|
11736
12592
|
modalDiv.querySelector('[data-action="accept"]').addEventListener('click', function () {
|
|
11737
12593
|
modal.close();
|
|
11738
12594
|
onAccept();
|
|
@@ -11741,8 +12597,6 @@ function showPrePromptModal(onAccept, onCancel) {
|
|
|
11741
12597
|
modal.close();
|
|
11742
12598
|
onCancel();
|
|
11743
12599
|
});
|
|
11744
|
-
|
|
11745
|
-
// Close on overlay click
|
|
11746
12600
|
overlay.addEventListener('click', function (e) {
|
|
11747
12601
|
if (e.target === overlay) {
|
|
11748
12602
|
modal.close();
|
|
@@ -14321,6 +15175,12 @@ function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf
|
|
|
14321
15175
|
|
|
14322
15176
|
|
|
14323
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
|
+
|
|
14324
15184
|
/**
|
|
14325
15185
|
|
|
14326
15186
|
* AudioPlayer v2 - Handles multiple audio formats with codec support
|
|
@@ -14357,8 +15217,6 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
14357
15217
|
|
|
14358
15218
|
// Version identification log
|
|
14359
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;');
|
|
14360
|
-
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;');
|
|
14361
|
-
console.log('%cBuild: 2026-02-26-MIC-SPEAKER-RESET', 'background: #e0e7ff; color: #1e40af; font-size: 11px; padding: 2px 6px; border-radius: 3px;');
|
|
14362
15220
|
console.log('');
|
|
14363
15221
|
_this.config = config;
|
|
14364
15222
|
_this.audioContext = null;
|
|
@@ -15692,43 +16550,84 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
15692
16550
|
}, {
|
|
15693
16551
|
key: "initializeAudioContext",
|
|
15694
16552
|
value: function initializeAudioContext() {
|
|
15695
|
-
var _this$outputFormat5,
|
|
16553
|
+
var _this$outputFormat5,
|
|
16554
|
+
_this7 = this,
|
|
16555
|
+
_this$outputFormat6;
|
|
15696
16556
|
// Use negotiated sample rate, default to 44100Hz to match server output
|
|
15697
|
-
// This ensures AudioContext matches server output before format negotiation completes
|
|
15698
16557
|
var desiredSampleRate = ((_this$outputFormat5 = this.outputFormat) === null || _this$outputFormat5 === void 0 ? void 0 : _this$outputFormat5.sampleRate) || 44100;
|
|
15699
|
-
|
|
15700
|
-
// If backend sends non-standard rate (like 22050Hz), check if browser supports it
|
|
15701
|
-
// If not, prefer 44100Hz (default) or 48000Hz to avoid resampling issues
|
|
15702
16558
|
if (desiredSampleRate !== 48000 && desiredSampleRate !== 44100) {
|
|
15703
|
-
// For non-standard rates, try to use browser's native rate to avoid resampling
|
|
15704
|
-
// This prevents tiny cuts caused by browser resampling
|
|
15705
16559
|
console.log("\u2139\uFE0F AudioPlayer: Backend requested ".concat(desiredSampleRate, "Hz, but browser may resample"));
|
|
15706
16560
|
console.log(" Consider requesting 44100Hz or 48000Hz from backend to avoid resampling and improve quality");
|
|
15707
16561
|
}
|
|
15708
16562
|
|
|
15709
|
-
// Check if AudioContext exists and
|
|
16563
|
+
// Check if current instance AudioContext exists and matches
|
|
15710
16564
|
if (this.audioContext) {
|
|
15711
16565
|
var currentSampleRate = this.audioContext.sampleRate;
|
|
15712
|
-
// If sample rate differs significantly, recreate the AudioContext
|
|
15713
16566
|
if (Math.abs(currentSampleRate - desiredSampleRate) > 100) {
|
|
15714
16567
|
console.warn("\u26A0\uFE0F AudioPlayer: AudioContext sample rate (".concat(currentSampleRate, "Hz) doesn't match format (").concat(desiredSampleRate, "Hz), recreating..."));
|
|
15715
16568
|
this.stopImmediate();
|
|
15716
16569
|
this._cleanupAudioContext();
|
|
15717
16570
|
} else {
|
|
15718
|
-
// Sample rate matches (or close enough), no need to recreate
|
|
15719
16571
|
return;
|
|
15720
16572
|
}
|
|
15721
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
|
+
}
|
|
15722
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
|
+
}
|
|
15723
16621
|
try {
|
|
15724
|
-
// Try to create with specific sample rate
|
|
15725
16622
|
this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
|
|
15726
16623
|
sampleRate: desiredSampleRate,
|
|
15727
|
-
latencyHint: 'playback'
|
|
16624
|
+
latencyHint: 'playback'
|
|
15728
16625
|
});
|
|
15729
|
-
console.log("\u2705 AudioContext created at ".concat(this.audioContext.sampleRate, "Hz (requested: ").concat(desiredSampleRate, "Hz)"));
|
|
15730
16626
|
|
|
15731
|
-
//
|
|
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)"));
|
|
15732
16631
|
if (Math.abs(this.audioContext.sampleRate - desiredSampleRate) > 100) {
|
|
15733
16632
|
console.error("\u274C CRITICAL: Browser sample rate mismatch!");
|
|
15734
16633
|
console.error(" Requested: ".concat(desiredSampleRate, "Hz"));
|
|
@@ -15739,34 +16638,23 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
15739
16638
|
console.warn("\u26A0\uFE0F Browser adjusted sample rate: ".concat(desiredSampleRate, "Hz \u2192 ").concat(this.audioContext.sampleRate, "Hz"));
|
|
15740
16639
|
console.warn(" Browser will automatically resample audio.");
|
|
15741
16640
|
}
|
|
15742
|
-
|
|
15743
|
-
// ✅ NEW: Monitor AudioContext state changes (handles mic permission, tab switching, etc.)
|
|
15744
16641
|
this.setupAudioContextStateMonitoring();
|
|
15745
|
-
|
|
15746
|
-
// Create GainNode for volume/mute control
|
|
15747
16642
|
this.gainNode = this.audioContext.createGain();
|
|
15748
|
-
this.gainNode.gain.value = 1.0;
|
|
16643
|
+
this.gainNode.gain.value = 1.0;
|
|
15749
16644
|
this.gainNode.connect(this.audioContext.destination);
|
|
15750
16645
|
console.log('✅ AudioPlayer: GainNode created for volume control');
|
|
15751
|
-
|
|
15752
|
-
// Prime audio hardware for Android
|
|
15753
16646
|
this._primeAudioContext();
|
|
15754
16647
|
} catch (error) {
|
|
15755
|
-
// Fallback to default if browser doesn't support custom sample rate
|
|
15756
16648
|
console.error("\u274C Failed to create AudioContext:", error);
|
|
15757
16649
|
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
16650
|
+
_sharedPlayerContext = this.audioContext;
|
|
16651
|
+
_sharedPlayerSampleRate = this.audioContext.sampleRate;
|
|
15758
16652
|
console.log("\u2139\uFE0F Using browser default: ".concat(this.audioContext.sampleRate, "Hz"));
|
|
15759
|
-
|
|
15760
|
-
// Setup monitoring for fallback AudioContext too
|
|
15761
16653
|
this.setupAudioContextStateMonitoring();
|
|
15762
|
-
|
|
15763
|
-
// Create GainNode for volume/mute control
|
|
15764
16654
|
this.gainNode = this.audioContext.createGain();
|
|
15765
|
-
this.gainNode.gain.value = 1.0;
|
|
16655
|
+
this.gainNode.gain.value = 1.0;
|
|
15766
16656
|
this.gainNode.connect(this.audioContext.destination);
|
|
15767
16657
|
console.log('✅ AudioPlayer: GainNode created for volume control (fallback)');
|
|
15768
|
-
|
|
15769
|
-
// Prime audio hardware for Android
|
|
15770
16658
|
this._primeAudioContext();
|
|
15771
16659
|
}
|
|
15772
16660
|
}
|
|
@@ -15827,7 +16715,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
15827
16715
|
}, {
|
|
15828
16716
|
key: "_cleanupAudioContext",
|
|
15829
16717
|
value: function _cleanupAudioContext() {
|
|
15830
|
-
console.log('[TTP AudioPlayer] 🧹 Cleaning up AudioContext (
|
|
16718
|
+
console.log('[TTP AudioPlayer] 🧹 Cleaning up AudioContext (iOS-safe: keeping shared context alive)');
|
|
15831
16719
|
|
|
15832
16720
|
// Disconnect and cleanup gainNode
|
|
15833
16721
|
if (this.gainNode) {
|
|
@@ -15851,24 +16739,34 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
15851
16739
|
this._waitForReadyStateHandler = null;
|
|
15852
16740
|
}
|
|
15853
16741
|
|
|
15854
|
-
//
|
|
15855
|
-
|
|
15856
|
-
|
|
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
|
+
});
|
|
15857
16750
|
}
|
|
15858
16751
|
|
|
15859
|
-
//
|
|
16752
|
+
// Release instance reference but keep shared context alive
|
|
15860
16753
|
this.audioContext = null;
|
|
15861
16754
|
this._audioContextPrimed = false;
|
|
15862
16755
|
}
|
|
15863
16756
|
|
|
15864
16757
|
/**
|
|
15865
|
-
*
|
|
15866
|
-
*
|
|
16758
|
+
* Fully close the shared AudioContext.
|
|
16759
|
+
* Only call this when the widget is being completely removed from the page.
|
|
15867
16760
|
*/
|
|
15868
16761
|
}, {
|
|
15869
16762
|
key: "setupAudioContextStateMonitoring",
|
|
15870
|
-
value:
|
|
15871
|
-
|
|
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;
|
|
15872
16770
|
if (!this.audioContext) {
|
|
15873
16771
|
return;
|
|
15874
16772
|
}
|
|
@@ -15881,35 +16779,35 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
15881
16779
|
// Create handler that references this.audioContext dynamically
|
|
15882
16780
|
this._audioContextStateChangeHandler = function () {
|
|
15883
16781
|
// Null check required because audioContext may be cleaned up while handler is queued
|
|
15884
|
-
if (!
|
|
16782
|
+
if (!_this8.audioContext) {
|
|
15885
16783
|
console.warn('⚠️ AudioPlayer: State change handler fired but AudioContext is null');
|
|
15886
16784
|
return;
|
|
15887
16785
|
}
|
|
15888
|
-
console.log("\uD83C\uDFB5 AudioPlayer: AudioContext state changed to: ".concat(
|
|
15889
|
-
if (
|
|
16786
|
+
console.log("\uD83C\uDFB5 AudioPlayer: AudioContext state changed to: ".concat(_this8.audioContext.state));
|
|
16787
|
+
if (_this8.audioContext.state === 'suspended' && _this8.isPlaying) {
|
|
15890
16788
|
// AudioContext was suspended during playback (tab switch, mic permission, etc.)
|
|
15891
16789
|
console.warn('⚠️ AudioPlayer: AudioContext suspended during playback');
|
|
15892
16790
|
// Note: Playback will pause automatically, but we should handle queue processing
|
|
15893
16791
|
// The state change will be handled when we try to process next frame
|
|
15894
|
-
} else if (
|
|
16792
|
+
} else if (_this8.audioContext.state === 'running' && !_this8.isPlaying && (_this8.audioQueue.length > 0 || _this8.pcmChunkQueue.length > 0 || _this8.preparedBuffer.length > 0)) {
|
|
15895
16793
|
// AudioContext resumed and we have queued frames
|
|
15896
16794
|
// This handles: mic permission grant, tab switching back, browser resume, etc.
|
|
15897
16795
|
console.log('✅ AudioPlayer: AudioContext resumed - resuming queue processing');
|
|
15898
16796
|
|
|
15899
16797
|
// Resume queue processing if we have frames
|
|
15900
|
-
if (
|
|
16798
|
+
if (_this8.audioQueue.length > 0 && !_this8.isProcessingQueue) {
|
|
15901
16799
|
setTimeout(function () {
|
|
15902
|
-
return
|
|
16800
|
+
return _this8.processQueue();
|
|
15903
16801
|
}, 50);
|
|
15904
16802
|
}
|
|
15905
|
-
if (
|
|
16803
|
+
if (_this8.pcmChunkQueue.length > 0 && !_this8.isProcessingPcmQueue) {
|
|
15906
16804
|
setTimeout(function () {
|
|
15907
|
-
return
|
|
16805
|
+
return _this8.processPcmQueue();
|
|
15908
16806
|
}, 50);
|
|
15909
16807
|
}
|
|
15910
|
-
if (
|
|
16808
|
+
if (_this8.preparedBuffer.length > 0 && !_this8.isSchedulingFrames) {
|
|
15911
16809
|
setTimeout(function () {
|
|
15912
|
-
return
|
|
16810
|
+
return _this8.scheduleFrames();
|
|
15913
16811
|
}, 50);
|
|
15914
16812
|
}
|
|
15915
16813
|
}
|
|
@@ -15928,7 +16826,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
15928
16826
|
key: "processQueue",
|
|
15929
16827
|
value: (function () {
|
|
15930
16828
|
var _processQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
|
|
15931
|
-
var
|
|
16829
|
+
var _this9 = this;
|
|
15932
16830
|
var audioBlob, wasFirstPlay, audioContext, arrayBuffer, audioBuffer, shouldEmitStart, source, _t4;
|
|
15933
16831
|
return _regenerator().w(function (_context7) {
|
|
15934
16832
|
while (1) switch (_context7.p = _context7.n) {
|
|
@@ -15994,22 +16892,22 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
15994
16892
|
// Handle end
|
|
15995
16893
|
|
|
15996
16894
|
source.onended = function () {
|
|
15997
|
-
|
|
15998
|
-
|
|
16895
|
+
_this9.currentSource = null;
|
|
16896
|
+
_this9.isProcessingQueue = false;
|
|
15999
16897
|
|
|
16000
16898
|
// Process next chunk
|
|
16001
16899
|
|
|
16002
|
-
if (
|
|
16900
|
+
if (_this9.audioQueue.length > 0) {
|
|
16003
16901
|
setTimeout(function () {
|
|
16004
|
-
return
|
|
16902
|
+
return _this9.processQueue();
|
|
16005
16903
|
}, 50);
|
|
16006
16904
|
} else {
|
|
16007
16905
|
// No more chunks - stop after delay
|
|
16008
16906
|
|
|
16009
16907
|
setTimeout(function () {
|
|
16010
|
-
if (
|
|
16011
|
-
|
|
16012
|
-
|
|
16908
|
+
if (_this9.audioQueue.length === 0 && !_this9.currentSource) {
|
|
16909
|
+
_this9.isPlaying = false;
|
|
16910
|
+
_this9.emit('playbackStopped');
|
|
16013
16911
|
}
|
|
16014
16912
|
}, 100);
|
|
16015
16913
|
}
|
|
@@ -16032,7 +16930,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
16032
16930
|
if (this.audioQueue.length > 0) {
|
|
16033
16931
|
this.isProcessingQueue = false;
|
|
16034
16932
|
setTimeout(function () {
|
|
16035
|
-
return
|
|
16933
|
+
return _this9.processQueue();
|
|
16036
16934
|
}, 100);
|
|
16037
16935
|
} else {
|
|
16038
16936
|
this.isPlaying = false;
|
|
@@ -16181,7 +17079,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
16181
17079
|
}, {
|
|
16182
17080
|
key: "markNewSentence",
|
|
16183
17081
|
value: function markNewSentence(text) {
|
|
16184
|
-
var
|
|
17082
|
+
var _this0 = this;
|
|
16185
17083
|
var wasStopped = this._isStopped;
|
|
16186
17084
|
var isCurrentlyPlaying = this.isPlaying || this.scheduledSources.size > 0;
|
|
16187
17085
|
|
|
@@ -16237,20 +17135,20 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
16237
17135
|
var sentenceText = text; // Capture for timeout callback
|
|
16238
17136
|
this._emptySentenceTimeout = setTimeout(function () {
|
|
16239
17137
|
// Check if this sentence still has no chunks after timeout
|
|
16240
|
-
if (
|
|
17138
|
+
if (_this0.pendingSentenceText === sentenceText && _this0.scheduledBuffers === 0 && _this0.preparedBuffer.length === 0 && _this0.pcmChunkQueue.length === 0 && !_this0._isStopped) {
|
|
16241
17139
|
console.warn("\u26A0\uFE0F AudioPlayer: Empty sentence detected after 5s timeout - no chunks received for: \"".concat(sentenceText.substring(0, 40), "...\""));
|
|
16242
17140
|
// Clear pending sentence to unblock next sentence
|
|
16243
|
-
if (
|
|
16244
|
-
|
|
17141
|
+
if (_this0.pendingSentenceText === sentenceText) {
|
|
17142
|
+
_this0.pendingSentenceText = null;
|
|
16245
17143
|
}
|
|
16246
17144
|
// Emit playbackStopped to allow next sentence to start
|
|
16247
17145
|
// Only if we're not currently playing (to avoid interrupting real playback)
|
|
16248
|
-
if (!
|
|
17146
|
+
if (!_this0.isPlaying && _this0.scheduledSources.size === 0) {
|
|
16249
17147
|
console.log('🛑 AudioPlayer: Emitting playbackStopped for empty sentence timeout');
|
|
16250
|
-
|
|
17148
|
+
_this0.emit('playbackStopped');
|
|
16251
17149
|
}
|
|
16252
17150
|
}
|
|
16253
|
-
|
|
17151
|
+
_this0._emptySentenceTimeout = null;
|
|
16254
17152
|
}, 5000); // 5 second timeout - adjust based on expected chunk arrival rate
|
|
16255
17153
|
}
|
|
16256
17154
|
|
|
@@ -16260,14 +17158,14 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
16260
17158
|
}, {
|
|
16261
17159
|
key: "startTranscriptChecker",
|
|
16262
17160
|
value: function startTranscriptChecker() {
|
|
16263
|
-
var
|
|
17161
|
+
var _this1 = this;
|
|
16264
17162
|
if (this.isCheckingTranscripts) return;
|
|
16265
17163
|
this.isCheckingTranscripts = true;
|
|
16266
17164
|
console.log('📝 AudioPlayer: Transcript checker started');
|
|
16267
17165
|
var _checkLoop = function checkLoop() {
|
|
16268
|
-
if (!
|
|
16269
|
-
var currentTime =
|
|
16270
|
-
var _iterator = _createForOfIteratorHelper(
|
|
17166
|
+
if (!_this1.isCheckingTranscripts || !_this1.audioContext) return;
|
|
17167
|
+
var currentTime = _this1.audioContext.currentTime;
|
|
17168
|
+
var _iterator = _createForOfIteratorHelper(_this1.sentenceTimings),
|
|
16271
17169
|
_step;
|
|
16272
17170
|
try {
|
|
16273
17171
|
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
@@ -16275,7 +17173,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
16275
17173
|
if (!timing.displayed && currentTime >= timing.startTime) {
|
|
16276
17174
|
timing.displayed = true;
|
|
16277
17175
|
console.log("\uD83D\uDCDD AudioPlayer: Display transcript at ".concat(currentTime.toFixed(3), "s: \"").concat(timing.text.substring(0, 40), "...\""));
|
|
16278
|
-
|
|
17176
|
+
_this1.emit('transcriptDisplay', {
|
|
16279
17177
|
text: timing.text
|
|
16280
17178
|
});
|
|
16281
17179
|
}
|
|
@@ -16285,10 +17183,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
16285
17183
|
} finally {
|
|
16286
17184
|
_iterator.f();
|
|
16287
17185
|
}
|
|
16288
|
-
if (
|
|
17186
|
+
if (_this1.isPlaying || _this1.scheduledBuffers > 0) {
|
|
16289
17187
|
requestAnimationFrame(_checkLoop);
|
|
16290
17188
|
} else {
|
|
16291
|
-
|
|
17189
|
+
_this1.isCheckingTranscripts = false;
|
|
16292
17190
|
console.log('📝 AudioPlayer: Transcript checker stopped');
|
|
16293
17191
|
}
|
|
16294
17192
|
};
|
|
@@ -16409,6 +17307,16 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
16409
17307
|
this._cleanupAudioContext();
|
|
16410
17308
|
this.removeAllListeners();
|
|
16411
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
|
+
}
|
|
16412
17320
|
}]);
|
|
16413
17321
|
}(_shared_EventEmitter_js__WEBPACK_IMPORTED_MODULE_0__["default"]);
|
|
16414
17322
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (AudioPlayer);
|
|
@@ -19112,6 +20020,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
19112
20020
|
this.audioRecorder.destroy();
|
|
19113
20021
|
}
|
|
19114
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
|
+
|
|
19115
20027
|
// Cleanup client tools registry
|
|
19116
20028
|
if (this.clientToolsRegistry) {
|
|
19117
20029
|
var tools = this.clientToolsRegistry.getRegisteredTools();
|
|
@@ -20425,6 +21337,7 @@ var AgentSDK = /*#__PURE__*/function () {
|
|
|
20425
21337
|
this.onAudioStartPlaying = config.onAudioStartPlaying || function () {};
|
|
20426
21338
|
this.onAudioStoppedPlaying = config.onAudioStoppedPlaying || function () {};
|
|
20427
21339
|
this.onSubtitleDisplay = config.onSubtitleDisplay || function () {};
|
|
21340
|
+
this.onUnsupportedAction = null;
|
|
20428
21341
|
}
|
|
20429
21342
|
|
|
20430
21343
|
/**
|
|
@@ -20696,6 +21609,13 @@ var AgentSDK = /*#__PURE__*/function () {
|
|
|
20696
21609
|
case 'error':
|
|
20697
21610
|
this.onError(new Error(message.message || message.error || 'Unknown error'));
|
|
20698
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;
|
|
20699
21619
|
default:
|
|
20700
21620
|
// Handle other message types
|
|
20701
21621
|
break;
|
|
@@ -21136,6 +22056,17 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
21136
22056
|
}
|
|
21137
22057
|
var widgetMode = ((_this$config$behavior = _this.config.behavior) === null || _this$config$behavior === void 0 ? void 0 : _this$config$behavior.mode) || 'unified';
|
|
21138
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
|
+
}
|
|
21139
22070
|
_this.showLanding();
|
|
21140
22071
|
}
|
|
21141
22072
|
},
|
|
@@ -21150,6 +22081,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
21150
22081
|
// Callback to show error toast (for domain errors, etc.)
|
|
21151
22082
|
onErrorToast: function onErrorToast(message, type) {
|
|
21152
22083
|
_this.showErrorToast(message, type);
|
|
22084
|
+
},
|
|
22085
|
+
onMobileError: function onMobileError(message) {
|
|
22086
|
+
_this.showMobileLandingError(message);
|
|
21153
22087
|
}
|
|
21154
22088
|
});
|
|
21155
22089
|
this.voiceInterface = new _VoiceInterface_js__WEBPACK_IMPORTED_MODULE_1__.VoiceInterface(voiceConfig);
|
|
@@ -21176,13 +22110,25 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
21176
22110
|
this.createWidget();
|
|
21177
22111
|
this.setupEventHandlers();
|
|
21178
22112
|
|
|
21179
|
-
// Start open if configured
|
|
22113
|
+
// Start open if configured
|
|
21180
22114
|
if (this.config.behavior.startOpen || this.config.behavior.autoOpen) {
|
|
21181
|
-
|
|
21182
|
-
|
|
21183
|
-
|
|
21184
|
-
this.
|
|
21185
|
-
|
|
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
|
+
}
|
|
21186
22132
|
}
|
|
21187
22133
|
}
|
|
21188
22134
|
|
|
@@ -21236,9 +22182,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
21236
22182
|
}, {
|
|
21237
22183
|
key: "open",
|
|
21238
22184
|
value: function open() {
|
|
21239
|
-
var _this$
|
|
22185
|
+
var _this$shadowRoot6;
|
|
21240
22186
|
if (this.isOpen) return;
|
|
21241
|
-
var panel = (_this$
|
|
22187
|
+
var panel = (_this$shadowRoot6 = this.shadowRoot) === null || _this$shadowRoot6 === void 0 ? void 0 : _this$shadowRoot6.getElementById('text-chat-panel');
|
|
21242
22188
|
if (panel) {
|
|
21243
22189
|
this.isOpen = true;
|
|
21244
22190
|
panel.classList.add('open');
|
|
@@ -21442,7 +22388,10 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
21442
22388
|
voiceCardIcon: cleanLandingConfig.voiceCardIcon || '🎤',
|
|
21443
22389
|
textCardIcon: cleanLandingConfig.textCardIcon || '💬',
|
|
21444
22390
|
voiceCardTitle: cleanLandingConfig.voiceCardTitle || null,
|
|
21445
|
-
textCardTitle: cleanLandingConfig.textCardTitle || null
|
|
22391
|
+
textCardTitle: cleanLandingConfig.textCardTitle || null,
|
|
22392
|
+
callButtonText: cleanLandingConfig.callButtonText || null,
|
|
22393
|
+
chatButtonText: cleanLandingConfig.chatButtonText || null,
|
|
22394
|
+
statusText: cleanLandingConfig.statusText || null
|
|
21446
22395
|
}, cleanLandingConfig);
|
|
21447
22396
|
}(),
|
|
21448
22397
|
// Header Configuration (top of panel)
|
|
@@ -21472,7 +22421,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
21472
22421
|
backgroundColor: cleanHeaderConfig.backgroundColor || '#7C3AED',
|
|
21473
22422
|
// Default purple
|
|
21474
22423
|
textColor: cleanHeaderConfig.textColor || '#FFFFFF',
|
|
21475
|
-
showCloseButton: cleanHeaderConfig.showCloseButton !== false
|
|
22424
|
+
showCloseButton: cleanHeaderConfig.showCloseButton !== false,
|
|
22425
|
+
mobileLabel: cleanHeaderConfig.mobileLabel || 'Chat'
|
|
21476
22426
|
}, cleanHeaderConfig);
|
|
21477
22427
|
return finalHeader;
|
|
21478
22428
|
}(),
|
|
@@ -21678,10 +22628,10 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
21678
22628
|
var resetCSS = '';
|
|
21679
22629
|
if (this.config.useShadowDOM) {
|
|
21680
22630
|
// Shadow DOM mode - use :host selector
|
|
21681
|
-
resetCSS = "\n :host {\n all: initial;\n display: block;\n pointer-events: none;\n }\n img {\n max-width: none !important;\n }\n #text-chat-button img {\n width: 28px !important;\n height: 28px !important;\n }\n #text-chat-button {\n pointer-events: auto;\n }\n #text-chat-panel {\n pointer-events: auto;\n }\n ";
|
|
22631
|
+
resetCSS = "\n :host {\n all: initial;\n display: block;\n pointer-events: none;\n }\n img {\n max-width: none !important;\n }\n #text-chat-button img {\n width: 28px !important;\n height: 28px !important;\n }\n #text-chat-button {\n pointer-events: auto;\n }\n #text-chat-panel {\n pointer-events: auto;\n }\n .ttp-mobile-fab {\n pointer-events: auto;\n }\n #ttpMobileLanding {\n pointer-events: auto;\n }\n ";
|
|
21682
22632
|
} else {
|
|
21683
22633
|
// Regular DOM mode - targeted reset without breaking widget styles
|
|
21684
|
-
resetCSS = "\n /* Reset only problematic inherited styles, not all styles */\n #text-chat-button,\n #text-chat-panel,\n #text-chat-panel * {\n box-sizing: border-box;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n }\n \n /* Prevent theme CSS from affecting images */\n #text-chat-button img,\n #text-chat-panel img {\n max-width: none !important;\n width: auto;\n height: auto;\n }\n \n #text-chat-button img {\n width: 28px !important;\n height: 28px !important;\n }\n \n /* Ensure button and panel can receive clicks */\n #text-chat-button,\n #text-chat-panel {\n pointer-events: auto;\n }\n \n /* Reset any inherited text styles that might break layout */\n #text-chat-panel {\n text-align: left;\n line-height: normal;\n letter-spacing: normal;\n word-spacing: normal;\n text-transform: none;\n text-indent: 0;\n text-shadow: none;\n white-space: normal;\n }\n \n /* Ensure flexbox works properly */\n #text-chat-panel .widget-shell,\n #text-chat-panel .panel-inner,\n #text-chat-panel .widget-container,\n #text-chat-panel .landing-screen,\n #text-chat-panel .voice-interface,\n #text-chat-panel .text-interface,\n #text-chat-panel .widget-header,\n #text-chat-panel .mode-selection {\n display: flex;\n }\n \n #text-chat-panel .widget-shell,\n #text-chat-panel .panel-inner,\n #text-chat-panel .widget-container,\n #text-chat-panel .landing-screen,\n #text-chat-panel .voice-interface,\n #text-chat-panel .text-interface {\n flex-direction: column;\n }\n \n #text-chat-panel .mode-selection {\n flex-direction: row;\n }\n \n /* Ensure proper stacking */\n #text-chat-button {\n z-index: 10001 !important;\n }\n \n #text-chat-panel {\n z-index: 10000 !important;\n }\n ";
|
|
22634
|
+
resetCSS = "\n /* Reset only problematic inherited styles, not all styles */\n #text-chat-button,\n #text-chat-panel,\n #text-chat-panel * {\n box-sizing: border-box;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n }\n \n /* Prevent theme CSS from affecting images */\n #text-chat-button img,\n #text-chat-panel img {\n max-width: none !important;\n width: auto;\n height: auto;\n }\n \n #text-chat-button img {\n width: 28px !important;\n height: 28px !important;\n }\n \n /* Ensure button and panel can receive clicks */\n #text-chat-button,\n #text-chat-panel,\n .ttp-mobile-fab,\n #ttpMobileLanding {\n pointer-events: auto;\n }\n \n /* Reset any inherited text styles that might break layout */\n #text-chat-panel {\n text-align: left;\n line-height: normal;\n letter-spacing: normal;\n word-spacing: normal;\n text-transform: none;\n text-indent: 0;\n text-shadow: none;\n white-space: normal;\n }\n \n /* Ensure flexbox works properly */\n #text-chat-panel .widget-shell,\n #text-chat-panel .panel-inner,\n #text-chat-panel .widget-container,\n #text-chat-panel .landing-screen,\n #text-chat-panel .voice-interface,\n #text-chat-panel .text-interface,\n #text-chat-panel .widget-header,\n #text-chat-panel .mode-selection {\n display: flex;\n }\n \n #text-chat-panel .widget-shell,\n #text-chat-panel .panel-inner,\n #text-chat-panel .widget-container,\n #text-chat-panel .landing-screen,\n #text-chat-panel .voice-interface,\n #text-chat-panel .text-interface {\n flex-direction: column;\n }\n \n #text-chat-panel .mode-selection {\n flex-direction: row;\n }\n \n /* Ensure proper stacking */\n #text-chat-button {\n z-index: 10001 !important;\n }\n \n #text-chat-panel {\n z-index: 10000 !important;\n }\n ";
|
|
21685
22635
|
}
|
|
21686
22636
|
var widgetHTML = this.generateWidgetHTML();
|
|
21687
22637
|
if (this.config.useShadowDOM) {
|
|
@@ -21699,10 +22649,22 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
21699
22649
|
var panel = this.shadowRoot.getElementById('text-chat-panel');
|
|
21700
22650
|
if (panel) {
|
|
21701
22651
|
if (this.config.behavior.startOpen || this.config.behavior.autoOpen) {
|
|
21702
|
-
|
|
21703
|
-
|
|
21704
|
-
|
|
21705
|
-
|
|
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
|
+
}
|
|
21706
22668
|
} else {
|
|
21707
22669
|
// Widget starts closed - show prompt
|
|
21708
22670
|
this.showPrompt();
|
|
@@ -21850,7 +22812,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
21850
22812
|
isPromptEnabled: isPromptEnabled,
|
|
21851
22813
|
willGenerateHTML: !!promptBubbleHTML
|
|
21852
22814
|
});
|
|
21853
|
-
return "\n ".concat(styleTag, "\n \n ").concat(this.config.behavior.hidden ? '' : "\n <div id=\"text-chat-button-container\">\n ".concat(promptBubbleHTML, "\n ").concat(isPromptEnabled && promptConfig.showPulseRings !== false ? this.generatePulseRingsHTML(promptConfig) : '', "\n <button id=\"text-chat-button\" \n aria-label=\"").concat(this.config.accessibility.ariaLabel, "\"\n aria-description=\"").concat(this.config.accessibility.ariaDescription, "\">\n
|
|
22815
|
+
return "\n ".concat(styleTag, "\n \n ").concat(this.config.behavior.hidden ? '' : "\n <div id=\"text-chat-button-container\">\n ".concat(promptBubbleHTML, "\n ").concat(isPromptEnabled && promptConfig.showPulseRings !== false ? this.generatePulseRingsHTML(promptConfig) : '', "\n <button id=\"text-chat-button\" \n aria-label=\"").concat(this.config.accessibility.ariaLabel, "\"\n aria-description=\"").concat(this.config.accessibility.ariaDescription, "\">\n ").concat(iconHTML, "\n </button>\n <button class=\"ttp-mobile-fab\" aria-label=\"").concat(this.config.accessibility.ariaLabel, "\">\n <div class=\"ttp-mobile-fab__icon-wrap\">\n <div class=\"ttp-mobile-fab__waveform\">\n <span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span>\n </div>\n </div>\n <div class=\"ttp-mobile-fab__label-wrap\">\n <span class=\"ttp-mobile-fab__label\">").concat(header.mobileLabel || 'Chat', "</span>\n <span class=\"ttp-mobile-fab__status\">\n <span class=\"ttp-mobile-fab__dot\"></span>\n ").concat(header.onlineIndicatorText !== undefined && header.onlineIndicatorText !== null ? header.onlineIndicatorText : t('online'), "\n </span>\n </div>\n </button>\n </div>\n "), "\n\n ").concat(this.config.behavior.hidden ? '' : "\n <div id=\"ttpMobileLanding\" class=\"ttp-mobile-landing\">\n <div class=\"ttp-mobile-landing__header\">\n <div class=\"ttp-mobile-landing__avatar\">\n <div class=\"ttp-mobile-landing__waveform\">\n <span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span>\n </div>\n </div>\n <div class=\"ttp-mobile-landing__info\">\n <div class=\"ttp-mobile-landing__name\">".concat(header.title || 'Chat Assistant', "</div>\n <div class=\"ttp-mobile-landing__status\">\n <span class=\"ttp-mobile-landing__dot\"></span>\n ").concat(this.config.landing.statusText || t('online') + ' • ' + (this.config.landing.subtitle || 'Ready to help'), "\n </div>\n </div>\n <button class=\"ttp-mobile-landing__close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n <div class=\"ttp-mobile-landing__error\" id=\"ttpMobileLandingError\" style=\"display:none;\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/>\n </svg>\n <span class=\"ttp-mobile-landing__error-text\"></span>\n </div>\n <div class=\"ttp-mobile-landing__actions\">\n ").concat(showVoice ? "\n <button class=\"ttp-mobile-landing__btn ttp-mobile-landing__btn--call\" id=\"ttpMobileCallBtn\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z\"/>\n </svg>\n ".concat(this.config.landing.callButtonText || this.config.landing.voiceCardTitle || t('voiceCall'), "\n </button>\n ") : '', "\n ").concat(showText ? "\n <button class=\"ttp-mobile-landing__btn ttp-mobile-landing__btn--text\" id=\"ttpMobileChatBtn\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/>\n </svg>\n ".concat(this.config.landing.chatButtonText || this.config.landing.textCardTitle || t('textChat'), "\n </button>\n ") : '', "\n </div>\n ").concat(this.config.footer.show !== false ? "\n <div class=\"ttp-mobile-landing__footer\">\n <div class=\"ttp-mobile-landing__powered\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"10\" height=\"10\">\n <polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/>\n </svg>\n Powered by <a href=\"https://talktopc.com\" target=\"_blank\" rel=\"noopener\">TalkToPC</a>\n </div>\n </div>\n " : '', "\n </div>\n "), "\n \n <div id=\"text-chat-panel\">\n <div class=\"widget-shell\">\n <div class=\"panel-inner widget-container\" style=\"direction: ").concat(this.config.direction, ";\">\n <div class=\"widget-header\" style=\"background: ").concat(header.backgroundColor || '#7C3AED', " !important; color: ").concat(header.textColor || '#FFFFFF', " !important;\">\n <div>\n ").concat(header.showTitle ? "<div class=\"header-title\">".concat(header.title, "</div>") : '', "\n <div class=\"header-status\" style=\"color: ").concat(header.onlineIndicatorColor || 'rgba(255,255,255,0.7)', " !important;\">\n <span class=\"status-dot\" style=\"background: ").concat(header.onlineIndicatorDotColor || '#10b981', " !important;\"></span>\n <span>").concat(header.onlineIndicatorText !== undefined && header.onlineIndicatorText !== null ? header.onlineIndicatorText : t('online'), "</span>\n </div>\n </div>\n \n <div style=\"display: flex; gap: 6px; align-items: center; position: relative; z-index: 1;\">\n <!-- New Chat Button (hide on landing screen, show otherwise) -->\n <button class=\"header-icon new-chat-btn\" id=\"newChatBtn\" title=\"").concat(getTooltip('newChat'), "\" style=\"").concat(showLanding ? 'display: none;' : '', "\">\n <span style=\"font-size: 18px; font-weight: bold;\">+</span>\n </button>\n \n <!-- Back Button (only show in unified mode) -->\n ").concat(widgetMode === 'unified' ? "<button class=\"header-icon back-btn\" id=\"backBtn\" title=\"".concat(getTooltip('back'), "\" style=\"display: none;\">\n <span style=\"font-size: 16px;\">\u2039</span>\n </button>") : '', "\n \n <!-- Close Button -->\n ").concat(header.showCloseButton ? '<button class="header-icon close-btn" id="closeBtn" title="' + getTooltip('close') + '">' + '<span style="font-size: 18px; font-weight: bold;">×</span>' + '</button>' : '', "\n </div>\n </div>\n\n ").concat(showLanding && this.landingScreen ? this.landingScreen.generateHTML() : '', "\n\n ").concat(showVoice ? this.voiceInterface.generateHTML() : '', "\n ").concat(showText ? this.textInterface.generateHTML() : '', "\n ").concat(this.generateFooterHTML(), "\n </div>\n </div>\n </div>\n ");
|
|
21854
22816
|
}
|
|
21855
22817
|
|
|
21856
22818
|
/**
|
|
@@ -21976,7 +22938,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
21976
22938
|
// Note: Removed headerColor variable - landing screen colors now use explicit defaults
|
|
21977
22939
|
// to prevent buttons from inheriting header backgroundColor
|
|
21978
22940
|
|
|
21979
|
-
return "\n /* MOBILE FIRST - Default styles for all devices */\n #text-chat-widget {\n position: fixed !important;\n z-index: 10000;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n /* Mobile defaults */\n right: 10px;\n bottom: 10px;\n left: auto;\n top: auto;\n }\n \n /* Desktop positioning (only on larger screens) */\n @media (min-width: 769px) {\n #text-chat-widget {\n ".concat(positionStyles, "\n right: ").concat(this.config.position.horizontal === 'right' ? '20px' : 'auto', ";\n left: ").concat(this.config.position.horizontal === 'left' ? '20px' : 'auto', ";\n bottom: ").concat(this.config.position.vertical === 'bottom' ? '20px' : 'auto', ";\n top: ").concat(this.config.position.vertical === 'top' ? '20px' : 'auto', ";\n }\n }\n \n /* Mobile override (force mobile positioning) */\n @media (max-width: 768px) {\n #text-chat-widget {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n transform: none !important;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-widget {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n #text-chat-button {\n position: relative;\n width: ").concat(buttonSize, "px;\n height: ").concat(buttonSize, "px;\n margin: 0;\n flex-shrink: 0;\n border-radius: ").concat(btn.shape === 'circle' ? '50%' : btn.shape === 'square' ? '0' : '12px', ";\n background: ").concat(btn.backgroundColor || icon.backgroundColor || '#7C3AED', ";\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all ").concat(anim.duration, "s ease;\n box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n -webkit-touch-callout: none;\n user-select: none;\n min-width: 44px;\n min-height: 44px;\n z-index: 1;\n }\n \n @media (max-width: 768px) {\n #text-chat-widget {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n transform: none !important;\n }\n \n #text-chat-button-container {\n width: 56px !important;\n height: 56px !important;\n }\n \n #text-chat-button {\n width: 56px !important;\n height: 56px !important;\n min-width: 56px !important;\n min-height: 56px !important;\n max-width: 56px !important;\n max-height: 56px !important;\n }\n \n #text-chat-panel {\n position: fixed !important;\n left: 10px !important;\n right: 10px !important;\n bottom: 92px !important; /* 56px button + 20px gap + 16px footer */\n top: 60px !important; /* Add top spacing */\n width: auto !important;\n max-width: none !important;\n height: auto !important; /* Change from max-height to auto */\n max-height: none !important; /* Remove max-height */\n transform: none !important;\n margin: 0 !important;\n }\n \n #text-chat-panel .widget-header {\n padding: 10px 14px;\n min-height: 56px;\n }\n \n #text-chat-panel .header-title {\n font-size: 15px;\n }\n \n #text-chat-panel .header-icon {\n width: 40px;\n height: 40px;\n min-width: 40px;\n min-height: 40px;\n }\n \n #text-chat-input {\n font-size: 16px !important; /* Prevents iOS zoom on focus */\n padding: 12px 16px !important;\n min-height: 48px !important;\n }\n \n #text-chat-send {\n min-width: 48px !important;\n min-height: 48px !important;\n width: 48px !important;\n height: 48px !important;\n }\n \n .landing-screen {\n padding: 16px;\n }\n \n .landing-logo {\n font-size: 40px;\n }\n \n .landing-title {\n font-size: 18px;\n margin-bottom: 16px;\n }\n \n .mode-selection {\n flex-direction: column;\n gap: 12px;\n align-items: center;\n }\n \n .mode-card {\n max-width: 100%;\n width: 100%;\n padding: 16px;\n }\n \n .mode-card-icon {\n width: 50px;\n height: 50px;\n font-size: 28px;\n }\n \n .mode-card-title {\n font-size: 14px;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-widget {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n \n #text-chat-button-container {\n width: 54px !important;\n height: 54px !important;\n }\n \n #text-chat-button {\n width: 54px !important;\n height: 54px !important;\n min-width: 54px !important;\n min-height: 54px !important;\n }\n \n #text-chat-panel {\n left: 8px !important;\n right: 8px !important;\n bottom: 86px !important; /* 54px button + 20px gap + 12px footer */\n top: 50px !important; /* Add top spacing for very small screens */\n height: auto !important;\n max-height: none !important;\n }\n \n #text-chat-panel .widget-header {\n padding: 8px 12px;\n min-height: 52px;\n }\n \n #text-chat-panel .header-title {\n font-size: 14px;\n }\n \n .landing-logo {\n font-size: 36px;\n }\n \n .landing-title {\n font-size: 16px;\n }\n }\n \n ").concat(anim.enableHover ? "\n #text-chat-button:hover {\n ".concat(btn.hoverColor ? "background: ".concat(btn.hoverColor, ";") : '', "\n transform: scale(1.05);\n box-shadow: 0 8px 20px rgba(102, 126, 234, 0.5);\n }\n ") : '', "\n \n /* Prompt Animation Keyframes */\n @keyframes widget-shimmer {\n 0% { transform: translateX(-100%); }\n 100% { transform: translateX(200%); }\n }\n \n @keyframes widget-ripple {\n 0% { transform: scale(1); opacity: 0.6; }\n 100% { transform: scale(2.5); opacity: 0; }\n }\n \n @keyframes widget-float {\n 0%, 100% { transform: translateX(-50%) translateY(0); }\n 50% { transform: translateX(-50%) translateY(-8px); }\n }\n \n @keyframes widget-bounce {\n 0%, 100% { transform: translateX(-50%) translateY(0); }\n 50% { transform: translateX(-50%) translateY(-10px); }\n }\n \n @keyframes widget-pulse-ring {\n 0% { transform: scale(1); opacity: 0.4; }\n 100% { transform: scale(1.8); opacity: 0; }\n }\n \n /* Prompt Bubble Container */\n #text-chat-button-container {\n position: fixed;\n ").concat(this.config.position.vertical === 'bottom' ? "bottom: ".concat(((_this$config$position = this.config.position.offset) === null || _this$config$position === void 0 ? void 0 : _this$config$position.y) || 20, "px;") : "top: ".concat(((_this$config$position2 = this.config.position.offset) === null || _this$config$position2 === void 0 ? void 0 : _this$config$position2.y) || 20, "px;"), "\n ").concat(this.config.position.horizontal === 'right' ? "right: ".concat(((_this$config$position3 = this.config.position.offset) === null || _this$config$position3 === void 0 ? void 0 : _this$config$position3.x) || 20, "px;") : "left: ".concat(((_this$config$position4 = this.config.position.offset) === null || _this$config$position4 === void 0 ? void 0 : _this$config$position4.x) || 20, "px;"), "\n width: ").concat(buttonSize, "px;\n height: ").concat(buttonSize, "px;\n z-index: 10001;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n \n @media (max-width: 768px) {\n #text-chat-button-container {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-button-container {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n /* Prompt Bubble Styles */\n .prompt-bubble {\n position: absolute;\n z-index: 10002;\n pointer-events: none;\n white-space: nowrap;\n }\n \n .prompt-bubble.top {\n bottom: calc(100% + 18px);\n left: 50%;\n transform: translateX(-50%);\n }\n \n .prompt-bubble.left {\n right: calc(100% + 12px);\n top: 50%;\n transform: translateY(-50%);\n }\n \n .prompt-bubble.right {\n left: calc(100% + 12px);\n top: 50%;\n transform: translateY(-50%);\n }\n \n /* Ensure animations preserve horizontal centering for top position */\n .prompt-bubble.top.animation-bounce {\n animation: widget-bounce 1s ease-in-out infinite;\n transform: translateX(-50%); /* Keep centering */\n }\n \n .prompt-bubble.top.animation-float {\n animation: widget-float 2s ease-in-out infinite;\n transform: translateX(-50%); /* Keep centering */\n }\n \n .prompt-bubble.top.animation-pulse {\n animation: pulse 2s ease-in-out infinite;\n transform: translateX(-50%); /* Keep centering */\n }\n \n .prompt-bubble-content {\n position: relative;\n padding: 8px 16px;\n border-radius: 20px;\n font-weight: 500;\n font-size: 14px;\n box-shadow: 0 4px 15px rgba(124, 58, 237, 0.3);\n overflow: hidden;\n }\n \n .prompt-bubble-shimmer {\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent);\n animation: widget-shimmer 2s infinite;\n }\n \n .prompt-bubble.animation-bounce {\n animation: widget-bounce 1s ease-in-out infinite;\n }\n \n .prompt-bubble.animation-pulse {\n animation: pulse 2s ease-in-out infinite;\n }\n \n .prompt-bubble.animation-float {\n animation: widget-float 2s ease-in-out infinite;\n }\n \n /* Ensure top-positioned bubbles maintain horizontal centering during animations */\n .prompt-bubble.top.animation-bounce,\n .prompt-bubble.top.animation-float,\n .prompt-bubble.top.animation-pulse {\n /* Animation keyframes already include translateX(-50%) for centering */\n }\n \n /* Prompt Bubble Arrow */\n .prompt-bubble-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border: 8px solid transparent;\n }\n \n .prompt-bubble.top .prompt-bubble-arrow {\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-top-color: var(--prompt-bubble-bg-color, #7c3aed);\n border-bottom: none;\n margin-left: 0;\n }\n \n .prompt-bubble.left .prompt-bubble-arrow {\n left: 100%;\n top: 50%;\n transform: translateY(-50%);\n border-left-color: var(--prompt-bubble-bg-color, #7c3aed);\n border-right: none;\n }\n \n .prompt-bubble.right .prompt-bubble-arrow {\n right: 100%;\n top: 50%;\n transform: translateY(-50%);\n border-right-color: var(--prompt-bubble-bg-color, #7c3aed);\n border-left: none;\n }\n \n /* Pulse Rings */\n .prompt-pulse-rings {\n position: absolute;\n inset: 0;\n border-radius: 50%;\n pointer-events: none;\n }\n \n .prompt-pulse-ring {\n position: absolute;\n inset: 0;\n border-radius: 50%;\n animation: widget-pulse-ring 2s ease-out infinite;\n }\n \n .prompt-pulse-ring:nth-child(2) {\n animation-delay: 0.5s;\n }\n \n /* Mobile adjustments for prompt bubble */\n @media (max-width: 768px) {\n .prompt-bubble-content {\n font-size: 12px;\n padding: 6px 12px;\n }\n \n .prompt-bubble.top {\n bottom: calc(100% + 14px);\n }\n \n .prompt-bubble.left,\n .prompt-bubble.right {\n display: none; /* Hide side prompts on mobile */\n }\n }\n \n #text-chat-panel {\n display: none").concat(important, ";\n position: fixed;\n bottom: calc(").concat(buttonSize, "px + 20px + 20px); /* Button + gap + reduced footer offset */\n ").concat(this.config.position.horizontal === 'right' ? 'right: 20px;' : 'left: 20px;', "\n width: ").concat(panel.width, "px;\n max-width: calc(100vw - 40px);\n height: ").concat(panel.height, "px;\n max-height: calc(100vh - ").concat(buttonSize, "px - 40px - 20px); /* Account for footer height */\n background: transparent;\n border-radius: ").concat(panel.borderRadius, "px;\n border: none;\n flex-direction: column;\n overflow: hidden;\n ").concat(panel.backdropFilter ? "backdrop-filter: ".concat(panel.backdropFilter, ";") : '', "\n ").concat(anim.enableSlide ? "transition: all ".concat(anim.duration, "s ease;") : '', "\n box-sizing: border-box;\n }\n \n #text-chat-panel.open {\n display: flex").concat(important, ";\n ").concat(anim.enableSlide ? 'transform: translateY(0); opacity: 1;' : '', "\n }\n\n /* Shell for gradient border/background */\n .widget-shell { width: 100%; height: 100%; padding: 0; border-radius: ").concat(panel.borderRadius, "px; background: transparent; box-shadow: 0 20px 60px rgba(0,0,0,0.15); overflow: hidden; display: flex; flex-direction: column; box-sizing: border-box; }\n .panel-inner { width: 100%; height: 100%; background: #ffffff; border-radius: ").concat(panel.borderRadius, "px; border: ").concat(panel.border, "; overflow: hidden; display:flex; flex-direction: column; padding: 0; box-sizing: border-box; max-width: 100%; }\n\n /* New structure styles matching provided design */\n #text-chat-panel .widget-container {\n width: 100%; height: 100%; background: #FFFFFF; overflow: visible; display: flex; flex-direction: column; border-radius: ").concat(panel.borderRadius, "px;\n container-type: size;\n }\n \n /* Ensure content areas can scroll when height is constrained */\n #text-chat-panel .widget-container > .landing-screen,\n #text-chat-panel .widget-container > .voice-interface,\n #text-chat-panel .widget-container > .text-interface {\n flex: 1;\n overflow-y: auto;\n overflow-x: visible; /* Change from hidden to visible */\n }\n \n /* Header should not scroll */\n #text-chat-panel .widget-header {\n padding: 14px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-top-left-radius: ").concat(panel.borderRadius, "px;\n border-top-right-radius: ").concat(panel.borderRadius, "px;\n flex-shrink: 0;\n box-sizing: border-box;\n position: relative;\n overflow: hidden;\n background: ").concat(header.backgroundColor || '#7C3AED').concat(important, ";\n color: ").concat(header.textColor || '#FFFFFF').concat(important, ";\n }\n \n #text-chat-panel .widget-header::before {\n content: '';\n position: absolute;\n top: -50%;\n right: -20%;\n width: 180px;\n height: 180px;\n background: radial-gradient(circle, rgba(255,255,255,0.08) 0%, transparent 70%);\n pointer-events: none;\n }\n \n #text-chat-panel .widget-header > div:first-child {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n position: relative;\n z-index: 1;\n }\n \n #text-chat-panel .header-title { font-size: 15px; font-weight: 600; margin: 0; }\n #text-chat-panel .header-status { display: flex; align-items: center; gap: 5px; font-size: 12px; margin: 0; }\n #text-chat-panel .status-dot { width: 6px; height: 6px; border-radius: 50%; animation: pulse 2s ease-in-out infinite; }\n /* Online indicator customization - inline styles take precedence */\n @keyframes pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.15); opacity: 0.8; } }\n /* Header icon buttons */\n .header-icon {\n background: rgba(255, 255, 255, 0.1);\n border: none;\n color: white;\n width: 34px;\n height: 34px;\n min-width: 34px;\n min-height: 34px;\n border-radius: 10px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n flex-shrink: 0;\n font-size: 16px;\n padding: 0;\n box-sizing: border-box;\n position: relative;\n z-index: 1;\n }\n \n .header-icon:hover {\n background: rgba(255, 255, 255, 0.2);\n }\n \n .header-icon svg {\n pointer-events: none;\n stroke: white;\n fill: none;\n }\n \n .back-btn.visible {\n display: flex !important;\n }\n\n ").concat(showLanding && this.landingScreen ? this.landingScreen.generateCSS() : '', "\n\n ").concat(showVoice ? this.voiceInterface.generateCSS() : '', "\n ").concat(showText ? this.textInterface.generateCSS() : '', "\n \n /* Footer Branding */\n .widget-footer {\n box-sizing: border-box;\n }\n \n .footer-brand-link {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n \n .footer-brand-link:hover {\n opacity: 0.9;\n }\n \n @media (max-width: 768px) {\n .widget-footer {\n height: 32px;\n }\n .widget-footer span {\n font-size: 10px;\n }\n .widget-footer span:last-child {\n font-size: 8px;\n }\n }\n \n #text-chat-send-hint {\n text-align: center;\n line-height: 1.4;\n }\n \n .agent-thinking {\n font-style: italic;\n color: #6B7280;\n }\n ");
|
|
22941
|
+
return "\n /* MOBILE FIRST - Default styles for all devices */\n #text-chat-widget {\n position: fixed !important;\n z-index: 10000;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n /* Mobile defaults */\n right: 10px;\n bottom: 10px;\n left: auto;\n top: auto;\n }\n \n /* Desktop positioning (only on larger screens) */\n @media (min-width: 769px) {\n #text-chat-widget {\n ".concat(positionStyles, "\n right: ").concat(this.config.position.horizontal === 'right' ? '20px' : 'auto', ";\n left: ").concat(this.config.position.horizontal === 'left' ? '20px' : 'auto', ";\n bottom: ").concat(this.config.position.vertical === 'bottom' ? '20px' : 'auto', ";\n top: ").concat(this.config.position.vertical === 'top' ? '20px' : 'auto', ";\n }\n }\n \n /* Mobile override (force mobile positioning) */\n @media (max-width: 768px) {\n #text-chat-widget {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n transform: none !important;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-widget {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n #text-chat-button {\n position: relative;\n width: ").concat(buttonSize, "px;\n height: ").concat(buttonSize, "px;\n margin: 0;\n flex-shrink: 0;\n border-radius: ").concat(btn.shape === 'circle' ? '50%' : btn.shape === 'square' ? '0' : '12px', ";\n background: ").concat(btn.backgroundColor || icon.backgroundColor || '#7C3AED', ";\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all ").concat(anim.duration, "s ease;\n box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n -webkit-touch-callout: none;\n user-select: none;\n min-width: 44px;\n min-height: 44px;\n z-index: 1;\n }\n \n @media (max-width: 768px) {\n #text-chat-widget {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n transform: none !important;\n }\n \n #text-chat-button-container {\n width: 56px !important;\n height: 56px !important;\n }\n \n #text-chat-button {\n width: 56px !important;\n height: 56px !important;\n min-width: 56px !important;\n min-height: 56px !important;\n max-width: 56px !important;\n max-height: 56px !important;\n }\n \n #text-chat-panel {\n position: fixed !important;\n left: 10px !important;\n right: 10px !important;\n bottom: 92px !important; /* 56px button + 20px gap + 16px footer */\n top: 60px !important; /* Add top spacing */\n width: auto !important;\n max-width: none !important;\n height: auto !important; /* Change from max-height to auto */\n max-height: none !important; /* Remove max-height */\n transform: none !important;\n margin: 0 !important;\n }\n \n #text-chat-panel .widget-header {\n padding: 10px 14px;\n min-height: 56px;\n }\n \n #text-chat-panel .header-title {\n font-size: 15px;\n }\n \n #text-chat-panel .header-icon {\n width: 40px;\n height: 40px;\n min-width: 40px;\n min-height: 40px;\n }\n \n #text-chat-input {\n font-size: 16px !important; /* Prevents iOS zoom on focus */\n padding: 12px 16px !important;\n min-height: 48px !important;\n }\n \n #text-chat-send {\n min-width: 48px !important;\n min-height: 48px !important;\n width: 48px !important;\n height: 48px !important;\n }\n \n .landing-screen {\n padding: 16px;\n }\n \n .landing-logo {\n font-size: 40px;\n }\n \n .landing-title {\n font-size: 18px;\n margin-bottom: 16px;\n }\n \n .mode-selection {\n flex-direction: column;\n gap: 12px;\n align-items: center;\n }\n \n .mode-card {\n max-width: 100%;\n width: 100%;\n padding: 16px;\n }\n \n .mode-card-icon {\n width: 50px;\n height: 50px;\n font-size: 28px;\n }\n \n .mode-card-title {\n font-size: 14px;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-widget {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n \n #text-chat-button-container {\n width: 54px !important;\n height: 54px !important;\n }\n \n #text-chat-button {\n width: 54px !important;\n height: 54px !important;\n min-width: 54px !important;\n min-height: 54px !important;\n }\n \n #text-chat-panel {\n left: 8px !important;\n right: 8px !important;\n bottom: 86px !important; /* 54px button + 20px gap + 12px footer */\n top: 50px !important; /* Add top spacing for very small screens */\n height: auto !important;\n max-height: none !important;\n }\n \n #text-chat-panel .widget-header {\n padding: 8px 12px;\n min-height: 52px;\n }\n \n #text-chat-panel .header-title {\n font-size: 14px;\n }\n \n .landing-logo {\n font-size: 36px;\n }\n \n .landing-title {\n font-size: 16px;\n }\n }\n \n ").concat(anim.enableHover ? "\n #text-chat-button:hover {\n ".concat(btn.hoverColor ? "background: ".concat(btn.hoverColor, ";") : '', "\n transform: scale(1.05);\n box-shadow: 0 8px 20px rgba(102, 126, 234, 0.5);\n }\n ") : '', "\n \n /* Prompt Animation Keyframes */\n @keyframes widget-shimmer {\n 0% { transform: translateX(-100%); }\n 100% { transform: translateX(200%); }\n }\n \n @keyframes widget-ripple {\n 0% { transform: scale(1); opacity: 0.6; }\n 100% { transform: scale(2.5); opacity: 0; }\n }\n \n @keyframes widget-float {\n 0%, 100% { transform: translateX(-50%) translateY(0); }\n 50% { transform: translateX(-50%) translateY(-8px); }\n }\n \n @keyframes widget-bounce {\n 0%, 100% { transform: translateX(-50%) translateY(0); }\n 50% { transform: translateX(-50%) translateY(-10px); }\n }\n \n @keyframes widget-pulse-ring {\n 0% { transform: scale(1); opacity: 0.4; }\n 100% { transform: scale(1.8); opacity: 0; }\n }\n \n /* Prompt Bubble Container */\n #text-chat-button-container {\n position: fixed;\n ").concat(this.config.position.vertical === 'bottom' ? "bottom: ".concat(((_this$config$position = this.config.position.offset) === null || _this$config$position === void 0 ? void 0 : _this$config$position.y) || 20, "px;") : "top: ".concat(((_this$config$position2 = this.config.position.offset) === null || _this$config$position2 === void 0 ? void 0 : _this$config$position2.y) || 20, "px;"), "\n ").concat(this.config.position.horizontal === 'right' ? "right: ".concat(((_this$config$position3 = this.config.position.offset) === null || _this$config$position3 === void 0 ? void 0 : _this$config$position3.x) || 20, "px;") : "left: ".concat(((_this$config$position4 = this.config.position.offset) === null || _this$config$position4 === void 0 ? void 0 : _this$config$position4.x) || 20, "px;"), "\n width: ").concat(buttonSize, "px;\n height: ").concat(buttonSize, "px;\n z-index: 10001;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n \n @media (max-width: 768px) {\n #text-chat-button-container {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-button-container {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n /* Prompt Bubble Styles */\n .prompt-bubble {\n position: absolute;\n z-index: 10002;\n pointer-events: none;\n white-space: nowrap;\n }\n \n .prompt-bubble.top {\n bottom: calc(100% + 18px);\n left: 50%;\n transform: translateX(-50%);\n }\n \n .prompt-bubble.left {\n right: calc(100% + 12px);\n top: 50%;\n transform: translateY(-50%);\n }\n \n .prompt-bubble.right {\n left: calc(100% + 12px);\n top: 50%;\n transform: translateY(-50%);\n }\n \n /* Ensure animations preserve horizontal centering for top position */\n .prompt-bubble.top.animation-bounce {\n animation: widget-bounce 1s ease-in-out infinite;\n transform: translateX(-50%); /* Keep centering */\n }\n \n .prompt-bubble.top.animation-float {\n animation: widget-float 2s ease-in-out infinite;\n transform: translateX(-50%); /* Keep centering */\n }\n \n .prompt-bubble.top.animation-pulse {\n animation: pulse 2s ease-in-out infinite;\n transform: translateX(-50%); /* Keep centering */\n }\n \n .prompt-bubble-content {\n position: relative;\n padding: 8px 16px;\n border-radius: 20px;\n font-weight: 500;\n font-size: 14px;\n box-shadow: 0 4px 15px rgba(124, 58, 237, 0.3);\n overflow: hidden;\n }\n \n .prompt-bubble-shimmer {\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent);\n animation: widget-shimmer 2s infinite;\n }\n \n .prompt-bubble.animation-bounce {\n animation: widget-bounce 1s ease-in-out infinite;\n }\n \n .prompt-bubble.animation-pulse {\n animation: pulse 2s ease-in-out infinite;\n }\n \n .prompt-bubble.animation-float {\n animation: widget-float 2s ease-in-out infinite;\n }\n \n /* Ensure top-positioned bubbles maintain horizontal centering during animations */\n .prompt-bubble.top.animation-bounce,\n .prompt-bubble.top.animation-float,\n .prompt-bubble.top.animation-pulse {\n /* Animation keyframes already include translateX(-50%) for centering */\n }\n \n /* Prompt Bubble Arrow */\n .prompt-bubble-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border: 8px solid transparent;\n }\n \n .prompt-bubble.top .prompt-bubble-arrow {\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-top-color: var(--prompt-bubble-bg-color, #7c3aed);\n border-bottom: none;\n margin-left: 0;\n }\n \n .prompt-bubble.left .prompt-bubble-arrow {\n left: 100%;\n top: 50%;\n transform: translateY(-50%);\n border-left-color: var(--prompt-bubble-bg-color, #7c3aed);\n border-right: none;\n }\n \n .prompt-bubble.right .prompt-bubble-arrow {\n right: 100%;\n top: 50%;\n transform: translateY(-50%);\n border-right-color: var(--prompt-bubble-bg-color, #7c3aed);\n border-left: none;\n }\n \n /* Pulse Rings */\n .prompt-pulse-rings {\n position: absolute;\n inset: 0;\n border-radius: 50%;\n pointer-events: none;\n }\n \n .prompt-pulse-ring {\n position: absolute;\n inset: 0;\n border-radius: 50%;\n animation: widget-pulse-ring 2s ease-out infinite;\n }\n \n .prompt-pulse-ring:nth-child(2) {\n animation-delay: 0.5s;\n }\n \n /* Mobile adjustments for prompt bubble */\n @media (max-width: 768px) {\n .prompt-bubble-content {\n font-size: 12px;\n padding: 6px 12px;\n }\n \n .prompt-bubble.top {\n bottom: calc(100% + 14px);\n }\n \n .prompt-bubble.left,\n .prompt-bubble.right {\n display: none; /* Hide side prompts on mobile */\n }\n }\n \n #text-chat-panel {\n display: none").concat(important, ";\n position: fixed;\n bottom: calc(").concat(buttonSize, "px + 20px + 20px); /* Button + gap + reduced footer offset */\n ").concat(this.config.position.horizontal === 'right' ? 'right: 20px;' : 'left: 20px;', "\n width: ").concat(panel.width, "px;\n max-width: calc(100vw - 40px);\n height: ").concat(panel.height, "px;\n max-height: calc(100vh - ").concat(buttonSize, "px - 40px - 20px); /* Account for footer height */\n background: transparent;\n border-radius: ").concat(panel.borderRadius, "px;\n border: none;\n flex-direction: column;\n overflow: hidden;\n ").concat(panel.backdropFilter ? "backdrop-filter: ".concat(panel.backdropFilter, ";") : '', "\n ").concat(anim.enableSlide ? "transition: all ".concat(anim.duration, "s ease;") : '', "\n box-sizing: border-box;\n }\n \n #text-chat-panel.open {\n display: flex").concat(important, ";\n ").concat(anim.enableSlide ? 'transform: translateY(0); opacity: 1;' : '', "\n }\n\n /* Shell for gradient border/background */\n .widget-shell { width: 100%; height: 100%; padding: 0; border-radius: ").concat(panel.borderRadius, "px; background: transparent; box-shadow: 0 20px 60px rgba(0,0,0,0.15); overflow: hidden; display: flex; flex-direction: column; box-sizing: border-box; }\n .panel-inner { width: 100%; height: 100%; background: #ffffff; border-radius: ").concat(panel.borderRadius, "px; border: ").concat(panel.border, "; overflow: hidden; display:flex; flex-direction: column; padding: 0; box-sizing: border-box; max-width: 100%; }\n\n /* New structure styles matching provided design */\n #text-chat-panel .widget-container {\n width: 100%; height: 100%; background: #FFFFFF; overflow: visible; display: flex; flex-direction: column; border-radius: ").concat(panel.borderRadius, "px;\n container-type: size;\n }\n \n /* Ensure content areas can scroll when height is constrained */\n #text-chat-panel .widget-container > .landing-screen,\n #text-chat-panel .widget-container > .voice-interface,\n #text-chat-panel .widget-container > .text-interface {\n flex: 1;\n overflow-y: auto;\n overflow-x: visible; /* Change from hidden to visible */\n }\n \n /* Header should not scroll */\n #text-chat-panel .widget-header {\n padding: 14px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-top-left-radius: ").concat(panel.borderRadius, "px;\n border-top-right-radius: ").concat(panel.borderRadius, "px;\n flex-shrink: 0;\n box-sizing: border-box;\n position: relative;\n overflow: hidden;\n background: ").concat(header.backgroundColor || '#7C3AED').concat(important, ";\n color: ").concat(header.textColor || '#FFFFFF').concat(important, ";\n }\n \n #text-chat-panel .widget-header::before {\n content: '';\n position: absolute;\n top: -50%;\n right: -20%;\n width: 180px;\n height: 180px;\n background: radial-gradient(circle, rgba(255,255,255,0.08) 0%, transparent 70%);\n pointer-events: none;\n }\n \n #text-chat-panel .widget-header > div:first-child {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n position: relative;\n z-index: 1;\n }\n \n #text-chat-panel .header-title { font-size: 15px; font-weight: 600; margin: 0; }\n #text-chat-panel .header-status { display: flex; align-items: center; gap: 5px; font-size: 12px; margin: 0; }\n #text-chat-panel .status-dot { width: 6px; height: 6px; border-radius: 50%; animation: pulse 2s ease-in-out infinite; }\n /* Online indicator customization - inline styles take precedence */\n @keyframes pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.15); opacity: 0.8; } }\n /* Header icon buttons */\n .header-icon {\n background: rgba(255, 255, 255, 0.1);\n border: none;\n color: white;\n width: 34px;\n height: 34px;\n min-width: 34px;\n min-height: 34px;\n border-radius: 10px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n flex-shrink: 0;\n font-size: 16px;\n padding: 0;\n box-sizing: border-box;\n position: relative;\n z-index: 1;\n }\n \n .header-icon:hover {\n background: rgba(255, 255, 255, 0.2);\n }\n \n .header-icon svg {\n pointer-events: none;\n stroke: white;\n fill: none;\n }\n \n .back-btn.visible {\n display: flex !important;\n }\n\n ").concat(showLanding && this.landingScreen ? this.landingScreen.generateCSS() : '', "\n\n ").concat(showVoice ? this.voiceInterface.generateCSS() : '', "\n ").concat(showText ? this.textInterface.generateCSS() : '', "\n \n /* Footer Branding */\n .widget-footer {\n box-sizing: border-box;\n }\n \n .footer-brand-link {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n \n .footer-brand-link:hover {\n opacity: 0.9;\n }\n \n @media (max-width: 768px) {\n .widget-footer {\n height: 32px;\n }\n .widget-footer span {\n font-size: 10px;\n }\n .widget-footer span:last-child {\n font-size: 8px;\n }\n }\n \n #text-chat-send-hint {\n text-align: center;\n line-height: 1.4;\n }\n \n .agent-thinking {\n font-style: italic;\n color: #6B7280;\n }\n\n /* ========================================\n MOBILE PILL BUTTON & LANDING OVERLAY\n Hidden on desktop, shown on mobile only\n ======================================== */\n .ttp-mobile-fab {\n display: none;\n }\n\n .ttp-mobile-landing {\n display: none;\n }\n\n @keyframes ttpWaveAnimation {\n 0%, 100% { transform: scaleY(0.4); }\n 50% { transform: scaleY(1); }\n }\n\n @keyframes ttpPulseAnimation {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n @media (max-width: 768px) {\n /* Hide desktop circle button on mobile */\n #text-chat-button {\n display: none !important;\n }\n\n /* Re-flow the container for pill shape */\n #text-chat-button-container {\n width: auto !important;\n height: auto !important;\n min-width: 0 !important;\n min-height: 0 !important;\n max-width: none !important;\n max-height: none !important;\n }\n\n /* Show mobile pill button */\n .ttp-mobile-fab {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 16px 10px 12px;\n background: linear-gradient(135deg, #581c87, ").concat(header.backgroundColor || '#7C3AED', ");\n border: none;\n border-radius: 24px;\n cursor: pointer;\n box-shadow: 0 8px 24px rgba(88, 28, 135, 0.4);\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n z-index: 1;\n font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n }\n\n .ttp-mobile-fab:hover {\n transform: translateY(-3px);\n box-shadow: 0 12px 32px rgba(88, 28, 135, 0.5);\n }\n\n .ttp-mobile-fab__icon-wrap {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.15);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n\n .ttp-mobile-fab__waveform {\n display: flex;\n align-items: center;\n gap: 2px;\n height: 16px;\n }\n\n .ttp-mobile-fab__waveform .bar {\n width: 2px;\n background: #fff;\n border-radius: 1px;\n animation: ttpWaveAnimation 0.8s ease-in-out infinite;\n }\n\n .ttp-mobile-fab__waveform .bar:nth-child(1) { height: 5px; animation-delay: 0s; }\n .ttp-mobile-fab__waveform .bar:nth-child(2) { height: 10px; animation-delay: 0.1s; }\n .ttp-mobile-fab__waveform .bar:nth-child(3) { height: 16px; animation-delay: 0.2s; }\n .ttp-mobile-fab__waveform .bar:nth-child(4) { height: 8px; animation-delay: 0.3s; }\n .ttp-mobile-fab__waveform .bar:nth-child(5) { height: 12px; animation-delay: 0.4s; }\n\n .ttp-mobile-fab__label-wrap {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 1px;\n }\n\n .ttp-mobile-fab__label {\n color: #fff;\n font-size: 14px;\n font-weight: 600;\n line-height: 1.2;\n }\n\n .ttp-mobile-fab__status {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: rgba(255, 255, 255, 0.7);\n line-height: 1.2;\n }\n\n .ttp-mobile-fab__dot {\n width: 6px;\n height: 6px;\n background: ").concat(header.onlineIndicatorDotColor || '#10b981', ";\n border-radius: 50%;\n box-shadow: 0 0 6px rgba(16, 185, 129, 0.6);\n }\n\n /* ---- Mobile Landing Overlay ---- */\n .ttp-mobile-landing.active {\n display: block;\n position: fixed;\n bottom: 16px;\n left: 12px;\n right: 12px;\n background: linear-gradient(135deg, #581c87 0%, ").concat(header.backgroundColor || '#7C3AED', " 100%);\n border-radius: 20px;\n overflow: hidden;\n box-shadow:\n 0 10px 40px rgba(88, 28, 135, 0.4),\n 0 0 0 1px rgba(255, 255, 255, 0.1) inset;\n z-index: 10002;\n font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;\n }\n\n .ttp-mobile-landing::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: linear-gradient(90deg,\n transparent 0%,\n rgba(255, 255, 255, 0.3) 50%,\n transparent 100%);\n }\n\n .ttp-mobile-landing__header {\n padding: 14px 16px;\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .ttp-mobile-landing__avatar {\n width: 44px;\n height: 44px;\n border-radius: 12px;\n background: rgba(255, 255, 255, 0.15);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n\n .ttp-mobile-landing__waveform {\n display: flex;\n align-items: center;\n gap: 3px;\n height: 20px;\n }\n\n .ttp-mobile-landing__waveform .bar {\n width: 3px;\n background: rgba(255, 255, 255, 0.9);\n border-radius: 2px;\n animation: ttpWaveAnimation 0.8s ease-in-out infinite;\n }\n\n .ttp-mobile-landing__waveform .bar:nth-child(1) { height: 6px; animation-delay: 0s; }\n .ttp-mobile-landing__waveform .bar:nth-child(2) { height: 12px; animation-delay: 0.1s; }\n .ttp-mobile-landing__waveform .bar:nth-child(3) { height: 20px; animation-delay: 0.2s; }\n .ttp-mobile-landing__waveform .bar:nth-child(4) { height: 10px; animation-delay: 0.3s; }\n .ttp-mobile-landing__waveform .bar:nth-child(5) { height: 16px; animation-delay: 0.4s; }\n\n .ttp-mobile-landing__info {\n flex: 1;\n min-width: 0;\n }\n\n .ttp-mobile-landing__name {\n color: #fff;\n font-size: 15px;\n font-weight: 600;\n margin-bottom: 2px;\n }\n\n .ttp-mobile-landing__status {\n color: rgba(255, 255, 255, 0.7);\n font-size: 12px;\n display: flex;\n align-items: center;\n gap: 5px;\n }\n\n .ttp-mobile-landing__dot {\n width: 6px;\n height: 6px;\n background: ").concat(header.onlineIndicatorDotColor || '#10b981', ";\n border-radius: 50%;\n animation: ttpPulseAnimation 2s ease-in-out infinite;\n }\n\n .ttp-mobile-landing__close {\n width: 32px;\n height: 32px;\n border-radius: 10px;\n background: rgba(255, 255, 255, 0.1);\n border: none;\n color: rgba(255, 255, 255, 0.7);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s;\n flex-shrink: 0;\n }\n\n .ttp-mobile-landing__close:hover {\n background: rgba(255, 255, 255, 0.2);\n color: #fff;\n }\n\n .ttp-mobile-landing__error {\n margin: 0 14px 10px;\n padding: 10px 12px;\n background: #fef2f2;\n border: 1px solid #fecaca;\n border-radius: 10px;\n display: flex;\n align-items: flex-start;\n gap: 8px;\n color: #b91c1c;\n font-size: 13px;\n line-height: 1.4;\n }\n .ttp-mobile-landing__error svg {\n flex-shrink: 0;\n margin-top: 1px;\n stroke: #dc2626;\n }\n\n .ttp-mobile-landing__actions {\n padding: 0 14px 14px;\n display: flex;\n gap: 10px;\n }\n\n .ttp-mobile-landing__btn {\n flex: 1;\n padding: 14px;\n border-radius: 12px;\n border: none;\n cursor: pointer;\n font-family: inherit;\n font-size: 14px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n }\n\n .ttp-mobile-landing__btn svg {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n }\n\n .ttp-mobile-landing__btn--call {\n background: #fff;\n color: ").concat(header.backgroundColor || '#7C3AED', ";\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n }\n\n .ttp-mobile-landing__btn--call:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);\n }\n\n .ttp-mobile-landing__btn--text {\n background: rgba(255, 255, 255, 0.15);\n color: #fff;\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .ttp-mobile-landing__btn--text:hover {\n background: rgba(255, 255, 255, 0.2);\n border-color: rgba(255, 255, 255, 0.2);\n }\n\n .ttp-mobile-landing__footer {\n padding: 10px 16px;\n display: flex;\n justify-content: center;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n }\n\n .ttp-mobile-landing__powered {\n color: rgba(255, 255, 255, 0.4);\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .ttp-mobile-landing__powered svg {\n width: 10px;\n height: 10px;\n color: #f59e0b;\n }\n\n .ttp-mobile-landing__powered a {\n color: rgba(255, 255, 255, 0.6);\n text-decoration: none;\n }\n\n .ttp-mobile-landing__powered a:hover {\n color: #fff;\n }\n }\n ");
|
|
21980
22942
|
}
|
|
21981
22943
|
}, {
|
|
21982
22944
|
key: "setupWidgetEvents",
|
|
@@ -22037,8 +22999,18 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22037
22999
|
if ((_this6$voiceInterface3 = _this6.voiceInterface) !== null && _this6$voiceInterface3 !== void 0 && _this6$voiceInterface3.isActive) {
|
|
22038
23000
|
// If call is active, show message inside widget instead of modal
|
|
22039
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();
|
|
22040
23012
|
} else {
|
|
22041
|
-
//
|
|
23013
|
+
// Desktop: show landing inside panel
|
|
22042
23014
|
_this6.showLanding();
|
|
22043
23015
|
}
|
|
22044
23016
|
};
|
|
@@ -22047,7 +23019,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22047
23019
|
this.landingScreen.setupEventHandlers({
|
|
22048
23020
|
onSelectVoice: function () {
|
|
22049
23021
|
var _onSelectVoice = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
22050
|
-
var _this6$voiceInterface4, _this6$voiceInterface8, _this6$shadowRoot, _this6$voiceInterface5, _this6$shadowRoot2, _this6$voiceInterface6, panel, idleState, floatingButton, _this6$voiceInterface7, _this6$voiceInterface9, _this6$voiceInterface1, _this6$config$behavio, _idleState, isDomainError, statusTitle, _this6$voiceInterface0, titleText, domainErrorTitle, _widgetMode, _this6$voiceInterface11, _this6$voiceInterface12, _this6$config$behavio2, _this6$voiceInterface10, _this6$voiceInterface13, _this6$
|
|
23022
|
+
var _this6$voiceInterface4, _this6$voiceInterface8, _this6$shadowRoot, _this6$voiceInterface5, _this6$shadowRoot2, _this6$shadowRoot3, _this6$voiceInterface6, panel, idleState, floatingButton, mobileFab, _this6$voiceInterface7, _this6$voiceInterface9, _this6$voiceInterface1, _this6$config$behavio, _idleState, isDomainError, statusTitle, _this6$voiceInterface0, titleText, domainErrorTitle, _widgetMode, _this6$voiceInterface11, _this6$voiceInterface12, _this6$config$behavio2, _this6$voiceInterface10, _this6$voiceInterface13, _this6$shadowRoot4, _this6$shadowRoot5, existingBar, _floatingButton, _mobileFab, _widgetMode2, _t;
|
|
22051
23023
|
return _regenerator().w(function (_context) {
|
|
22052
23024
|
while (1) switch (_context.p = _context.n) {
|
|
22053
23025
|
case 0:
|
|
@@ -22097,6 +23069,10 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22097
23069
|
if (floatingButton) {
|
|
22098
23070
|
floatingButton.style.display = 'none';
|
|
22099
23071
|
}
|
|
23072
|
+
mobileFab = (_this6$shadowRoot3 = _this6.shadowRoot) === null || _this6$shadowRoot3 === void 0 ? void 0 : _this6$shadowRoot3.querySelector('.ttp-mobile-fab');
|
|
23073
|
+
if (mobileFab) {
|
|
23074
|
+
mobileFab.style.display = 'none';
|
|
23075
|
+
}
|
|
22100
23076
|
// Create minimized bar with connecting state
|
|
22101
23077
|
if (!((_this6$voiceInterface6 = _this6.voiceInterface) !== null && _this6$voiceInterface6 !== void 0 && _this6$voiceInterface6.createMobileMinimizedBar)) {
|
|
22102
23078
|
_context.n = 3;
|
|
@@ -22206,10 +23182,14 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22206
23182
|
_this6.voiceInterface.removeMobileMinimizedBar();
|
|
22207
23183
|
}
|
|
22208
23184
|
// Restore floating button if it was hidden
|
|
22209
|
-
_floatingButton = ((_this6$
|
|
23185
|
+
_floatingButton = ((_this6$shadowRoot4 = _this6.shadowRoot) === null || _this6$shadowRoot4 === void 0 ? void 0 : _this6$shadowRoot4.getElementById('text-chat-button')) || document.getElementById('text-chat-button');
|
|
22210
23186
|
if (_floatingButton) {
|
|
22211
23187
|
_floatingButton.style.display = '';
|
|
22212
23188
|
}
|
|
23189
|
+
_mobileFab = (_this6$shadowRoot5 = _this6.shadowRoot) === null || _this6$shadowRoot5 === void 0 ? void 0 : _this6$shadowRoot5.querySelector('.ttp-mobile-fab');
|
|
23190
|
+
if (_mobileFab) {
|
|
23191
|
+
_mobileFab.style.display = '';
|
|
23192
|
+
}
|
|
22213
23193
|
}
|
|
22214
23194
|
|
|
22215
23195
|
// Show user-friendly error message (unless it's a user cancellation)
|
|
@@ -22302,6 +23282,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22302
23282
|
}
|
|
22303
23283
|
}
|
|
22304
23284
|
|
|
23285
|
+
// Mobile floating button & landing overlay
|
|
23286
|
+
this.setupMobileWidgetEvents();
|
|
23287
|
+
|
|
22305
23288
|
// Keyboard navigation
|
|
22306
23289
|
if (this.config.accessibility.keyboardNavigation) {
|
|
22307
23290
|
this.setupKeyboardNavigation();
|
|
@@ -22385,13 +23368,84 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22385
23368
|
// Hide new chat button on voice interface (not applicable)
|
|
22386
23369
|
if (newChatBtn) newChatBtn.style.display = 'none';
|
|
22387
23370
|
}
|
|
23371
|
+
}, {
|
|
23372
|
+
key: "setupMobileWidgetEvents",
|
|
23373
|
+
value: function setupMobileWidgetEvents() {
|
|
23374
|
+
var _this7 = this;
|
|
23375
|
+
if (!this.shadowRoot) return;
|
|
23376
|
+
var mobileFab = this.shadowRoot.querySelector('.ttp-mobile-fab');
|
|
23377
|
+
var mobileLanding = this.shadowRoot.getElementById('ttpMobileLanding');
|
|
23378
|
+
if (!mobileFab || !mobileLanding) return;
|
|
23379
|
+
var closeMobileLanding = function closeMobileLanding() {
|
|
23380
|
+
mobileLanding.classList.remove('active');
|
|
23381
|
+
mobileFab.style.display = '';
|
|
23382
|
+
_this7.isMobileLandingOpen = false;
|
|
23383
|
+
};
|
|
23384
|
+
var openMobileLanding = function openMobileLanding() {
|
|
23385
|
+
if (_this7.config.promptAnimation.hideAfterClick) {
|
|
23386
|
+
_this7.hidePrompt();
|
|
23387
|
+
}
|
|
23388
|
+
// Clear any previous error banner
|
|
23389
|
+
var errorBanner = _this7.shadowRoot.getElementById('ttpMobileLandingError');
|
|
23390
|
+
if (errorBanner) errorBanner.style.display = 'none';
|
|
23391
|
+
mobileFab.style.display = 'none';
|
|
23392
|
+
mobileLanding.classList.add('active');
|
|
23393
|
+
_this7.isMobileLandingOpen = true;
|
|
23394
|
+
};
|
|
23395
|
+
mobileFab.onclick = openMobileLanding;
|
|
23396
|
+
var mobileClose = mobileLanding.querySelector('.ttp-mobile-landing__close');
|
|
23397
|
+
if (mobileClose) {
|
|
23398
|
+
mobileClose.onclick = closeMobileLanding;
|
|
23399
|
+
}
|
|
23400
|
+
var mobileCallBtn = this.shadowRoot.getElementById('ttpMobileCallBtn');
|
|
23401
|
+
if (mobileCallBtn) {
|
|
23402
|
+
mobileCallBtn.onclick = function () {
|
|
23403
|
+
closeMobileLanding();
|
|
23404
|
+
var desktopVoiceCard = _this7.shadowRoot.getElementById('mode-card-voice');
|
|
23405
|
+
if (desktopVoiceCard) {
|
|
23406
|
+
desktopVoiceCard.click();
|
|
23407
|
+
} else {
|
|
23408
|
+
_this7.showVoice();
|
|
23409
|
+
}
|
|
23410
|
+
};
|
|
23411
|
+
}
|
|
23412
|
+
var mobileChatBtn = this.shadowRoot.getElementById('ttpMobileChatBtn');
|
|
23413
|
+
if (mobileChatBtn) {
|
|
23414
|
+
mobileChatBtn.onclick = function () {
|
|
23415
|
+
closeMobileLanding();
|
|
23416
|
+
var panel = _this7.shadowRoot.getElementById('text-chat-panel');
|
|
23417
|
+
if (panel) {
|
|
23418
|
+
panel.classList.add('open');
|
|
23419
|
+
_this7.isOpen = true;
|
|
23420
|
+
}
|
|
23421
|
+
mobileFab.style.display = 'none';
|
|
23422
|
+
_this7.showText();
|
|
23423
|
+
};
|
|
23424
|
+
}
|
|
23425
|
+
|
|
23426
|
+
// Close mobile landing on outside click
|
|
23427
|
+
document.addEventListener('click', function (e) {
|
|
23428
|
+
if (!_this7.isMobileLandingOpen) return;
|
|
23429
|
+
var path = e.composedPath();
|
|
23430
|
+
if (!path.includes(mobileLanding) && !path.includes(mobileFab)) {
|
|
23431
|
+
closeMobileLanding();
|
|
23432
|
+
}
|
|
23433
|
+
});
|
|
23434
|
+
|
|
23435
|
+
// Close mobile landing on Escape
|
|
23436
|
+
document.addEventListener('keydown', function (e) {
|
|
23437
|
+
if (e.key === 'Escape' && _this7.isMobileLandingOpen) {
|
|
23438
|
+
closeMobileLanding();
|
|
23439
|
+
}
|
|
23440
|
+
});
|
|
23441
|
+
}
|
|
22388
23442
|
}, {
|
|
22389
23443
|
key: "setupKeyboardNavigation",
|
|
22390
23444
|
value: function setupKeyboardNavigation() {
|
|
22391
|
-
var
|
|
23445
|
+
var _this8 = this;
|
|
22392
23446
|
document.addEventListener('keydown', function (e) {
|
|
22393
|
-
if (e.key === 'Escape' &&
|
|
22394
|
-
|
|
23447
|
+
if (e.key === 'Escape' && _this8.isOpen) {
|
|
23448
|
+
_this8.togglePanel();
|
|
22395
23449
|
}
|
|
22396
23450
|
});
|
|
22397
23451
|
}
|
|
@@ -22402,7 +23456,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22402
23456
|
}, {
|
|
22403
23457
|
key: "setupPromptAnimation",
|
|
22404
23458
|
value: function setupPromptAnimation() {
|
|
22405
|
-
var
|
|
23459
|
+
var _this9 = this;
|
|
22406
23460
|
if (!this.shadowRoot) return;
|
|
22407
23461
|
var promptConfig = this.config.promptAnimation || {};
|
|
22408
23462
|
// Default to disabled if not specified (enabled === true means explicitly enabled)
|
|
@@ -22432,7 +23486,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22432
23486
|
// Auto-hide after configured seconds (only if widget is closed)
|
|
22433
23487
|
if (!this.isOpen && promptConfig.hideAfterSeconds !== null && promptConfig.hideAfterSeconds > 0) {
|
|
22434
23488
|
this.promptAutoHideTimer = setTimeout(function () {
|
|
22435
|
-
|
|
23489
|
+
_this9.hidePrompt();
|
|
22436
23490
|
}, promptConfig.hideAfterSeconds * 1000);
|
|
22437
23491
|
}
|
|
22438
23492
|
|
|
@@ -22444,7 +23498,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22444
23498
|
// Wrap the callback to also hide pulse rings
|
|
22445
23499
|
this.config.onConversationStart = function () {
|
|
22446
23500
|
// Hide pulse rings when call starts
|
|
22447
|
-
var pulseRings =
|
|
23501
|
+
var pulseRings = _this9.shadowRoot.getElementById('prompt-pulse-rings');
|
|
22448
23502
|
if (pulseRings) pulseRings.style.display = 'none';
|
|
22449
23503
|
|
|
22450
23504
|
// Call original callback if it exists
|
|
@@ -22456,7 +23510,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22456
23510
|
// Also listen to recordingStarted event from SDK if available
|
|
22457
23511
|
if (this.voiceInterface.sdk.voiceSDK) {
|
|
22458
23512
|
this.voiceInterface.sdk.voiceSDK.on('recordingStarted', function () {
|
|
22459
|
-
var pulseRings =
|
|
23513
|
+
var pulseRings = _this9.shadowRoot.getElementById('prompt-pulse-rings');
|
|
22460
23514
|
if (pulseRings) pulseRings.style.display = 'none';
|
|
22461
23515
|
});
|
|
22462
23516
|
}
|
|
@@ -22504,7 +23558,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22504
23558
|
}, {
|
|
22505
23559
|
key: "showPrompt",
|
|
22506
23560
|
value: function showPrompt() {
|
|
22507
|
-
var
|
|
23561
|
+
var _this0 = this;
|
|
22508
23562
|
if (!this.shadowRoot) return;
|
|
22509
23563
|
var promptConfig = this.config.promptAnimation || {};
|
|
22510
23564
|
// Default to enabled if not specified
|
|
@@ -22524,8 +23578,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22524
23578
|
// Function to actually show the elements
|
|
22525
23579
|
var doShow = function doShow() {
|
|
22526
23580
|
// Show prompt bubble (check both shadow root and document for compatibility)
|
|
22527
|
-
var promptBubble =
|
|
22528
|
-
if (!promptBubble &&
|
|
23581
|
+
var promptBubble = _this0.shadowRoot.getElementById('prompt-bubble');
|
|
23582
|
+
if (!promptBubble && _this0.shadowRoot !== document) {
|
|
22529
23583
|
promptBubble = document.getElementById('prompt-bubble');
|
|
22530
23584
|
}
|
|
22531
23585
|
if (promptBubble) {
|
|
@@ -22539,8 +23593,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22539
23593
|
|
|
22540
23594
|
// Show pulse rings if enabled
|
|
22541
23595
|
if (promptConfig.showPulseRings !== false) {
|
|
22542
|
-
var _pulseRings =
|
|
22543
|
-
if (!_pulseRings &&
|
|
23596
|
+
var _pulseRings = _this0.shadowRoot.getElementById('prompt-pulse-rings');
|
|
23597
|
+
if (!_pulseRings && _this0.shadowRoot !== document) {
|
|
22544
23598
|
_pulseRings = document.getElementById('prompt-pulse-rings');
|
|
22545
23599
|
}
|
|
22546
23600
|
if (_pulseRings) {
|
|
@@ -22577,7 +23631,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22577
23631
|
}
|
|
22578
23632
|
// Start new timer
|
|
22579
23633
|
this.promptAutoHideTimer = setTimeout(function () {
|
|
22580
|
-
|
|
23634
|
+
_this0.hidePrompt();
|
|
22581
23635
|
}, promptConfig.hideAfterSeconds * 1000);
|
|
22582
23636
|
}
|
|
22583
23637
|
}
|
|
@@ -22593,7 +23647,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22593
23647
|
}, {
|
|
22594
23648
|
key: "_doTogglePanel",
|
|
22595
23649
|
value: function _doTogglePanel() {
|
|
22596
|
-
var
|
|
23650
|
+
var _this1 = this;
|
|
22597
23651
|
if (!this.shadowRoot) return;
|
|
22598
23652
|
this.isOpen = !this.isOpen;
|
|
22599
23653
|
var panel = this.shadowRoot.getElementById('text-chat-panel');
|
|
@@ -22606,22 +23660,27 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22606
23660
|
// Panel is opening - hide prompt
|
|
22607
23661
|
this.hidePrompt();
|
|
22608
23662
|
setTimeout(function () {
|
|
22609
|
-
var input =
|
|
23663
|
+
var input = _this1.shadowRoot.getElementById('messageInput');
|
|
22610
23664
|
if (input) input.focus();
|
|
22611
23665
|
}, 100);
|
|
22612
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
|
+
}
|
|
22613
23672
|
// Panel is closing - show prompt (if enabled)
|
|
22614
23673
|
// Call showPrompt immediately, then also retry after a delay to ensure it's visible
|
|
22615
23674
|
this.showPrompt();
|
|
22616
23675
|
setTimeout(function () {
|
|
22617
|
-
var
|
|
23676
|
+
var _this1$config$promptA;
|
|
22618
23677
|
// Double-check and ensure prompt is visible
|
|
22619
|
-
var promptBubble =
|
|
23678
|
+
var promptBubble = _this1.shadowRoot.getElementById('prompt-bubble');
|
|
22620
23679
|
if (promptBubble && promptBubble.style.display === 'none') {
|
|
22621
23680
|
promptBubble.style.display = 'block';
|
|
22622
23681
|
}
|
|
22623
|
-
var pulseRings =
|
|
22624
|
-
if (pulseRings && ((
|
|
23682
|
+
var pulseRings = _this1.shadowRoot.getElementById('prompt-pulse-rings');
|
|
23683
|
+
if (pulseRings && ((_this1$config$promptA = _this1.config.promptAnimation) === null || _this1$config$promptA === void 0 ? void 0 : _this1$config$promptA.showPulseRings) !== false) {
|
|
22625
23684
|
if (pulseRings.style.display === 'none') {
|
|
22626
23685
|
pulseRings.style.display = 'block';
|
|
22627
23686
|
}
|
|
@@ -22661,13 +23720,14 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22661
23720
|
padding: '12px 24px',
|
|
22662
23721
|
borderRadius: '8px',
|
|
22663
23722
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
22664
|
-
zIndex: '
|
|
23723
|
+
zIndex: '10003',
|
|
22665
23724
|
fontSize: '14px',
|
|
22666
23725
|
fontWeight: '500',
|
|
22667
23726
|
maxWidth: '90%',
|
|
22668
23727
|
textAlign: 'center',
|
|
22669
23728
|
animation: 'slideUp 0.3s ease-out',
|
|
22670
|
-
fontFamily: 'inherit'
|
|
23729
|
+
fontFamily: 'inherit',
|
|
23730
|
+
pointerEvents: 'auto'
|
|
22671
23731
|
});
|
|
22672
23732
|
|
|
22673
23733
|
// Add CSS animation if not already added
|
|
@@ -22692,6 +23752,25 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
22692
23752
|
}, 300);
|
|
22693
23753
|
}, 4000);
|
|
22694
23754
|
}
|
|
23755
|
+
}, {
|
|
23756
|
+
key: "showMobileLandingError",
|
|
23757
|
+
value: function showMobileLandingError(message) {
|
|
23758
|
+
if (!this.shadowRoot) return;
|
|
23759
|
+
var mobileLanding = this.shadowRoot.getElementById('ttpMobileLanding');
|
|
23760
|
+
var mobileFab = this.shadowRoot.querySelector('.ttp-mobile-fab');
|
|
23761
|
+
var errorBanner = this.shadowRoot.getElementById('ttpMobileLandingError');
|
|
23762
|
+
if (!mobileLanding || !errorBanner) return;
|
|
23763
|
+
|
|
23764
|
+
// Set error text
|
|
23765
|
+
var errorText = errorBanner.querySelector('.ttp-mobile-landing__error-text');
|
|
23766
|
+
if (errorText) errorText.textContent = message;
|
|
23767
|
+
errorBanner.style.display = 'flex';
|
|
23768
|
+
|
|
23769
|
+
// Show the mobile landing overlay with the error
|
|
23770
|
+
mobileLanding.classList.add('active');
|
|
23771
|
+
if (mobileFab) mobileFab.style.display = 'none';
|
|
23772
|
+
this.isMobileLandingOpen = true;
|
|
23773
|
+
}
|
|
22695
23774
|
|
|
22696
23775
|
/**
|
|
22697
23776
|
* Show confirmation modal (reusing modal pattern)
|
|
@@ -23366,12 +24445,12 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
23366
24445
|
key: "_flushPendingClientTools",
|
|
23367
24446
|
value: function _flushPendingClientTools() {
|
|
23368
24447
|
var _this$voiceInterface1,
|
|
23369
|
-
|
|
24448
|
+
_this10 = this;
|
|
23370
24449
|
if (this._pendingClientTools && (_this$voiceInterface1 = this.voiceInterface) !== null && _this$voiceInterface1 !== void 0 && _this$voiceInterface1.sdk) {
|
|
23371
24450
|
this._pendingClientTools.forEach(function (_ref2) {
|
|
23372
24451
|
var name = _ref2.name,
|
|
23373
24452
|
handler = _ref2.handler;
|
|
23374
|
-
|
|
24453
|
+
_this10.voiceInterface.sdk.registerToolHandler(name, handler);
|
|
23375
24454
|
});
|
|
23376
24455
|
this._pendingClientTools = null;
|
|
23377
24456
|
console.log('TTPChatWidget: Flushed pending client tools');
|
|
@@ -24211,6 +25290,18 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
24211
25290
|
return _ref2.apply(this, arguments);
|
|
24212
25291
|
};
|
|
24213
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
|
+
};
|
|
24214
25305
|
}
|
|
24215
25306
|
|
|
24216
25307
|
/**
|
|
@@ -24463,7 +25554,7 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
24463
25554
|
value: (function () {
|
|
24464
25555
|
var _startVoiceCall = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
|
|
24465
25556
|
var _this2 = this;
|
|
24466
|
-
var permissionState, deviceInfo;
|
|
25557
|
+
var permissionState, deviceInfo, idleState, activeState;
|
|
24467
25558
|
return _regenerator().w(function (_context4) {
|
|
24468
25559
|
while (1) switch (_context4.n) {
|
|
24469
25560
|
case 0:
|
|
@@ -24506,14 +25597,19 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
24506
25597
|
_context4.n = 4;
|
|
24507
25598
|
break;
|
|
24508
25599
|
}
|
|
24509
|
-
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';
|
|
24510
25607
|
return _context4.a(2, new Promise(function (resolve, reject) {
|
|
24511
25608
|
(0,_shared_MicPermissionModals_js__WEBPACK_IMPORTED_MODULE_2__.showPrePromptModal)(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
|
|
24512
25609
|
var _t;
|
|
24513
25610
|
return _regenerator().w(function (_context3) {
|
|
24514
25611
|
while (1) switch (_context3.p = _context3.n) {
|
|
24515
25612
|
case 0:
|
|
24516
|
-
// User accepted pre-prompt - proceed with voice call
|
|
24517
25613
|
console.log('✅ Pre-prompt accepted, proceeding with voice call...');
|
|
24518
25614
|
_context3.p = 1;
|
|
24519
25615
|
_context3.n = 2;
|
|
@@ -24531,10 +25627,11 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
24531
25627
|
}
|
|
24532
25628
|
}, _callee3, null, [[1, 3]]);
|
|
24533
25629
|
})), function () {
|
|
24534
|
-
// User cancelled pre-prompt - reset connecting state
|
|
24535
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';
|
|
24536
25634
|
_this2.resetConnectingState();
|
|
24537
|
-
// Reject so caller knows call was cancelled
|
|
24538
25635
|
reject(new Error('Call cancelled by user'));
|
|
24539
25636
|
});
|
|
24540
25637
|
}));
|
|
@@ -24560,7 +25657,7 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
24560
25657
|
value: (function () {
|
|
24561
25658
|
var _proceedWithVoiceCall = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
|
|
24562
25659
|
var _this3 = this;
|
|
24563
|
-
var isResumeCall, panel, header, toggleText, _voiceInterface, originalSection, compactSection, idleState, _panel, fallbackPanel, sampleRate, mediaStream, floatingButton, fallbackButton, existingBar, _floatingButton, _fallbackButton, _idleState, _panel2, _fallbackPanel, deviceInfo, _idleState2, activeState, voiceInterface, connected, serverRejected, originalOnError, originalOnDisconnected, attempts, _this$sdk$voiceSDK, error, _existingBar, _existingBar2, _idleState3, _panel3, _fallbackPanel2, _floatingButton2, _fallbackButton2, updateTimer, _existingBar3, _existingBar4, _idleState4, _panel4, _fallbackPanel3, _floatingButton3, _fallbackButton3, _updateTimer, _deviceInfo, _t2, _t3, _t4, _t5, _t6, _t7;
|
|
25660
|
+
var isResumeCall, panel, header, toggleText, _voiceInterface, originalSection, compactSection, idleState, _panel, fallbackPanel, sampleRate, mediaStream, floatingButton, fallbackButton, mobileFabGrant, existingBar, _floatingButton, _fallbackButton, mobileFabDeny, _idleState, _panel2, _fallbackPanel, deviceInfo, _idleState2, activeState, voiceInterface, connected, serverRejected, originalOnError, originalOnDisconnected, attempts, _this$sdk$voiceSDK, error, _existingBar, _existingBar2, _idleState3, _panel3, _fallbackPanel2, _floatingButton2, _fallbackButton2, updateTimer, _existingBar3, _existingBar4, _idleState4, _panel4, _fallbackPanel3, _floatingButton3, _fallbackButton3, _updateTimer, _deviceInfo, _t2, _t3, _t4, _t5, _t6, _t7;
|
|
24564
25661
|
return _regenerator().w(function (_context5) {
|
|
24565
25662
|
while (1) switch (_context5.p = _context5.n) {
|
|
24566
25663
|
case 0:
|
|
@@ -24667,6 +25764,8 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
24667
25764
|
console.log('✅ Floating button hidden (permission granted, fallback)');
|
|
24668
25765
|
}
|
|
24669
25766
|
}
|
|
25767
|
+
mobileFabGrant = this.shadowRoot.querySelector('.ttp-mobile-fab');
|
|
25768
|
+
if (mobileFabGrant) mobileFabGrant.style.display = 'none';
|
|
24670
25769
|
_context5.n = 4;
|
|
24671
25770
|
break;
|
|
24672
25771
|
case 3:
|
|
@@ -24692,6 +25791,8 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
24692
25791
|
_fallbackButton.style.display = '';
|
|
24693
25792
|
}
|
|
24694
25793
|
}
|
|
25794
|
+
mobileFabDeny = this.shadowRoot.querySelector('.ttp-mobile-fab');
|
|
25795
|
+
if (mobileFabDeny) mobileFabDeny.style.display = '';
|
|
24695
25796
|
_idleState = this.shadowRoot.getElementById('voiceIdleState');
|
|
24696
25797
|
if (_idleState) {
|
|
24697
25798
|
_idleState.style.display = 'flex';
|
|
@@ -25262,7 +26363,7 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
25262
26363
|
key: "endCallOnServerRejection",
|
|
25263
26364
|
value: (function () {
|
|
25264
26365
|
var _endCallOnServerRejection = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
|
|
25265
|
-
var voiceInterfaceEl, landingScreen, _this$sdk$voiceSDK2, _this$sdk$voiceSDK3, audioRecorder, isRecording, _this$sdk$voiceSDK4, activeState, idleState, panel, fallbackPanel, floatingButton, fallbackButton, _t8, _t9, _t0, _t1, _t10;
|
|
26366
|
+
var voiceInterfaceEl, landingScreen, _this$sdk$voiceSDK2, _this$sdk$voiceSDK3, audioRecorder, isRecording, _this$sdk$voiceSDK4, activeState, idleState, panel, fallbackPanel, floatingButton, fallbackButton, mobileFabEnd, errorMsg, _t8, _t9, _t0, _t1, _t10;
|
|
25266
26367
|
return _regenerator().w(function (_context6) {
|
|
25267
26368
|
while (1) switch (_context6.p = _context6.n) {
|
|
25268
26369
|
case 0:
|
|
@@ -25475,7 +26576,7 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
25475
26576
|
console.log('🚫 showDomainError() completed, isShowingDomainError:', this.isShowingDomainError);
|
|
25476
26577
|
}
|
|
25477
26578
|
|
|
25478
|
-
// Mobile: restore panel visibility
|
|
26579
|
+
// Mobile: restore panel visibility and show error toast
|
|
25479
26580
|
if (this.isMobile) {
|
|
25480
26581
|
panel = this.shadowRoot.getElementById('text-chat-panel');
|
|
25481
26582
|
if (panel) {
|
|
@@ -25497,10 +26598,20 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
25497
26598
|
fallbackButton.style.display = '';
|
|
25498
26599
|
}
|
|
25499
26600
|
}
|
|
26601
|
+
mobileFabEnd = this.shadowRoot.querySelector('.ttp-mobile-fab');
|
|
26602
|
+
if (mobileFabEnd) mobileFabEnd.style.display = '';
|
|
25500
26603
|
|
|
25501
26604
|
// Remove mobile minimized bar if it exists
|
|
25502
26605
|
this.removeMobileMinimizedBar();
|
|
25503
26606
|
this.stopWaveformAnimation();
|
|
26607
|
+
|
|
26608
|
+
// Show error in mobile landing overlay since the panel isn't visible
|
|
26609
|
+
errorMsg = this.t('domainNotValidated') || 'Domain not whitelisted. Please contact support.';
|
|
26610
|
+
if (this.config.onMobileError) {
|
|
26611
|
+
this.config.onMobileError(errorMsg);
|
|
26612
|
+
} else if (this.config.onErrorToast) {
|
|
26613
|
+
this.config.onErrorToast(errorMsg, 'error');
|
|
26614
|
+
}
|
|
25504
26615
|
}
|
|
25505
26616
|
case 23:
|
|
25506
26617
|
return _context6.a(2);
|
|
@@ -25640,13 +26751,15 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
25640
26751
|
this.removeMobileMinimizedBar();
|
|
25641
26752
|
this.messages = [];
|
|
25642
26753
|
|
|
25643
|
-
//
|
|
25644
|
-
|
|
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)
|
|
25645
26759
|
var panel = this.shadowRoot.getElementById('text-chat-panel');
|
|
25646
26760
|
if (panel) {
|
|
25647
|
-
panel.style.display = '';
|
|
26761
|
+
panel.style.display = '';
|
|
25648
26762
|
} else {
|
|
25649
|
-
// Fallback: try document if not in Shadow DOM
|
|
25650
26763
|
var fallbackPanel = document.getElementById('text-chat-panel');
|
|
25651
26764
|
if (fallbackPanel) {
|
|
25652
26765
|
fallbackPanel.style.display = '';
|
|
@@ -27055,7 +28168,7 @@ var Mobile = /*#__PURE__*/function () {
|
|
|
27055
28168
|
container.id = 'mobile-voice-call-bar-container';
|
|
27056
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) {
|
|
27057
28170
|
return "<div class=\"mobile-waveform-bar\" data-index=\"".concat(i, "\"></div>");
|
|
27058
|
-
}).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 ");
|
|
27059
28172
|
|
|
27060
28173
|
// Append to body
|
|
27061
28174
|
document.body.appendChild(container);
|
|
@@ -27354,7 +28467,7 @@ var Styles = /*#__PURE__*/function () {
|
|
|
27354
28467
|
}, {
|
|
27355
28468
|
key: "_getSharedMobileCSS",
|
|
27356
28469
|
value: function _getSharedMobileCSS(important) {
|
|
27357
|
-
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 ");
|
|
27358
28471
|
}
|
|
27359
28472
|
|
|
27360
28473
|
/**
|
|
@@ -27364,7 +28477,7 @@ var Styles = /*#__PURE__*/function () {
|
|
|
27364
28477
|
key: "generateMobileCSS",
|
|
27365
28478
|
value: function generateMobileCSS() {
|
|
27366
28479
|
var important = this.config.useShadowDOM === false ? ' !important' : '';
|
|
27367
|
-
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 ");
|
|
27368
28481
|
}
|
|
27369
28482
|
}]);
|
|
27370
28483
|
}();
|
|
@@ -27692,6 +28805,14 @@ var TextHandlers = /*#__PURE__*/function () {
|
|
|
27692
28805
|
input.value = '';
|
|
27693
28806
|
if (sendBtn) sendBtn.disabled = true;
|
|
27694
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
|
+
|
|
27695
28816
|
// Show sending status
|
|
27696
28817
|
this.showTextInputStatus(isMobile ? 'mobile' : 'desktop', 'sending', 'Sending...');
|
|
27697
28818
|
|