ttp-agent-sdk 2.34.0 → 2.34.4
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 +847 -229
- 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/widget-customization.html +1464 -198
- package/dist/examples/widget-customization.html +1464 -198
- package/examples/widget-customization.html +1464 -198
- package/package.json +1 -1
package/dist/agent-widget.dev.js
CHANGED
|
@@ -12543,7 +12543,15 @@ var LandingScreen = /*#__PURE__*/function () {
|
|
|
12543
12543
|
key: "generateHTML",
|
|
12544
12544
|
value: function generateHTML() {
|
|
12545
12545
|
var landing = this.config.landing || {};
|
|
12546
|
-
|
|
12546
|
+
|
|
12547
|
+
// Determine logo content based on logoType
|
|
12548
|
+
var logoContent = '';
|
|
12549
|
+
if (landing.logoType === 'image' && landing.logoImageUrl) {
|
|
12550
|
+
logoContent = "<img src=\"".concat(landing.logoImageUrl, "\" alt=\"Logo\" style=\"max-width: 44px; max-height: 44px; object-fit: contain; border: none; outline: none;\">");
|
|
12551
|
+
} else {
|
|
12552
|
+
logoContent = "<span class=\"landing-avatar-emoji\">".concat(landing.logoIcon || landing.logo || '🤖', "</span>");
|
|
12553
|
+
}
|
|
12554
|
+
return "\n <div class=\"landing-screen\" id=\"landingScreen\">\n <!-- Avatar -->\n <div class=\"landing-avatar\">\n ".concat(logoContent, "\n </div>\n\n <!-- Title -->\n <h2 class=\"landing-title\">").concat(landing.title || this.t('landingTitle'), "</h2>\n <p class=\"landing-subtitle\">").concat(landing.subtitle || this.t('landingSubtitle') || 'Choose your preferred method', "</p>\n\n <!-- Mode Selection - Side by Side -->\n <div class=\"mode-selection\">\n <!-- Voice Call Option -->\n <button class=\"mode-card\" id=\"mode-card-voice\">\n <div class=\"mode-card-icon\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\"/>\n <line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\"/>\n </svg>\n </div>\n <span class=\"mode-card-title\">").concat(landing.voiceCardTitle || this.t('voiceCall'), "</span>\n </button>\n\n <!-- Text Chat Option -->\n <button class=\"mode-card\" id=\"mode-card-text\">\n <div class=\"mode-card-icon\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\"/>\n </svg>\n </div>\n <span class=\"mode-card-title\">").concat(landing.textCardTitle || this.t('textChat'), "</span>\n </button>\n </div>\n </div>\n ");
|
|
12547
12555
|
}
|
|
12548
12556
|
|
|
12549
12557
|
/**
|
|
@@ -12553,13 +12561,26 @@ var LandingScreen = /*#__PURE__*/function () {
|
|
|
12553
12561
|
key: "generateCSS",
|
|
12554
12562
|
value: function generateCSS() {
|
|
12555
12563
|
var landing = this.config.landing || {};
|
|
12556
|
-
var header = this.config.header || {};
|
|
12557
12564
|
var anim = this.config.animation || {};
|
|
12558
|
-
var headerColor = header.backgroundColor || '#7C3AED';
|
|
12559
12565
|
|
|
12560
12566
|
// Add !important when not using Shadow DOM
|
|
12561
12567
|
var important = this.config.useShadowDOM === false ? ' !important' : '';
|
|
12562
|
-
return "\n /* ===== LANDING SCREEN - New Design ===== */\n .landing-screen { \n display: none".concat(important, "; \n flex: 1; \n padding: 32px 24px; \n background: ").concat(landing.backgroundColor || 'linear-gradient(180deg, #ffffff 0%, rgba(168, 85, 247, 0.03) 100%)', "; \n align-items: center; \n justify-content: flex-start; \n flex-direction: column; \n gap: 0; \n overflow-y: auto; \n min-height: 0;\n text-align: center;\n }\n \n .landing-screen.active { \n display: flex").concat(important, "; \n }\n\n /* Avatar - Richer purple gradient */\n .landing-avatar {\n width: 88px;\n height: 88px;\n border-radius: 22px;\n margin-bottom: 20px;\n
|
|
12568
|
+
return "\n /* ===== LANDING SCREEN - New Design ===== */\n .landing-screen { \n display: none".concat(important, "; \n flex: 1; \n padding: 32px 24px; \n background: ").concat(landing.backgroundColor || 'linear-gradient(180deg, #ffffff 0%, rgba(168, 85, 247, 0.03) 100%)', "; \n align-items: center; \n justify-content: flex-start; \n flex-direction: column; \n gap: 0; \n overflow-y: auto; \n min-height: 0;\n text-align: center;\n }\n \n .landing-screen.active { \n display: flex").concat(important, "; \n }\n\n /* Avatar - Richer purple gradient */\n .landing-avatar {\n width: 88px;\n height: 88px;\n border-radius: 22px;\n margin-bottom: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n ").concat(function () {
|
|
12569
|
+
// Determine background based on logo type and background settings
|
|
12570
|
+
if (landing.logoType === 'image') {
|
|
12571
|
+
// For image logos, use logoBackgroundColor if enabled, otherwise transparent
|
|
12572
|
+
if (landing.logoBackgroundEnabled !== false) {
|
|
12573
|
+
var bgColor = landing.logoBackgroundColor || landing.avatarBackground || 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
|
|
12574
|
+
return "background: ".concat(bgColor, "; box-shadow: 0 8px 28px rgba(102, 126, 234, 0.35); border: none;");
|
|
12575
|
+
} else {
|
|
12576
|
+
return "background: transparent; box-shadow: none; border: none; outline: none;";
|
|
12577
|
+
}
|
|
12578
|
+
} else {
|
|
12579
|
+
// For icon logos, use avatarBackground (default behavior)
|
|
12580
|
+
var _bgColor = landing.avatarBackground || 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
|
|
12581
|
+
return "background: ".concat(_bgColor, "; box-shadow: 0 8px 28px rgba(102, 126, 234, 0.35); border: none;");
|
|
12582
|
+
}
|
|
12583
|
+
}(), "\n }\n \n .landing-avatar img {\n border: none !important;\n outline: none !important;\n }\n\n .landing-avatar-emoji {\n font-size: 44px;\n line-height: 1;\n }\n\n /* Title & Subtitle */\n .landing-title { \n font-size: 20px; \n color: ").concat(landing.titleColor || '#1e1b4b', "; \n font-weight: 600; \n margin-bottom: 6px; \n text-align: center;\n line-height: 1.3;\n }\n\n .landing-subtitle {\n font-size: 14px;\n color: ").concat(landing.subtitleColor || '#64748b', ";\n margin-bottom: 28px;\n text-align: center;\n }\n\n /* Mode Selection - Side by Side */\n .mode-selection { \n display: flex; \n gap: 12px; \n width: 100%; \n justify-content: center; \n }\n\n .mode-card { \n flex: 1; \n max-width: 160px; \n padding: 20px 16px;\n background: ").concat(landing.modeCardBackgroundColor || '#ffffff', "; \n border: 1px solid ").concat(landing.modeCardBorderColor || 'rgba(0, 0, 0, 0.06)', "; \n border-radius: 18px; \n cursor: pointer; \n display: flex; \n flex-direction: column; \n align-items: center; \n gap: 12px; \n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); \n box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06); \n font-family: inherit;\n }\n\n .mode-card:hover { \n transform: translateY(-4px); \n box-shadow: 0 8px 24px rgba(124, 58, 237, 0.2); \n border-color: rgba(124, 58, 237, 0.3); \n }\n\n .mode-card:active {\n transform: translateY(-2px);\n }\n\n /* Mode Card Icon - Vibrant purple gradient */\n .mode-card-icon { \n width: 56px; \n height: 56px; \n display: flex; \n align-items: center; \n justify-content: center; \n border-radius: 16px; \n background: ").concat(landing.modeCardIconBackgroundColor || 'linear-gradient(135deg, #7c3aed, #a855f7)', "; \n color: #fff; \n box-shadow: 0 4px 14px rgba(124, 58, 237, 0.35);\n }\n\n .mode-card-icon svg {\n width: 26px;\n height: 26px;\n stroke: white;\n fill: none;\n }\n\n .mode-card-title { \n color: ").concat(landing.modeCardTitleColor || '#1e1b4b', "; \n font-weight: 600; \n font-size: 14px;\n text-align: center;\n }\n\n /* Mobile Responsive */\n @media (max-width: 768px) {\n .landing-screen {\n padding: 24px 16px;\n }\n\n .landing-avatar {\n width: 72px;\n height: 72px;\n border-radius: 18px;\n margin-bottom: 16px;\n }\n\n .landing-avatar-emoji {\n font-size: 36px;\n }\n\n .landing-title {\n font-size: 18px;\n }\n\n .landing-subtitle {\n font-size: 13px;\n margin-bottom: 20px;\n }\n\n .mode-selection {\n gap: 10px;\n }\n\n .mode-card {\n padding: 16px 12px;\n max-width: 140px;\n }\n\n .mode-card-icon {\n width: 48px;\n height: 48px;\n border-radius: 14px;\n }\n\n .mode-card-icon svg {\n width: 22px;\n height: 22px;\n }\n\n .mode-card-title {\n font-size: 13px;\n }\n }\n\n @media (max-width: 380px) {\n .mode-selection {\n flex-direction: column;\n align-items: center;\n }\n\n .mode-card {\n max-width: 100%;\n width: 100%;\n flex-direction: row;\n padding: 14px 18px;\n gap: 14px;\n }\n\n .mode-card-icon {\n flex-shrink: 0;\n }\n\n .mode-card-title {\n text-align: left;\n }\n }\n ");
|
|
12563
12584
|
}
|
|
12564
12585
|
|
|
12565
12586
|
/**
|
|
@@ -12651,7 +12672,9 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
|
|
|
12651
12672
|
|
|
12652
12673
|
var TTPChatWidget = /*#__PURE__*/function () {
|
|
12653
12674
|
function TTPChatWidget() {
|
|
12654
|
-
var _this$config$
|
|
12675
|
+
var _this$config$text,
|
|
12676
|
+
_this$config$text2,
|
|
12677
|
+
_this$config$voice,
|
|
12655
12678
|
_this$config$voice2,
|
|
12656
12679
|
_this$config$voice3,
|
|
12657
12680
|
_this$config$voice4,
|
|
@@ -12692,6 +12715,10 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
12692
12715
|
// Initialize interfaces with proper config (after shadow root is created)
|
|
12693
12716
|
// Voice interface needs voice config merged with main config
|
|
12694
12717
|
var voiceConfig = _objectSpread(_objectSpread(_objectSpread({}, this.config), this.config.voice), {}, {
|
|
12718
|
+
// Pass through text input placeholder for voice text input
|
|
12719
|
+
inputPlaceholder: ((_this$config$text = this.config.text) === null || _this$config$text === void 0 ? void 0 : _this$config$text.inputPlaceholder) || this.config.inputPlaceholder || 'Type your message...',
|
|
12720
|
+
// Pass through send button color for voice send button
|
|
12721
|
+
sendButtonColor: ((_this$config$text2 = this.config.text) === null || _this$config$text2 === void 0 ? void 0 : _this$config$text2.sendButtonColor) || this.config.sendButtonColor || '#7C3AED',
|
|
12695
12722
|
language: ((_this$config$voice = this.config.voice) === null || _this$config$voice === void 0 ? void 0 : _this$config$voice.language) || this.config.language || 'en',
|
|
12696
12723
|
signedUrl: this.config.signedUrl,
|
|
12697
12724
|
// Pass through signedUrl if provided
|
|
@@ -12853,7 +12880,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
12853
12880
|
}, {
|
|
12854
12881
|
key: "mergeWithDefaults",
|
|
12855
12882
|
value: function mergeWithDefaults(userConfig) {
|
|
12856
|
-
var _userConfig$button, _userConfig$button2, _userConfig$
|
|
12883
|
+
var _userConfig$button, _userConfig$button2, _userConfig$icon, _userConfig$icon2, _userConfig$icon3, _userConfig$icon4, _userConfig$icon5, _userConfig$icon6, _userConfig$button3, _userConfig$button4, _userConfig$button5, _userConfig$icon7, _userConfig$button6, _userConfig$button7, _userConfig$button8, _userConfig$button9, _userConfig$icon8, _userConfig$panel, _userConfig$panel2, _userConfig$panel3, _userConfig$panel4, _userConfig$panel5, _userConfig$panel6, _userConfig$voice, _userConfig$panel7, _userConfig$voice2, _userConfig$panel8, _userConfig$voice3, _userConfig$panel9, _userConfig$voice4, _userConfig$panel0, _userConfig$voice5, _userConfig$panel1, _userConfig$voice6, _userConfig$panel10, _userConfig$voice7, _userConfig$voice8, _userConfig$voice9, _userConfig$voice0, _userConfig$voice1, _userConfig$voice10, _userConfig$voice11, _userConfig$voice12, _userConfig$voice13, _userConfig$voice14, _userConfig$voice15, _userConfig$voice16, _userConfig$voice17, _userConfig$voice18, _userConfig$voice19, _userConfig$voice20, _userConfig$voice21, _userConfig$voice22, _userConfig$text, _userConfig$panel11, _userConfig$text2, _userConfig$panel12, _userConfig$text3, _userConfig$panel13, _userConfig$text4, _userConfig$panel14, _userConfig$text5, _userConfig$panel15, _userConfig$text6, _userConfig$panel16, _userConfig$text7, _userConfig$panel17, _userConfig$text8, _userConfig$panel18, _userConfig$text9, _userConfig$panel19, _userConfig$text0, _userConfig$panel20, _userConfig$text1, _userConfig$panel21, _userConfig$text10, _userConfig$panel22, _userConfig$text11, _userConfig$panel23, _userConfig$text12, _userConfig$panel24, _userConfig$text13, _userConfig$panel25, _userConfig$text14, _userConfig$panel26, _userConfig$text15, _userConfig$panel27, _userConfig$text16, _userConfig$panel28, _userConfig$text17, _userConfig$panel29, _userConfig$footer, _userConfig$footer2, _userConfig$footer3, _userConfig$footer4, _userConfig$tooltips, _userConfig$tooltips2, _userConfig$tooltips3, _userConfig$tooltips4, _userConfig$tooltips5, _userConfig$tooltips6, _userConfig$messages, _userConfig$messages2, _userConfig$messages3, _userConfig$messages4, _userConfig$messages5, _userConfig$messages6, _userConfig$messages7, _userConfig$messages8, _userConfig$messages9, _userConfig$messages0, _userConfig$messages1, _userConfig$messages10, _userConfig$messages11, _userConfig$animation, _userConfig$animation2, _userConfig$animation3, _userConfig$animation4, _userConfig$promptAni, _userConfig$promptAni2, _userConfig$promptAni3, _userConfig$promptAni4, _userConfig$promptAni5, _userConfig$promptAni6, _userConfig$promptAni7, _userConfig$promptAni8, _userConfig$promptAni9, _userConfig$promptAni0, _userConfig$behavior, _userConfig$behavior2, _userConfig$behavior3, _userConfig$behavior4, _userConfig$behavior5, _userConfig$behavior6, _userConfig$behavior7, _userConfig$behavior8, _userConfig$accessibi, _userConfig$accessibi2, _userConfig$accessibi3;
|
|
12857
12884
|
// Handle legacy position string format
|
|
12858
12885
|
var positionConfig = userConfig.position || 'bottom-right';
|
|
12859
12886
|
if (typeof positionConfig === 'string') {
|
|
@@ -12872,9 +12899,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
12872
12899
|
// Handle legacy primaryColor - default to white (for floating button)
|
|
12873
12900
|
var primaryColor = userConfig.primaryColor || ((_userConfig$button = userConfig.button) === null || _userConfig$button === void 0 ? void 0 : _userConfig$button.primaryColor) || ((_userConfig$button2 = userConfig.button) === null || _userConfig$button2 === void 0 ? void 0 : _userConfig$button2.backgroundColor) || '#FFFFFF';
|
|
12874
12901
|
|
|
12875
|
-
//
|
|
12876
|
-
//
|
|
12877
|
-
|
|
12902
|
+
// FIX: Don't calculate headerColor here - it causes buttons to inherit header color
|
|
12903
|
+
// Instead, use explicit defaults for landing screen colors that don't depend on header
|
|
12904
|
+
|
|
12878
12905
|
return _objectSpread({
|
|
12879
12906
|
// Required (agentId is required, appId is optional)
|
|
12880
12907
|
agentId: userConfig.agentId,
|
|
@@ -12948,19 +12975,21 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
12948
12975
|
avatarActiveBackgroundColor: ((_userConfig$voice8 = userConfig.voice) === null || _userConfig$voice8 === void 0 ? void 0 : _userConfig$voice8.avatarActiveBackgroundColor) || '#667eea',
|
|
12949
12976
|
statusTitleColor: ((_userConfig$voice9 = userConfig.voice) === null || _userConfig$voice9 === void 0 ? void 0 : _userConfig$voice9.statusTitleColor) || '#1e293b',
|
|
12950
12977
|
statusSubtitleColor: ((_userConfig$voice0 = userConfig.voice) === null || _userConfig$voice0 === void 0 ? void 0 : _userConfig$voice0.statusSubtitleColor) || '#64748b',
|
|
12951
|
-
|
|
12952
|
-
|
|
12953
|
-
|
|
12954
|
-
|
|
12955
|
-
|
|
12956
|
-
|
|
12957
|
-
|
|
12958
|
-
|
|
12959
|
-
|
|
12960
|
-
|
|
12961
|
-
|
|
12978
|
+
statusDotColor: ((_userConfig$voice1 = userConfig.voice) === null || _userConfig$voice1 === void 0 ? void 0 : _userConfig$voice1.statusDotColor) || '#10b981',
|
|
12979
|
+
statusText: ((_userConfig$voice10 = userConfig.voice) === null || _userConfig$voice10 === void 0 ? void 0 : _userConfig$voice10.statusText) || null,
|
|
12980
|
+
startCallButtonColor: ((_userConfig$voice11 = userConfig.voice) === null || _userConfig$voice11 === void 0 ? void 0 : _userConfig$voice11.startCallButtonColor) || '#667eea',
|
|
12981
|
+
startCallButtonTextColor: ((_userConfig$voice12 = userConfig.voice) === null || _userConfig$voice12 === void 0 ? void 0 : _userConfig$voice12.startCallButtonTextColor) || '#FFFFFF',
|
|
12982
|
+
startCallTitle: ((_userConfig$voice13 = userConfig.voice) === null || _userConfig$voice13 === void 0 ? void 0 : _userConfig$voice13.startCallTitle) || null,
|
|
12983
|
+
startCallSubtitle: ((_userConfig$voice14 = userConfig.voice) === null || _userConfig$voice14 === void 0 ? void 0 : _userConfig$voice14.startCallSubtitle) || null,
|
|
12984
|
+
startCallButtonText: ((_userConfig$voice15 = userConfig.voice) === null || _userConfig$voice15 === void 0 ? void 0 : _userConfig$voice15.startCallButtonText) || null,
|
|
12985
|
+
transcriptBackgroundColor: ((_userConfig$voice16 = userConfig.voice) === null || _userConfig$voice16 === void 0 ? void 0 : _userConfig$voice16.transcriptBackgroundColor) || '#FFFFFF',
|
|
12986
|
+
transcriptTextColor: ((_userConfig$voice17 = userConfig.voice) === null || _userConfig$voice17 === void 0 ? void 0 : _userConfig$voice17.transcriptTextColor) || '#1e293b',
|
|
12987
|
+
transcriptLabelColor: ((_userConfig$voice18 = userConfig.voice) === null || _userConfig$voice18 === void 0 ? void 0 : _userConfig$voice18.transcriptLabelColor) || '#94a3b8',
|
|
12988
|
+
controlButtonColor: ((_userConfig$voice19 = userConfig.voice) === null || _userConfig$voice19 === void 0 ? void 0 : _userConfig$voice19.controlButtonColor) || '#FFFFFF',
|
|
12989
|
+
controlButtonSecondaryColor: ((_userConfig$voice20 = userConfig.voice) === null || _userConfig$voice20 === void 0 ? void 0 : _userConfig$voice20.controlButtonSecondaryColor) || '#64748b',
|
|
12990
|
+
endCallButtonColor: ((_userConfig$voice21 = userConfig.voice) === null || _userConfig$voice21 === void 0 ? void 0 : _userConfig$voice21.endCallButtonColor) || '#ef4444',
|
|
12962
12991
|
// Voice language setting
|
|
12963
|
-
language: ((_userConfig$
|
|
12992
|
+
language: ((_userConfig$voice22 = userConfig.voice) === null || _userConfig$voice22 === void 0 ? void 0 : _userConfig$voice22.language) || userConfig.language || 'en'
|
|
12964
12993
|
}, userConfig.voice),
|
|
12965
12994
|
// Text-specific Configuration
|
|
12966
12995
|
text: _objectSpread({
|
|
@@ -12992,31 +13021,73 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
12992
13021
|
inputPadding: ((_userConfig$text17 = userConfig.text) === null || _userConfig$text17 === void 0 ? void 0 : _userConfig$text17.inputPadding) || ((_userConfig$panel29 = userConfig.panel) === null || _userConfig$panel29 === void 0 ? void 0 : _userConfig$panel29.inputPadding) || '6px 14px'
|
|
12993
13022
|
}, userConfig.text),
|
|
12994
13023
|
// Landing Screen Configuration (only for unified mode)
|
|
12995
|
-
|
|
12996
|
-
|
|
12997
|
-
|
|
12998
|
-
|
|
12999
|
-
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13003
|
-
|
|
13004
|
-
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13008
|
-
|
|
13009
|
-
|
|
13010
|
-
|
|
13024
|
+
// FIX: Use explicit defaults instead of headerColor to prevent buttons from inheriting header color
|
|
13025
|
+
landing: function () {
|
|
13026
|
+
// Filter out empty strings from userConfig.landing to prevent overriding defaults
|
|
13027
|
+
var cleanLandingConfig = {};
|
|
13028
|
+
if (userConfig.landing) {
|
|
13029
|
+
Object.keys(userConfig.landing).forEach(function (key) {
|
|
13030
|
+
var value = userConfig.landing[key];
|
|
13031
|
+
// Include non-empty values, but also include boolean false values
|
|
13032
|
+
// Skip only: null, undefined, and empty strings
|
|
13033
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
13034
|
+
cleanLandingConfig[key] = value;
|
|
13035
|
+
} else if (value === false) {
|
|
13036
|
+
// Explicitly include false boolean values (e.g., logoBackgroundEnabled: false)
|
|
13037
|
+
cleanLandingConfig[key] = value;
|
|
13038
|
+
}
|
|
13039
|
+
});
|
|
13040
|
+
}
|
|
13041
|
+
return _objectSpread({
|
|
13042
|
+
backgroundColor: cleanLandingConfig.backgroundColor || 'linear-gradient(180deg, #f8fafc 0%, #e0e7ff 100%)',
|
|
13043
|
+
logo: cleanLandingConfig.logo || '🤖',
|
|
13044
|
+
title: cleanLandingConfig.title || null,
|
|
13045
|
+
// null means use default translated text
|
|
13046
|
+
titleColor: cleanLandingConfig.titleColor || '#1e293b',
|
|
13047
|
+
modeCardBackgroundColor: cleanLandingConfig.modeCardBackgroundColor || '#FFFFFF',
|
|
13048
|
+
modeCardBorderColor: cleanLandingConfig.modeCardBorderColor || '#E2E8F0',
|
|
13049
|
+
modeCardHoverBorderColor: cleanLandingConfig.modeCardHoverBorderColor || '#7C3AED',
|
|
13050
|
+
// Fixed: Use explicit default instead of headerColor
|
|
13051
|
+
modeCardIconBackgroundColor: cleanLandingConfig.modeCardIconBackgroundColor || '#7C3AED',
|
|
13052
|
+
// Fixed: Use explicit default instead of headerColor
|
|
13053
|
+
modeCardTitleColor: cleanLandingConfig.modeCardTitleColor || '#111827',
|
|
13054
|
+
voiceCardIcon: cleanLandingConfig.voiceCardIcon || '🎤',
|
|
13055
|
+
textCardIcon: cleanLandingConfig.textCardIcon || '💬',
|
|
13056
|
+
voiceCardTitle: cleanLandingConfig.voiceCardTitle || null,
|
|
13057
|
+
textCardTitle: cleanLandingConfig.textCardTitle || null
|
|
13058
|
+
}, cleanLandingConfig);
|
|
13059
|
+
}(),
|
|
13011
13060
|
// Header Configuration (top of panel)
|
|
13012
|
-
header:
|
|
13013
|
-
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
|
|
13061
|
+
header: function () {
|
|
13062
|
+
// Filter out empty strings to prevent overriding defaults
|
|
13063
|
+
var cleanHeaderConfig = {};
|
|
13064
|
+
if (userConfig.header) {
|
|
13065
|
+
Object.keys(userConfig.header).forEach(function (key) {
|
|
13066
|
+
var value = userConfig.header[key];
|
|
13067
|
+
// For online indicator properties, allow empty strings (they might be intentionally set)
|
|
13068
|
+
// For other properties, only include non-empty values
|
|
13069
|
+
if (key.startsWith('onlineIndicator')) {
|
|
13070
|
+
if (value !== null && value !== undefined) {
|
|
13071
|
+
cleanHeaderConfig[key] = value;
|
|
13072
|
+
}
|
|
13073
|
+
} else {
|
|
13074
|
+
// Only include non-empty values (skip empty strings, null, undefined)
|
|
13075
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
13076
|
+
cleanHeaderConfig[key] = value;
|
|
13077
|
+
}
|
|
13078
|
+
}
|
|
13079
|
+
});
|
|
13080
|
+
}
|
|
13081
|
+
var finalHeader = _objectSpread({
|
|
13082
|
+
title: cleanHeaderConfig.title || 'Chat Assistant',
|
|
13083
|
+
showTitle: cleanHeaderConfig.showTitle !== false,
|
|
13084
|
+
backgroundColor: cleanHeaderConfig.backgroundColor || '#7C3AED',
|
|
13085
|
+
// Default purple
|
|
13086
|
+
textColor: cleanHeaderConfig.textColor || '#FFFFFF',
|
|
13087
|
+
showCloseButton: cleanHeaderConfig.showCloseButton !== false
|
|
13088
|
+
}, cleanHeaderConfig);
|
|
13089
|
+
return finalHeader;
|
|
13090
|
+
}(),
|
|
13020
13091
|
// Footer Configuration (TTP Branding - always shown by default)
|
|
13021
13092
|
footer: _objectSpread({
|
|
13022
13093
|
show: ((_userConfig$footer = userConfig.footer) === null || _userConfig$footer === void 0 ? void 0 : _userConfig$footer.show) !== false,
|
|
@@ -13042,8 +13113,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13042
13113
|
systemBackgroundColor: ((_userConfig$messages3 = userConfig.messages) === null || _userConfig$messages3 === void 0 ? void 0 : _userConfig$messages3.systemBackgroundColor) || '#DCFCE7',
|
|
13043
13114
|
errorBackgroundColor: ((_userConfig$messages4 = userConfig.messages) === null || _userConfig$messages4 === void 0 ? void 0 : _userConfig$messages4.errorBackgroundColor) || '#FEE2E2',
|
|
13044
13115
|
textColor: ((_userConfig$messages5 = userConfig.messages) === null || _userConfig$messages5 === void 0 ? void 0 : _userConfig$messages5.textColor) || '#1F2937',
|
|
13045
|
-
|
|
13046
|
-
|
|
13116
|
+
// Fallback for backward compatibility
|
|
13117
|
+
userTextColor: ((_userConfig$messages6 = userConfig.messages) === null || _userConfig$messages6 === void 0 ? void 0 : _userConfig$messages6.userTextColor) || ((_userConfig$messages7 = userConfig.messages) === null || _userConfig$messages7 === void 0 ? void 0 : _userConfig$messages7.textColor) || '#1F2937',
|
|
13118
|
+
agentTextColor: ((_userConfig$messages8 = userConfig.messages) === null || _userConfig$messages8 === void 0 ? void 0 : _userConfig$messages8.agentTextColor) || ((_userConfig$messages9 = userConfig.messages) === null || _userConfig$messages9 === void 0 ? void 0 : _userConfig$messages9.textColor) || '#1F2937',
|
|
13119
|
+
userAvatarIcon: ((_userConfig$messages0 = userConfig.messages) === null || _userConfig$messages0 === void 0 ? void 0 : _userConfig$messages0.userAvatarIcon) || '👤',
|
|
13120
|
+
agentAvatarIcon: ((_userConfig$messages1 = userConfig.messages) === null || _userConfig$messages1 === void 0 ? void 0 : _userConfig$messages1.agentAvatarIcon) || '🤖',
|
|
13121
|
+
fontSize: ((_userConfig$messages10 = userConfig.messages) === null || _userConfig$messages10 === void 0 ? void 0 : _userConfig$messages10.fontSize) || '14px',
|
|
13122
|
+
borderRadius: ((_userConfig$messages11 = userConfig.messages) === null || _userConfig$messages11 === void 0 ? void 0 : _userConfig$messages11.borderRadius) || 16
|
|
13047
13123
|
}, userConfig.messages),
|
|
13048
13124
|
// Animation Configuration
|
|
13049
13125
|
animation: _objectSpread({
|
|
@@ -13052,6 +13128,25 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13052
13128
|
enableSlide: ((_userConfig$animation3 = userConfig.animation) === null || _userConfig$animation3 === void 0 ? void 0 : _userConfig$animation3.enableSlide) !== false,
|
|
13053
13129
|
duration: ((_userConfig$animation4 = userConfig.animation) === null || _userConfig$animation4 === void 0 ? void 0 : _userConfig$animation4.duration) || 0.3
|
|
13054
13130
|
}, userConfig.animation),
|
|
13131
|
+
// Prompt Animation Configuration
|
|
13132
|
+
promptAnimation: _objectSpread({
|
|
13133
|
+
enabled: ((_userConfig$promptAni = userConfig.promptAnimation) === null || _userConfig$promptAni === void 0 ? void 0 : _userConfig$promptAni.enabled) === true,
|
|
13134
|
+
// Default false
|
|
13135
|
+
text: ((_userConfig$promptAni2 = userConfig.promptAnimation) === null || _userConfig$promptAni2 === void 0 ? void 0 : _userConfig$promptAni2.text) || 'Try me!',
|
|
13136
|
+
backgroundColor: ((_userConfig$promptAni3 = userConfig.promptAnimation) === null || _userConfig$promptAni3 === void 0 ? void 0 : _userConfig$promptAni3.backgroundColor) || 'linear-gradient(135deg, #7c3aed, #4f46e5)',
|
|
13137
|
+
textColor: ((_userConfig$promptAni4 = userConfig.promptAnimation) === null || _userConfig$promptAni4 === void 0 ? void 0 : _userConfig$promptAni4.textColor) || '#ffffff',
|
|
13138
|
+
animationType: ((_userConfig$promptAni5 = userConfig.promptAnimation) === null || _userConfig$promptAni5 === void 0 ? void 0 : _userConfig$promptAni5.animationType) || 'bounce',
|
|
13139
|
+
// 'bounce' | 'pulse' | 'float' | 'none'
|
|
13140
|
+
showShimmer: ((_userConfig$promptAni6 = userConfig.promptAnimation) === null || _userConfig$promptAni6 === void 0 ? void 0 : _userConfig$promptAni6.showShimmer) !== false,
|
|
13141
|
+
// Default true
|
|
13142
|
+
showPulseRings: ((_userConfig$promptAni7 = userConfig.promptAnimation) === null || _userConfig$promptAni7 === void 0 ? void 0 : _userConfig$promptAni7.showPulseRings) !== false,
|
|
13143
|
+
// Default true
|
|
13144
|
+
hideAfterClick: ((_userConfig$promptAni8 = userConfig.promptAnimation) === null || _userConfig$promptAni8 === void 0 ? void 0 : _userConfig$promptAni8.hideAfterClick) !== false,
|
|
13145
|
+
// Default true
|
|
13146
|
+
hideAfterSeconds: ((_userConfig$promptAni9 = userConfig.promptAnimation) === null || _userConfig$promptAni9 === void 0 ? void 0 : _userConfig$promptAni9.hideAfterSeconds) !== undefined ? userConfig.promptAnimation.hideAfterSeconds : null,
|
|
13147
|
+
// null = never
|
|
13148
|
+
position: ((_userConfig$promptAni0 = userConfig.promptAnimation) === null || _userConfig$promptAni0 === void 0 ? void 0 : _userConfig$promptAni0.position) || 'top'
|
|
13149
|
+
}, userConfig.promptAnimation),
|
|
13055
13150
|
// Behavior Configuration
|
|
13056
13151
|
behavior: _objectSpread({
|
|
13057
13152
|
autoOpen: ((_userConfig$behavior = userConfig.behavior) === null || _userConfig$behavior === void 0 ? void 0 : _userConfig$behavior.autoOpen) || false,
|
|
@@ -13177,6 +13272,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13177
13272
|
}, {
|
|
13178
13273
|
key: "createWidget",
|
|
13179
13274
|
value: function createWidget() {
|
|
13275
|
+
var _this4 = this;
|
|
13180
13276
|
// Ensure container exists
|
|
13181
13277
|
if (!this.container) {
|
|
13182
13278
|
if (this.config.useShadowDOM) {
|
|
@@ -13216,8 +13312,29 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13216
13312
|
if (this.config.behavior.startOpen || this.config.behavior.autoOpen) {
|
|
13217
13313
|
this.isOpen = true;
|
|
13218
13314
|
panel.classList.add('open');
|
|
13315
|
+
// Hide prompt if widget starts open
|
|
13316
|
+
this.hidePrompt();
|
|
13317
|
+
} else {
|
|
13318
|
+
// Widget starts closed - show prompt
|
|
13319
|
+
this.showPrompt();
|
|
13219
13320
|
}
|
|
13321
|
+
} else {
|
|
13322
|
+
// Panel not found yet, but widget should start closed - show prompt
|
|
13323
|
+
// Use setTimeout to ensure prompt HTML is generated first
|
|
13324
|
+
setTimeout(function () {
|
|
13325
|
+
_this4.showPrompt();
|
|
13326
|
+
}, 100);
|
|
13220
13327
|
}
|
|
13328
|
+
|
|
13329
|
+
// Also ensure prompt visibility is correct after a short delay
|
|
13330
|
+
// This handles cases where setupPromptAnimation was called before widget HTML was ready
|
|
13331
|
+
setTimeout(function () {
|
|
13332
|
+
if (!_this4.isOpen) {
|
|
13333
|
+
_this4.showPrompt();
|
|
13334
|
+
} else {
|
|
13335
|
+
_this4.hidePrompt();
|
|
13336
|
+
}
|
|
13337
|
+
}, 200);
|
|
13221
13338
|
}
|
|
13222
13339
|
|
|
13223
13340
|
/**
|
|
@@ -13268,7 +13385,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13268
13385
|
key: "generateWidgetHTML",
|
|
13269
13386
|
value: function generateWidgetHTML() {
|
|
13270
13387
|
var _this$config$behavior3,
|
|
13271
|
-
|
|
13388
|
+
_this5 = this;
|
|
13272
13389
|
var pos = this.config.position;
|
|
13273
13390
|
var btn = this.config.button;
|
|
13274
13391
|
var icon = this.config.icon;
|
|
@@ -13307,15 +13424,15 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13307
13424
|
|
|
13308
13425
|
// Helper function to get translated text
|
|
13309
13426
|
var t = function t(key) {
|
|
13310
|
-
var lang =
|
|
13311
|
-
var translations =
|
|
13427
|
+
var lang = _this5.config.language || 'en';
|
|
13428
|
+
var translations = _this5.translations[lang] || _this5.translations.en;
|
|
13312
13429
|
return translations[key] || key;
|
|
13313
13430
|
};
|
|
13314
13431
|
|
|
13315
13432
|
// Helper function to get tooltip text
|
|
13316
13433
|
var getTooltip = function getTooltip(key) {
|
|
13317
|
-
var
|
|
13318
|
-
var tooltip = (
|
|
13434
|
+
var _this5$config$tooltip;
|
|
13435
|
+
var tooltip = (_this5$config$tooltip = _this5.config.tooltips) === null || _this5$config$tooltip === void 0 ? void 0 : _this5$config$tooltip[key];
|
|
13319
13436
|
if (tooltip !== null && tooltip !== undefined) return tooltip;
|
|
13320
13437
|
// Use translations for default tooltips
|
|
13321
13438
|
var defaults = {
|
|
@@ -13331,7 +13448,20 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13331
13448
|
|
|
13332
13449
|
// Only include style tag in Shadow DOM mode (styles injected into head for regular DOM)
|
|
13333
13450
|
var styleTag = this.config.useShadowDOM ? "\n <style>\n ".concat(this.generateCSS(positionStyles, buttonSize, iconSize), "\n ").concat(this.config.customStyles, "\n </style>\n ") : '';
|
|
13334
|
-
|
|
13451
|
+
|
|
13452
|
+
// Generate prompt bubble HTML if enabled
|
|
13453
|
+
var promptConfig = this.config.promptAnimation || {};
|
|
13454
|
+
// Default to disabled if not specified (enabled === true means explicitly enabled)
|
|
13455
|
+
var isPromptEnabled = promptConfig.enabled === true;
|
|
13456
|
+
var promptBubbleHTML = isPromptEnabled ? this.generatePromptBubbleHTML(promptConfig, buttonSize) : '';
|
|
13457
|
+
console.log('📝 generateWidgetHTML - promptAnimation:', {
|
|
13458
|
+
hasConfig: !!this.config.promptAnimation,
|
|
13459
|
+
promptConfig: promptConfig,
|
|
13460
|
+
enabled: promptConfig.enabled,
|
|
13461
|
+
isPromptEnabled: isPromptEnabled,
|
|
13462
|
+
willGenerateHTML: !!promptBubbleHTML
|
|
13463
|
+
});
|
|
13464
|
+
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 </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 ");
|
|
13335
13465
|
}
|
|
13336
13466
|
|
|
13337
13467
|
/**
|
|
@@ -13393,6 +13523,47 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13393
13523
|
return "<svg viewBox=\"0 0 24 24\" style=\"width: ".concat(size, "px; height: ").concat(size, "px; fill: white;\">\n <path d=\"M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z\"/>\n </svg>");
|
|
13394
13524
|
}
|
|
13395
13525
|
}
|
|
13526
|
+
|
|
13527
|
+
/**
|
|
13528
|
+
* Generate prompt bubble HTML
|
|
13529
|
+
*/
|
|
13530
|
+
}, {
|
|
13531
|
+
key: "generatePromptBubbleHTML",
|
|
13532
|
+
value: function generatePromptBubbleHTML(promptConfig, buttonSize) {
|
|
13533
|
+
// Extract solid color from gradient for arrow (fallback)
|
|
13534
|
+
var arrowColor = '#7c3aed';
|
|
13535
|
+
if (promptConfig.backgroundColor && promptConfig.backgroundColor.includes('gradient')) {
|
|
13536
|
+
// Try to extract first color from gradient
|
|
13537
|
+
var match = promptConfig.backgroundColor.match(/#[0-9a-fA-F]{6}/);
|
|
13538
|
+
if (match) {
|
|
13539
|
+
arrowColor = match[0];
|
|
13540
|
+
}
|
|
13541
|
+
} else {
|
|
13542
|
+
arrowColor = promptConfig.backgroundColor;
|
|
13543
|
+
}
|
|
13544
|
+
var animationClass = promptConfig.animationType === 'none' ? '' : "animation-".concat(promptConfig.animationType);
|
|
13545
|
+
var shimmerHTML = promptConfig.showShimmer ? '<div class="prompt-bubble-shimmer"></div>' : '';
|
|
13546
|
+
return "\n <div class=\"prompt-bubble ".concat(promptConfig.position, " ").concat(animationClass, "\" \n id=\"prompt-bubble\"\n role=\"tooltip\"\n style=\"--prompt-bubble-bg-color: ").concat(arrowColor, ";\">\n <div class=\"prompt-bubble-content\" \n style=\"background: ").concat(promptConfig.backgroundColor, "; color: ").concat(promptConfig.textColor, ";\">\n ").concat(shimmerHTML, "\n <span style=\"position: relative; z-index: 1;\">").concat(promptConfig.text, "</span>\n </div>\n <div class=\"prompt-bubble-arrow\"></div>\n </div>\n ");
|
|
13547
|
+
}
|
|
13548
|
+
|
|
13549
|
+
/**
|
|
13550
|
+
* Generate pulse rings HTML
|
|
13551
|
+
*/
|
|
13552
|
+
}, {
|
|
13553
|
+
key: "generatePulseRingsHTML",
|
|
13554
|
+
value: function generatePulseRingsHTML(promptConfig) {
|
|
13555
|
+
// Extract solid color from gradient for rings (fallback)
|
|
13556
|
+
var ringColor = '#7c3aed';
|
|
13557
|
+
if (promptConfig.backgroundColor && promptConfig.backgroundColor.includes('gradient')) {
|
|
13558
|
+
var match = promptConfig.backgroundColor.match(/#[0-9a-fA-F]{6}/);
|
|
13559
|
+
if (match) {
|
|
13560
|
+
ringColor = match[0];
|
|
13561
|
+
}
|
|
13562
|
+
} else {
|
|
13563
|
+
ringColor = promptConfig.backgroundColor;
|
|
13564
|
+
}
|
|
13565
|
+
return "\n <div class=\"prompt-pulse-rings\" id=\"prompt-pulse-rings\">\n <div class=\"prompt-pulse-ring\" style=\"background-color: ".concat(ringColor, "33;\"></div>\n <div class=\"prompt-pulse-ring\" style=\"background-color: ").concat(ringColor, "1a;\"></div>\n </div>\n ");
|
|
13566
|
+
}
|
|
13396
13567
|
}, {
|
|
13397
13568
|
key: "generateCSS",
|
|
13398
13569
|
value: function generateCSS(positionStyles, buttonSize, iconSize) {
|
|
@@ -13413,20 +13584,28 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13413
13584
|
// Add !important to display rules when not using Shadow DOM (to override theme CSS)
|
|
13414
13585
|
var important = this.config.useShadowDOM === false ? ' !important' : '';
|
|
13415
13586
|
|
|
13416
|
-
//
|
|
13417
|
-
|
|
13418
|
-
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: 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 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: 10001;\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 {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\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 {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\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 #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 }\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; color: rgba(255,255,255,0.7); margin: 0; }\n #text-chat-panel .status-dot { width: 6px; height: 6px; background: #10b981; border-radius: 50%; animation: pulse 2s ease-in-out infinite; }\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 ");
|
|
13587
|
+
// Note: Removed headerColor variable - landing screen colors now use explicit defaults
|
|
13588
|
+
// to prevent buttons from inheriting header backgroundColor
|
|
13589
|
+
|
|
13590
|
+
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 ");
|
|
13419
13591
|
}
|
|
13420
13592
|
}, {
|
|
13421
13593
|
key: "setupWidgetEvents",
|
|
13422
13594
|
value: function setupWidgetEvents() {
|
|
13423
|
-
var
|
|
13595
|
+
var _this6 = this,
|
|
13424
13596
|
_this$config$behavior5;
|
|
13425
13597
|
if (!this.shadowRoot) return;
|
|
13598
|
+
|
|
13599
|
+
// Setup prompt animation visibility and interactions
|
|
13600
|
+
this.setupPromptAnimation();
|
|
13426
13601
|
var openBtn = this.shadowRoot.getElementById('text-chat-button');
|
|
13427
13602
|
if (openBtn) {
|
|
13428
13603
|
openBtn.onclick = function () {
|
|
13429
|
-
|
|
13604
|
+
// Hide prompt after click if configured
|
|
13605
|
+
if (_this6.config.promptAnimation.hideAfterClick) {
|
|
13606
|
+
_this6.hidePrompt();
|
|
13607
|
+
}
|
|
13608
|
+
_this6.togglePanel();
|
|
13430
13609
|
};
|
|
13431
13610
|
}
|
|
13432
13611
|
var closeBtn = this.shadowRoot.getElementById('closeBtn');
|
|
@@ -13434,7 +13613,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13434
13613
|
closeBtn.onclick = function () {
|
|
13435
13614
|
// Just close the widget panel - call continues in background
|
|
13436
13615
|
// User can reopen widget to continue the call
|
|
13437
|
-
|
|
13616
|
+
_this6._doTogglePanel();
|
|
13438
13617
|
};
|
|
13439
13618
|
}
|
|
13440
13619
|
|
|
@@ -13451,27 +13630,27 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13451
13630
|
// Setup back button handler
|
|
13452
13631
|
if (backBtn) {
|
|
13453
13632
|
backBtn.onclick = function () {
|
|
13454
|
-
var
|
|
13633
|
+
var _this6$voiceInterface, _this6$voiceInterface3;
|
|
13455
13634
|
// Check if we're showing a domain error - if so, clear it and go back to landing
|
|
13456
|
-
if ((
|
|
13457
|
-
var
|
|
13635
|
+
if ((_this6$voiceInterface = _this6.voiceInterface) !== null && _this6$voiceInterface !== void 0 && _this6$voiceInterface.isShowingDomainError) {
|
|
13636
|
+
var _this6$voiceInterface2;
|
|
13458
13637
|
console.log('🔙 Back button clicked - clearing domain error and returning to landing');
|
|
13459
13638
|
// Clear the domain error flag
|
|
13460
|
-
|
|
13639
|
+
_this6.voiceInterface.isShowingDomainError = false;
|
|
13461
13640
|
// Reset the connecting state (this will restore normal UI)
|
|
13462
|
-
if ((
|
|
13463
|
-
|
|
13641
|
+
if ((_this6$voiceInterface2 = _this6.voiceInterface) !== null && _this6$voiceInterface2 !== void 0 && _this6$voiceInterface2.resetConnectingState) {
|
|
13642
|
+
_this6.voiceInterface.resetConnectingState();
|
|
13464
13643
|
}
|
|
13465
13644
|
// Now show landing (will work since error flag is cleared)
|
|
13466
|
-
|
|
13645
|
+
_this6.showLanding();
|
|
13467
13646
|
return;
|
|
13468
13647
|
}
|
|
13469
|
-
if ((
|
|
13648
|
+
if ((_this6$voiceInterface3 = _this6.voiceInterface) !== null && _this6$voiceInterface3 !== void 0 && _this6$voiceInterface3.isActive) {
|
|
13470
13649
|
// If call is active, show message inside widget instead of modal
|
|
13471
|
-
|
|
13650
|
+
_this6.showBackButtonWarning();
|
|
13472
13651
|
} else {
|
|
13473
13652
|
// If not active, just show landing
|
|
13474
|
-
|
|
13653
|
+
_this6.showLanding();
|
|
13475
13654
|
}
|
|
13476
13655
|
};
|
|
13477
13656
|
}
|
|
@@ -13479,34 +13658,34 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13479
13658
|
this.landingScreen.setupEventHandlers({
|
|
13480
13659
|
onSelectVoice: function () {
|
|
13481
13660
|
var _onSelectVoice = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
13482
|
-
var
|
|
13661
|
+
var _this6$voiceInterface4, _this6$voiceInterface5, _this6$voiceInterface6, _this6$voiceInterface8, _this6$config$behavio, idleState, isDomainError, statusTitle, _this6$voiceInterface7, titleText, domainErrorTitle, _widgetMode, _this6$voiceInterface0, _this6$config$behavio2, _this6$voiceInterface9, _widgetMode2, _t;
|
|
13483
13662
|
return _regenerator().w(function (_context) {
|
|
13484
13663
|
while (1) switch (_context.p = _context.n) {
|
|
13485
13664
|
case 0:
|
|
13486
|
-
if (!
|
|
13665
|
+
if (!_this6.isStartingCall) {
|
|
13487
13666
|
_context.n = 1;
|
|
13488
13667
|
break;
|
|
13489
13668
|
}
|
|
13490
13669
|
console.log('⚠️ Call already starting, ignoring duplicate click');
|
|
13491
13670
|
return _context.a(2);
|
|
13492
13671
|
case 1:
|
|
13493
|
-
|
|
13672
|
+
_this6.isStartingCall = true;
|
|
13494
13673
|
_context.p = 2;
|
|
13495
13674
|
// Show voice interface first (needed for UI state)
|
|
13496
13675
|
// Note: If showVoice() throws, finally block will still reset flag
|
|
13497
|
-
|
|
13676
|
+
_this6.showVoice();
|
|
13498
13677
|
|
|
13499
13678
|
// Show connecting/loading state immediately
|
|
13500
13679
|
// This provides feedback during WebSocket connection and SDK initialization
|
|
13501
|
-
if ((
|
|
13502
|
-
|
|
13680
|
+
if ((_this6$voiceInterface4 = _this6.voiceInterface) !== null && _this6$voiceInterface4 !== void 0 && _this6$voiceInterface4.showConnectingState) {
|
|
13681
|
+
_this6.voiceInterface.showConnectingState();
|
|
13503
13682
|
}
|
|
13504
13683
|
|
|
13505
13684
|
// Start the call
|
|
13506
13685
|
_context.n = 3;
|
|
13507
|
-
return
|
|
13686
|
+
return _this6.startVoiceCall();
|
|
13508
13687
|
case 3:
|
|
13509
|
-
if (!((
|
|
13688
|
+
if (!((_this6$voiceInterface5 = _this6.voiceInterface) !== null && _this6$voiceInterface5 !== void 0 && _this6$voiceInterface5.isActive)) {
|
|
13510
13689
|
_context.n = 4;
|
|
13511
13690
|
break;
|
|
13512
13691
|
}
|
|
@@ -13519,13 +13698,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13519
13698
|
|
|
13520
13699
|
// CRITICAL: Check if we're showing a domain error - if so, stay on voice interface
|
|
13521
13700
|
// We check by looking at the status title text content
|
|
13522
|
-
idleState = (
|
|
13701
|
+
idleState = (_this6$voiceInterface6 = _this6.voiceInterface) === null || _this6$voiceInterface6 === void 0 || (_this6$voiceInterface6 = _this6$voiceInterface6.shadowRoot) === null || _this6$voiceInterface6 === void 0 ? void 0 : _this6$voiceInterface6.getElementById('voiceIdleState');
|
|
13523
13702
|
isDomainError = false;
|
|
13524
13703
|
if (idleState && idleState.style.display !== 'none') {
|
|
13525
13704
|
statusTitle = idleState.querySelector('.voice-status-title');
|
|
13526
13705
|
if (statusTitle) {
|
|
13527
13706
|
titleText = statusTitle.textContent.trim();
|
|
13528
|
-
domainErrorTitle = (
|
|
13707
|
+
domainErrorTitle = (_this6$voiceInterface7 = _this6.voiceInterface) === null || _this6$voiceInterface7 === void 0 ? void 0 : _this6$voiceInterface7.t('domainNotValidated');
|
|
13529
13708
|
console.log('🔍 Checking for domain error - titleText:', titleText, 'domainErrorTitle:', domainErrorTitle);
|
|
13530
13709
|
if (titleText === domainErrorTitle || titleText.includes('Domain not') || titleText.includes('not whitelisted')) {
|
|
13531
13710
|
isDomainError = true;
|
|
@@ -13538,21 +13717,21 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13538
13717
|
break;
|
|
13539
13718
|
}
|
|
13540
13719
|
// Set currentView to voice so we don't return to landing
|
|
13541
|
-
|
|
13720
|
+
_this6.currentView = 'voice';
|
|
13542
13721
|
// Ensure voice interface is visible
|
|
13543
|
-
|
|
13722
|
+
_this6.showVoice();
|
|
13544
13723
|
// Don't reset connecting state or return to landing - stay on voice interface showing error
|
|
13545
13724
|
console.log('✅ Staying on voice interface with domain error displayed');
|
|
13546
13725
|
return _context.a(2);
|
|
13547
13726
|
case 5:
|
|
13548
13727
|
// Not a domain error - reset connecting state and return to landing
|
|
13549
13728
|
console.log('⚠️ Not a domain error - returning to landing screen');
|
|
13550
|
-
if ((
|
|
13551
|
-
|
|
13729
|
+
if ((_this6$voiceInterface8 = _this6.voiceInterface) !== null && _this6$voiceInterface8 !== void 0 && _this6$voiceInterface8.resetConnectingState) {
|
|
13730
|
+
_this6.voiceInterface.resetConnectingState();
|
|
13552
13731
|
}
|
|
13553
|
-
_widgetMode = ((
|
|
13732
|
+
_widgetMode = ((_this6$config$behavio = _this6.config.behavior) === null || _this6$config$behavio === void 0 ? void 0 : _this6$config$behavio.mode) || 'unified';
|
|
13554
13733
|
if (_widgetMode === 'unified') {
|
|
13555
|
-
|
|
13734
|
+
_this6.showLanding();
|
|
13556
13735
|
}
|
|
13557
13736
|
case 6:
|
|
13558
13737
|
_context.n = 9;
|
|
@@ -13566,8 +13745,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13566
13745
|
}
|
|
13567
13746
|
// Server rejection is expected - the error handler will show domain error message
|
|
13568
13747
|
// Reset connecting state but don't log as error
|
|
13569
|
-
if ((
|
|
13570
|
-
|
|
13748
|
+
if ((_this6$voiceInterface9 = _this6.voiceInterface) !== null && _this6$voiceInterface9 !== void 0 && _this6$voiceInterface9.resetConnectingState) {
|
|
13749
|
+
_this6.voiceInterface.resetConnectingState();
|
|
13571
13750
|
}
|
|
13572
13751
|
// Don't show toast for server rejection - domain error handler will show message
|
|
13573
13752
|
return _context.a(2);
|
|
@@ -13576,24 +13755,24 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13576
13755
|
console.error('❌ Failed to start voice call:', _t);
|
|
13577
13756
|
|
|
13578
13757
|
// Reset connecting state on error
|
|
13579
|
-
if ((
|
|
13580
|
-
|
|
13758
|
+
if ((_this6$voiceInterface0 = _this6.voiceInterface) !== null && _this6$voiceInterface0 !== void 0 && _this6$voiceInterface0.resetConnectingState) {
|
|
13759
|
+
_this6.voiceInterface.resetConnectingState();
|
|
13581
13760
|
}
|
|
13582
13761
|
|
|
13583
13762
|
// Show user-friendly error message (unless it's a user cancellation)
|
|
13584
13763
|
if (_t.message !== 'Call cancelled by user') {
|
|
13585
|
-
|
|
13764
|
+
_this6.showErrorToast(_t.message || 'Failed to start voice call. Please try again.', 'error');
|
|
13586
13765
|
}
|
|
13587
13766
|
|
|
13588
13767
|
// Return to landing screen on error
|
|
13589
|
-
_widgetMode2 = ((
|
|
13768
|
+
_widgetMode2 = ((_this6$config$behavio2 = _this6.config.behavior) === null || _this6$config$behavio2 === void 0 ? void 0 : _this6$config$behavio2.mode) || 'unified';
|
|
13590
13769
|
if (_widgetMode2 === 'unified') {
|
|
13591
|
-
|
|
13770
|
+
_this6.showLanding();
|
|
13592
13771
|
}
|
|
13593
13772
|
case 9:
|
|
13594
13773
|
_context.p = 9;
|
|
13595
13774
|
// ✅ Always reset flag, even if showVoice() or startVoiceCall() throws
|
|
13596
|
-
|
|
13775
|
+
_this6.isStartingCall = false;
|
|
13597
13776
|
return _context.f(9);
|
|
13598
13777
|
case 10:
|
|
13599
13778
|
return _context.a(2);
|
|
@@ -13606,7 +13785,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13606
13785
|
return onSelectVoice;
|
|
13607
13786
|
}(),
|
|
13608
13787
|
onSelectText: function onSelectText() {
|
|
13609
|
-
return
|
|
13788
|
+
return _this6.showText();
|
|
13610
13789
|
}
|
|
13611
13790
|
});
|
|
13612
13791
|
|
|
@@ -13662,7 +13841,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13662
13841
|
var newChatBtn = this.shadowRoot.getElementById('newChatBtn');
|
|
13663
13842
|
if (newChatBtn) {
|
|
13664
13843
|
newChatBtn.onclick = function () {
|
|
13665
|
-
return
|
|
13844
|
+
return _this6.textInterface.startNewChat();
|
|
13666
13845
|
};
|
|
13667
13846
|
// Hide new chat button on landing screen initially
|
|
13668
13847
|
if (showLanding) {
|
|
@@ -13756,13 +13935,199 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13756
13935
|
}, {
|
|
13757
13936
|
key: "setupKeyboardNavigation",
|
|
13758
13937
|
value: function setupKeyboardNavigation() {
|
|
13759
|
-
var
|
|
13938
|
+
var _this7 = this;
|
|
13760
13939
|
document.addEventListener('keydown', function (e) {
|
|
13761
|
-
if (e.key === 'Escape' &&
|
|
13762
|
-
|
|
13940
|
+
if (e.key === 'Escape' && _this7.isOpen) {
|
|
13941
|
+
_this7.togglePanel();
|
|
13763
13942
|
}
|
|
13764
13943
|
});
|
|
13765
13944
|
}
|
|
13945
|
+
|
|
13946
|
+
/**
|
|
13947
|
+
* Setup prompt animation visibility and auto-hide timer
|
|
13948
|
+
*/
|
|
13949
|
+
}, {
|
|
13950
|
+
key: "setupPromptAnimation",
|
|
13951
|
+
value: function setupPromptAnimation() {
|
|
13952
|
+
var _this8 = this;
|
|
13953
|
+
if (!this.shadowRoot) return;
|
|
13954
|
+
var promptConfig = this.config.promptAnimation || {};
|
|
13955
|
+
// Default to disabled if not specified (enabled === true means explicitly enabled)
|
|
13956
|
+
var isPromptEnabled = promptConfig.enabled === true;
|
|
13957
|
+
console.log('🎨 setupPromptAnimation called:', {
|
|
13958
|
+
hasPromptConfig: !!this.config.promptAnimation,
|
|
13959
|
+
promptConfig: promptConfig,
|
|
13960
|
+
enabled: promptConfig.enabled,
|
|
13961
|
+
isPromptEnabled: isPromptEnabled,
|
|
13962
|
+
isOpen: this.isOpen
|
|
13963
|
+
});
|
|
13964
|
+
if (!isPromptEnabled) {
|
|
13965
|
+
// Hide prompt if disabled
|
|
13966
|
+
console.log('❌ Prompt disabled, hiding');
|
|
13967
|
+
this.hidePrompt();
|
|
13968
|
+
return;
|
|
13969
|
+
}
|
|
13970
|
+
|
|
13971
|
+
// Show or hide prompt based on widget state
|
|
13972
|
+
// If widget is closed, show the prompt; if open, hide it
|
|
13973
|
+
if (this.isOpen) {
|
|
13974
|
+
this.hidePrompt();
|
|
13975
|
+
} else {
|
|
13976
|
+
this.showPrompt();
|
|
13977
|
+
}
|
|
13978
|
+
|
|
13979
|
+
// Auto-hide after configured seconds (only if widget is closed)
|
|
13980
|
+
if (!this.isOpen && promptConfig.hideAfterSeconds !== null && promptConfig.hideAfterSeconds > 0) {
|
|
13981
|
+
this.promptAutoHideTimer = setTimeout(function () {
|
|
13982
|
+
_this8.hidePrompt();
|
|
13983
|
+
}, promptConfig.hideAfterSeconds * 1000);
|
|
13984
|
+
}
|
|
13985
|
+
|
|
13986
|
+
// Hide pulse rings when voice call starts (listen to voice interface events)
|
|
13987
|
+
if (this.voiceInterface && this.voiceInterface.sdk) {
|
|
13988
|
+
// Store original onConversationStart if it exists
|
|
13989
|
+
var originalOnConversationStart = this.config.onConversationStart;
|
|
13990
|
+
|
|
13991
|
+
// Wrap the callback to also hide pulse rings
|
|
13992
|
+
this.config.onConversationStart = function () {
|
|
13993
|
+
// Hide pulse rings when call starts
|
|
13994
|
+
var pulseRings = _this8.shadowRoot.getElementById('prompt-pulse-rings');
|
|
13995
|
+
if (pulseRings) pulseRings.style.display = 'none';
|
|
13996
|
+
|
|
13997
|
+
// Call original callback if it exists
|
|
13998
|
+
if (originalOnConversationStart) {
|
|
13999
|
+
originalOnConversationStart.apply(void 0, arguments);
|
|
14000
|
+
}
|
|
14001
|
+
};
|
|
14002
|
+
|
|
14003
|
+
// Also listen to recordingStarted event from SDK if available
|
|
14004
|
+
if (this.voiceInterface.sdk.voiceSDK) {
|
|
14005
|
+
this.voiceInterface.sdk.voiceSDK.on('recordingStarted', function () {
|
|
14006
|
+
var pulseRings = _this8.shadowRoot.getElementById('prompt-pulse-rings');
|
|
14007
|
+
if (pulseRings) pulseRings.style.display = 'none';
|
|
14008
|
+
});
|
|
14009
|
+
}
|
|
14010
|
+
}
|
|
14011
|
+
}
|
|
14012
|
+
|
|
14013
|
+
/**
|
|
14014
|
+
* Hide the prompt bubble and pulse rings
|
|
14015
|
+
*/
|
|
14016
|
+
}, {
|
|
14017
|
+
key: "hidePrompt",
|
|
14018
|
+
value: function hidePrompt() {
|
|
14019
|
+
if (!this.shadowRoot) return;
|
|
14020
|
+
|
|
14021
|
+
// Clear auto-hide timer if it exists
|
|
14022
|
+
if (this.promptAutoHideTimer) {
|
|
14023
|
+
clearTimeout(this.promptAutoHideTimer);
|
|
14024
|
+
this.promptAutoHideTimer = null;
|
|
14025
|
+
}
|
|
14026
|
+
|
|
14027
|
+
// Hide prompt bubble (check both shadow root and document for compatibility)
|
|
14028
|
+
var promptBubble = this.shadowRoot.getElementById('prompt-bubble');
|
|
14029
|
+
if (!promptBubble && this.shadowRoot !== document) {
|
|
14030
|
+
promptBubble = document.getElementById('prompt-bubble');
|
|
14031
|
+
}
|
|
14032
|
+
if (promptBubble) {
|
|
14033
|
+
promptBubble.style.display = 'none';
|
|
14034
|
+
promptBubble.style.visibility = 'hidden';
|
|
14035
|
+
}
|
|
14036
|
+
|
|
14037
|
+
// Hide pulse rings (check both shadow root and document for compatibility)
|
|
14038
|
+
var pulseRings = this.shadowRoot.getElementById('prompt-pulse-rings');
|
|
14039
|
+
if (!pulseRings && this.shadowRoot !== document) {
|
|
14040
|
+
pulseRings = document.getElementById('prompt-pulse-rings');
|
|
14041
|
+
}
|
|
14042
|
+
if (pulseRings) {
|
|
14043
|
+
pulseRings.style.display = 'none';
|
|
14044
|
+
pulseRings.style.visibility = 'hidden';
|
|
14045
|
+
}
|
|
14046
|
+
}
|
|
14047
|
+
|
|
14048
|
+
/**
|
|
14049
|
+
* Show the prompt bubble and pulse rings (when widget is closed)
|
|
14050
|
+
*/
|
|
14051
|
+
}, {
|
|
14052
|
+
key: "showPrompt",
|
|
14053
|
+
value: function showPrompt() {
|
|
14054
|
+
var _this9 = this;
|
|
14055
|
+
if (!this.shadowRoot) return;
|
|
14056
|
+
var promptConfig = this.config.promptAnimation || {};
|
|
14057
|
+
// Default to enabled if not specified
|
|
14058
|
+
var isPromptEnabled = promptConfig.enabled !== false;
|
|
14059
|
+
console.log('👁️ showPrompt called:', {
|
|
14060
|
+
hasPromptConfig: !!this.config.promptAnimation,
|
|
14061
|
+
promptConfig: promptConfig,
|
|
14062
|
+
enabled: promptConfig.enabled,
|
|
14063
|
+
isPromptEnabled: isPromptEnabled,
|
|
14064
|
+
shadowRoot: !!this.shadowRoot
|
|
14065
|
+
});
|
|
14066
|
+
if (!isPromptEnabled) {
|
|
14067
|
+
console.log('❌ Prompt not enabled, skipping show');
|
|
14068
|
+
return; // Don't show if disabled
|
|
14069
|
+
}
|
|
14070
|
+
|
|
14071
|
+
// Function to actually show the elements
|
|
14072
|
+
var doShow = function doShow() {
|
|
14073
|
+
// Show prompt bubble (check both shadow root and document for compatibility)
|
|
14074
|
+
var promptBubble = _this9.shadowRoot.getElementById('prompt-bubble');
|
|
14075
|
+
if (!promptBubble && _this9.shadowRoot !== document) {
|
|
14076
|
+
promptBubble = document.getElementById('prompt-bubble');
|
|
14077
|
+
}
|
|
14078
|
+
if (promptBubble) {
|
|
14079
|
+
promptBubble.style.display = 'block';
|
|
14080
|
+
promptBubble.style.visibility = 'visible';
|
|
14081
|
+
promptBubble.style.opacity = '1';
|
|
14082
|
+
console.log('✅ Prompt bubble shown:', promptBubble);
|
|
14083
|
+
} else {
|
|
14084
|
+
console.warn('⚠️ Prompt bubble not found in shadowRoot or document');
|
|
14085
|
+
}
|
|
14086
|
+
|
|
14087
|
+
// Show pulse rings if enabled
|
|
14088
|
+
if (promptConfig.showPulseRings !== false) {
|
|
14089
|
+
var _pulseRings = _this9.shadowRoot.getElementById('prompt-pulse-rings');
|
|
14090
|
+
if (!_pulseRings && _this9.shadowRoot !== document) {
|
|
14091
|
+
_pulseRings = document.getElementById('prompt-pulse-rings');
|
|
14092
|
+
}
|
|
14093
|
+
if (_pulseRings) {
|
|
14094
|
+
_pulseRings.style.display = 'block';
|
|
14095
|
+
_pulseRings.style.visibility = 'visible';
|
|
14096
|
+
_pulseRings.style.opacity = '1';
|
|
14097
|
+
}
|
|
14098
|
+
}
|
|
14099
|
+
};
|
|
14100
|
+
|
|
14101
|
+
// Try to show immediately
|
|
14102
|
+
doShow();
|
|
14103
|
+
|
|
14104
|
+
// If elements don't exist yet, retry after delays (for widget initialization)
|
|
14105
|
+
var promptBubble = this.shadowRoot.getElementById('prompt-bubble');
|
|
14106
|
+
var pulseRings = promptConfig.showPulseRings !== false ? this.shadowRoot.getElementById('prompt-pulse-rings') : null;
|
|
14107
|
+
if (!promptBubble || promptConfig.showPulseRings !== false && !pulseRings) {
|
|
14108
|
+
// Retry after short delay
|
|
14109
|
+
setTimeout(function () {
|
|
14110
|
+
doShow();
|
|
14111
|
+
}, 100);
|
|
14112
|
+
|
|
14113
|
+
// Retry after longer delay if still not found
|
|
14114
|
+
setTimeout(function () {
|
|
14115
|
+
doShow();
|
|
14116
|
+
}, 500);
|
|
14117
|
+
}
|
|
14118
|
+
|
|
14119
|
+
// Restart auto-hide timer if configured
|
|
14120
|
+
if (promptConfig.hideAfterSeconds !== null && promptConfig.hideAfterSeconds > 0) {
|
|
14121
|
+
// Clear any existing timer
|
|
14122
|
+
if (this.promptAutoHideTimer) {
|
|
14123
|
+
clearTimeout(this.promptAutoHideTimer);
|
|
14124
|
+
}
|
|
14125
|
+
// Start new timer
|
|
14126
|
+
this.promptAutoHideTimer = setTimeout(function () {
|
|
14127
|
+
_this9.hidePrompt();
|
|
14128
|
+
}, promptConfig.hideAfterSeconds * 1000);
|
|
14129
|
+
}
|
|
14130
|
+
}
|
|
13766
14131
|
}, {
|
|
13767
14132
|
key: "togglePanel",
|
|
13768
14133
|
value: function togglePanel() {
|
|
@@ -13775,7 +14140,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13775
14140
|
}, {
|
|
13776
14141
|
key: "_doTogglePanel",
|
|
13777
14142
|
value: function _doTogglePanel() {
|
|
13778
|
-
var
|
|
14143
|
+
var _this0 = this;
|
|
13779
14144
|
if (!this.shadowRoot) return;
|
|
13780
14145
|
this.isOpen = !this.isOpen;
|
|
13781
14146
|
var panel = this.shadowRoot.getElementById('text-chat-panel');
|
|
@@ -13783,12 +14148,32 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
13783
14148
|
panel.classList.toggle('open');
|
|
13784
14149
|
}
|
|
13785
14150
|
|
|
13786
|
-
//
|
|
14151
|
+
// Hide/show prompt animation based on panel state
|
|
13787
14152
|
if (this.isOpen) {
|
|
14153
|
+
// Panel is opening - hide prompt
|
|
14154
|
+
this.hidePrompt();
|
|
13788
14155
|
setTimeout(function () {
|
|
13789
|
-
var input =
|
|
14156
|
+
var input = _this0.shadowRoot.getElementById('messageInput');
|
|
13790
14157
|
if (input) input.focus();
|
|
13791
14158
|
}, 100);
|
|
14159
|
+
} else {
|
|
14160
|
+
// Panel is closing - show prompt (if enabled)
|
|
14161
|
+
// Call showPrompt immediately, then also retry after a delay to ensure it's visible
|
|
14162
|
+
this.showPrompt();
|
|
14163
|
+
setTimeout(function () {
|
|
14164
|
+
var _this0$config$promptA;
|
|
14165
|
+
// Double-check and ensure prompt is visible
|
|
14166
|
+
var promptBubble = _this0.shadowRoot.getElementById('prompt-bubble');
|
|
14167
|
+
if (promptBubble && promptBubble.style.display === 'none') {
|
|
14168
|
+
promptBubble.style.display = 'block';
|
|
14169
|
+
}
|
|
14170
|
+
var pulseRings = _this0.shadowRoot.getElementById('prompt-pulse-rings');
|
|
14171
|
+
if (pulseRings && ((_this0$config$promptA = _this0.config.promptAnimation) === null || _this0$config$promptA === void 0 ? void 0 : _this0$config$promptA.showPulseRings) !== false) {
|
|
14172
|
+
if (pulseRings.style.display === 'none') {
|
|
14173
|
+
pulseRings.style.display = 'block';
|
|
14174
|
+
}
|
|
14175
|
+
}
|
|
14176
|
+
}, 100);
|
|
13792
14177
|
}
|
|
13793
14178
|
|
|
13794
14179
|
// Auto-connect if enabled
|
|
@@ -14073,8 +14458,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
14073
14458
|
mergedConfig.text = _objectSpread(_objectSpread({}, this.config.text), newConfig.text);
|
|
14074
14459
|
// Deep merge sendButtonHint if it exists
|
|
14075
14460
|
if (newConfig.text.sendButtonHint) {
|
|
14076
|
-
var _this$config$
|
|
14077
|
-
mergedConfig.text.sendButtonHint = _objectSpread(_objectSpread({}, (_this$config$
|
|
14461
|
+
var _this$config$text3;
|
|
14462
|
+
mergedConfig.text.sendButtonHint = _objectSpread(_objectSpread({}, (_this$config$text3 = this.config.text) === null || _this$config$text3 === void 0 ? void 0 : _this$config$text3.sendButtonHint), newConfig.text.sendButtonHint);
|
|
14078
14463
|
}
|
|
14079
14464
|
}
|
|
14080
14465
|
|
|
@@ -14300,6 +14685,12 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
14300
14685
|
}, {
|
|
14301
14686
|
key: "destroy",
|
|
14302
14687
|
value: function destroy() {
|
|
14688
|
+
// Clear prompt animation timer if it exists
|
|
14689
|
+
if (this.promptAutoHideTimer) {
|
|
14690
|
+
clearTimeout(this.promptAutoHideTimer);
|
|
14691
|
+
this.promptAutoHideTimer = null;
|
|
14692
|
+
}
|
|
14693
|
+
|
|
14303
14694
|
// Remove shadow host if it exists
|
|
14304
14695
|
var host = document.getElementById('ttp-widget-shadow-host');
|
|
14305
14696
|
if (host) {
|
|
@@ -14541,12 +14932,12 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
14541
14932
|
key: "_flushPendingClientTools",
|
|
14542
14933
|
value: function _flushPendingClientTools() {
|
|
14543
14934
|
var _this$voiceInterface1,
|
|
14544
|
-
|
|
14935
|
+
_this1 = this;
|
|
14545
14936
|
if (this._pendingClientTools && (_this$voiceInterface1 = this.voiceInterface) !== null && _this$voiceInterface1 !== void 0 && _this$voiceInterface1.sdk) {
|
|
14546
14937
|
this._pendingClientTools.forEach(function (_ref2) {
|
|
14547
14938
|
var name = _ref2.name,
|
|
14548
14939
|
handler = _ref2.handler;
|
|
14549
|
-
|
|
14940
|
+
_this1.voiceInterface.sdk.registerToolHandler(name, handler);
|
|
14550
14941
|
});
|
|
14551
14942
|
this._pendingClientTools = null;
|
|
14552
14943
|
console.log('TTPChatWidget: Flushed pending client tools');
|
|
@@ -14703,7 +15094,7 @@ var TextInterface = /*#__PURE__*/function () {
|
|
|
14703
15094
|
|
|
14704
15095
|
// Add !important to display rules when not using Shadow DOM (to override theme CSS)
|
|
14705
15096
|
var important = this.config.useShadowDOM === false ? ' !important' : '';
|
|
14706
|
-
return "\n /* Messages container using new classes */\n #messagesContainer { \n flex: 1; \n overflow-y: auto; \n overflow-x: hidden; \n padding: 20px; \n background: #f8fafc; \n display: flex; \n flex-direction: column; \n gap: 16px; \n min-height: 0; \n }\n .empty-state { \n flex: 1; \n display: flex; \n flex-direction: column; \n align-items: center; \n justify-content: center; \n gap: 12px; \n color: #64748b; \n text-align: center; \n padding: 20px; \n }\n .empty-state-icon { font-size: 48px; opacity: 0.3; }\n .empty-state-title { font-size: 20px; font-weight: 700; color: #334155; }\n .empty-state-text { font-size: 13px; max-width: 280px; }\n\n .text-interface { \n display: none".concat(important, "; \n flex: 1; \n flex-direction: column; \n min-height: 0; \n overflow: hidden; \n }\n .text-interface.active { display: flex").concat(important, "; }\n \n .message { \n display: flex; \n gap: 8px; \n padding: 4px 0; \n max-width: 100%; \n align-items: center; \n }\n .message.edge-left { flex-direction: row; }\n .message.edge-right { flex-direction: row-reverse; }\n .message-bubble { \n padding: 12px; \n border-radius: ").concat(messages.borderRadius, "px; \n max-width: 80%; \n font-size: ").concat(messages.fontSize, "; \n
|
|
15097
|
+
return "\n /* Messages container using new classes */\n #messagesContainer { \n flex: 1; \n overflow-y: auto; \n overflow-x: hidden; \n padding: 20px; \n background: #f8fafc; \n display: flex; \n flex-direction: column; \n gap: 16px; \n min-height: 0; \n }\n .empty-state { \n flex: 1; \n display: flex; \n flex-direction: column; \n align-items: center; \n justify-content: center; \n gap: 12px; \n color: #64748b; \n text-align: center; \n padding: 20px; \n }\n .empty-state-icon { font-size: 48px; opacity: 0.3; }\n .empty-state-title { font-size: 20px; font-weight: 700; color: #334155; }\n .empty-state-text { font-size: 13px; max-width: 280px; }\n\n .text-interface { \n display: none".concat(important, "; \n flex: 1; \n flex-direction: column; \n min-height: 0; \n overflow: hidden; \n }\n .text-interface.active { display: flex").concat(important, "; }\n \n .message { \n display: flex; \n gap: 8px; \n padding: 4px 0; \n max-width: 100%; \n align-items: center; \n }\n .message.edge-left { flex-direction: row; }\n .message.edge-right { flex-direction: row-reverse; }\n .message-bubble { \n padding: 12px; \n border-radius: ").concat(messages.borderRadius, "px").concat(important, "; \n max-width: 80%; \n font-size: ").concat(messages.fontSize, "; \n word-wrap: break-word; \n text-align: ").concat(this.config.direction === 'rtl' ? 'right' : 'left', "; \n direction: ").concat(this.config.direction || 'ltr', ";\n }\n .message.user .message-bubble { \n background: ").concat(messages.userBackgroundColor).concat(important, "; \n color: ").concat(messages.userTextColor || messages.textColor).concat(important, "; \n }\n .message.agent .message-bubble { \n background: ").concat(messages.agentBackgroundColor).concat(important, "; \n color: ").concat(messages.agentTextColor || messages.textColor).concat(important, "; \n }\n .message.user { \n align-self: ").concat(this.config.direction === 'rtl' ? 'flex-start' : 'flex-end', "; \n }\n .message.agent { \n align-self: ").concat(this.config.direction === 'rtl' ? 'flex-end' : 'flex-start', "; \n }\n .message .message-bubble { \n text-align: ").concat(this.config.direction === 'rtl' ? 'right' : 'left', " !important; \n }\n ").concat(this.config.direction === 'rtl' ? "\n .message-bubble {\n text-align: right !important;\n }\n " : '', "\n .message-avatar { \n width: 20px; \n height: 20px; \n display: flex; \n align-items: center; \n justify-content: center; \n flex-shrink: 0; \n color: inherit; \n font-size: 18px; \n line-height: 1; \n background: transparent; \n border: none; \n }\n .message-avatar.user { background: transparent; }\n .message-avatar.agent { background: transparent; }\n \n .message.system {\n background: ").concat(messages.systemBackgroundColor, ";\n align-self: flex-start;\n }\n .message.error {\n background: ").concat(messages.errorBackgroundColor, ";\n align-self: flex-start;\n }\n \n .input-container {\n display: flex;\n gap: 8px;\n padding: 12px 16px;\n background: #FFFFFF;\n border-top: 1px solid #E5E7EB;\n align-items: center;\n flex-shrink: 0;\n flex-direction: ").concat(this.config.direction === 'rtl' ? 'row-reverse' : 'row', ";\n }\n \n .input-wrapper {\n position: relative;\n display: flex;\n align-items: center;\n }\n \n .message-input {\n width: 100%;\n min-height: 32px;\n max-height: 120px;\n padding: ").concat(inputPadding, ";\n border: 1px solid ").concat(inputBorderColor, ";\n border-radius: ").concat(inputBorderRadius, "px;\n font-size: ").concat(inputFontSize, ";\n font-family: inherit;\n line-height: 1.3;\n resize: none;\n overflow-y: auto;\n background: ").concat(inputBackgroundColor, ";\n color: ").concat(inputTextColor, ";\n vertical-align: top;\n margin: 0;\n display: block;\n white-space: pre-wrap;\n word-wrap: break-word;\n text-align: ").concat(this.config.direction === 'rtl' ? 'right' : 'left', ";\n direction: ").concat(this.config.direction || 'ltr', ";\n -webkit-appearance: none;\n appearance: none;\n box-sizing: border-box;\n }\n \n .message-input:focus {\n outline: none;\n border-color: ").concat(inputFocusColor, ";\n background: ").concat(inputBackgroundColor === '#FFFFFF' ? '#FFFFFF' : inputBackgroundColor, ";\n box-shadow: 0 0 0 3px ").concat(inputFocusColor, "33;\n }\n \n .message-input::placeholder {\n color: #9CA3AF;\n text-align: ").concat(this.config.direction === 'rtl' ? 'right' : 'left', ";\n }\n \n .send-button {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n border: none;\n background: ").concat(sendButtonColor, ";\n color: ").concat(sendButtonTextColor, ";\n font-size: ").concat(this.config.sendButtonFontSize || ((_this$config$panel15 = this.config.panel) === null || _this$config$panel15 === void 0 ? void 0 : _this$config$panel15.sendButtonFontSize) || '18px', ";\n font-weight: ").concat(this.config.sendButtonFontWeight || ((_this$config$panel16 = this.config.panel) === null || _this$config$panel16 === void 0 ? void 0 : _this$config$panel16.sendButtonFontWeight) || '500', ";\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n transition: all 0.2s ease;\n box-shadow: 0 4px 12px rgba(124, 60, 237, 0.3);\n }\n \n .send-button:hover:not(:disabled) {\n background: ").concat(sendButtonHoverColor, ";\n transform: scale(1.05);\n box-shadow: 0 6px 16px rgba(124, 60, 237, 0.4);\n }\n \n .send-button-hint {\n width: 100%;\n text-align: center;\n margin-top: 4px;\n }\n \n .send-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n \n .typing-indicator {\n display: inline-flex;\n gap: 4px;\n align-items: center;\n }\n \n .typing-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #64748b;\n animation: typingDot 1.4s ease-in-out infinite;\n }\n \n .typing-dot:nth-child(2) { animation-delay: 0.2s; }\n .typing-dot:nth-child(3) { animation-delay: 0.4s; }\n \n @keyframes typingDot {\n 0%, 60%, 100% { transform: translateY(0); opacity: 0.7; }\n 30% { transform: translateY(-8px); opacity: 1; }\n }\n \n .error-message {\n padding: 12px;\n background: ").concat(messages.errorBackgroundColor, ";\n border-radius: ").concat(messages.borderRadius, "px;\n color: #991B1B;\n font-size: ").concat(messages.fontSize, ";\n margin: 8px 0;\n }\n \n @media (max-width: 768px) {\n #messagesContainer {\n padding: 12px;\n gap: 12px;\n }\n \n .message-bubble {\n max-width: 85%;\n font-size: 14px;\n padding: 10px;\n }\n \n .text-input-container {\n padding: 10px;\n gap: 8px;\n }\n \n #text-chat-input {\n font-size: 16px !important; /* Prevents iOS zoom on focus */\n padding: 10px 14px;\n min-height: 44px;\n }\n \n #text-chat-send {\n min-width: 56px;\n min-height: 44px;\n width: 56px;\n height: 44px;\n }\n \n .empty-state-icon {\n font-size: 40px;\n }\n \n .empty-state-title {\n font-size: 18px;\n }\n \n .empty-state-text {\n font-size: 12px;\n }\n }\n \n @media (max-width: 480px) {\n #messagesContainer {\n padding: 10px;\n gap: 10px;\n }\n \n .message-bubble {\n max-width: 90%;\n font-size: 13px;\n padding: 8px;\n }\n \n .text-input-container {\n padding: 8px;\n }\n \n #text-chat-input {\n font-size: 16px !important;\n padding: 8px 12px;\n }\n }\n ");
|
|
14707
15098
|
}
|
|
14708
15099
|
|
|
14709
15100
|
/**
|
|
@@ -14938,6 +15329,7 @@ var TextInterface = /*#__PURE__*/function () {
|
|
|
14938
15329
|
}, {
|
|
14939
15330
|
key: "addMessage",
|
|
14940
15331
|
value: function addMessage(type, text) {
|
|
15332
|
+
var _this$config$messages, _this$config$messages2;
|
|
14941
15333
|
var messages = this.shadowRoot.getElementById('messagesContainer');
|
|
14942
15334
|
if (!messages) return;
|
|
14943
15335
|
|
|
@@ -14951,7 +15343,8 @@ var TextInterface = /*#__PURE__*/function () {
|
|
|
14951
15343
|
message.className = "message ".concat(type, " ").concat(edgeClass);
|
|
14952
15344
|
var avatar = document.createElement('div');
|
|
14953
15345
|
avatar.className = "message-avatar ".concat(type);
|
|
14954
|
-
|
|
15346
|
+
var avatarIcon = type === 'user' ? ((_this$config$messages = this.config.messages) === null || _this$config$messages === void 0 ? void 0 : _this$config$messages.userAvatarIcon) || '👤' : ((_this$config$messages2 = this.config.messages) === null || _this$config$messages2 === void 0 ? void 0 : _this$config$messages2.agentAvatarIcon) || '🤖';
|
|
15347
|
+
avatar.textContent = avatarIcon;
|
|
14955
15348
|
var bubble = document.createElement('div');
|
|
14956
15349
|
bubble.className = 'message-bubble';
|
|
14957
15350
|
bubble.textContent = text;
|
|
@@ -14969,6 +15362,7 @@ var TextInterface = /*#__PURE__*/function () {
|
|
|
14969
15362
|
}, {
|
|
14970
15363
|
key: "beginStreaming",
|
|
14971
15364
|
value: function beginStreaming() {
|
|
15365
|
+
var _this$config$messages3;
|
|
14972
15366
|
var messages = this.shadowRoot.getElementById('messagesContainer');
|
|
14973
15367
|
if (!messages) return;
|
|
14974
15368
|
|
|
@@ -14980,7 +15374,7 @@ var TextInterface = /*#__PURE__*/function () {
|
|
|
14980
15374
|
el.id = 'agent-streaming';
|
|
14981
15375
|
var avatar = document.createElement('div');
|
|
14982
15376
|
avatar.className = 'message-avatar agent';
|
|
14983
|
-
avatar.textContent = '🤖';
|
|
15377
|
+
avatar.textContent = ((_this$config$messages3 = this.config.messages) === null || _this$config$messages3 === void 0 ? void 0 : _this$config$messages3.agentAvatarIcon) || '🤖';
|
|
14984
15378
|
var bubble = document.createElement('div');
|
|
14985
15379
|
bubble.className = 'message-bubble';
|
|
14986
15380
|
// show typing dots until first chunk
|
|
@@ -15051,11 +15445,11 @@ var TextInterface = /*#__PURE__*/function () {
|
|
|
15051
15445
|
// Check if this is a domain validation error
|
|
15052
15446
|
var isDomainError = error && (error.message === 'DOMAIN_NOT_WHITELISTED' || typeof error === 'string' && error.includes('DOMAIN_NOT_WHITELISTED') || error.message && error.message.includes('Domain not whitelisted') || typeof error === 'string' && error.includes('Domain not whitelisted'));
|
|
15053
15447
|
if (isDomainError) {
|
|
15054
|
-
var _this$config$
|
|
15448
|
+
var _this$config$messages4;
|
|
15055
15449
|
// Show domain error with title and message
|
|
15056
15450
|
var errorContainer = document.createElement('div');
|
|
15057
15451
|
errorContainer.className = 'error-message';
|
|
15058
|
-
errorContainer.style.cssText = 'padding: 16px; margin: 12px; border-radius: 8px; background: ' + (((_this$config$
|
|
15452
|
+
errorContainer.style.cssText = 'padding: 16px; margin: 12px; border-radius: 8px; background: ' + (((_this$config$messages4 = this.config.messages) === null || _this$config$messages4 === void 0 ? void 0 : _this$config$messages4.errorBackgroundColor) || '#FEE2E2') + ';';
|
|
15059
15453
|
var title = document.createElement('div');
|
|
15060
15454
|
title.style.cssText = 'font-weight: 600; font-size: 16px; margin-bottom: 8px; color: #991B1B;';
|
|
15061
15455
|
title.textContent = this.t('domainNotValidated');
|
|
@@ -17471,8 +17865,16 @@ var Desktop = /*#__PURE__*/function () {
|
|
|
17471
17865
|
// Update both old and new status text elements
|
|
17472
17866
|
var statusText = this.shadowRoot.getElementById('compactStatusText') || this.shadowRoot.getElementById('desktopStatusText');
|
|
17473
17867
|
if (!statusText) return;
|
|
17474
|
-
|
|
17475
|
-
|
|
17868
|
+
|
|
17869
|
+
// Use custom statusText from config if provided, otherwise use translations
|
|
17870
|
+
var customStatusText = this.config.statusText;
|
|
17871
|
+
var statusMessage;
|
|
17872
|
+
if (customStatusText) {
|
|
17873
|
+
statusMessage = customStatusText;
|
|
17874
|
+
} else {
|
|
17875
|
+
var isListening = this.voiceInterface.isActive && !this.voiceInterface.isMicMuted;
|
|
17876
|
+
statusMessage = isListening ? this.t('listening') : this.t('connected');
|
|
17877
|
+
}
|
|
17476
17878
|
statusText.textContent = statusMessage;
|
|
17477
17879
|
|
|
17478
17880
|
// Also update the old one if it exists
|
|
@@ -18126,17 +18528,27 @@ var Mobile = /*#__PURE__*/function () {
|
|
|
18126
18528
|
var headerStatusText = document.getElementById('mobileHeaderStatusText');
|
|
18127
18529
|
var statusDot = document.getElementById('mobileStatusDot');
|
|
18128
18530
|
if (!statusText && !headerStatusText) return;
|
|
18531
|
+
|
|
18532
|
+
// Use custom statusText from config if provided, otherwise use translations
|
|
18533
|
+
var customStatusText = this.config.statusText;
|
|
18129
18534
|
var displayText = '';
|
|
18130
18535
|
var dotColor = '#6b7280';
|
|
18131
|
-
if (
|
|
18132
|
-
|
|
18133
|
-
|
|
18134
|
-
|
|
18135
|
-
displayText = this.t('speaking') || 'Speaking...';
|
|
18136
|
-
dotColor = '#a855f7';
|
|
18536
|
+
if (customStatusText) {
|
|
18537
|
+
// Use custom status text for all states
|
|
18538
|
+
displayText = customStatusText;
|
|
18539
|
+
dotColor = this.config.statusDotColor || '#10b981';
|
|
18137
18540
|
} else {
|
|
18138
|
-
|
|
18139
|
-
|
|
18541
|
+
// Use dynamic status based on state
|
|
18542
|
+
if (status === 'listening' || status === 'recording') {
|
|
18543
|
+
displayText = this.t('listening') || 'Listening...';
|
|
18544
|
+
dotColor = '#22c55e';
|
|
18545
|
+
} else if (status === 'speaking') {
|
|
18546
|
+
displayText = this.t('speaking') || 'Speaking...';
|
|
18547
|
+
dotColor = '#a855f7';
|
|
18548
|
+
} else {
|
|
18549
|
+
displayText = this.t('paused') || 'Paused';
|
|
18550
|
+
dotColor = '#facc15';
|
|
18551
|
+
}
|
|
18140
18552
|
}
|
|
18141
18553
|
if (statusText) statusText.textContent = displayText;
|
|
18142
18554
|
if (headerStatusText) headerStatusText.textContent = displayText;
|
|
@@ -18266,22 +18678,30 @@ var Styles = /*#__PURE__*/function () {
|
|
|
18266
18678
|
return _createClass(Styles, [{
|
|
18267
18679
|
key: "generateCSS",
|
|
18268
18680
|
value: function generateCSS() {
|
|
18681
|
+
var _this$config$text;
|
|
18269
18682
|
var avatarBg = this.config.avatarBackgroundColor || '#667eea';
|
|
18270
18683
|
var avatarActiveBg = this.config.avatarActiveBackgroundColor || this.config.avatarBackgroundColor || '#667eea';
|
|
18271
18684
|
var statusTitleColor = this.config.statusTitleColor || '#1e293b';
|
|
18272
18685
|
var statusSubtitleColor = this.config.statusSubtitleColor || '#64748b';
|
|
18686
|
+
var statusDotColor = this.config.statusDotColor || '#10b981';
|
|
18687
|
+
var statusText = this.config.statusText || null; // Custom status text (null = use translations)
|
|
18273
18688
|
var startCallBtnColor = this.config.startCallButtonColor || '#667eea';
|
|
18274
18689
|
var startCallBtnTextColor = this.config.startCallButtonTextColor || '#FFFFFF';
|
|
18275
18690
|
var transcriptBg = this.config.transcriptBackgroundColor || '#FFFFFF';
|
|
18276
18691
|
var transcriptTextColor = this.config.transcriptTextColor || '#1e293b';
|
|
18277
18692
|
var transcriptLabelColor = this.config.transcriptLabelColor || '#94a3b8';
|
|
18278
18693
|
var controlBtnColor = this.config.controlButtonColor || '#FFFFFF';
|
|
18694
|
+
var liveTranscriptTextColor = this.config.liveTranscriptTextColor || this.config.transcriptTextColor || '#64748b';
|
|
18695
|
+
var liveTranscriptFontSize = this.config.liveTranscriptFontSize || '14px';
|
|
18279
18696
|
var controlBtnSecondaryColor = this.config.controlButtonSecondaryColor || '#64748b';
|
|
18697
|
+
var liveIndicatorDotColor = this.config.liveIndicatorDotColor || '#10b981';
|
|
18698
|
+
var liveIndicatorTextColor = this.config.liveIndicatorTextColor || '#10b981';
|
|
18699
|
+
var micButtonColor = this.config.micButtonColor || controlBtnColor;
|
|
18700
|
+
var speakerButtonColor = this.config.speakerButtonColor || controlBtnColor;
|
|
18280
18701
|
var endCallBtnColor = this.config.endCallButtonColor || '#ef4444';
|
|
18281
|
-
|
|
18282
|
-
|
|
18283
|
-
var important = this.config.useShadowDOM === false ? ' !important' : '';
|
|
18284
|
-
return "\n /* Voice Interface Styles - Ultra-compact for Wix iframes */\n .voice-interface { \n display: none".concat(important, "; \n flex: 1; \n flex-direction: column;\n align-items: stretch; \n justify-content: flex-start; \n padding: 0; \n background: linear-gradient(180deg, #f8fafc 0%, #e0e7ff 100%);\n overflow: hidden;\n min-height: 0;\n height: 100%;\n width: 100%;\n box-sizing: border-box;\n }\n .voice-interface.active { display: flex").concat(important, "; }\n \n /* Voice States - Better scaling gaps */\n #voiceIdleState {\n display: flex;\n flex-direction: column;\n align-items: center;\n width: 100%;\n height: 100%;\n min-height: 0;\n justify-content: center;\n overflow: hidden;\n flex: 1;\n box-sizing: border-box;\n gap: clamp(6px, 1.5vh, 12px);\n }\n \n #voiceActiveState {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n width: 100%;\n height: 100%;\n min-height: 0;\n justify-content: flex-start;\n overflow: hidden;\n flex: 1;\n box-sizing: border-box;\n gap: 0;\n padding: 0;\n }\n \n #voiceActiveState[style*=\"display: none\"] {\n display: none !important;\n }\n \n /* Voice Avatar - Scales better from small to large */\n .voice-avatar,\n .voice-avatar-active {\n width: clamp(80px, 18vh, 160px);\n height: clamp(80px, 18vh, 160px);\n aspect-ratio: 1;\n border-radius: 50%;\n background: linear-gradient(135deg, ").concat(avatarBg, " 0%, ").concat(avatarActiveBg, " 100%);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: clamp(40px, 9vh, 80px);\n margin: 0;\n box-shadow: 0 8px 30px rgba(102, 126, 234, 0.3);\n transition: all 0.3s ease;\n flex-shrink: 0;\n position: relative;\n }\n \n .voice-avatar-active {\n animation: avatarPulse 2s ease-in-out infinite;\n }\n \n @keyframes avatarPulse {\n 0%, 100% { \n transform: scale(1);\n }\n 50% { \n transform: scale(1.05);\n }\n }\n \n .voice-avatar-active.speaking {\n animation: avatarSpeak 0.5s ease-in-out infinite;\n }\n \n @keyframes avatarSpeak {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.08); }\n }\n \n /* Voice Rings Animation */\n .voice-rings {\n position: absolute;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n }\n \n .voice-ring {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n border: 2px solid rgba(102, 126, 234, 0.3);\n border-radius: 50%;\n opacity: 0;\n animation: ringExpand 2s ease-out infinite;\n }\n \n .voice-ring:nth-child(2) { animation-delay: 0.5s; }\n .voice-ring:nth-child(3) { animation-delay: 1s; }\n \n @keyframes ringExpand {\n 0% {\n width: 100%;\n height: 100%;\n opacity: 0.6;\n }\n 100% {\n width: 160%;\n height: 160%;\n opacity: 0;\n }\n }\n \n /* Voice Status - Scales with container */\n .voice-status {\n text-align: center;\n margin: 0;\n flex-shrink: 0;\n min-height: 0;\n }\n \n .voice-status-title {\n font-size: clamp(12px, 3vh, 20px);\n font-weight: 600;\n color: ").concat(statusTitleColor, ";\n margin-bottom: clamp(2px, 0.5vh, 4px);\n line-height: 1.2;\n }\n \n .voice-status-subtitle {\n font-size: clamp(10px, 2vh, 14px);\n color: ").concat(statusSubtitleColor, ";\n line-height: 1.2;\n }\n \n /* Start Call Button - Scales with container */\n .start-call-btn {\n margin: 0;\n width: min(280px, 70vw)").concat(important, ";\n height: clamp(48px, 8vh, 64px)").concat(important, ";\n border-radius: clamp(24px, 4vh, 32px)").concat(important, ";\n border: none").concat(important, ";\n background: ").concat(startCallBtnColor).concat(important, ";\n color: ").concat(startCallBtnTextColor).concat(important, ";\n font-size: clamp(14px, 2.5vh, 18px)").concat(important, ";\n font-weight: 600").concat(important, ";\n cursor: pointer").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n gap: clamp(8px, 1.5vh, 12px);\n box-shadow: 0 12px 30px rgba(102, 126, 234, 0.4);\n transition: all 0.3s ease;\n flex-shrink: 0;\n }\n \n .start-call-btn svg {\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n width: auto").concat(important, ";\n height: auto").concat(important, ";\n }\n \n .start-call-btn svg {\n width: clamp(22px, 4vh, 32px);\n height: clamp(22px, 4vh, 32px);\n }\n \n .start-call-btn:hover {\n transform: translateY(-2px);\n box-shadow: 0 12px 28px rgba(102, 126, 234, 0.5);\n }\n \n .start-call-btn:active {\n transform: translateY(-1px);\n }\n .start-call-btn:disabled {\n cursor: not-allowed").concat(important, ";\n opacity: 0.6").concat(important, ";\n }\n \n /* Connecting Spinner */\n .connecting-spinner {\n display: inline-block;\n width: 16px;\n height: 16px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: ").concat(startCallBtnTextColor, ";\n border-radius: 50%;\n animation: spin 1s linear infinite;\n margin-right: 8px;\n vertical-align: middle;\n }\n \n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n \n /* Voice Transcript - Scales with container */\n .voice-transcript {\n background: ").concat(transcriptBg, ";\n padding: clamp(6px, 1.5vh, 12px);\n border-radius: 10px;\n width: min(360px, calc(100% - 40px));\n margin: 0;\n min-height: clamp(80px, 15vh, 120px);\n max-height: clamp(100px, 18vh, 140px);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n flex-shrink: 1;\n overflow-y: auto;\n overflow-x: hidden;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n }\n \n .transcript-label {\n font-size: clamp(8px, 1.4vh, 11px);\n color: ").concat(transcriptLabelColor, ";\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: clamp(3px, 0.6vh, 6px);\n flex-shrink: 0;\n word-wrap: break-word;\n }\n \n .transcript-text {\n font-size: clamp(11px, 2vh, 15px);\n color: ").concat(transcriptTextColor, ";\n line-height: 1.4;\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n word-wrap: break-word;\n overflow-wrap: break-word;\n }\n \n .transcript-text.empty {\n color: #cbd5e1;\n font-style: italic;\n }\n \n /* Voice Text Input Area - Type while on voice call */\n .voice-text-input-area {\n width: 100%;\n padding: 14px 16px;\n margin: 0;\n flex-shrink: 0;\n background: #ffffff;\n border-top: 1px solid rgba(0, 0, 0, 0.06);\n box-sizing: border-box;\n }\n \n .voice-input-wrapper {\n display: flex;\n align-items: center;\n gap: 10px;\n background: #f1f5f9;\n border-radius: 12px;\n padding: 4px 4px 4px 14px;\n border: 1px solid transparent;\n transition: all 0.2s ease;\n }\n \n .voice-input-wrapper:focus-within {\n background: #fff;\n border-color: #a855f7;\n box-shadow: 0 0 0 3px rgba(168, 85, 247, 0.1);\n }\n \n .voice-text-input {\n flex: 1;\n border: none;\n outline: none;\n font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 14px;\n color: #1e1b4b;\n background: transparent;\n padding: 10px 0;\n }\n \n .voice-text-input::placeholder {\n color: #94a3b8;\n }\n \n .voice-send-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n height: 38px;\n border-radius: 10px;\n border: none;\n background: linear-gradient(135deg, #7c3aed, #a855f7);\n color: #fff;\n cursor: pointer;\n transition: transform 0.2s ease;\n flex-shrink: 0;\n }\n \n .voice-send-btn:hover:not(:disabled) {\n transform: scale(1.05);\n }\n \n .voice-send-btn:active:not(:disabled) {\n transform: scale(0.95);\n }\n \n .voice-send-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n }\n \n .voice-send-btn svg {\n width: 16px;\n height: 16px;\n }\n \n .voice-input-hint {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 5px;\n margin-top: 10px;\n font-size: 11px;\n color: #64748b;\n transition: color 0.2s ease;\n }\n \n .voice-input-hint svg {\n width: 12px;\n height: 12px;\n color: #10b981;\n }\n \n /* Text input status states */\n .voice-input-hint--sending {\n color: #666;\n }\n \n .voice-input-hint--success {\n color: #28a745;\n }\n \n .voice-input-hint--queued {\n color: #ffc107;\n }\n \n .voice-input-hint--error {\n color: #dc3545;\n }\n \n /* Disabled input styling */\n .voice-text-input:disabled,\n #mobileTextInput:disabled {\n background-color: #f5f5f5;\n cursor: not-allowed;\n }\n \n .voice-send-btn:disabled,\n #mobileSendBtn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n \n /* Voice Controls - Scales with container */\n .voice-controls {\n display: flex;\n gap: clamp(8px, 1.8vh, 14px);\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n margin: 0;\n padding-top: 0;\n }\n \n .voice-control-btn {\n width: clamp(48px, 9vh, 72px)").concat(important, ";\n height: clamp(48px, 9vh, 72px)").concat(important, ";\n min-width: clamp(48px, 9vh, 72px)").concat(important, ";\n min-height: clamp(48px, 9vh, 72px)").concat(important, ";\n max-width: clamp(48px, 9vh, 72px)").concat(important, ";\n max-height: clamp(48px, 9vh, 72px)").concat(important, ";\n aspect-ratio: 1").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n transition: all 0.2s;\n position: relative").concat(important, ";\n flex-shrink: 0;\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 .voice-control-btn svg {\n width: 48%").concat(important, ";\n height: 48%").concat(important, ";\n min-width: 48%").concat(important, ";\n min-height: 48%").concat(important, ";\n max-width: 48%").concat(important, ";\n max-height: 48%").concat(important, ";\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n margin: 0").concat(important, ";\n padding: 0").concat(important, ";\n position: relative").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n \n .voice-control-btn.primary {\n width: clamp(54px, 10vh, 80px)").concat(important, ";\n height: clamp(54px, 10vh, 80px)").concat(important, ";\n min-width: clamp(54px, 10vh, 80px)").concat(important, ";\n min-height: clamp(54px, 10vh, 80px)").concat(important, ";\n max-width: clamp(54px, 10vh, 80px)").concat(important, ";\n max-height: clamp(54px, 10vh, 80px)").concat(important, ";\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)").concat(important, ";\n color: white").concat(important, ";\n box-shadow: 0 6px 18px rgba(102, 126, 234, 0.4);\n }\n \n .voice-control-btn.primary:hover {\n transform: scale(1.05);\n }\n \n .voice-control-btn.primary.active {\n background: white").concat(important, ";\n animation: recordingPulse 1.5s ease-in-out infinite;\n }\n \n .voice-control-btn.primary.active svg {\n fill: ").concat(endCallBtnColor).concat(important, ";\n }\n \n /* More specific selectors for WordPress compatibility */\n #endCallBtn.voice-control-btn.primary.active,\n #muteBtn.voice-control-btn.secondary,\n #speakerBtn.voice-control-btn.secondary {\n border-radius: 50% !important;\n width: clamp(48px, 9vh, 72px) !important;\n height: clamp(48px, 9vh, 72px) !important;\n min-width: clamp(48px, 9vh, 72px) !important;\n min-height: clamp(48px, 9vh, 72px) !important;\n max-width: clamp(48px, 9vh, 72px) !important;\n max-height: clamp(48px, 9vh, 72px) !important;\n aspect-ratio: 1 !important;\n overflow: hidden !important;\n }\n \n #endCallBtn.voice-control-btn.primary.active {\n width: clamp(54px, 10vh, 80px) !important;\n height: clamp(54px, 10vh, 80px) !important;\n min-width: clamp(54px, 10vh, 80px) !important;\n min-height: clamp(54px, 10vh, 80px) !important;\n max-width: clamp(54px, 10vh, 80px) !important;\n max-height: clamp(54px, 10vh, 80px) !important;\n }\n \n #endCallBtn.voice-control-btn.primary.active svg,\n #muteBtn.voice-control-btn.secondary svg,\n #speakerBtn.voice-control-btn.secondary svg {\n display: block !important;\n visibility: visible !important;\n opacity: 1 !important;\n width: 48% !important;\n height: 48% !important;\n margin: 0 !important;\n padding: 0 !important;\n position: relative !important;\n }\n \n @keyframes recordingPulse {\n 0%, 100% { \n box-shadow: 0 6px 16px rgba(239, 68, 68, 0.4);\n }\n 50% { \n box-shadow: 0 6px 24px rgba(239, 68, 68, 0.7);\n }\n }\n \n .voice-control-btn.secondary {\n background: ").concat(controlBtnColor).concat(important, ";\n color: ").concat(controlBtnSecondaryColor).concat(important, ";\n box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);\n width: clamp(48px, 9vh, 72px)").concat(important, ";\n height: clamp(48px, 9vh, 72px)").concat(important, ";\n min-width: clamp(48px, 9vh, 72px)").concat(important, ";\n min-height: clamp(48px, 9vh, 72px)").concat(important, ";\n max-width: clamp(48px, 9vh, 72px)").concat(important, ";\n max-height: clamp(48px, 9vh, 72px)").concat(important, ";\n }\n \n .voice-control-btn.secondary:hover {\n background: #f8fafc").concat(important, ";\n transform: scale(1.05);\n }\n \n .voice-control-btn.secondary.muted {\n background: #f3f4f6").concat(important, ";\n }\n \n .voice-control-btn.secondary.muted .mute-icon {\n stroke: #9ca3af !important;\n }\n \n .voice-control-btn.secondary svg {\n width: 48%").concat(important, ";\n height: 48%").concat(important, ";\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n margin: 0").concat(important, ";\n padding: 0").concat(important, ";\n position: relative").concat(important, ";\n }\n \n .voice-timer {\n position: absolute;\n bottom: clamp(-18px, -3vh, -24px);\n font-size: clamp(9px, 1.6vh, 12px);\n color: #64748b;\n font-weight: 500;\n white-space: nowrap;\n }\n \n /* ===== DESKTOP VOICE SECTION - New Design ===== */\n .desktop-voice-section {\n padding: 16px 20px;\n background: linear-gradient(180deg, #ffffff 0%, rgba(168, 85, 247, 0.04) 100%);\n width: 100%;\n flex-shrink: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n box-sizing: border-box;\n text-align: center;\n }\n \n /* Ensure all children are centered */\n .desktop-voice-section > * {\n margin-left: auto;\n margin-right: auto;\n }\n \n .desktop-timer {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 5px 10px;\n background: rgba(0, 0, 0, 0.05);\n border-radius: 20px;\n font-family: 'JetBrains Mono', 'Courier New', monospace;\n font-size: 11px;\n color: #64748b;\n margin: 0 auto;\n }\n \n .desktop-timer .timer-dot {\n width: 5px;\n height: 5px;\n background: #ef4444;\n border-radius: 50%;\n animation: timerPulse 1s ease-in-out infinite;\n }\n \n @keyframes timerPulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n }\n \n .desktop-avatar-container {\n margin-bottom: 12px;\n }\n \n .desktop-avatar-large {\n width: 80px !important;\n height: 80px !important;\n border-radius: 20px !important;\n margin: 0 auto 12px !important;\n box-shadow: 0 8px 24px rgba(168, 85, 247, 0.35) !important;\n background: linear-gradient(135deg, ").concat(avatarBg, " 0%, ").concat(avatarActiveBg, " 100%) !important;\n }\n \n .desktop-waveform {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 36px;\n width: 100%;\n }\n \n .desktop-waveform-bar {\n width: 4px;\n background: rgba(255, 255, 255, 0.9);\n border-radius: 2px;\n animation: waveformAnimation 0.8s ease-in-out infinite;\n }\n \n .desktop-waveform-bar[data-index=\"0\"] { height: 14px; animation-delay: 0s; }\n .desktop-waveform-bar[data-index=\"1\"] { height: 26px; animation-delay: 0.1s; }\n .desktop-waveform-bar[data-index=\"2\"] { height: 36px; animation-delay: 0.2s; }\n .desktop-waveform-bar[data-index=\"3\"] { height: 22px; animation-delay: 0.3s; }\n .desktop-waveform-bar[data-index=\"4\"] { height: 16px; animation-delay: 0.4s; }\n \n @keyframes waveformAnimation {\n 0%, 100% { transform: scaleY(0.5); }\n 50% { transform: scaleY(1); }\n }\n \n .desktop-waveform.paused .desktop-waveform-bar {\n animation-play-state: paused;\n }\n\n /* New main waveform (used in updated template) */\n .desktop-main-waveform {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 3px;\n height: 48px;\n width: 100%;\n max-width: 200px;\n margin: 0 auto;\n padding: 0 20px;\n }\n\n .desktop-main-waveform .waveform-bar {\n width: 3px;\n background: #a855f7;\n border-radius: 2px;\n height: 8px;\n animation: waveformAnimation 0.8s ease-in-out infinite;\n }\n\n .desktop-main-waveform .waveform-bar:nth-child(1) { height: 12px; animation-delay: 0s; }\n .desktop-main-waveform .waveform-bar:nth-child(2) { height: 20px; animation-delay: 0.05s; }\n .desktop-main-waveform .waveform-bar:nth-child(3) { height: 28px; animation-delay: 0.1s; }\n .desktop-main-waveform .waveform-bar:nth-child(4) { height: 36px; animation-delay: 0.15s; }\n .desktop-main-waveform .waveform-bar:nth-child(5) { height: 44px; animation-delay: 0.2s; }\n .desktop-main-waveform .waveform-bar:nth-child(6) { height: 50px; animation-delay: 0.25s; }\n .desktop-main-waveform .waveform-bar:nth-child(7) { height: 54px; animation-delay: 0.3s; }\n .desktop-main-waveform .waveform-bar:nth-child(8) { height: 56px; animation-delay: 0.35s; }\n .desktop-main-waveform .waveform-bar:nth-child(9) { height: 54px; animation-delay: 0.4s; }\n .desktop-main-waveform .waveform-bar:nth-child(10) { height: 50px; animation-delay: 0.45s; }\n .desktop-main-waveform .waveform-bar:nth-child(11) { height: 44px; animation-delay: 0.5s; }\n .desktop-main-waveform .waveform-bar:nth-child(12) { height: 36px; animation-delay: 0.55s; }\n .desktop-main-waveform .waveform-bar:nth-child(13) { height: 28px; animation-delay: 0.6s; }\n .desktop-main-waveform .waveform-bar:nth-child(14) { height: 20px; animation-delay: 0.65s; }\n .desktop-main-waveform .waveform-bar:nth-child(15) { height: 12px; animation-delay: 0.7s; }\n\n .desktop-status {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 5px;\n font-size: 12px;\n color: #10b981;\n font-weight: 500;\n width: 100%;\n text-align: center;\n margin: 0 auto;\n }\n \n .desktop-status .status-dot {\n width: 5px;\n height: 5px;\n background: #10b981;\n border-radius: 50%;\n animation: statusPulse 1.5s ease-in-out infinite;\n }\n \n @keyframes statusPulse {\n 0%, 100% { transform: scale(1); opacity: 1; }\n 50% { transform: scale(1.15); opacity: 0.8; }\n }\n \n .desktop-live-waveform {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 3px;\n height: 48px;\n margin-bottom: 20px;\n }\n \n .desktop-live-bar {\n width: 3px;\n background: linear-gradient(180deg, ").concat(avatarBg, ", ").concat(avatarActiveBg, ");\n border-radius: 2px;\n transition: height 0.1s ease;\n height: 8px;\n }\n \n .desktop-controls {\n display: flex;\n gap: 8px;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin: 0 auto;\n }\n \n .desktop-control-btn {\n width: 46px;\n height: 46px;\n border-radius: 50%;\n border: none;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n \n .desktop-control-btn svg {\n width: 20px;\n height: 20px;\n }\n \n .desktop-mic-icon,\n .desktop-speaker-icon {\n width: 20px !important;\n height: 20px !important;\n display: block;\n flex-shrink: 0;\n }\n \n .desktop-control-btn.secondary.muted .desktop-mic-icon,\n .desktop-control-btn.secondary.muted .desktop-speaker-icon {\n width: 20px !important;\n height: 20px !important;\n }\n \n \n .desktop-control-btn.secondary:hover {\n background: rgba(0, 0, 0, 0.1);\n color: #1e293b;\n transform: scale(1.05);\n }\n .desktop-control-btn.secondary.muted {\n background: #f3f4f6;\n color: #9ca3af;\n }\n \n .desktop-control-btn.secondary.muted .desktop-mic-icon,\n .desktop-control-btn.secondary.muted .desktop-speaker-icon {\n stroke: #9ca3af;\n }\n \n .desktop-control-btn.secondary {\n position: relative;\n }\n .desktop-mic-off-icon,\n .desktop-speaker-off-icon {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 46px;\n height: 46px;\n transform: translate(-50%, -50%);\n z-index: 10;\n }\n .desktop-control-btn.danger {\n width: 50px;\n height: 50px;\n background: linear-gradient(135deg, #ef4444, #dc2626);\n color: #ffffff;\n box-shadow: 0 4px 14px rgba(239, 68, 68, 0.4);\n overflow: visible;\n }\n \n .desktop-control-btn.danger svg {\n width: 20px;\n height: 20px;\n overflow: visible;\n }\n \n .desktop-control-btn.danger:hover {\n transform: scale(1.06);\n box-shadow: 0 6px 20px rgba(239, 68, 68, 0.5);\n }\n \n .desktop-control-btn.danger:hover {\n transform: scale(1.06);\n box-shadow: 0 6px 20px rgba(239, 68, 68, 0.5);\n }\n \n \n /* ===== COMPACT VOICE SECTION (Single-row layout) ===== */\n .compact-voice-section {\n padding: 10px 14px;\n background: linear-gradient(180deg, #ffffff, rgba(168, 85, 247, 0.04));\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border-bottom: 1px solid rgba(0, 0, 0, 0.06);\n min-height: 62px;\n flex-shrink: 0;\n }\n \n .compact-left {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n \n .compact-avatar {\n width: 42px;\n height: 42px;\n min-width: 42px;\n min-height: 42px;\n border-radius: 12px;\n background: transparent;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: visible;\n position: relative;\n }\n \n /* Waveform container in compact avatar - ensure it's visible */\n .compact-avatar .desktop-main-waveform {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 2px;\n height: 100%;\n width: 100%;\n padding: 4px;\n transform: scale(0.75);\n transform-origin: center;\n }\n \n /* Ensure waveform bars are visible and animated in compact mode */\n .compact-avatar .desktop-main-waveform .waveform-bar {\n width: 2px;\n min-width: 2px;\n background: #a855f7;\n border-radius: 1px;\n flex-shrink: 0;\n animation: waveformAnimation 0.8s ease-in-out infinite;\n }\n \n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(1) { height: 6px; animation-delay: 0s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(2) { height: 10px; animation-delay: 0.05s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(3) { height: 14px; animation-delay: 0.1s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(4) { height: 18px; animation-delay: 0.15s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(5) { height: 22px; animation-delay: 0.2s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(6) { height: 26px; animation-delay: 0.25s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(7) { height: 28px; animation-delay: 0.3s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(8) { height: 30px; animation-delay: 0.35s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(9) { height: 28px; animation-delay: 0.4s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(10) { height: 26px; animation-delay: 0.45s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(11) { height: 22px; animation-delay: 0.5s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(12) { height: 18px; animation-delay: 0.55s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(13) { height: 14px; animation-delay: 0.6s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(14) { height: 10px; animation-delay: 0.65s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(15) { height: 6px; animation-delay: 0.7s; }\n\n }\n \n .compact-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n \n .compact-timer {\n display: flex;\n align-items: center;\n gap: 5px;\n font-family: 'JetBrains Mono', 'Courier New', monospace;\n font-size: 13px;\n font-weight: 600;\n color: #1e1b4b;\n }\n \n .compact-timer .timer-dot {\n width: 6px;\n height: 6px;\n background: #ef4444;\n border-radius: 50%;\n animation: timerPulse 1s ease-in-out infinite;\n }\n \n .compact-status {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: #10b981;\n }\n \n .compact-status .status-dot {\n width: 5px;\n height: 5px;\n background: #10b981;\n border-radius: 50%;\n animation: statusPulse 1.5s ease-in-out infinite;\n }\n \n @keyframes statusPulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.7; transform: scale(1.1); }\n }\n \n .compact-controls {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n \n .compact-control-btn {\n width: 36px;\n height: 36px;\n min-width: 36px;\n min-height: 36px;\n border-radius: 50%;\n border: none;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s ease;\n padding: 0;\n position: relative;\n }\n \n .compact-control-btn svg {\n width: 16px;\n height: 16px;\n }\n \n .compact-control-btn.secondary {\n background: #f1f5f9;\n color: #64748b;\n }\n \n .compact-control-btn.secondary:hover {\n background: #e2e8f0;\n color: #475569;\n }\n \n .compact-control-btn.secondary.muted {\n background: rgba(239, 68, 68, 0.1);\n color: #ef4444;\n }\n \n \n .compact-control-btn.secondary.muted .desktop-mic-icon,\n .compact-control-btn.secondary.muted .desktop-speaker-icon {\n stroke: #ef4444;\n }\n \n .compact-control-btn.secondary.muted .desktop-mic-off-icon,\n .compact-control-btn.secondary.muted .desktop-speaker-off-icon {\n width: 46px;\n height: 46px;\n }\n\n .compact-control-btn.danger {\n width: 40px;\n height: 40px;\n min-width: 40px;\n min-height: 40px;\n background: linear-gradient(135deg, #ef4444, #dc2626);\n color: white;\n box-shadow: 0 3px 10px rgba(239, 68, 68, 0.35);\n }\n \n .compact-control-btn.danger:hover {\n transform: scale(1.05);\n box-shadow: 0 4px 14px rgba(239, 68, 68, 0.45);\n }\n \n .compact-control-btn.danger svg {\n width: 18px;\n height: 18px;\n overflow: visible;\n }\n \n/* ===== CONVERSATION PANEL ===== */\n .desktop-conversation-panel {\n border-top: 1px solid rgba(0, 0, 0, 0.06);\n width: 100%;\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow: hidden;\n flex: 1;\n }\n \n .conversation-header {\n padding: 10px 16px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: #fafafa;\n cursor: pointer;\n flex-shrink: 0;\n border-bottom: 1px solid rgba(0, 0, 0, 0.04);\n transition: background 0.2s ease;\n }\n \n .conversation-header:hover {\n background: #f5f5f5;\n }\n \n .conversation-label {\n font-size: 11px;\n font-weight: 600;\n color: #64748b;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n \n .conversation-toggle {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: #94a3b8;\n font-weight: 500;\n }\n \n .conversation-toggle svg {\n transition: transform 0.2s ease;\n }\n \n .conversation-header.expanded .conversation-toggle svg {\n transform: rotate(180deg);\n }\n \n /* ===== COLLAPSED: Live Transcript Only (2 lines) ===== */\n .live-transcript-collapsed {\n padding: 12px 16px;\n background: #fafafa;\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex-shrink: 0;\n min-height: 50px;\n }\n \n .live-indicator {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 9px;\n font-weight: 700;\n color: #10b981;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n }\n \n .live-indicator .live-dot {\n width: 5px;\n height: 5px;\n background: #10b981;\n border-radius: 50%;\n animation: statusPulse 1.5s ease-in-out infinite;\n }\n \n .live-text-collapsed {\n font-size: 13px;\n color: #1e1b4b;\n line-height: 1.5;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n \n .live-text-collapsed.muted {\n color: #94a3b8;\n font-style: italic;\n }\n \n .typing-cursor {\n display: inline-block;\n width: 2px;\n height: 14px;\n background: #a855f7;\n margin-left: 2px;\n animation: blink 1s step-end infinite;\n vertical-align: middle;\n }\n \n @keyframes blink {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0; }\n }\n \n /* ===== EXPANDED: Full Conversation History ===== */\n .conversation-history {\n flex: 1;\n flex-direction: column;\n gap: 10px;\n overflow-y: auto;\n padding: 14px 16px;\n min-height: 0;\n }\n .conversation-history.visible {\n display: flex;\n }\n\n .conversation-messages {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n /* Message Rows */\n .message-row {\n display: flex;\n gap: 8px;\n animation: msgIn 0.25s ease;\n }\n \n @keyframes msgIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n @keyframes slideIn {\n from { opacity: 0; transform: translateY(-10px); }\n to { opacity: 1; transform: translateY(0); }\n }\n \n @keyframes slideOut {\n from { opacity: 1; transform: translateY(0); }\n to { opacity: 0; transform: translateY(-10px); }\n }\n \n .message-row.user {\n flex-direction: row-reverse;\n }\n \n .message-avatar {\n width: 28px;\n height: 28px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n\n .message-avatar.assistant {\n background: linear-gradient(135deg, #c084fc, #a855f7);\n }\n\n .message-avatar.assistant .orb {\n width: 12px;\n height: 12px;\n background: rgba(255, 255, 255, 0.9);\n border-radius: 50%;\n }\n\n .message-avatar.user {\n background: linear-gradient(135deg, #64748b, #475569);\n color: #fff;\n font-size: 11px;\n font-weight: 600;\n }\n\n .message-bubble {\n max-width: 80%;\n padding: 9px 13px;\n border-radius: 12px;\n font-size: 13px;\n line-height: 1.5;\n }\n \n .message-row.assistant .message-bubble {\n background: #fff;\n color: #1e1b4b;\n border: 1px solid rgba(0, 0, 0, 0.06);\n border-bottom-left-radius: 4px;\n }\n \n .message-row.user .message-bubble {\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n color: #fff;\n border-bottom-right-radius: 4px;\n }\n \n /* Live message at bottom of history */\n .live-message-row {\n display: flex;\n gap: 8px;\n padding-top: 8px;\n border-top: 1px dashed rgba(0, 0, 0, 0.08);\n margin-top: auto;\n }\n \n .live-message-row .message-bubble {\n background: #fff;\n border: 1px solid rgba(16, 185, 129, 0.3);\n color: #1e1b4b;\n position: relative;\n border-radius: 14px;\n border-bottom-left-radius: 4px;\n }\n \n .live-message-row .live-badge {\n position: absolute;\n top: -8px;\n left: 10px;\n background: #10b981;\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.05em;\n }\n \n /* Hide/show based on expanded state */\n .desktop-conversation-panel.collapsed .conversation-history {\n display: none;\n }\n \n .desktop-conversation-panel.collapsed .live-transcript-collapsed {\n display: flex;\n }\n \n .desktop-conversation-panel.expanded .conversation-history {\n display: flex;\n max-height: 400px;\n }\n \n .desktop-conversation-panel.expanded .live-transcript-collapsed {\n display: none;\n }\n \n /* ===== DARK MODE SUPPORT ===== */\n @media (prefers-color-scheme: dark) {\n .desktop-voice-section {\n background: linear-gradient(180deg, #1e1e2e 0%, rgba(168, 85, 247, 0.08) 100%);\n }\n\n .desktop-timer {\n color: #94a3b8;\n }\n\n .desktop-status {\n color: #34d399;\n }\n\n .desktop-status .status-dot {\n \n .desktop-control-btn.secondary.muted {\n background: rgba(255, 255, 255, 0.1);\n color: #9ca3af;\n \n .compact-voice-section {\n background: linear-gradient(180deg, #1e1e2e 0%, rgba(168, 85, 247, 0.08) 100%);\n border-bottom: 1px solid rgba(255, 255, 255, 0.08);\n }\n \n .compact-timer {\n color: #e2e8f0;\n }\n \n .compact-status {\n color: #34d399;\n }\n \n .compact-status .status-dot {\n background: #34d399;\n }\n \n .compact-control-btn.secondary {\n background: rgba(255, 255, 255, 0.08);\n color: #94a3b8;\n }\n \n .compact-control-btn.secondary:hover {\n background: rgba(255, 255, 255, 0.15);\n color: #e2e8f0;\n }\n \n .compact-control-btn.secondary.muted {\n background: rgba(239, 68, 68, 0.2);\n color: #f87171;\n }\n\n }\n \n .desktop-control-btn.secondary.muted svg {\n \n .desktop-control-btn.secondary {\n position: relative;\n }\n \n .desktop-mic-off-icon,\n .desktop-speaker-off-icon {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 20px;\n height: 20px;\n }\n \n .desktop-control-btn.secondary.muted {\n background: #f3f4f6;\n color: #9ca3af;\n }\n \n .desktop-control-btn.secondary.muted svg {\n \n .desktop-control-btn.secondary {\n position: relative;\n }\n \n .desktop-mic-off-icon,\n .desktop-speaker-off-icon {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 20px;\n height: 20px;\n }\n stroke: #9ca3af;\n }\n stroke: #9ca3af;\n }\n background: #34d399;\n }\n\n .desktop-control-btn.secondary {\n background: rgba(255, 255, 255, 0.08);\n color: #94a3b8;\n }\n\n .desktop-control-btn.secondary:hover {\n background: rgba(255, 255, 255, 0.15);\n color: #e2e8f0;\n }\n\n .desktop-conversation-panel {\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n }\n\n .conversation-header {\n background: #1a1a2e;\n }\n\n .conversation-header:hover {\n background: #252540;\n }\n\n .conversation-label {\n color: #94a3b8;\n }\n\n .conversation-toggle {\n color: #64748b;\n }\n\n .live-transcript-preview {\n background: #1a1a2e;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n }\n\n .live-indicator {\n color: #34d399;\n }\n\n .live-indicator .live-dot {\n background: #34d399;\n }\n\n .live-text {\n color: #e2e8f0;\n }\n\n .live-text.muted {\n color: #64748b;\n }\n\n .conversation-history {\n background: #1a1a2e;\n }\n\n .conversation-msg.assistant .conversation-msg-bubble {\n background: #252540;\n color: #e2e8f0;\n border: 1px solid rgba(255, 255, 255, 0.08);\n }\n\n .voice-text-input-area {\n background: #1a1a2e;\n }\n\n .voice-text-input {\n background: #252540 !important;\n color: #e2e8f0 !important;\n border-color: rgba(255, 255, 255, 0.1) !important;\n }\n\n .voice-text-input::placeholder {\n color: #64748b !important;\n }\n\n .voice-text-input:focus {\n border-color: ").concat(avatarBg, " !important;\n box-shadow: 0 0 0 3px rgba(168, 85, 247, 0.2) !important;\n }\n\n .voice-input-hint {\n color: #64748b !important;\n }\n\n .voice-send-btn {\n background: linear-gradient(135deg, ").concat(avatarBg, ", ").concat(avatarActiveBg, ") !important;\n }\n\n .voice-send-btn:disabled {\n background: #374151 !important;\n color: #4b5563 !important;\n }\n }\n\n /* Mobile optimization */\n @media (max-width: 768px) {\n .voice-interface {\n padding: 6px 10px;\n }\n }\n \n @media (max-width: 480px) {\n .voice-interface {\n padding: 5px 8px;\n }\n }\n \n /* Mobile Voice Call UI - Shared styles (also used in generateMobileCSS) */\n ").concat(this._getSharedMobileCSS(important), "\n ");
|
|
18702
|
+
var voiceSendButtonColor = this.config.sendButtonColor || ((_this$config$text = this.config.text) === null || _this$config$text === void 0 ? void 0 : _this$config$text.sendButtonColor) || this.config.voiceSendButtonColor || '#7C3AED';
|
|
18703
|
+
var important = ' !important';
|
|
18704
|
+
return "\n /* Voice Interface Styles - Ultra-compact for Wix iframes */\n .voice-interface { \n display: none".concat(important, "; \n flex: 1; \n flex-direction: column;\n align-items: stretch; \n justify-content: flex-start; \n padding: 0; \n background: linear-gradient(180deg, #f8fafc 0%, #e0e7ff 100%);\n overflow: hidden;\n min-height: 0;\n height: 100%;\n width: 100%;\n box-sizing: border-box;\n }\n .voice-interface.active { display: flex").concat(important, "; }\n \n /* Voice States - Better scaling gaps */\n #voiceIdleState {\n display: flex;\n flex-direction: column;\n align-items: center;\n width: 100%;\n height: 100%;\n min-height: 0;\n justify-content: center;\n overflow: hidden;\n flex: 1;\n box-sizing: border-box;\n gap: clamp(6px, 1.5vh, 12px);\n }\n \n #voiceActiveState {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n width: 100%;\n height: 100%;\n min-height: 0;\n justify-content: flex-start;\n overflow: hidden;\n flex: 1;\n box-sizing: border-box;\n gap: 0;\n padding: 0;\n }\n \n #voiceActiveState[style*=\"display: none\"] {\n display: none !important;\n }\n \n /* Voice Avatar - Scales better from small to large */\n .voice-avatar,\n .voice-avatar-active {\n width: clamp(80px, 18vh, 160px);\n height: clamp(80px, 18vh, 160px);\n aspect-ratio: 1;\n border-radius: 50%;\n background: linear-gradient(135deg, ").concat(avatarBg, " 0%, ").concat(avatarActiveBg, " 100%);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: clamp(40px, 9vh, 80px);\n margin: 0;\n box-shadow: 0 8px 30px rgba(102, 126, 234, 0.3);\n transition: all 0.3s ease;\n flex-shrink: 0;\n position: relative;\n }\n \n .voice-avatar-active {\n animation: avatarPulse 2s ease-in-out infinite;\n }\n \n @keyframes avatarPulse {\n 0%, 100% { \n transform: scale(1);\n }\n 50% { \n transform: scale(1.05);\n }\n }\n \n .voice-avatar-active.speaking {\n animation: avatarSpeak 0.5s ease-in-out infinite;\n }\n \n @keyframes avatarSpeak {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.08); }\n }\n \n /* Voice Rings Animation */\n .voice-rings {\n position: absolute;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n }\n \n .voice-ring {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n border: 2px solid rgba(102, 126, 234, 0.3);\n border-radius: 50%;\n opacity: 0;\n animation: ringExpand 2s ease-out infinite;\n }\n \n .voice-ring:nth-child(2) { animation-delay: 0.5s; }\n .voice-ring:nth-child(3) { animation-delay: 1s; }\n \n @keyframes ringExpand {\n 0% {\n width: 100%;\n height: 100%;\n opacity: 0.6;\n }\n 100% {\n width: 160%;\n height: 160%;\n opacity: 0;\n }\n }\n \n /* Voice Status - Scales with container */\n .voice-status {\n text-align: center;\n margin: 0;\n flex-shrink: 0;\n min-height: 0;\n }\n \n .voice-status-title {\n font-size: clamp(12px, 3vh, 20px);\n font-weight: 600;\n color: ").concat(statusTitleColor, ";\n margin-bottom: clamp(2px, 0.5vh, 4px);\n line-height: 1.2;\n }\n \n .voice-status-subtitle {\n font-size: clamp(10px, 2vh, 14px);\n color: ").concat(statusSubtitleColor, ";\n line-height: 1.2;\n }\n \n /* Start Call Button - Scales with container */\n .start-call-btn {\n margin: 0;\n width: min(280px, 70vw)").concat(important, ";\n height: clamp(48px, 8vh, 64px)").concat(important, ";\n border-radius: clamp(24px, 4vh, 32px)").concat(important, ";\n border: none").concat(important, ";\n background: ").concat(startCallBtnColor).concat(important, ";\n color: ").concat(startCallBtnTextColor).concat(important, ";\n font-size: clamp(14px, 2.5vh, 18px)").concat(important, ";\n font-weight: 600").concat(important, ";\n cursor: pointer").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n gap: clamp(8px, 1.5vh, 12px);\n box-shadow: 0 12px 30px rgba(102, 126, 234, 0.4);\n transition: all 0.3s ease;\n flex-shrink: 0;\n }\n \n .start-call-btn svg {\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n width: auto").concat(important, ";\n height: auto").concat(important, ";\n }\n \n .start-call-btn svg {\n width: clamp(22px, 4vh, 32px);\n height: clamp(22px, 4vh, 32px);\n }\n \n .start-call-btn:hover {\n transform: translateY(-2px);\n box-shadow: 0 12px 28px rgba(102, 126, 234, 0.5);\n }\n \n .start-call-btn:active {\n transform: translateY(-1px);\n }\n .start-call-btn:disabled {\n cursor: not-allowed").concat(important, ";\n opacity: 0.6").concat(important, ";\n }\n \n /* Connecting Spinner */\n .connecting-spinner {\n display: inline-block;\n width: 16px;\n height: 16px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: ").concat(startCallBtnTextColor, ";\n border-radius: 50%;\n animation: spin 1s linear infinite;\n margin-right: 8px;\n vertical-align: middle;\n }\n \n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n \n /* Voice Transcript - Scales with container */\n .voice-transcript {\n background: ").concat(transcriptBg, ";\n padding: clamp(6px, 1.5vh, 12px);\n border-radius: 10px;\n width: min(360px, calc(100% - 40px));\n margin: 0;\n min-height: clamp(80px, 15vh, 120px);\n max-height: clamp(100px, 18vh, 140px);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n flex-shrink: 1;\n overflow-y: auto;\n overflow-x: hidden;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n }\n \n .transcript-label {\n font-size: clamp(8px, 1.4vh, 11px);\n color: ").concat(transcriptLabelColor, ";\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: clamp(3px, 0.6vh, 6px);\n flex-shrink: 0;\n word-wrap: break-word;\n }\n \n .transcript-text {\n font-size: clamp(11px, 2vh, 15px);\n color: ").concat(transcriptTextColor, ";\n line-height: 1.4;\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n word-wrap: break-word;\n overflow-wrap: break-word;\n }\n \n .transcript-text.empty {\n color: #cbd5e1;\n font-style: italic;\n }\n \n /* Voice Text Input Area - Type while on voice call */\n .voice-text-input-area {\n width: 100%;\n padding: 14px 16px;\n margin: 0;\n flex-shrink: 0;\n background: #ffffff;\n border-top: 1px solid rgba(0, 0, 0, 0.06);\n box-sizing: border-box;\n }\n \n .voice-input-wrapper {\n display: flex;\n align-items: center;\n gap: 10px;\n background: #f1f5f9;\n border-radius: 12px;\n padding: 4px 4px 4px 14px;\n border: 1px solid transparent;\n transition: all 0.2s ease;\n }\n \n .voice-input-wrapper:focus-within {\n background: #fff;\n border-color: #a855f7;\n box-shadow: 0 0 0 3px rgba(168, 85, 247, 0.1);\n }\n \n .voice-text-input {\n flex: 1;\n border: none;\n outline: none;\n font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 14px;\n color: ").concat(liveTranscriptTextColor).concat(important, ";\n background: transparent;\n padding: 10px 0;\n }\n \n .voice-text-input::placeholder {\n color: #94a3b8;\n }\n \n .voice-send-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n height: 38px;\n border-radius: 10px;\n border: none;\n background: ").concat(voiceSendButtonColor).concat(important, ";\n color: #fff;\n cursor: pointer;\n transition: transform 0.2s ease;\n flex-shrink: 0;\n }\n \n .voice-send-btn:hover:not(:disabled) {\n transform: scale(1.05);\n }\n \n .voice-send-btn:active:not(:disabled) {\n transform: scale(0.95);\n }\n \n .voice-send-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n }\n \n .voice-send-btn svg {\n width: 16px;\n height: 16px;\n }\n \n .voice-input-hint {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 5px;\n margin-top: 10px;\n font-size: 11px;\n color: #64748b;\n transition: color 0.2s ease;\n }\n \n .voice-input-hint svg {\n width: 12px;\n height: 12px;\n color: #10b981;\n }\n \n /* Text input status states */\n .voice-input-hint--sending {\n color: #666;\n }\n \n .voice-input-hint--success {\n color: #28a745;\n }\n \n .voice-input-hint--queued {\n color: #ffc107;\n }\n \n .voice-input-hint--error {\n color: #dc3545;\n }\n \n /* Disabled input styling */\n .voice-text-input:disabled,\n #mobileTextInput:disabled {\n background-color: #f5f5f5;\n cursor: not-allowed;\n }\n \n .voice-send-btn:disabled,\n #mobileSendBtn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n \n /* Voice Controls - Scales with container */\n .voice-controls {\n display: flex;\n gap: clamp(8px, 1.8vh, 14px);\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n margin: 0;\n padding-top: 0;\n }\n \n .voice-control-btn {\n width: clamp(48px, 9vh, 72px)").concat(important, ";\n height: clamp(48px, 9vh, 72px)").concat(important, ";\n min-width: clamp(48px, 9vh, 72px)").concat(important, ";\n min-height: clamp(48px, 9vh, 72px)").concat(important, ";\n max-width: clamp(48px, 9vh, 72px)").concat(important, ";\n max-height: clamp(48px, 9vh, 72px)").concat(important, ";\n aspect-ratio: 1").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n transition: all 0.2s;\n position: relative").concat(important, ";\n flex-shrink: 0;\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 .voice-control-btn svg {\n width: 48%").concat(important, ";\n height: 48%").concat(important, ";\n min-width: 48%").concat(important, ";\n min-height: 48%").concat(important, ";\n max-width: 48%").concat(important, ";\n max-height: 48%").concat(important, ";\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n margin: 0").concat(important, ";\n padding: 0").concat(important, ";\n position: relative").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n \n .voice-control-btn.primary {\n width: clamp(54px, 10vh, 80px)").concat(important, ";\n height: clamp(54px, 10vh, 80px)").concat(important, ";\n min-width: clamp(54px, 10vh, 80px)").concat(important, ";\n min-height: clamp(54px, 10vh, 80px)").concat(important, ";\n max-width: clamp(54px, 10vh, 80px)").concat(important, ";\n max-height: clamp(54px, 10vh, 80px)").concat(important, ";\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)").concat(important, ";\n color: white").concat(important, ";\n box-shadow: 0 6px 18px rgba(102, 126, 234, 0.4);\n }\n \n .voice-control-btn.primary:hover {\n transform: scale(1.05);\n }\n \n .voice-control-btn.primary.active {\n background: ").concat(endCallBtnColor).concat(important, ";\n animation: recordingPulse 1.5s ease-in-out infinite;\n }\n .voice-control-btn.primary.active svg {\n fill: white").concat(important, ";\n }\n \n /* More specific selectors for WordPress compatibility */\n #endCallBtn.voice-control-btn.primary.active,\n #muteBtn.voice-control-btn.secondary,\n #speakerBtn.voice-control-btn.secondary {\n border-radius: 50% !important;\n width: clamp(48px, 9vh, 72px) !important;\n height: clamp(48px, 9vh, 72px) !important;\n min-width: clamp(48px, 9vh, 72px) !important;\n min-height: clamp(48px, 9vh, 72px) !important;\n max-width: clamp(48px, 9vh, 72px) !important;\n max-height: clamp(48px, 9vh, 72px) !important;\n aspect-ratio: 1 !important;\n overflow: hidden !important;\n }\n \n #endCallBtn.voice-control-btn.primary.active {\n width: clamp(54px, 10vh, 80px) !important;\n height: clamp(54px, 10vh, 80px) !important;\n min-width: clamp(54px, 10vh, 80px) !important;\n min-height: clamp(54px, 10vh, 80px) !important;\n max-width: clamp(54px, 10vh, 80px) !important;\n max-height: clamp(54px, 10vh, 80px) !important;\n }\n \n \n /* Microphone button specific background color */\n #muteBtn.voice-control-btn.secondary {\n background: ").concat(micButtonColor).concat(important, ";\n }\n \n /* Speaker button specific background color */\n #speakerBtn.voice-control-btn.secondary {\n background: ").concat(speakerButtonColor).concat(important, ";\n }\n \n #endCallBtn.voice-control-btn.primary.active svg,\n #muteBtn.voice-control-btn.secondary svg,\n #speakerBtn.voice-control-btn.secondary svg {\n display: block !important;\n visibility: visible !important;\n opacity: 1 !important;\n width: 48% !important;\n height: 48% !important;\n margin: 0 !important;\n padding: 0 !important;\n position: relative !important;\n }\n \n @keyframes recordingPulse {\n 0%, 100% { \n box-shadow: 0 6px 16px rgba(239, 68, 68, 0.4);\n }\n 50% { \n box-shadow: 0 6px 24px rgba(239, 68, 68, 0.7);\n }\n }\n \n .voice-control-btn.secondary {\n background: ").concat(controlBtnColor).concat(important, ";\n color: ").concat(controlBtnSecondaryColor).concat(important, ";\n box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);\n width: clamp(48px, 9vh, 72px)").concat(important, ";\n height: clamp(48px, 9vh, 72px)").concat(important, ";\n min-width: clamp(48px, 9vh, 72px)").concat(important, ";\n min-height: clamp(48px, 9vh, 72px)").concat(important, ";\n max-width: clamp(48px, 9vh, 72px)").concat(important, ";\n max-height: clamp(48px, 9vh, 72px)").concat(important, ";\n }\n \n .voice-control-btn.secondary:hover {\n background: #f8fafc").concat(important, ";\n transform: scale(1.05);\n }\n \n .voice-control-btn.secondary.muted {\n background: #f3f4f6").concat(important, ";\n }\n \n .voice-control-btn.secondary.muted .mute-icon {\n stroke: #9ca3af !important;\n }\n \n .voice-control-btn.secondary svg {\n width: 48%").concat(important, ";\n height: 48%").concat(important, ";\n display: block").concat(important, ";\n visibility: visible").concat(important, ";\n opacity: 1").concat(important, ";\n margin: 0").concat(important, ";\n padding: 0").concat(important, ";\n position: relative").concat(important, ";\n }\n \n .voice-timer {\n position: absolute;\n bottom: clamp(-18px, -3vh, -24px);\n font-size: clamp(9px, 1.6vh, 12px);\n color: #64748b;\n font-weight: 500;\n white-space: nowrap;\n }\n \n /* ===== DESKTOP VOICE SECTION - New Design ===== */\n .desktop-voice-section {\n padding: 16px 20px;\n background: linear-gradient(180deg, #ffffff 0%, rgba(168, 85, 247, 0.04) 100%);\n width: 100%;\n flex-shrink: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n box-sizing: border-box;\n text-align: center;\n }\n \n /* Ensure all children are centered */\n .desktop-voice-section > * {\n margin-left: auto;\n margin-right: auto;\n }\n \n .desktop-timer {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 5px 10px;\n background: rgba(0, 0, 0, 0.05);\n border-radius: 20px;\n font-family: 'JetBrains Mono', 'Courier New', monospace;\n font-size: 11px;\n color: #64748b;\n margin: 0 auto;\n }\n \n .desktop-timer .timer-dot {\n width: 5px;\n height: 5px;\n background: #ef4444;\n border-radius: 50%;\n animation: timerPulse 1s ease-in-out infinite;\n }\n \n @keyframes timerPulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n }\n \n .desktop-avatar-container {\n margin-bottom: 12px;\n }\n \n .desktop-avatar-large {\n width: 80px !important;\n height: 80px !important;\n border-radius: 20px !important;\n margin: 0 auto 12px !important;\n box-shadow: 0 8px 24px rgba(168, 85, 247, 0.35) !important;\n background: linear-gradient(135deg, ").concat(avatarBg, " 0%, ").concat(avatarActiveBg, " 100%) !important;\n }\n \n .desktop-waveform {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 36px;\n width: 100%;\n }\n \n .desktop-waveform-bar {\n width: 4px;\n background: rgba(255, 255, 255, 0.9);\n border-radius: 2px;\n animation: waveformAnimation 0.8s ease-in-out infinite;\n }\n \n .desktop-waveform-bar[data-index=\"0\"] { height: 14px; animation-delay: 0s; }\n .desktop-waveform-bar[data-index=\"1\"] { height: 26px; animation-delay: 0.1s; }\n .desktop-waveform-bar[data-index=\"2\"] { height: 36px; animation-delay: 0.2s; }\n .desktop-waveform-bar[data-index=\"3\"] { height: 22px; animation-delay: 0.3s; }\n .desktop-waveform-bar[data-index=\"4\"] { height: 16px; animation-delay: 0.4s; }\n \n @keyframes waveformAnimation {\n 0%, 100% { transform: scaleY(0.5); }\n 50% { transform: scaleY(1); }\n }\n \n .desktop-waveform.paused .desktop-waveform-bar {\n animation-play-state: paused;\n }\n\n /* New main waveform (used in updated template) */\n .desktop-main-waveform {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 3px;\n height: 48px;\n width: 100%;\n max-width: 200px;\n margin: 0 auto;\n padding: 0 20px;\n }\n\n .desktop-main-waveform .waveform-bar {\n width: 3px;\n background: #a855f7;\n border-radius: 2px;\n height: 8px;\n animation: waveformAnimation 0.8s ease-in-out infinite;\n }\n\n .desktop-main-waveform .waveform-bar:nth-child(1) { height: 12px; animation-delay: 0s; }\n .desktop-main-waveform .waveform-bar:nth-child(2) { height: 20px; animation-delay: 0.05s; }\n .desktop-main-waveform .waveform-bar:nth-child(3) { height: 28px; animation-delay: 0.1s; }\n .desktop-main-waveform .waveform-bar:nth-child(4) { height: 36px; animation-delay: 0.15s; }\n .desktop-main-waveform .waveform-bar:nth-child(5) { height: 44px; animation-delay: 0.2s; }\n .desktop-main-waveform .waveform-bar:nth-child(6) { height: 50px; animation-delay: 0.25s; }\n .desktop-main-waveform .waveform-bar:nth-child(7) { height: 54px; animation-delay: 0.3s; }\n .desktop-main-waveform .waveform-bar:nth-child(8) { height: 56px; animation-delay: 0.35s; }\n .desktop-main-waveform .waveform-bar:nth-child(9) { height: 54px; animation-delay: 0.4s; }\n .desktop-main-waveform .waveform-bar:nth-child(10) { height: 50px; animation-delay: 0.45s; }\n .desktop-main-waveform .waveform-bar:nth-child(11) { height: 44px; animation-delay: 0.5s; }\n .desktop-main-waveform .waveform-bar:nth-child(12) { height: 36px; animation-delay: 0.55s; }\n .desktop-main-waveform .waveform-bar:nth-child(13) { height: 28px; animation-delay: 0.6s; }\n .desktop-main-waveform .waveform-bar:nth-child(14) { height: 20px; animation-delay: 0.65s; }\n .desktop-main-waveform .waveform-bar:nth-child(15) { height: 12px; animation-delay: 0.7s; }\n\n .desktop-status {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 5px;\n font-size: 12px;\n color: ").concat(statusTitleColor, ";\n font-weight: 500;\n width: 100%;\n text-align: center;\n margin: 0 auto;\n }\n \n .desktop-status .status-dot {\n width: 5px;\n height: 5px;\n background: ").concat(statusDotColor, ";\n border-radius: 50%;\n animation: statusPulse 1.5s ease-in-out infinite;\n }\n \n @keyframes statusPulse {\n 0%, 100% { transform: scale(1); opacity: 1; }\n 50% { transform: scale(1.15); opacity: 0.8; }\n }\n \n .desktop-live-waveform {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 3px;\n height: 48px;\n margin-bottom: 20px;\n }\n \n .desktop-live-bar {\n width: 3px;\n background: linear-gradient(180deg, ").concat(avatarBg, ", ").concat(avatarActiveBg, ");\n border-radius: 2px;\n transition: height 0.1s ease;\n height: 8px;\n }\n \n .desktop-controls {\n display: flex;\n gap: 8px;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin: 0 auto;\n }\n \n .desktop-control-btn {\n width: 46px;\n height: 46px;\n border-radius: 50%;\n border: none;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n \n .desktop-control-btn svg {\n width: 20px;\n height: 20px;\n }\n \n .desktop-mic-icon,\n .desktop-speaker-icon {\n width: 20px !important;\n height: 20px !important;\n display: block;\n flex-shrink: 0;\n }\n \n .desktop-control-btn.secondary.muted .desktop-mic-icon,\n .desktop-control-btn.secondary.muted .desktop-speaker-icon {\n width: 20px !important;\n height: 20px !important;\n }\n \n \n .desktop-control-btn.secondary:hover {\n background: rgba(0, 0, 0, 0.1);\n color: #1e293b;\n transform: scale(1.05);\n }\n .desktop-control-btn.secondary.muted {\n background: #f3f4f6;\n color: #9ca3af;\n }\n \n .desktop-control-btn.secondary.muted .desktop-mic-icon,\n .desktop-control-btn.secondary.muted .desktop-speaker-icon {\n stroke: #9ca3af;\n }\n \n .desktop-control-btn.secondary {\n position: relative;\n }\n \n \n /* Desktop microphone button background color */\n #desktopMuteBtn.desktop-control-btn.secondary,\n #desktopMuteBtnCompact.compact-control-btn.secondary {\n background: ").concat(micButtonColor).concat(important, ";\n }\n \n /* Desktop speaker button background color */\n #desktopSpeakerBtn.desktop-control-btn.secondary,\n #desktopSpeakerBtnCompact.compact-control-btn.secondary {\n background: ").concat(speakerButtonColor).concat(important, ";\n }\n .desktop-mic-off-icon,\n .desktop-speaker-off-icon {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 46px;\n height: 46px;\n transform: translate(-50%, -50%);\n z-index: 10;\n }\n .desktop-control-btn.danger {\n width: 50px;\n height: 50px;\n background: ").concat(endCallBtnColor).concat(important, ";\n color: #ffffff;\n box-shadow: 0 4px 14px rgba(239, 68, 68, 0.4);\n overflow: visible;\n }\n \n .desktop-control-btn.danger svg {\n width: 20px;\n height: 20px;\n overflow: visible;\n }\n \n .desktop-control-btn.danger:hover {\n transform: scale(1.06);\n box-shadow: 0 6px 20px rgba(239, 68, 68, 0.5);\n }\n \n .desktop-control-btn.danger:hover {\n transform: scale(1.06);\n box-shadow: 0 6px 20px rgba(239, 68, 68, 0.5);\n }\n \n \n /* ===== COMPACT VOICE SECTION (Single-row layout) ===== */\n .compact-voice-section {\n padding: 10px 14px;\n background: linear-gradient(180deg, #ffffff, rgba(168, 85, 247, 0.04));\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border-bottom: 1px solid rgba(0, 0, 0, 0.06);\n min-height: 62px;\n flex-shrink: 0;\n }\n \n .compact-left {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n \n .compact-avatar {\n width: 42px;\n height: 42px;\n min-width: 42px;\n min-height: 42px;\n border-radius: 12px;\n background: transparent;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: visible;\n position: relative;\n }\n \n /* Waveform container in compact avatar - ensure it's visible */\n .compact-avatar .desktop-main-waveform {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 2px;\n height: 100%;\n width: 100%;\n padding: 4px;\n transform: scale(0.75);\n transform-origin: center;\n }\n \n /* Ensure waveform bars are visible and animated in compact mode */\n .compact-avatar .desktop-main-waveform .waveform-bar {\n width: 2px;\n min-width: 2px;\n background: #a855f7;\n border-radius: 1px;\n flex-shrink: 0;\n animation: waveformAnimation 0.8s ease-in-out infinite;\n }\n \n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(1) { height: 6px; animation-delay: 0s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(2) { height: 10px; animation-delay: 0.05s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(3) { height: 14px; animation-delay: 0.1s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(4) { height: 18px; animation-delay: 0.15s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(5) { height: 22px; animation-delay: 0.2s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(6) { height: 26px; animation-delay: 0.25s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(7) { height: 28px; animation-delay: 0.3s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(8) { height: 30px; animation-delay: 0.35s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(9) { height: 28px; animation-delay: 0.4s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(10) { height: 26px; animation-delay: 0.45s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(11) { height: 22px; animation-delay: 0.5s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(12) { height: 18px; animation-delay: 0.55s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(13) { height: 14px; animation-delay: 0.6s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(14) { height: 10px; animation-delay: 0.65s; }\n .compact-avatar .desktop-main-waveform .waveform-bar:nth-child(15) { height: 6px; animation-delay: 0.7s; }\n\n }\n \n .compact-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n \n .compact-timer {\n display: flex;\n align-items: center;\n gap: 5px;\n font-family: 'JetBrains Mono', 'Courier New', monospace;\n font-size: 13px;\n font-weight: 600;\n color: ").concat(liveTranscriptTextColor).concat(important, ";\n }\n \n .compact-timer .timer-dot {\n width: 6px;\n height: 6px;\n background: #ef4444;\n border-radius: 50%;\n animation: timerPulse 1s ease-in-out infinite;\n }\n \n .compact-status {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: ").concat(statusTitleColor, ";\n }\n \n .compact-status .status-dot {\n width: 5px;\n height: 5px;\n background: ").concat(statusDotColor, ";\n border-radius: 50%;\n animation: statusPulse 1.5s ease-in-out infinite;\n }\n \n @keyframes statusPulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.7; transform: scale(1.1); }\n }\n \n .compact-controls {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n \n .compact-control-btn {\n width: 36px;\n height: 36px;\n min-width: 36px;\n min-height: 36px;\n border-radius: 50%;\n border: none;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s ease;\n padding: 0;\n position: relative;\n }\n \n .compact-control-btn svg {\n width: 16px;\n height: 16px;\n }\n \n .compact-control-btn.secondary {\n background: #f1f5f9;\n color: #64748b;\n }\n \n \n /* Compact speaker button background color */\n #desktopSpeakerBtnCompact.compact-control-btn.secondary {\n background: ").concat(speakerButtonColor).concat(important, ";\n }\n /* Compact microphone button background color */\n #desktopMuteBtnCompact.compact-control-btn.secondary {\n background: ").concat(micButtonColor).concat(important, ";\n }\n \n .compact-control-btn.secondary:hover {\n background: #e2e8f0;\n color: #475569;\n }\n \n .compact-control-btn.secondary.muted {\n background: rgba(239, 68, 68, 0.1);\n color: #ef4444;\n }\n \n \n .compact-control-btn.secondary.muted .desktop-mic-icon,\n .compact-control-btn.secondary.muted .desktop-speaker-icon {\n stroke: #ef4444;\n }\n \n .compact-control-btn.secondary.muted .desktop-mic-off-icon,\n .compact-control-btn.secondary.muted .desktop-speaker-off-icon {\n width: 46px;\n height: 46px;\n }\n\n .compact-control-btn.danger {\n width: 40px;\n height: 40px;\n min-width: 40px;\n min-height: 40px;\n background: ").concat(endCallBtnColor).concat(important, ";\n color: white;\n box-shadow: 0 3px 10px rgba(239, 68, 68, 0.35);\n }\n \n .compact-control-btn.danger:hover {\n transform: scale(1.05);\n box-shadow: 0 4px 14px rgba(239, 68, 68, 0.45);\n }\n \n .compact-control-btn.danger svg {\n width: 18px;\n height: 18px;\n overflow: visible;\n }\n \n/* ===== CONVERSATION PANEL ===== */\n .desktop-conversation-panel {\n border-top: 1px solid rgba(0, 0, 0, 0.06);\n width: 100%;\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow: hidden;\n flex: 1;\n }\n \n .conversation-header {\n padding: 10px 16px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: #fafafa;\n cursor: pointer;\n flex-shrink: 0;\n border-bottom: 1px solid rgba(0, 0, 0, 0.04);\n transition: background 0.2s ease;\n }\n \n .conversation-header:hover {\n background: #f5f5f5;\n }\n \n .conversation-label {\n font-size: 11px;\n font-weight: 600;\n color: #64748b;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n \n .conversation-toggle {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: #94a3b8;\n font-weight: 500;\n }\n \n .conversation-toggle svg {\n transition: transform 0.2s ease;\n }\n \n .conversation-header.expanded .conversation-toggle svg {\n transform: rotate(180deg);\n }\n \n /* ===== COLLAPSED: Live Transcript Only (2 lines) ===== */\n .live-transcript-collapsed {\n padding: 12px 16px;\n background: #fafafa;\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex-shrink: 0;\n min-height: 50px;\n }\n \n .live-indicator {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 9px;\n font-weight: 700;\n color: ").concat(liveIndicatorTextColor).concat(important, ";\n text-transform: uppercase;\n letter-spacing: 0.08em;\n }\n \n .live-indicator .live-dot {\n height: 5px;\n width: 5px;\n background: ").concat(liveIndicatorDotColor).concat(important, ";\n border-radius: 50%;\n animation: statusPulse 1.5s ease-in-out infinite;\n }\n \n .live-text-collapsed {\n font-size: ").concat(liveTranscriptFontSize).concat(important, ";\n color: ").concat(liveTranscriptTextColor).concat(important, ";\n line-height: 1.5;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n \n .live-text-collapsed.muted {\n color: #94a3b8;\n font-style: italic;\n }\n \n .typing-cursor {\n display: inline-block;\n width: 2px;\n height: 14px;\n background: #a855f7;\n margin-left: 2px;\n animation: blink 1s step-end infinite;\n vertical-align: middle;\n }\n \n @keyframes blink {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0; }\n }\n \n /* ===== EXPANDED: Full Conversation History ===== */\n .conversation-history {\n flex: 1;\n flex-direction: column;\n gap: 10px;\n overflow-y: auto;\n padding: 14px 16px;\n min-height: 0;\n }\n .conversation-history.visible {\n display: flex;\n }\n\n .conversation-messages {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n /* Message Rows */\n .message-row {\n display: flex;\n gap: 8px;\n animation: msgIn 0.25s ease;\n }\n \n @keyframes msgIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n @keyframes slideIn {\n from { opacity: 0; transform: translateY(-10px); }\n to { opacity: 1; transform: translateY(0); }\n }\n \n @keyframes slideOut {\n from { opacity: 1; transform: translateY(0); }\n to { opacity: 0; transform: translateY(-10px); }\n }\n \n .message-row.user {\n flex-direction: row-reverse;\n }\n \n .message-avatar {\n width: 28px;\n height: 28px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n\n .message-avatar.assistant {\n background: linear-gradient(135deg, #c084fc, #a855f7);\n }\n\n .message-avatar.assistant .orb {\n width: 12px;\n height: 12px;\n background: rgba(255, 255, 255, 0.9);\n border-radius: 50%;\n }\n\n .message-avatar.user {\n background: linear-gradient(135deg, #64748b, #475569);\n color: #fff;\n font-size: 11px;\n font-weight: 600;\n }\n\n .message-bubble {\n max-width: 80%;\n padding: 9px 13px;\n border-radius: 12px;\n font-size: 13px;\n line-height: 1.5;\n }\n \n .message-row.assistant .message-bubble {\n background: #fff;\n color: ").concat(liveTranscriptTextColor).concat(important, ";\n border: 1px solid rgba(0, 0, 0, 0.06);\n border-bottom-left-radius: 4px;\n }\n \n .message-row.user .message-bubble {\n background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);\n color: #fff;\n border-bottom-right-radius: 4px;\n }\n \n /* Live message at bottom of history */\n .live-message-row {\n display: flex;\n gap: 8px;\n padding-top: 8px;\n border-top: 1px dashed rgba(0, 0, 0, 0.08);\n margin-top: auto;\n }\n \n .live-message-row .message-bubble {\n background: #fff;\n border: 1px solid rgba(16, 185, 129, 0.3);\n color: ").concat(liveTranscriptTextColor).concat(important, ";\n position: relative;\n border-radius: 14px;\n border-bottom-left-radius: 4px;\n }\n \n .live-message-row .live-badge {\n position: absolute;\n top: -8px;\n left: 10px;\n background: #10b981;\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.05em;\n }\n \n /* Hide/show based on expanded state */\n .desktop-conversation-panel.collapsed .conversation-history {\n display: none;\n }\n \n .desktop-conversation-panel.collapsed .live-transcript-collapsed {\n display: flex;\n }\n \n .desktop-conversation-panel.expanded .conversation-history {\n display: flex;\n max-height: 400px;\n }\n \n .desktop-conversation-panel.expanded .live-transcript-collapsed {\n display: none;\n }\n \n /* ===== DARK MODE SUPPORT ===== */\n @media (prefers-color-scheme: dark) {\n .desktop-voice-section {\n background: linear-gradient(180deg, #1e1e2e 0%, rgba(168, 85, 247, 0.08) 100%);\n }\n\n .desktop-timer {\n color: #94a3b8;\n }\n\n .desktop-status {\n color: #34d399;\n }\n\n .desktop-status .status-dot {\n \n .desktop-control-btn.secondary.muted {\n background: rgba(255, 255, 255, 0.1);\n color: #9ca3af;\n \n .compact-voice-section {\n background: linear-gradient(180deg, #1e1e2e 0%, rgba(168, 85, 247, 0.08) 100%);\n border-bottom: 1px solid rgba(255, 255, 255, 0.08);\n }\n \n .compact-timer {\n color: #e2e8f0;\n }\n \n .compact-status {\n color: #34d399;\n }\n \n .compact-status .status-dot {\n background: #34d399;\n }\n \n .compact-control-btn.secondary {\n background: rgba(255, 255, 255, 0.08);\n color: #94a3b8;\n }\n \n .compact-control-btn.secondary:hover {\n background: rgba(255, 255, 255, 0.15);\n color: #e2e8f0;\n }\n \n .compact-control-btn.secondary.muted {\n background: rgba(239, 68, 68, 0.2);\n color: #f87171;\n }\n\n }\n \n .desktop-control-btn.secondary.muted svg {\n \n .desktop-control-btn.secondary {\n position: relative;\n }\n \n /* Desktop microphone button background color */\n #desktopMuteBtn.desktop-control-btn.secondary,\n #desktopMuteBtnCompact.compact-control-btn.secondary {\n background: ").concat(micButtonColor).concat(important, ";\n }\n \n .desktop-mic-off-icon,\n .desktop-speaker-off-icon {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 20px;\n height: 20px;\n }\n \n .desktop-control-btn.secondary.muted {\n background: #f3f4f6;\n color: #9ca3af;\n }\n \n .desktop-control-btn.secondary.muted svg {\n \n .desktop-control-btn.secondary {\n position: relative;\n }\n \n /* Desktop microphone button background color */\n #desktopMuteBtn.desktop-control-btn.secondary,\n #desktopMuteBtnCompact.compact-control-btn.secondary {\n background: ").concat(micButtonColor).concat(important, ";\n }\n \n .desktop-mic-off-icon,\n .desktop-speaker-off-icon {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 20px;\n height: 20px;\n }\n stroke: #9ca3af;\n }\n stroke: #9ca3af;\n }\n background: #34d399;\n }\n\n .desktop-control-btn.secondary {\n background: rgba(255, 255, 255, 0.08);\n color: #94a3b8;\n }\n\n .desktop-control-btn.secondary:hover {\n background: rgba(255, 255, 255, 0.15);\n color: #e2e8f0;\n }\n\n .desktop-conversation-panel {\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n }\n\n .conversation-header {\n background: #1a1a2e;\n }\n\n .conversation-header:hover {\n background: #252540;\n }\n\n .conversation-label {\n color: #94a3b8;\n }\n\n .conversation-toggle {\n color: #64748b;\n }\n\n .live-transcript-preview {\n background: #1a1a2e;\n }\n .live-indicator {\n color: ").concat(liveIndicatorTextColor).concat(important, ";\n }\n .live-indicator .live-dot {\n background: ").concat(liveIndicatorDotColor).concat(important, ";\n }\n\n .live-text {\n color: #e2e8f0;\n }\n\n .live-text.muted {\n color: #64748b;\n }\n\n .conversation-history {\n background: #1a1a2e;\n }\n\n .conversation-msg.assistant .conversation-msg-bubble {\n background: #252540;\n color: #e2e8f0;\n border: 1px solid rgba(255, 255, 255, 0.08);\n }\n\n .voice-text-input-area {\n background: #1a1a2e;\n }\n\n .voice-text-input {\n background: #252540 !important;\n color: #e2e8f0 !important;\n border-color: rgba(255, 255, 255, 0.1) !important;\n }\n\n .voice-text-input::placeholder {\n color: #64748b !important;\n }\n\n .voice-text-input:focus {\n border-color: ").concat(avatarBg, " !important;\n box-shadow: 0 0 0 3px rgba(168, 85, 247, 0.2) !important;\n }\n\n .voice-input-hint {\n color: #64748b !important;\n }\n\n .voice-send-btn {\n background: ").concat(voiceSendButtonColor, " !important;\n }\n\n .voice-send-btn:disabled {\n background: #374151 !important;\n color: #4b5563 !important;\n }\n }\n\n /* Mobile optimization */\n @media (max-width: 768px) {\n .voice-interface {\n padding: 6px 10px;\n }\n }\n \n @media (max-width: 480px) {\n .voice-interface {\n padding: 5px 8px;\n }\n }\n \n /* Mobile Voice Call UI - Shared styles (also used in generateMobileCSS) */\n ").concat(this._getSharedMobileCSS(important), "\n ");
|
|
18285
18705
|
}
|
|
18286
18706
|
|
|
18287
18707
|
/**
|
|
@@ -18291,7 +18711,7 @@ var Styles = /*#__PURE__*/function () {
|
|
|
18291
18711
|
}, {
|
|
18292
18712
|
key: "_getSharedMobileCSS",
|
|
18293
18713
|
value: function _getSharedMobileCSS(important) {
|
|
18294
|
-
return "\n .mobile-duration-dot {\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 {\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 {\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 {\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 ");
|
|
18714
|
+
return "\n .mobile-duration-dot {\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 {\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 {\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 /* Mobile microphone button background color */\n #mobileMuteBtn.mobile-control-btn {\n background: ").concat(this.config.micButtonColor || this.config.controlButtonColor || '#FFFFFF').concat(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 {\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 ");
|
|
18295
18715
|
}
|
|
18296
18716
|
|
|
18297
18717
|
/**
|
|
@@ -22611,9 +23031,18 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
22611
23031
|
|
|
22612
23032
|
// Queue for prepared AudioBuffers (ready to schedule)
|
|
22613
23033
|
_this.preparedBuffer = [];
|
|
23034
|
+
|
|
23035
|
+
// Maximum buffer sizes to prevent unbounded memory growth
|
|
23036
|
+
// If backend sends sentences faster than playback, oldest frames are dropped
|
|
23037
|
+
_this.MAX_PREPARED_BUFFER_SIZE = 200; // Max prepared frames (~10-12 seconds at 600ms per frame)
|
|
23038
|
+
_this.MAX_PCM_CHUNK_QUEUE_SIZE = 50; // Max raw PCM chunks
|
|
23039
|
+
|
|
22614
23040
|
_this.isProcessingPcmQueue = false;
|
|
22615
23041
|
_this.isSchedulingFrames = false;
|
|
22616
23042
|
|
|
23043
|
+
// Timeout to detect empty sentences (audio_start but no chunks)
|
|
23044
|
+
_this._emptySentenceTimeout = null;
|
|
23045
|
+
|
|
22617
23046
|
// Minimal scheduling delay to avoid scheduling audio in the past
|
|
22618
23047
|
// REMOVED: Lookahead buffering was causing quality degradation due to browser resampling/timing issues
|
|
22619
23048
|
// Now we only schedule with minimal delay (20ms) just enough to avoid gaps
|
|
@@ -22624,6 +23053,15 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
22624
23053
|
_this.sentenceTimings = []; // [{startTime, text, displayed}, ...]
|
|
22625
23054
|
_this.isCheckingTranscripts = false;
|
|
22626
23055
|
|
|
23056
|
+
// Flag to prevent queuing new audio after stopImmediate() is called (barge-in protection)
|
|
23057
|
+
// This prevents race conditions where audio chunks arrive after stop but before sources are stopped
|
|
23058
|
+
// Cleared when markNewSentence() is called (signals new audio is starting)
|
|
23059
|
+
_this._isStopped = false;
|
|
23060
|
+
|
|
23061
|
+
// Track current sentence ID to reject chunks from previous sentences
|
|
23062
|
+
// Incremented each time markNewSentence() is called
|
|
23063
|
+
_this._currentSentenceId = 0;
|
|
23064
|
+
|
|
22627
23065
|
// Codec registry
|
|
22628
23066
|
|
|
22629
23067
|
_this.codecs = {
|
|
@@ -22724,6 +23162,12 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
22724
23162
|
key: "playAudio",
|
|
22725
23163
|
value: function playAudio(binaryData) {
|
|
22726
23164
|
var _this2 = this;
|
|
23165
|
+
// CRITICAL: Ignore audio if playback was stopped (barge-in protection)
|
|
23166
|
+
// This prevents overlapping audio when chunks arrive after stopImmediate() is called
|
|
23167
|
+
if (this._isStopped) {
|
|
23168
|
+
console.log('🛑 AudioPlayer: Ignoring audio chunk - playback was stopped (barge-in)');
|
|
23169
|
+
return;
|
|
23170
|
+
}
|
|
22727
23171
|
try {
|
|
22728
23172
|
var audioBlob = this.createAudioBlob(binaryData);
|
|
22729
23173
|
this.audioQueue.push(audioBlob);
|
|
@@ -22754,13 +23198,34 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
22754
23198
|
value: (function () {
|
|
22755
23199
|
var _playChunk = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(pcmData) {
|
|
22756
23200
|
var _this3 = this;
|
|
22757
|
-
var preparedFrame, _this$audioContext;
|
|
23201
|
+
var preparedFrame, dropped, _this$audioContext;
|
|
22758
23202
|
return _regenerator().w(function (_context) {
|
|
22759
23203
|
while (1) switch (_context.n) {
|
|
22760
23204
|
case 0:
|
|
23205
|
+
if (!this._isStopped) {
|
|
23206
|
+
_context.n = 1;
|
|
23207
|
+
break;
|
|
23208
|
+
}
|
|
23209
|
+
console.log('🛑 AudioPlayer: Ignoring PCM chunk - playback was stopped (barge-in)');
|
|
23210
|
+
return _context.a(2);
|
|
23211
|
+
case 1:
|
|
22761
23212
|
// Pre-process frame immediately (convert to AudioBuffer)
|
|
22762
23213
|
preparedFrame = this.prepareChunk(pcmData);
|
|
22763
23214
|
if (preparedFrame) {
|
|
23215
|
+
// CRITICAL: Clear empty sentence timeout since chunks are arriving
|
|
23216
|
+
// This resets the timer for the current sentence
|
|
23217
|
+
if (this._emptySentenceTimeout) {
|
|
23218
|
+
clearTimeout(this._emptySentenceTimeout);
|
|
23219
|
+
this._emptySentenceTimeout = null;
|
|
23220
|
+
}
|
|
23221
|
+
|
|
23222
|
+
// CRITICAL: Prevent unbounded buffer growth
|
|
23223
|
+
// If backend sends sentences faster than playback, drop oldest frames
|
|
23224
|
+
if (this.preparedBuffer.length >= this.MAX_PREPARED_BUFFER_SIZE) {
|
|
23225
|
+
dropped = this.preparedBuffer.shift(); // Drop oldest frame
|
|
23226
|
+
console.warn("\u26A0\uFE0F AudioPlayer: preparedBuffer at max size (".concat(this.MAX_PREPARED_BUFFER_SIZE, "), dropped oldest frame"));
|
|
23227
|
+
}
|
|
23228
|
+
|
|
22764
23229
|
// Add prepared frame to buffer
|
|
22765
23230
|
this.preparedBuffer.push(preparedFrame);
|
|
22766
23231
|
|
|
@@ -22768,7 +23233,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
22768
23233
|
|
|
22769
23234
|
// Use requestAnimationFrame to avoid blocking, but ensure scheduling happens
|
|
22770
23235
|
|
|
22771
|
-
if (!this.isSchedulingFrames) {
|
|
23236
|
+
if (!this.isSchedulingFrames && !this._isStopped) {
|
|
22772
23237
|
// Schedule immediately if not already scheduling
|
|
22773
23238
|
|
|
22774
23239
|
this.schedulePreparedFrames();
|
|
@@ -22780,7 +23245,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
22780
23245
|
// Use a short timeout to ensure we check again after current scheduling completes
|
|
22781
23246
|
|
|
22782
23247
|
setTimeout(function () {
|
|
22783
|
-
if (_this3.preparedBuffer.length > 0 && !_this3.isSchedulingFrames) {
|
|
23248
|
+
if (_this3.preparedBuffer.length > 0 && !_this3.isSchedulingFrames && !_this3._isStopped) {
|
|
22784
23249
|
_this3.schedulePreparedFrames();
|
|
22785
23250
|
}
|
|
22786
23251
|
}, 5); // Very short delay to check after current scheduling completes
|
|
@@ -22802,7 +23267,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
22802
23267
|
// Emit error event
|
|
22803
23268
|
this.emit('playbackError', new Error('Failed to prepare PCM chunk for playback'));
|
|
22804
23269
|
}
|
|
22805
|
-
case
|
|
23270
|
+
case 2:
|
|
22806
23271
|
return _context.a(2);
|
|
22807
23272
|
}
|
|
22808
23273
|
}, _callee, this);
|
|
@@ -22919,16 +23384,23 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
22919
23384
|
value: (function () {
|
|
22920
23385
|
var _schedulePreparedFrames = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
|
|
22921
23386
|
var _this4 = this;
|
|
22922
|
-
var queuedFrames, targetLookaheadFrames, _this$audioContext2, _this$audioContext3, _this$audioContext4, _this$audioContext5, scheduledCount, _loop, _t;
|
|
23387
|
+
var queuedFrames, targetLookaheadFrames, _this$audioContext2, _this$audioContext3, _this$audioContext4, _this$audioContext5, scheduledCount, _loop, _ret, _t;
|
|
22923
23388
|
return _regenerator().w(function (_context3) {
|
|
22924
23389
|
while (1) switch (_context3.p = _context3.n) {
|
|
22925
23390
|
case 0:
|
|
22926
|
-
if (!this.
|
|
23391
|
+
if (!this._isStopped) {
|
|
22927
23392
|
_context3.n = 1;
|
|
22928
23393
|
break;
|
|
22929
23394
|
}
|
|
23395
|
+
console.log('🛑 AudioPlayer: Not scheduling frames - playback was stopped (barge-in)');
|
|
22930
23396
|
return _context3.a(2);
|
|
22931
23397
|
case 1:
|
|
23398
|
+
if (!this.isSchedulingFrames) {
|
|
23399
|
+
_context3.n = 2;
|
|
23400
|
+
break;
|
|
23401
|
+
}
|
|
23402
|
+
return _context3.a(2);
|
|
23403
|
+
case 2:
|
|
22932
23404
|
this.isSchedulingFrames = true;
|
|
22933
23405
|
|
|
22934
23406
|
// Schedule multiple frames ahead to ensure continuous playback
|
|
@@ -22940,7 +23412,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
22940
23412
|
if (targetLookaheadFrames === 0 && queuedFrames > 0) {
|
|
22941
23413
|
targetLookaheadFrames = 1; // At least schedule 1 frame
|
|
22942
23414
|
}
|
|
22943
|
-
_context3.p =
|
|
23415
|
+
_context3.p = 3;
|
|
22944
23416
|
// Initialize audio context if needed
|
|
22945
23417
|
|
|
22946
23418
|
if (!this.audioContext) {
|
|
@@ -22951,9 +23423,9 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
22951
23423
|
// This is critical for mobile devices where AudioContext initialization takes time
|
|
22952
23424
|
// Replaces fixed timeouts with event-driven waiting
|
|
22953
23425
|
console.log('🔍 [scheduleWAV] BEFORE waitForAudioContextReady - state:', (_this$audioContext2 = this.audioContext) === null || _this$audioContext2 === void 0 ? void 0 : _this$audioContext2.state, 'currentTime:', (_this$audioContext3 = this.audioContext) === null || _this$audioContext3 === void 0 ? void 0 : _this$audioContext3.currentTime);
|
|
22954
|
-
_context3.n =
|
|
23426
|
+
_context3.n = 4;
|
|
22955
23427
|
return this.waitForAudioContextReady();
|
|
22956
|
-
case
|
|
23428
|
+
case 4:
|
|
22957
23429
|
console.log('🔍 [scheduleWAV] AFTER waitForAudioContextReady - state:', (_this$audioContext4 = this.audioContext) === null || _this$audioContext4 === void 0 ? void 0 : _this$audioContext4.state, 'currentTime:', (_this$audioContext5 = this.audioContext) === null || _this$audioContext5 === void 0 ? void 0 : _this$audioContext5.currentTime);
|
|
22958
23430
|
|
|
22959
23431
|
// Schedule frames up to target lookahead (ensures smooth playback)
|
|
@@ -22964,14 +23436,21 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
22964
23436
|
return _regenerator().w(function (_context2) {
|
|
22965
23437
|
while (1) switch (_context2.n) {
|
|
22966
23438
|
case 0:
|
|
23439
|
+
if (!_this4._isStopped) {
|
|
23440
|
+
_context2.n = 1;
|
|
23441
|
+
break;
|
|
23442
|
+
}
|
|
23443
|
+
console.log('🛑 AudioPlayer: Stopping frame scheduling - playback was stopped');
|
|
23444
|
+
return _context2.a(2, 0);
|
|
23445
|
+
case 1:
|
|
22967
23446
|
// Get next prepared frame
|
|
22968
23447
|
preparedFrame = _this4.preparedBuffer.shift();
|
|
22969
23448
|
if (preparedFrame) {
|
|
22970
|
-
_context2.n =
|
|
23449
|
+
_context2.n = 2;
|
|
22971
23450
|
break;
|
|
22972
23451
|
}
|
|
22973
|
-
return _context2.a(2,
|
|
22974
|
-
case
|
|
23452
|
+
return _context2.a(2, 0);
|
|
23453
|
+
case 2:
|
|
22975
23454
|
// Create source and schedule playback
|
|
22976
23455
|
source = _this4.audioContext.createBufferSource();
|
|
22977
23456
|
source.buffer = preparedFrame.buffer;
|
|
@@ -23048,29 +23527,46 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23048
23527
|
// Track when this buffer finishes (for cleanup only)
|
|
23049
23528
|
|
|
23050
23529
|
source.onended = function () {
|
|
23051
|
-
//
|
|
23530
|
+
// CRITICAL: Check if playback was stopped before processing cleanup
|
|
23531
|
+
// This prevents race conditions where onended fires after stopImmediate() was called
|
|
23532
|
+
if (_this4._isStopped) {
|
|
23533
|
+
// Playback was stopped, ignore this callback
|
|
23534
|
+
return;
|
|
23535
|
+
}
|
|
23052
23536
|
|
|
23537
|
+
// CRITICAL: Only process if source is still in scheduledSources set
|
|
23538
|
+
// This prevents race conditions where stopImmediate() cleared the set but callback fires later
|
|
23539
|
+
if (!_this4.scheduledSources.has(source)) {
|
|
23540
|
+
// Source was already removed (probably by stopImmediate), ignore this callback
|
|
23541
|
+
return;
|
|
23542
|
+
}
|
|
23543
|
+
|
|
23544
|
+
// Remove from tracked sources
|
|
23053
23545
|
_this4.scheduledSources.delete(source);
|
|
23054
|
-
|
|
23546
|
+
|
|
23547
|
+
// Only decrement if we're still playing and buffer count is positive
|
|
23548
|
+
if (!_this4._isStopped && _this4.scheduledBuffers > 0) {
|
|
23549
|
+
_this4.scheduledBuffers--;
|
|
23550
|
+
}
|
|
23055
23551
|
|
|
23056
23552
|
// If no more scheduled buffers and no prepared frames, playback is complete
|
|
23057
23553
|
|
|
23058
|
-
if (_this4.scheduledBuffers === 0 && _this4.preparedBuffer.length === 0 && _this4.pcmChunkQueue.length === 0) {
|
|
23554
|
+
if (_this4.scheduledBuffers === 0 && _this4.preparedBuffer.length === 0 && _this4.pcmChunkQueue.length === 0 && !_this4._isStopped) {
|
|
23059
23555
|
_this4.isPlaying = false;
|
|
23060
23556
|
_this4.isSchedulingFrames = false;
|
|
23061
23557
|
console.log('🛑 AudioPlayer: Emitting playbackStopped event (all buffers finished)');
|
|
23062
23558
|
_this4.emit('playbackStopped');
|
|
23063
|
-
} else if (_this4.preparedBuffer.length > 0) {
|
|
23559
|
+
} else if (_this4.preparedBuffer.length > 0 && !_this4._isStopped) {
|
|
23064
23560
|
// More frames available, schedule them immediately
|
|
23065
23561
|
|
|
23066
23562
|
// Use setTimeout to avoid blocking, but schedule quickly
|
|
23067
23563
|
|
|
23068
23564
|
setTimeout(function () {
|
|
23069
|
-
if (_this4.preparedBuffer.length > 0 && !_this4.isSchedulingFrames) {
|
|
23565
|
+
if (_this4.preparedBuffer.length > 0 && !_this4.isSchedulingFrames && !_this4._isStopped) {
|
|
23070
23566
|
_this4.schedulePreparedFrames();
|
|
23071
23567
|
}
|
|
23072
23568
|
}, 0);
|
|
23073
|
-
} else if (_this4.scheduledBuffers > 0) {
|
|
23569
|
+
} else if (_this4.scheduledBuffers > 0 && !_this4._isStopped) {
|
|
23074
23570
|
// No more prepared frames but still have scheduled buffers playing
|
|
23075
23571
|
|
|
23076
23572
|
// Set up a check to schedule new frames when they arrive
|
|
@@ -23078,9 +23574,13 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23078
23574
|
// Keep checking periodically until we have no more scheduled buffers
|
|
23079
23575
|
|
|
23080
23576
|
var _checkForMoreFrames = function checkForMoreFrames() {
|
|
23577
|
+
// CRITICAL: Check if stopped before scheduling
|
|
23578
|
+
if (_this4._isStopped) {
|
|
23579
|
+
return;
|
|
23580
|
+
}
|
|
23081
23581
|
if (_this4.preparedBuffer.length > 0 && !_this4.isSchedulingFrames && _this4.scheduledBuffers > 0) {
|
|
23082
23582
|
_this4.schedulePreparedFrames();
|
|
23083
|
-
} else if (_this4.scheduledBuffers > 0) {
|
|
23583
|
+
} else if (_this4.scheduledBuffers > 0 && !_this4._isStopped) {
|
|
23084
23584
|
// Keep checking - frames might arrive soon
|
|
23085
23585
|
|
|
23086
23586
|
setTimeout(_checkForMoreFrames, 10);
|
|
@@ -23094,27 +23594,28 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23094
23594
|
console.log('🎵 AudioPlayer: Emitting playbackStarted event');
|
|
23095
23595
|
_this4.emit('playbackStarted');
|
|
23096
23596
|
}
|
|
23097
|
-
case
|
|
23597
|
+
case 3:
|
|
23098
23598
|
return _context2.a(2);
|
|
23099
23599
|
}
|
|
23100
23600
|
}, _loop);
|
|
23101
23601
|
});
|
|
23102
|
-
case
|
|
23602
|
+
case 5:
|
|
23103
23603
|
if (!(this.preparedBuffer.length > 0 && scheduledCount < targetLookaheadFrames)) {
|
|
23104
|
-
_context3.n =
|
|
23604
|
+
_context3.n = 8;
|
|
23105
23605
|
break;
|
|
23106
23606
|
}
|
|
23107
|
-
return _context3.d(_regeneratorValues(_loop()),
|
|
23108
|
-
case
|
|
23109
|
-
|
|
23110
|
-
|
|
23607
|
+
return _context3.d(_regeneratorValues(_loop()), 6);
|
|
23608
|
+
case 6:
|
|
23609
|
+
_ret = _context3.v;
|
|
23610
|
+
if (!(_ret === 0)) {
|
|
23611
|
+
_context3.n = 7;
|
|
23111
23612
|
break;
|
|
23112
23613
|
}
|
|
23113
|
-
return _context3.a(3,
|
|
23114
|
-
case 6:
|
|
23115
|
-
_context3.n = 4;
|
|
23116
|
-
break;
|
|
23614
|
+
return _context3.a(3, 8);
|
|
23117
23615
|
case 7:
|
|
23616
|
+
_context3.n = 5;
|
|
23617
|
+
break;
|
|
23618
|
+
case 8:
|
|
23118
23619
|
// All prepared frames scheduled, reset flag
|
|
23119
23620
|
|
|
23120
23621
|
this.isSchedulingFrames = false;
|
|
@@ -23123,11 +23624,11 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23123
23624
|
|
|
23124
23625
|
// Use requestAnimationFrame for smooth scheduling without blocking
|
|
23125
23626
|
|
|
23126
|
-
if (this.preparedBuffer.length > 0) {
|
|
23627
|
+
if (this.preparedBuffer.length > 0 && !this._isStopped) {
|
|
23127
23628
|
// More frames arrived, schedule them immediately
|
|
23128
23629
|
|
|
23129
23630
|
requestAnimationFrame(function () {
|
|
23130
|
-
if (_this4.preparedBuffer.length > 0 && !_this4.isSchedulingFrames) {
|
|
23631
|
+
if (_this4.preparedBuffer.length > 0 && !_this4.isSchedulingFrames && !_this4._isStopped) {
|
|
23131
23632
|
_this4.schedulePreparedFrames();
|
|
23132
23633
|
}
|
|
23133
23634
|
});
|
|
@@ -23135,37 +23636,37 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23135
23636
|
|
|
23136
23637
|
// Always set up a periodic check if we have scheduled buffers playing
|
|
23137
23638
|
// This ensures continuous playback even if frames arrive slowly
|
|
23138
|
-
if (this.scheduledBuffers > 0) {
|
|
23639
|
+
if (this.scheduledBuffers > 0 && !this._isStopped) {
|
|
23139
23640
|
// Set up a periodic check to schedule new frames as they arrive
|
|
23140
23641
|
// Use a shorter interval to catch new frames quickly
|
|
23141
23642
|
setTimeout(function () {
|
|
23142
|
-
if (_this4.preparedBuffer.length > 0 && !_this4.isSchedulingFrames && _this4.scheduledBuffers > 0) {
|
|
23643
|
+
if (_this4.preparedBuffer.length > 0 && !_this4.isSchedulingFrames && _this4.scheduledBuffers > 0 && !_this4._isStopped) {
|
|
23143
23644
|
_this4.schedulePreparedFrames();
|
|
23144
|
-
} else if (_this4.scheduledBuffers > 0) {
|
|
23645
|
+
} else if (_this4.scheduledBuffers > 0 && !_this4._isStopped) {
|
|
23145
23646
|
// Keep checking even if no frames yet - they might arrive soon
|
|
23146
23647
|
|
|
23147
23648
|
// Recursively check until we have no more scheduled buffers
|
|
23148
23649
|
|
|
23149
23650
|
setTimeout(function () {
|
|
23150
|
-
if (_this4.preparedBuffer.length > 0 && !_this4.isSchedulingFrames && _this4.scheduledBuffers > 0) {
|
|
23651
|
+
if (_this4.preparedBuffer.length > 0 && !_this4.isSchedulingFrames && _this4.scheduledBuffers > 0 && !_this4._isStopped) {
|
|
23151
23652
|
_this4.schedulePreparedFrames();
|
|
23152
23653
|
}
|
|
23153
23654
|
}, 10);
|
|
23154
23655
|
}
|
|
23155
23656
|
}, 10); // Check every 10ms for new frames
|
|
23156
23657
|
}
|
|
23157
|
-
_context3.n =
|
|
23658
|
+
_context3.n = 10;
|
|
23158
23659
|
break;
|
|
23159
|
-
case
|
|
23160
|
-
_context3.p =
|
|
23660
|
+
case 9:
|
|
23661
|
+
_context3.p = 9;
|
|
23161
23662
|
_t = _context3.v;
|
|
23162
23663
|
console.error('❌ AudioPlayer v2: Error scheduling frames:', _t);
|
|
23163
23664
|
this.emit('playbackError', _t);
|
|
23164
23665
|
this.isSchedulingFrames = false;
|
|
23165
|
-
case
|
|
23666
|
+
case 10:
|
|
23166
23667
|
return _context3.a(2);
|
|
23167
23668
|
}
|
|
23168
|
-
}, _callee2, this, [[
|
|
23669
|
+
}, _callee2, this, [[3, 9]]);
|
|
23169
23670
|
}));
|
|
23170
23671
|
function schedulePreparedFrames() {
|
|
23171
23672
|
return _schedulePreparedFrames.apply(this, arguments);
|
|
@@ -23186,14 +23687,21 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23186
23687
|
return _regenerator().w(function (_context4) {
|
|
23187
23688
|
while (1) switch (_context4.p = _context4.n) {
|
|
23188
23689
|
case 0:
|
|
23189
|
-
if (!this.
|
|
23690
|
+
if (!this._isStopped) {
|
|
23190
23691
|
_context4.n = 1;
|
|
23191
23692
|
break;
|
|
23192
23693
|
}
|
|
23694
|
+
console.log('🛑 AudioPlayer: Not processing PCM queue - playback was stopped (barge-in)');
|
|
23193
23695
|
return _context4.a(2);
|
|
23194
23696
|
case 1:
|
|
23697
|
+
if (!this.isProcessingPcmQueue) {
|
|
23698
|
+
_context4.n = 2;
|
|
23699
|
+
break;
|
|
23700
|
+
}
|
|
23701
|
+
return _context4.a(2);
|
|
23702
|
+
case 2:
|
|
23195
23703
|
this.isProcessingPcmQueue = true;
|
|
23196
|
-
_context4.p =
|
|
23704
|
+
_context4.p = 3;
|
|
23197
23705
|
// Initialize audio context if needed
|
|
23198
23706
|
|
|
23199
23707
|
if (!this.audioContext) {
|
|
@@ -23203,20 +23711,27 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23203
23711
|
// ✅ NEW: Wait for AudioContext to be in 'running' state before proceeding
|
|
23204
23712
|
// This is critical for mobile devices where AudioContext initialization takes time
|
|
23205
23713
|
// Replaces fixed timeouts with event-driven waiting
|
|
23206
|
-
_context4.n =
|
|
23714
|
+
_context4.n = 4;
|
|
23207
23715
|
return this.waitForAudioContextReady();
|
|
23208
|
-
case
|
|
23716
|
+
case 4:
|
|
23209
23717
|
if (!(this.pcmChunkQueue.length > 0)) {
|
|
23718
|
+
_context4.n = 7;
|
|
23719
|
+
break;
|
|
23720
|
+
}
|
|
23721
|
+
if (!this._isStopped) {
|
|
23210
23722
|
_context4.n = 5;
|
|
23211
23723
|
break;
|
|
23212
23724
|
}
|
|
23725
|
+
console.log('🛑 AudioPlayer: Stopping PCM queue processing - playback was stopped');
|
|
23726
|
+
return _context4.a(3, 7);
|
|
23727
|
+
case 5:
|
|
23213
23728
|
pcmData = this.pcmChunkQueue.shift();
|
|
23214
23729
|
if (pcmData) {
|
|
23215
|
-
_context4.n =
|
|
23730
|
+
_context4.n = 6;
|
|
23216
23731
|
break;
|
|
23217
23732
|
}
|
|
23218
|
-
return _context4.a(3,
|
|
23219
|
-
case
|
|
23733
|
+
return _context4.a(3, 4);
|
|
23734
|
+
case 6:
|
|
23220
23735
|
// Ensure even byte count for 16-bit PCM
|
|
23221
23736
|
processedData = pcmData;
|
|
23222
23737
|
if (pcmData.byteLength % 2 !== 0) {
|
|
@@ -23308,25 +23823,25 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23308
23823
|
this.isPlaying = true;
|
|
23309
23824
|
this.emit('playbackStarted');
|
|
23310
23825
|
}
|
|
23311
|
-
_context4.n =
|
|
23826
|
+
_context4.n = 4;
|
|
23312
23827
|
break;
|
|
23313
|
-
case
|
|
23828
|
+
case 7:
|
|
23314
23829
|
// end while loop
|
|
23315
23830
|
|
|
23316
23831
|
// All chunks scheduled, reset processing flag
|
|
23317
23832
|
this.isProcessingPcmQueue = false;
|
|
23318
|
-
_context4.n =
|
|
23833
|
+
_context4.n = 9;
|
|
23319
23834
|
break;
|
|
23320
|
-
case
|
|
23321
|
-
_context4.p =
|
|
23835
|
+
case 8:
|
|
23836
|
+
_context4.p = 8;
|
|
23322
23837
|
_t2 = _context4.v;
|
|
23323
23838
|
console.error('❌ AudioPlayer v2: Error playing chunk:', _t2);
|
|
23324
23839
|
this.emit('playbackError', _t2);
|
|
23325
23840
|
this.isProcessingPcmQueue = false;
|
|
23326
|
-
case
|
|
23841
|
+
case 9:
|
|
23327
23842
|
return _context4.a(2);
|
|
23328
23843
|
}
|
|
23329
|
-
}, _callee3, this, [[
|
|
23844
|
+
}, _callee3, this, [[3, 8]]);
|
|
23330
23845
|
}));
|
|
23331
23846
|
function processPcmQueue() {
|
|
23332
23847
|
return _processPcmQueue.apply(this, arguments);
|
|
@@ -24036,22 +24551,29 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24036
24551
|
return _regenerator().w(function (_context6) {
|
|
24037
24552
|
while (1) switch (_context6.p = _context6.n) {
|
|
24038
24553
|
case 0:
|
|
24039
|
-
if (!
|
|
24554
|
+
if (!this._isStopped) {
|
|
24040
24555
|
_context6.n = 1;
|
|
24041
24556
|
break;
|
|
24042
24557
|
}
|
|
24558
|
+
console.log('🛑 AudioPlayer: Not processing queue - playback was stopped (barge-in)');
|
|
24043
24559
|
return _context6.a(2);
|
|
24044
24560
|
case 1:
|
|
24561
|
+
if (!(this.isProcessingQueue || this.audioQueue.length === 0)) {
|
|
24562
|
+
_context6.n = 2;
|
|
24563
|
+
break;
|
|
24564
|
+
}
|
|
24565
|
+
return _context6.a(2);
|
|
24566
|
+
case 2:
|
|
24045
24567
|
this.isProcessingQueue = true;
|
|
24046
24568
|
audioBlob = this.audioQueue.shift();
|
|
24047
24569
|
if (audioBlob) {
|
|
24048
|
-
_context6.n =
|
|
24570
|
+
_context6.n = 3;
|
|
24049
24571
|
break;
|
|
24050
24572
|
}
|
|
24051
24573
|
this.isProcessingQueue = false;
|
|
24052
24574
|
return _context6.a(2);
|
|
24053
|
-
case
|
|
24054
|
-
_context6.p =
|
|
24575
|
+
case 3:
|
|
24576
|
+
_context6.p = 3;
|
|
24055
24577
|
wasFirstPlay = !this.isPlaying && this.currentSource === null; // Initialize audio context if needed
|
|
24056
24578
|
if (!this.audioContext) {
|
|
24057
24579
|
this.initializeAudioContext();
|
|
@@ -24060,17 +24582,17 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24060
24582
|
// ✅ NEW: Wait for AudioContext to be in 'running' state before proceeding
|
|
24061
24583
|
// This is critical for mobile devices where AudioContext initialization takes time
|
|
24062
24584
|
// Replaces fixed timeouts with event-driven waiting
|
|
24063
|
-
_context6.n =
|
|
24585
|
+
_context6.n = 4;
|
|
24064
24586
|
return this.waitForAudioContextReady();
|
|
24065
|
-
case
|
|
24587
|
+
case 4:
|
|
24066
24588
|
audioContext = this.audioContext; // Decode audio
|
|
24067
|
-
_context6.n =
|
|
24589
|
+
_context6.n = 5;
|
|
24068
24590
|
return audioBlob.arrayBuffer();
|
|
24069
|
-
case
|
|
24591
|
+
case 5:
|
|
24070
24592
|
arrayBuffer = _context6.v;
|
|
24071
|
-
_context6.n =
|
|
24593
|
+
_context6.n = 6;
|
|
24072
24594
|
return audioContext.decodeAudioData(arrayBuffer);
|
|
24073
|
-
case
|
|
24595
|
+
case 6:
|
|
24074
24596
|
audioBuffer = _context6.v;
|
|
24075
24597
|
shouldEmitStart = wasFirstPlay && !this.isPlaying && this.currentSource === null; // Create source
|
|
24076
24598
|
source = audioContext.createBufferSource();
|
|
@@ -24114,10 +24636,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24114
24636
|
// Start playback - AudioContext should now be fully ready
|
|
24115
24637
|
|
|
24116
24638
|
source.start();
|
|
24117
|
-
_context6.n =
|
|
24639
|
+
_context6.n = 8;
|
|
24118
24640
|
break;
|
|
24119
|
-
case
|
|
24120
|
-
_context6.p =
|
|
24641
|
+
case 7:
|
|
24642
|
+
_context6.p = 7;
|
|
24121
24643
|
_t3 = _context6.v;
|
|
24122
24644
|
console.error('❌ AudioPlayer v2: Error processing queue:', _t3);
|
|
24123
24645
|
this.currentSource = null;
|
|
@@ -24135,10 +24657,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24135
24657
|
this.isProcessingQueue = false;
|
|
24136
24658
|
this.emit('playbackStopped');
|
|
24137
24659
|
}
|
|
24138
|
-
case
|
|
24660
|
+
case 8:
|
|
24139
24661
|
return _context6.a(2);
|
|
24140
24662
|
}
|
|
24141
|
-
}, _callee5, this, [[
|
|
24663
|
+
}, _callee5, this, [[3, 7]]);
|
|
24142
24664
|
}));
|
|
24143
24665
|
function processQueue() {
|
|
24144
24666
|
return _processQueue.apply(this, arguments);
|
|
@@ -24179,6 +24701,15 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24179
24701
|
console.log(' scheduledSources.size:', this.scheduledSources.size);
|
|
24180
24702
|
console.log(' scheduledBuffers:', this.scheduledBuffers);
|
|
24181
24703
|
|
|
24704
|
+
// CRITICAL: Set stopped flag FIRST to prevent new audio from being queued/scheduled
|
|
24705
|
+
// This prevents race conditions where audio chunks arrive after stop but before sources are stopped
|
|
24706
|
+
// The flag will be cleared when markNewSentence() is called (signals new audio is starting)
|
|
24707
|
+
this._isStopped = true;
|
|
24708
|
+
|
|
24709
|
+
// CRITICAL: Stop scheduling immediately to prevent frames from being scheduled
|
|
24710
|
+
// This ensures schedulePreparedFrames() loop will exit on next _isStopped check
|
|
24711
|
+
this.isSchedulingFrames = false;
|
|
24712
|
+
|
|
24182
24713
|
// Stop current source (legacy queue-based system)
|
|
24183
24714
|
|
|
24184
24715
|
if (this.currentSource) {
|
|
@@ -24200,27 +24731,31 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24200
24731
|
if (this.scheduledSources.size > 0) {
|
|
24201
24732
|
console.log(" Stopping ".concat(this.scheduledSources.size, " scheduled sources..."));
|
|
24202
24733
|
var stoppedCount = 0;
|
|
24203
|
-
var _iterator = _createForOfIteratorHelper(this.scheduledSources),
|
|
24204
|
-
_step;
|
|
24205
|
-
try {
|
|
24206
|
-
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
24207
|
-
var source = _step.value;
|
|
24208
|
-
try {
|
|
24209
|
-
source.stop();
|
|
24210
|
-
stoppedCount++;
|
|
24211
|
-
} catch (e) {
|
|
24212
|
-
// Ignore if already stopped or not started yet
|
|
24213
24734
|
|
|
24214
|
-
|
|
24215
|
-
|
|
24735
|
+
// Store sources to stop and count before clearing the set
|
|
24736
|
+
var sourcesToStop = Array.from(this.scheduledSources);
|
|
24737
|
+
var sourcesCount = sourcesToStop.length;
|
|
24738
|
+
|
|
24739
|
+
// Clear the set BEFORE stopping sources to prevent onended callbacks from modifying it
|
|
24740
|
+
// This ensures onended callbacks will see empty set and return early
|
|
24741
|
+
this.scheduledSources.clear();
|
|
24742
|
+
|
|
24743
|
+
// CRITICAL: Reset scheduledBuffers to 0 BEFORE stopping sources
|
|
24744
|
+
// This prevents onended callbacks from decrementing it below 0
|
|
24745
|
+
// Any onended callbacks that fire will see scheduledSources is empty and return early
|
|
24746
|
+
this.scheduledBuffers = 0;
|
|
24747
|
+
for (var _i = 0, _sourcesToStop = sourcesToStop; _i < _sourcesToStop.length; _i++) {
|
|
24748
|
+
var source = _sourcesToStop[_i];
|
|
24749
|
+
try {
|
|
24750
|
+
source.stop();
|
|
24751
|
+
stoppedCount++;
|
|
24752
|
+
} catch (e) {
|
|
24753
|
+
// Ignore if already stopped or not started yet
|
|
24754
|
+
|
|
24755
|
+
console.log(' Source already stopped or not started:', e.message);
|
|
24216
24756
|
}
|
|
24217
|
-
} catch (err) {
|
|
24218
|
-
_iterator.e(err);
|
|
24219
|
-
} finally {
|
|
24220
|
-
_iterator.f();
|
|
24221
24757
|
}
|
|
24222
|
-
console.log(" Stopped ".concat(stoppedCount, " sources"));
|
|
24223
|
-
this.scheduledSources.clear();
|
|
24758
|
+
console.log(" Stopped ".concat(stoppedCount, " sources (cleared ").concat(sourcesCount, " from scheduledSources)"));
|
|
24224
24759
|
}
|
|
24225
24760
|
|
|
24226
24761
|
// Clear state
|
|
@@ -24236,10 +24771,15 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24236
24771
|
this.isProcessingPcmQueue = false;
|
|
24237
24772
|
this.isSchedulingFrames = false;
|
|
24238
24773
|
|
|
24239
|
-
//
|
|
24774
|
+
// Clear empty sentence timeout (barge-in means sentence was interrupted, not empty)
|
|
24775
|
+
if (this._emptySentenceTimeout) {
|
|
24776
|
+
clearTimeout(this._emptySentenceTimeout);
|
|
24777
|
+
this._emptySentenceTimeout = null;
|
|
24778
|
+
}
|
|
24240
24779
|
|
|
24780
|
+
// Reset scheduling properties
|
|
24781
|
+
// Note: scheduledBuffers was already reset to 0 above when clearing scheduledSources
|
|
24241
24782
|
this.nextStartTime = 0;
|
|
24242
|
-
this.scheduledBuffers = 0;
|
|
24243
24783
|
|
|
24244
24784
|
// Clear transcript state
|
|
24245
24785
|
this.clearTranscriptState();
|
|
@@ -24259,8 +24799,77 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24259
24799
|
}, {
|
|
24260
24800
|
key: "markNewSentence",
|
|
24261
24801
|
value: function markNewSentence(text) {
|
|
24802
|
+
var _this9 = this;
|
|
24803
|
+
var wasStopped = this._isStopped;
|
|
24804
|
+
var isCurrentlyPlaying = this.isPlaying || this.scheduledSources.size > 0;
|
|
24805
|
+
|
|
24806
|
+
// CRITICAL: Clear stopped flag when new audio starts (after barge-in)
|
|
24807
|
+
// This allows new audio chunks to be queued after barge-in
|
|
24808
|
+
if (this._isStopped) {
|
|
24809
|
+
console.log('🛑 AudioPlayer: Clearing stopped flag - new audio starting after barge-in');
|
|
24810
|
+
this._isStopped = false;
|
|
24811
|
+
|
|
24812
|
+
// CRITICAL: Reset scheduling state when starting a new sentence after a stop
|
|
24813
|
+
// This ensures the new sentence starts immediately without delay
|
|
24814
|
+
// Reset nextStartTime so first chunk schedules immediately (not in the past)
|
|
24815
|
+
this.nextStartTime = 0;
|
|
24816
|
+
|
|
24817
|
+
// CRITICAL: Reset scheduledBuffers to 0, but ensure it's not negative
|
|
24818
|
+
// This accounts for any onended callbacks that might fire from stopped sources
|
|
24819
|
+
// If scheduledBuffers is negative, it means onended callbacks fired after stopImmediate()
|
|
24820
|
+
// In that case, we should reset to 0 to start fresh
|
|
24821
|
+
if (this.scheduledBuffers < 0) {
|
|
24822
|
+
console.log("\uD83D\uDD04 AudioPlayer: scheduledBuffers was negative (".concat(this.scheduledBuffers, "), resetting to 0"));
|
|
24823
|
+
}
|
|
24824
|
+
this.scheduledBuffers = 0;
|
|
24825
|
+
|
|
24826
|
+
// CRITICAL: ALWAYS clear preparedBuffer after barge-in - any remaining chunks are from previous sentence
|
|
24827
|
+
// This prevents old audio from playing when new sentence starts after interruption
|
|
24828
|
+
if (this.preparedBuffer.length > 0) {
|
|
24829
|
+
console.log("\uD83D\uDD04 AudioPlayer: Clearing ".concat(this.preparedBuffer.length, " prepared frames from previous sentence (after barge-in)"));
|
|
24830
|
+
}
|
|
24831
|
+
this.preparedBuffer = [];
|
|
24832
|
+
|
|
24833
|
+
// CRITICAL: Also clear pcmChunkQueue to prevent raw chunks from previous sentence being processed
|
|
24834
|
+
if (this.pcmChunkQueue.length > 0) {
|
|
24835
|
+
console.log("\uD83D\uDD04 AudioPlayer: Clearing ".concat(this.pcmChunkQueue.length, " raw PCM chunks from previous sentence (after barge-in)"));
|
|
24836
|
+
}
|
|
24837
|
+
this.pcmChunkQueue = [];
|
|
24838
|
+
this.isProcessingPcmQueue = false;
|
|
24839
|
+
console.log('🔄 AudioPlayer: Reset scheduling state for new sentence (after barge-in)');
|
|
24840
|
+
} else if (isCurrentlyPlaying) {
|
|
24841
|
+
// New sentence received while audio is currently playing (no barge-in)
|
|
24842
|
+
// Don't clear buffers or reset state - let current sentence finish, then new one will play
|
|
24843
|
+
console.log("\uD83D\uDCDD AudioPlayer: New sentence queued while audio playing - will start after current sentence finishes");
|
|
24844
|
+
}
|
|
24845
|
+
|
|
24846
|
+
// Always update pending sentence text (for transcript display)
|
|
24262
24847
|
this.pendingSentenceText = text;
|
|
24263
|
-
console.log("\uD83D\uDCDD AudioPlayer: New sentence marked: \"".concat(text.substring(0, 40), "...\""));
|
|
24848
|
+
console.log("\uD83D\uDCDD AudioPlayer: New sentence marked: \"".concat(text.substring(0, 40), "...\" (wasStopped: ").concat(wasStopped, ", isPlaying: ").concat(isCurrentlyPlaying, ")"));
|
|
24849
|
+
|
|
24850
|
+
// CRITICAL: Set timeout to detect empty sentences (audio_start but no chunks)
|
|
24851
|
+
// This prevents queue blocking if backend sends audio_start but no audio chunks follow
|
|
24852
|
+
if (this._emptySentenceTimeout) {
|
|
24853
|
+
clearTimeout(this._emptySentenceTimeout);
|
|
24854
|
+
}
|
|
24855
|
+
var sentenceText = text; // Capture for timeout callback
|
|
24856
|
+
this._emptySentenceTimeout = setTimeout(function () {
|
|
24857
|
+
// Check if this sentence still has no chunks after timeout
|
|
24858
|
+
if (_this9.pendingSentenceText === sentenceText && _this9.scheduledBuffers === 0 && _this9.preparedBuffer.length === 0 && _this9.pcmChunkQueue.length === 0 && !_this9._isStopped) {
|
|
24859
|
+
console.warn("\u26A0\uFE0F AudioPlayer: Empty sentence detected after 5s timeout - no chunks received for: \"".concat(sentenceText.substring(0, 40), "...\""));
|
|
24860
|
+
// Clear pending sentence to unblock next sentence
|
|
24861
|
+
if (_this9.pendingSentenceText === sentenceText) {
|
|
24862
|
+
_this9.pendingSentenceText = null;
|
|
24863
|
+
}
|
|
24864
|
+
// Emit playbackStopped to allow next sentence to start
|
|
24865
|
+
// Only if we're not currently playing (to avoid interrupting real playback)
|
|
24866
|
+
if (!_this9.isPlaying && _this9.scheduledSources.size === 0) {
|
|
24867
|
+
console.log('🛑 AudioPlayer: Emitting playbackStopped for empty sentence timeout');
|
|
24868
|
+
_this9.emit('playbackStopped');
|
|
24869
|
+
}
|
|
24870
|
+
}
|
|
24871
|
+
_this9._emptySentenceTimeout = null;
|
|
24872
|
+
}, 5000); // 5 second timeout - adjust based on expected chunk arrival rate
|
|
24264
24873
|
}
|
|
24265
24874
|
|
|
24266
24875
|
/**
|
|
@@ -24269,35 +24878,35 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24269
24878
|
}, {
|
|
24270
24879
|
key: "startTranscriptChecker",
|
|
24271
24880
|
value: function startTranscriptChecker() {
|
|
24272
|
-
var
|
|
24881
|
+
var _this0 = this;
|
|
24273
24882
|
if (this.isCheckingTranscripts) return;
|
|
24274
24883
|
this.isCheckingTranscripts = true;
|
|
24275
24884
|
console.log('📝 AudioPlayer: Transcript checker started');
|
|
24276
24885
|
var _checkLoop = function checkLoop() {
|
|
24277
|
-
if (!
|
|
24278
|
-
var currentTime =
|
|
24279
|
-
var
|
|
24280
|
-
|
|
24886
|
+
if (!_this0.isCheckingTranscripts || !_this0.audioContext) return;
|
|
24887
|
+
var currentTime = _this0.audioContext.currentTime;
|
|
24888
|
+
var _iterator = _createForOfIteratorHelper(_this0.sentenceTimings),
|
|
24889
|
+
_step;
|
|
24281
24890
|
try {
|
|
24282
|
-
for (
|
|
24283
|
-
var timing =
|
|
24891
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
24892
|
+
var timing = _step.value;
|
|
24284
24893
|
if (!timing.displayed && currentTime >= timing.startTime) {
|
|
24285
24894
|
timing.displayed = true;
|
|
24286
24895
|
console.log("\uD83D\uDCDD AudioPlayer: Display transcript at ".concat(currentTime.toFixed(3), "s: \"").concat(timing.text.substring(0, 40), "...\""));
|
|
24287
|
-
|
|
24896
|
+
_this0.emit('transcriptDisplay', {
|
|
24288
24897
|
text: timing.text
|
|
24289
24898
|
});
|
|
24290
24899
|
}
|
|
24291
24900
|
}
|
|
24292
24901
|
} catch (err) {
|
|
24293
|
-
|
|
24902
|
+
_iterator.e(err);
|
|
24294
24903
|
} finally {
|
|
24295
|
-
|
|
24904
|
+
_iterator.f();
|
|
24296
24905
|
}
|
|
24297
|
-
if (
|
|
24906
|
+
if (_this0.isPlaying || _this0.scheduledBuffers > 0) {
|
|
24298
24907
|
requestAnimationFrame(_checkLoop);
|
|
24299
24908
|
} else {
|
|
24300
|
-
|
|
24909
|
+
_this0.isCheckingTranscripts = false;
|
|
24301
24910
|
console.log('📝 AudioPlayer: Transcript checker stopped');
|
|
24302
24911
|
}
|
|
24303
24912
|
};
|
|
@@ -25578,7 +26187,8 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
25578
26187
|
}, {
|
|
25579
26188
|
key: "handleMessage",
|
|
25580
26189
|
value: function handleMessage(event) {
|
|
25581
|
-
var
|
|
26190
|
+
var _this$audioPlayer$sch,
|
|
26191
|
+
_this7 = this;
|
|
25582
26192
|
// Binary message (audio data)
|
|
25583
26193
|
|
|
25584
26194
|
if (event.data instanceof ArrayBuffer || event.data instanceof Blob) {
|
|
@@ -25657,9 +26267,17 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
25657
26267
|
// Backend sends audio_start with transcript text before audio chunks
|
|
25658
26268
|
// Store the text in AudioPlayer for synced display when audio actually starts playing
|
|
25659
26269
|
console.log('📝 VoiceSDK v2: Received audio_start with text:', message.text);
|
|
26270
|
+
|
|
26271
|
+
// NOTE: We do NOT stop current audio here - that only happens on user barge-in (stop_playing)
|
|
26272
|
+
// If audio is already playing, the new sentence will queue and wait for current one to finish
|
|
26273
|
+
// This allows sentences to play sequentially without interruption
|
|
26274
|
+
if (this.audioPlayer && (this.audioPlayer.isPlaying || ((_this$audioPlayer$sch = this.audioPlayer.scheduledSources) === null || _this$audioPlayer$sch === void 0 ? void 0 : _this$audioPlayer$sch.size) > 0)) {
|
|
26275
|
+
console.log('📝 VoiceSDK v2: New sentence received while audio playing - will queue and wait for current sentence to finish');
|
|
26276
|
+
}
|
|
25660
26277
|
if (message.text && this.audioPlayer) {
|
|
25661
26278
|
// Use AudioPlayer's transcript sync mechanism
|
|
25662
26279
|
// AudioPlayer will emit transcriptDisplay when audio actually starts playing (synced with audioContext.currentTime)
|
|
26280
|
+
// If audio is currently playing, markNewSentence will queue this sentence
|
|
25663
26281
|
this.audioPlayer.markNewSentence(message.text);
|
|
25664
26282
|
}
|
|
25665
26283
|
// Also emit as message for other listeners
|