ttp-agent-sdk 2.44.0 → 2.45.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 +2150 -645
- 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/demo-sdk-loader.js +1 -0
- package/dist/demos/test-ecommerce.html +23 -42
- package/dist/demos/test-tour.html +15 -33
- package/dist/demos/test-widget.html +38 -18
- package/dist/demos/widget-customization-dev.html +15 -6
- package/dist/demos/widget-customization.html +14 -6
- package/dist/examples/demo-sdk-loader.js +1 -0
- package/dist/examples/test-ecommerce.html +23 -42
- package/dist/examples/test-tour.html +15 -33
- package/dist/examples/test-widget.html +38 -18
- package/dist/examples/widget-customization-dev.html +15 -6
- package/dist/examples/widget-customization.html +14 -6
- package/examples/demo-sdk-loader.js +249 -0
- package/examples/test-ecommerce.html +23 -42
- package/examples/test-tour.html +15 -33
- package/examples/test-widget.html +38 -18
- package/examples/widget-customization-dev.html +15 -6
- package/examples/widget-customization.html +14 -6
- package/package.json +2 -1
package/dist/agent-widget.dev.js
CHANGED
|
@@ -10627,6 +10627,264 @@ var AudioRecorder = /*#__PURE__*/function (_EventEmitter) {
|
|
|
10627
10627
|
}(_EventEmitter_js__WEBPACK_IMPORTED_MODULE_0__["default"]);
|
|
10628
10628
|
|
|
10629
10629
|
|
|
10630
|
+
/***/ }),
|
|
10631
|
+
|
|
10632
|
+
/***/ "./src/core/ClientScriptManager.js":
|
|
10633
|
+
/*!*****************************************!*\
|
|
10634
|
+
!*** ./src/core/ClientScriptManager.js ***!
|
|
10635
|
+
\*****************************************/
|
|
10636
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
10637
|
+
|
|
10638
|
+
"use strict";
|
|
10639
|
+
__webpack_require__.r(__webpack_exports__);
|
|
10640
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
10641
|
+
/* harmony export */ CLIENT_TOOLS_PARTNER_ID: () => (/* binding */ CLIENT_TOOLS_PARTNER_ID),
|
|
10642
|
+
/* harmony export */ ClientScriptManager: () => (/* binding */ ClientScriptManager)
|
|
10643
|
+
/* harmony export */ });
|
|
10644
|
+
/* harmony import */ var _ecommerce_PartnerScriptRunner_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../ecommerce/PartnerScriptRunner.js */ "./src/ecommerce/PartnerScriptRunner.js");
|
|
10645
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
10646
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
10647
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
10648
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
10649
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
10650
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
10651
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
10652
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
10653
|
+
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
10654
|
+
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
10655
|
+
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
10656
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
10657
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
10658
|
+
/**
|
|
10659
|
+
* ClientScriptManager
|
|
10660
|
+
*
|
|
10661
|
+
* Flavor-independent owner of the reserved `__client_tools__` partner bundle.
|
|
10662
|
+
*
|
|
10663
|
+
* The backend pushes a second, flavor-independent `partner_bundle` whose
|
|
10664
|
+
* `partner_id` is the sentinel `__client_tools__`. It carries per-tool client
|
|
10665
|
+
* scripts (each an optional `pre_code_js`, a required `code_js` main step, and
|
|
10666
|
+
* an optional `post_code_js`) plus auto-run library scripts. Unlike the
|
|
10667
|
+
* ecommerce flavor's partner bundle, this one must run regardless of which
|
|
10668
|
+
* flavor (if any) is active, on BOTH the voice (`VoiceSDK_v2`) and text-chat
|
|
10669
|
+
* (`TextChatSDK`) channels.
|
|
10670
|
+
*
|
|
10671
|
+
* This manager owns a single dedicated {@link PartnerScriptRunner} instance —
|
|
10672
|
+
* constructed with `{ genericCtx: true }` so client-tool scripts get a
|
|
10673
|
+
* platform-agnostic ctx (args / host / fetch / log / emit / runAdapter, no
|
|
10674
|
+
* ecommerce-only platform / normalizeCart / refreshUI). It reuses the runner
|
|
10675
|
+
* class as-is; it does not fork it.
|
|
10676
|
+
*
|
|
10677
|
+
* Coexistence: the ecommerce flavor keeps its own runner and its own
|
|
10678
|
+
* `partner_bundle` handling for every other partner id. Each SDK routes by
|
|
10679
|
+
* `partnerId` — `__client_tools__` → this manager, anything else → the existing
|
|
10680
|
+
* flavor path. Bundles are therefore kept separate per partner id.
|
|
10681
|
+
*
|
|
10682
|
+
* Wire contract (unchanged):
|
|
10683
|
+
* bundle : { t:'partner_bundle', partner_id:'__client_tools__',
|
|
10684
|
+
* adapters:{ tool:{ code_js, pre_code_js?, post_code_js?, ... } },
|
|
10685
|
+
* autoRun:{ name:{ code_js } } }
|
|
10686
|
+
* invoke : { t:'run_partner_script', requestId, partnerId:'__client_tools__', action, args }
|
|
10687
|
+
* result : { t:'run_partner_script_result', requestId, partnerId, action, ok, result|error }
|
|
10688
|
+
*/
|
|
10689
|
+
|
|
10690
|
+
|
|
10691
|
+
/** Reserved partner id for the flavor-independent client-tool bundle. */
|
|
10692
|
+
var CLIENT_TOOLS_PARTNER_ID = '__client_tools__';
|
|
10693
|
+
|
|
10694
|
+
/** Mirror of ClientToolsRegistry.MAX_RESULT_SIZE — 50KB cap on result payloads. */
|
|
10695
|
+
var MAX_RESULT_SIZE = 50000;
|
|
10696
|
+
var LOG = '[ClientScriptManager]';
|
|
10697
|
+
var ClientScriptManager = /*#__PURE__*/function () {
|
|
10698
|
+
/**
|
|
10699
|
+
* @param {object} opts
|
|
10700
|
+
* @param {(msg: object) => void} opts.send Send a JSON frame back to the backend
|
|
10701
|
+
* (VoiceSDK.sendMessage / TextChatSDK.sendRaw). Used for both run_partner_script_result
|
|
10702
|
+
* envelopes and adapter ctx.emit() pushes.
|
|
10703
|
+
* @param {string} [opts.label] Short tag for logs (e.g. 'voice' / 'text').
|
|
10704
|
+
*/
|
|
10705
|
+
function ClientScriptManager() {
|
|
10706
|
+
var _this = this;
|
|
10707
|
+
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
10708
|
+
send = _ref.send,
|
|
10709
|
+
_ref$label = _ref.label,
|
|
10710
|
+
label = _ref$label === void 0 ? '' : _ref$label;
|
|
10711
|
+
_classCallCheck(this, ClientScriptManager);
|
|
10712
|
+
this._send = typeof send === 'function' ? send : function () {};
|
|
10713
|
+
this._label = label;
|
|
10714
|
+
this.runner = new _ecommerce_PartnerScriptRunner_js__WEBPACK_IMPORTED_MODULE_0__.PartnerScriptRunner({
|
|
10715
|
+
genericCtx: true
|
|
10716
|
+
});
|
|
10717
|
+
// Unsolicited adapter pushes (ctx.emit) go back over the same channel.
|
|
10718
|
+
this.runner.setEmitCallback(function (msg) {
|
|
10719
|
+
return _this._send(msg);
|
|
10720
|
+
});
|
|
10721
|
+
}
|
|
10722
|
+
|
|
10723
|
+
/**
|
|
10724
|
+
* True when this message is a `partner_bundle` addressed to the sentinel
|
|
10725
|
+
* `__client_tools__` partner id. Both SDKs use this to route the bundle here
|
|
10726
|
+
* instead of (or in addition to) the flavor path.
|
|
10727
|
+
* @param {object} message
|
|
10728
|
+
* @returns {boolean}
|
|
10729
|
+
*/
|
|
10730
|
+
return _createClass(ClientScriptManager, [{
|
|
10731
|
+
key: "setBundle",
|
|
10732
|
+
value:
|
|
10733
|
+
/**
|
|
10734
|
+
* Handle a `partner_bundle` for `__client_tools__`. Mirrors
|
|
10735
|
+
* EcommerceFlavor's bundle handling: merge autoRun entries into the adapters
|
|
10736
|
+
* map (so ctx.runAdapter can reach them), load the bundle (replacing any
|
|
10737
|
+
* previous one for this partner — reconnect case), then execute each autoRun
|
|
10738
|
+
* entry exactly once.
|
|
10739
|
+
* @param {object} data the partner_bundle message
|
|
10740
|
+
*/
|
|
10741
|
+
function setBundle(data) {
|
|
10742
|
+
var _this2 = this;
|
|
10743
|
+
// emit callback is wired in the constructor and survives bundle reloads;
|
|
10744
|
+
// re-assert it here so it is always fresh for the active SDK instance.
|
|
10745
|
+
this.runner.setEmitCallback(function (msg) {
|
|
10746
|
+
return _this2._send(msg);
|
|
10747
|
+
});
|
|
10748
|
+
var merged = _objectSpread(_objectSpread({}, data), {}, {
|
|
10749
|
+
adapters: _objectSpread(_objectSpread({}, (data === null || data === void 0 ? void 0 : data.adapters) || {}), (data === null || data === void 0 ? void 0 : data.autoRun) || {})
|
|
10750
|
+
});
|
|
10751
|
+
this.runner.setBundle(merged);
|
|
10752
|
+
var _loop = function _loop() {
|
|
10753
|
+
var action = _Object$keys[_i];
|
|
10754
|
+
_this2.runner.runAdapter(action, {}).then(function (r) {
|
|
10755
|
+
return console.log("".concat(LOG).concat(_this2._tag(), " auto-run '").concat(action, "':"), r);
|
|
10756
|
+
}).catch(function (e) {
|
|
10757
|
+
return console.warn("".concat(LOG).concat(_this2._tag(), " auto-run '").concat(action, "' failed:"), e);
|
|
10758
|
+
});
|
|
10759
|
+
};
|
|
10760
|
+
for (var _i = 0, _Object$keys = Object.keys((data === null || data === void 0 ? void 0 : data.autoRun) || {}); _i < _Object$keys.length; _i++) {
|
|
10761
|
+
_loop();
|
|
10762
|
+
}
|
|
10763
|
+
}
|
|
10764
|
+
|
|
10765
|
+
/**
|
|
10766
|
+
* Execute a `run_partner_script` envelope against the client-tool runner and
|
|
10767
|
+
* post the `run_partner_script_result` back over the channel. Enforces the
|
|
10768
|
+
* 50KB result-size cap (oversize → ok:false, error:'RESULT_TOO_LARGE').
|
|
10769
|
+
*
|
|
10770
|
+
* An unknown action (e.g. bundle not yet received) resolves to ok:false with
|
|
10771
|
+
* the runner's `adapter_not_in_bundle` reason — same behavior as the existing
|
|
10772
|
+
* path.
|
|
10773
|
+
*
|
|
10774
|
+
* @param {object} message { requestId, partnerId, action, args }
|
|
10775
|
+
*/
|
|
10776
|
+
}, {
|
|
10777
|
+
key: "handleRunPartnerScript",
|
|
10778
|
+
value: (function () {
|
|
10779
|
+
var _handleRunPartnerScript = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(message) {
|
|
10780
|
+
var _ref2, requestId, partnerId, action, args, startedAt, result, elapsedMs, ok, serializedSize, sizeKB, limitKB, reason, _elapsedMs, _t;
|
|
10781
|
+
return _regenerator().w(function (_context) {
|
|
10782
|
+
while (1) switch (_context.p = _context.n) {
|
|
10783
|
+
case 0:
|
|
10784
|
+
_ref2 = message || {}, requestId = _ref2.requestId, partnerId = _ref2.partnerId, action = _ref2.action;
|
|
10785
|
+
args = message && message.args || {};
|
|
10786
|
+
if (requestId) {
|
|
10787
|
+
_context.n = 1;
|
|
10788
|
+
break;
|
|
10789
|
+
}
|
|
10790
|
+
console.warn("".concat(LOG).concat(this._tag(), " run_partner_script missing requestId; dropping"), message);
|
|
10791
|
+
return _context.a(2);
|
|
10792
|
+
case 1:
|
|
10793
|
+
startedAt = Date.now();
|
|
10794
|
+
console.log("".concat(LOG).concat(this._tag(), " run_partner_script dispatch partner=").concat(partnerId, " action=").concat(action, " requestId=").concat(requestId));
|
|
10795
|
+
_context.p = 2;
|
|
10796
|
+
_context.n = 3;
|
|
10797
|
+
return this.runner.runAdapter(action, args);
|
|
10798
|
+
case 3:
|
|
10799
|
+
result = _context.v;
|
|
10800
|
+
elapsedMs = Date.now() - startedAt;
|
|
10801
|
+
ok = result && _typeof(result) === 'object' && 'ok' in result ? !!result.ok : true; // 50KB result-size cap (mirror ClientToolsRegistry.MAX_RESULT_SIZE).
|
|
10802
|
+
serializedSize = 0;
|
|
10803
|
+
try {
|
|
10804
|
+
serializedSize = JSON.stringify(result).length;
|
|
10805
|
+
} catch (_unused) {
|
|
10806
|
+
serializedSize = 0;
|
|
10807
|
+
}
|
|
10808
|
+
if (!(serializedSize > MAX_RESULT_SIZE)) {
|
|
10809
|
+
_context.n = 4;
|
|
10810
|
+
break;
|
|
10811
|
+
}
|
|
10812
|
+
sizeKB = Math.round(serializedSize / 1024);
|
|
10813
|
+
limitKB = Math.round(MAX_RESULT_SIZE / 1024);
|
|
10814
|
+
console.error("".concat(LOG).concat(this._tag(), " result for '").concat(action, "' exceeds size limit (").concat(sizeKB, "KB > ").concat(limitKB, "KB)"));
|
|
10815
|
+
this._send({
|
|
10816
|
+
t: 'run_partner_script_result',
|
|
10817
|
+
requestId: requestId,
|
|
10818
|
+
partnerId: partnerId,
|
|
10819
|
+
action: action,
|
|
10820
|
+
ok: false,
|
|
10821
|
+
error: 'RESULT_TOO_LARGE'
|
|
10822
|
+
});
|
|
10823
|
+
return _context.a(2);
|
|
10824
|
+
case 4:
|
|
10825
|
+
if (ok) {
|
|
10826
|
+
console.log("".concat(LOG).concat(this._tag(), " run_partner_script ok partner=").concat(partnerId, " action=").concat(action, " requestId=").concat(requestId, " (").concat(elapsedMs, "ms)"));
|
|
10827
|
+
} else {
|
|
10828
|
+
reason = result && (result.reason || result.error) || 'unknown';
|
|
10829
|
+
console.warn("".concat(LOG).concat(this._tag(), " run_partner_script failed partner=").concat(partnerId, " action=").concat(action, " requestId=").concat(requestId, " reason=").concat(reason, " (").concat(elapsedMs, "ms)"), result);
|
|
10830
|
+
}
|
|
10831
|
+
this._send({
|
|
10832
|
+
t: 'run_partner_script_result',
|
|
10833
|
+
requestId: requestId,
|
|
10834
|
+
partnerId: partnerId,
|
|
10835
|
+
action: action,
|
|
10836
|
+
ok: ok,
|
|
10837
|
+
result: result
|
|
10838
|
+
});
|
|
10839
|
+
_context.n = 6;
|
|
10840
|
+
break;
|
|
10841
|
+
case 5:
|
|
10842
|
+
_context.p = 5;
|
|
10843
|
+
_t = _context.v;
|
|
10844
|
+
_elapsedMs = Date.now() - startedAt;
|
|
10845
|
+
console.error("".concat(LOG).concat(this._tag(), " run_partner_script threw partner=").concat(partnerId, " action=").concat(action, " requestId=").concat(requestId, " (").concat(_elapsedMs, "ms):"), _t);
|
|
10846
|
+
this._send({
|
|
10847
|
+
t: 'run_partner_script_result',
|
|
10848
|
+
requestId: requestId,
|
|
10849
|
+
partnerId: partnerId,
|
|
10850
|
+
action: action,
|
|
10851
|
+
ok: false,
|
|
10852
|
+
error: _t && _t.message || String(_t)
|
|
10853
|
+
});
|
|
10854
|
+
case 6:
|
|
10855
|
+
return _context.a(2);
|
|
10856
|
+
}
|
|
10857
|
+
}, _callee, this, [[2, 5]]);
|
|
10858
|
+
}));
|
|
10859
|
+
function handleRunPartnerScript(_x) {
|
|
10860
|
+
return _handleRunPartnerScript.apply(this, arguments);
|
|
10861
|
+
}
|
|
10862
|
+
return handleRunPartnerScript;
|
|
10863
|
+
}())
|
|
10864
|
+
}, {
|
|
10865
|
+
key: "_tag",
|
|
10866
|
+
value: function _tag() {
|
|
10867
|
+
return this._label ? " [".concat(this._label, "]") : '';
|
|
10868
|
+
}
|
|
10869
|
+
}], [{
|
|
10870
|
+
key: "isClientToolsBundle",
|
|
10871
|
+
value: function isClientToolsBundle(message) {
|
|
10872
|
+
return !!message && (message.t === 'partner_bundle' || message.type === 'partner_bundle') && message.partner_id === CLIENT_TOOLS_PARTNER_ID;
|
|
10873
|
+
}
|
|
10874
|
+
|
|
10875
|
+
/**
|
|
10876
|
+
* True when this run_partner_script envelope targets the sentinel partner id.
|
|
10877
|
+
* @param {object} message
|
|
10878
|
+
* @returns {boolean}
|
|
10879
|
+
*/
|
|
10880
|
+
}, {
|
|
10881
|
+
key: "isClientToolsScript",
|
|
10882
|
+
value: function isClientToolsScript(message) {
|
|
10883
|
+
return !!message && message.partnerId === CLIENT_TOOLS_PARTNER_ID;
|
|
10884
|
+
}
|
|
10885
|
+
}]);
|
|
10886
|
+
}();
|
|
10887
|
+
|
|
10630
10888
|
/***/ }),
|
|
10631
10889
|
|
|
10632
10890
|
/***/ "./src/core/ClientToolsRegistry.js":
|
|
@@ -10999,6 +11257,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
10999
11257
|
/* harmony export */ });
|
|
11000
11258
|
/* harmony import */ var _EventEmitter_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./EventEmitter.js */ "./src/core/EventEmitter.js");
|
|
11001
11259
|
/* harmony import */ var _ClientToolsRegistry_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ClientToolsRegistry.js */ "./src/core/ClientToolsRegistry.js");
|
|
11260
|
+
/* harmony import */ var _ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./ClientScriptManager.js */ "./src/core/ClientScriptManager.js");
|
|
11002
11261
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
11003
11262
|
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
11004
11263
|
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
@@ -11030,6 +11289,7 @@ function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf
|
|
|
11030
11289
|
*/
|
|
11031
11290
|
|
|
11032
11291
|
|
|
11292
|
+
|
|
11033
11293
|
var RECONNECT_DELAY_MS = 1000;
|
|
11034
11294
|
var MAX_RECONNECT_ATTEMPTS = 5;
|
|
11035
11295
|
var TEXT_CHAT_PATH = '/chat/text';
|
|
@@ -11067,6 +11327,18 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
|
|
|
11067
11327
|
}, function () {
|
|
11068
11328
|
return _this.config.conversationId;
|
|
11069
11329
|
}, config.sharedToolHandlers || null);
|
|
11330
|
+
|
|
11331
|
+
// Flavor-independent client-tool script manager (reserved partner id
|
|
11332
|
+
// `__client_tools__`). Owns its own PartnerScriptRunner so client-tool
|
|
11333
|
+
// scripts run regardless of flavor, alongside (and separate from) the
|
|
11334
|
+
// ecommerce flavor's partner runner. Sends result/emit frames over this
|
|
11335
|
+
// text-chat WebSocket so the response returns on the channel it arrived on.
|
|
11336
|
+
_this.clientScriptManager = new _ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_2__.ClientScriptManager({
|
|
11337
|
+
send: function send(message) {
|
|
11338
|
+
return _this.sendRaw(message);
|
|
11339
|
+
},
|
|
11340
|
+
label: 'text'
|
|
11341
|
+
});
|
|
11070
11342
|
return _this;
|
|
11071
11343
|
}
|
|
11072
11344
|
|
|
@@ -11190,6 +11462,16 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
|
|
|
11190
11462
|
return;
|
|
11191
11463
|
}
|
|
11192
11464
|
|
|
11465
|
+
// Flavor-independent client-tool bundle (partner_id `__client_tools__`)
|
|
11466
|
+
// is loaded by the dedicated ClientScriptManager. Every other partner id
|
|
11467
|
+
// falls through to the flavor (via the emitted 'message' event), so the
|
|
11468
|
+
// ecommerce flavor's own bundle handling keeps working untouched.
|
|
11469
|
+
if (data.t === 'partner_bundle' && _ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_2__.ClientScriptManager.isClientToolsBundle(data)) {
|
|
11470
|
+
_this2.clientScriptManager.setBundle(data);
|
|
11471
|
+
_this2.emit('message', data);
|
|
11472
|
+
return;
|
|
11473
|
+
}
|
|
11474
|
+
|
|
11193
11475
|
// Server responds with hello_ack (same format as voice SDK)
|
|
11194
11476
|
if (data.t === 'hello_ack') {
|
|
11195
11477
|
if (!data.ok) {
|
|
@@ -11412,9 +11694,15 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
|
|
|
11412
11694
|
console.warn('[TextChatSDK] run_partner_script missing requestId; dropping', message);
|
|
11413
11695
|
return _context2.a(2);
|
|
11414
11696
|
case 1:
|
|
11697
|
+
if (!_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_2__.ClientScriptManager.isClientToolsScript(message)) {
|
|
11698
|
+
_context2.n = 2;
|
|
11699
|
+
break;
|
|
11700
|
+
}
|
|
11701
|
+
return _context2.a(2, this.clientScriptManager.handleRunPartnerScript(message));
|
|
11702
|
+
case 2:
|
|
11415
11703
|
handler = (_this$clientToolsRegi = this.clientToolsRegistry) === null || _this$clientToolsRegi === void 0 || (_this$clientToolsRegi = _this$clientToolsRegi.handlers) === null || _this$clientToolsRegi === void 0 ? void 0 : _this$clientToolsRegi.get('run_partner_script');
|
|
11416
11704
|
if (handler) {
|
|
11417
|
-
_context2.n =
|
|
11705
|
+
_context2.n = 3;
|
|
11418
11706
|
break;
|
|
11419
11707
|
}
|
|
11420
11708
|
console.error("[TextChatSDK] no run_partner_script handler registered (ecommerce flavor not loaded?); requestId=".concat(requestId, " partner=").concat(partnerId, " action=").concat(action));
|
|
@@ -11427,15 +11715,15 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
|
|
|
11427
11715
|
error: 'no_handler_registered'
|
|
11428
11716
|
});
|
|
11429
11717
|
return _context2.a(2);
|
|
11430
|
-
case
|
|
11718
|
+
case 3:
|
|
11431
11719
|
startedAt = Date.now();
|
|
11432
|
-
_context2.p =
|
|
11433
|
-
_context2.n =
|
|
11720
|
+
_context2.p = 4;
|
|
11721
|
+
_context2.n = 5;
|
|
11434
11722
|
return handler({
|
|
11435
11723
|
action: action,
|
|
11436
11724
|
args: args
|
|
11437
11725
|
});
|
|
11438
|
-
case
|
|
11726
|
+
case 5:
|
|
11439
11727
|
result = _context2.v;
|
|
11440
11728
|
ok = result && _typeof(result) === 'object' && 'ok' in result ? !!result.ok : true;
|
|
11441
11729
|
elapsedMs = Date.now() - startedAt;
|
|
@@ -11453,10 +11741,10 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
|
|
|
11453
11741
|
ok: ok,
|
|
11454
11742
|
result: result
|
|
11455
11743
|
});
|
|
11456
|
-
_context2.n =
|
|
11744
|
+
_context2.n = 7;
|
|
11457
11745
|
break;
|
|
11458
|
-
case
|
|
11459
|
-
_context2.p =
|
|
11746
|
+
case 6:
|
|
11747
|
+
_context2.p = 6;
|
|
11460
11748
|
_t = _context2.v;
|
|
11461
11749
|
_elapsedMs = Date.now() - startedAt;
|
|
11462
11750
|
console.error("[TextChatSDK] run_partner_script threw partner=".concat(partnerId, " action=").concat(action, " requestId=").concat(requestId, " (").concat(_elapsedMs, "ms):"), _t);
|
|
@@ -11468,10 +11756,10 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
|
|
|
11468
11756
|
ok: false,
|
|
11469
11757
|
error: _t && _t.message || String(_t)
|
|
11470
11758
|
});
|
|
11471
|
-
case
|
|
11759
|
+
case 7:
|
|
11472
11760
|
return _context2.a(2);
|
|
11473
11761
|
}
|
|
11474
|
-
}, _callee2, this, [[
|
|
11762
|
+
}, _callee2, this, [[4, 6]]);
|
|
11475
11763
|
}));
|
|
11476
11764
|
function _handleRunPartnerScript(_x2) {
|
|
11477
11765
|
return _handleRunPartnerScript2.apply(this, arguments);
|
|
@@ -11522,7 +11810,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
|
|
|
11522
11810
|
|
|
11523
11811
|
// SDK build time for debugging
|
|
11524
11812
|
if (true) {
|
|
11525
|
-
helloMessage.lastBuildTime = "2026-
|
|
11813
|
+
helloMessage.lastBuildTime = "2026-06-12T20:23:48.560Z";
|
|
11526
11814
|
}
|
|
11527
11815
|
try {
|
|
11528
11816
|
this.ws.send(JSON.stringify(helloMessage));
|
|
@@ -12304,9 +12592,8 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
12304
12592
|
/* harmony import */ var _EcommercePresenter_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./EcommercePresenter.js */ "./src/ecommerce/EcommercePresenter.js");
|
|
12305
12593
|
/* harmony import */ var _ProductPicker_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ProductPicker.js */ "./src/ecommerce/ProductPicker.js");
|
|
12306
12594
|
/* harmony import */ var _CartToast_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./CartToast.js */ "./src/ecommerce/CartToast.js");
|
|
12307
|
-
/* harmony import */ var
|
|
12308
|
-
/* harmony import */ var
|
|
12309
|
-
/* harmony import */ var _ecommerceStyles_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./ecommerceStyles.js */ "./src/ecommerce/ecommerceStyles.js");
|
|
12595
|
+
/* harmony import */ var _FlyToCart_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./FlyToCart.js */ "./src/ecommerce/FlyToCart.js");
|
|
12596
|
+
/* harmony import */ var _ecommerceStyles_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./ecommerceStyles.js */ "./src/ecommerce/ecommerceStyles.js");
|
|
12310
12597
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
12311
12598
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
12312
12599
|
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
@@ -12325,8 +12612,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
|
|
|
12325
12612
|
* DesktopPresenter — Desktop UI strategy for e-commerce.
|
|
12326
12613
|
*
|
|
12327
12614
|
* Renders products via ProductPicker inside the shadow-root #ttpEcommerceArea,
|
|
12328
|
-
* manages panel expansion/shrinking, compact voice mode switching, CartToast
|
|
12329
|
-
* and CartSummary.
|
|
12615
|
+
* manages panel expansion/shrinking, compact voice mode switching, and CartToast.
|
|
12330
12616
|
*
|
|
12331
12617
|
* Implements the EcommercePresenter interface.
|
|
12332
12618
|
*/
|
|
@@ -12335,7 +12621,6 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
|
|
|
12335
12621
|
|
|
12336
12622
|
|
|
12337
12623
|
|
|
12338
|
-
|
|
12339
12624
|
var DesktopPresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
12340
12625
|
/**
|
|
12341
12626
|
* @param {Object} config
|
|
@@ -12351,7 +12636,7 @@ var DesktopPresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
|
12351
12636
|
_this.shadowRoot = config.shadowRoot;
|
|
12352
12637
|
_this.onProductAdd = config.onProductAdd || function () {};
|
|
12353
12638
|
_this.onProductUndo = config.onProductUndo || function () {};
|
|
12354
|
-
_this.flyToCart = new
|
|
12639
|
+
_this.flyToCart = new _FlyToCart_js__WEBPACK_IMPORTED_MODULE_3__.FlyToCart({
|
|
12355
12640
|
getCartTarget: function getCartTarget() {
|
|
12356
12641
|
return _this._getCartTarget();
|
|
12357
12642
|
},
|
|
@@ -12382,7 +12667,6 @@ var DesktopPresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
|
12382
12667
|
return _this._restoreWaveform();
|
|
12383
12668
|
}
|
|
12384
12669
|
});
|
|
12385
|
-
_this.cartSummary = new _CartSummary_js__WEBPACK_IMPORTED_MODULE_3__.CartSummary();
|
|
12386
12670
|
_this.container = document.createElement('div');
|
|
12387
12671
|
_this.container.id = 'ttpEcommerceArea';
|
|
12388
12672
|
_this.container.className = 'ttp-ecommerce-area';
|
|
@@ -12460,50 +12744,10 @@ var DesktopPresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
|
12460
12744
|
}
|
|
12461
12745
|
}, {
|
|
12462
12746
|
key: "_triggerCartBump",
|
|
12463
|
-
value: function _triggerCartBump() {
|
|
12464
|
-
var _this3 = this;
|
|
12465
|
-
this._cartBump = true;
|
|
12466
|
-
this._updateCartBumpStyles();
|
|
12467
|
-
setTimeout(function () {
|
|
12468
|
-
_this3._cartBump = false;
|
|
12469
|
-
_this3._updateCartBumpStyles();
|
|
12470
|
-
}, 450);
|
|
12471
|
-
}
|
|
12747
|
+
value: function _triggerCartBump() {}
|
|
12472
12748
|
}, {
|
|
12473
12749
|
key: "_triggerRipple",
|
|
12474
|
-
value: function _triggerRipple() {
|
|
12475
|
-
var _this4 = this;
|
|
12476
|
-
this._ripple = true;
|
|
12477
|
-
this._updateRippleStyles();
|
|
12478
|
-
setTimeout(function () {
|
|
12479
|
-
_this4._ripple = false;
|
|
12480
|
-
_this4._updateRippleStyles();
|
|
12481
|
-
}, 600);
|
|
12482
|
-
}
|
|
12483
|
-
}, {
|
|
12484
|
-
key: "_updateCartBumpStyles",
|
|
12485
|
-
value: function _updateCartBumpStyles() {
|
|
12486
|
-
if (!this.cartSummary.element) return;
|
|
12487
|
-
var info = this.cartSummary.element.querySelector('.ttp-cart-info');
|
|
12488
|
-
if (info) {
|
|
12489
|
-
info.style.transform = 'none';
|
|
12490
|
-
info.style.transition = '';
|
|
12491
|
-
}
|
|
12492
|
-
}
|
|
12493
|
-
}, {
|
|
12494
|
-
key: "_updateRippleStyles",
|
|
12495
|
-
value: function _updateRippleStyles() {
|
|
12496
|
-
if (!this.cartSummary.element || !this._ripple) return;
|
|
12497
|
-
var existing = this.cartSummary.element.querySelector('.ttp-fly-ripple');
|
|
12498
|
-
if (existing) existing.remove();
|
|
12499
|
-
var ripple = document.createElement('div');
|
|
12500
|
-
ripple.className = 'ttp-fly-ripple';
|
|
12501
|
-
this.cartSummary.element.style.position = 'relative';
|
|
12502
|
-
this.cartSummary.element.appendChild(ripple);
|
|
12503
|
-
setTimeout(function () {
|
|
12504
|
-
return ripple.remove();
|
|
12505
|
-
}, 600);
|
|
12506
|
-
}
|
|
12750
|
+
value: function _triggerRipple() {}
|
|
12507
12751
|
|
|
12508
12752
|
// ═══════════════════════════════════════════
|
|
12509
12753
|
// EcommercePresenter interface
|
|
@@ -12548,7 +12792,6 @@ var DesktopPresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
|
12548
12792
|
key: "onProductAdded",
|
|
12549
12793
|
value: function onProductAdded(product, cartInfo) {
|
|
12550
12794
|
this._ensureReady();
|
|
12551
|
-
this.cartSummary.update(cartInfo.itemCount, cartInfo.total, cartInfo.currency);
|
|
12552
12795
|
this._toastActive = true;
|
|
12553
12796
|
/* Hide products and shrink to toast height (not original) — full shrink happens when toast is gone */
|
|
12554
12797
|
this.hideProducts({
|
|
@@ -12567,9 +12810,9 @@ var DesktopPresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
|
12567
12810
|
}, {
|
|
12568
12811
|
key: "_setPanelHeightForToast",
|
|
12569
12812
|
value: function _setPanelHeightForToast() {
|
|
12570
|
-
var
|
|
12813
|
+
var _this3 = this;
|
|
12571
12814
|
var apply = function apply() {
|
|
12572
|
-
var panel =
|
|
12815
|
+
var panel = _this3._queryId('text-chat-panel');
|
|
12573
12816
|
if (!panel) return;
|
|
12574
12817
|
var height = Math.min(DesktopPresenter.TOAST_HEIGHT_PX, Math.floor(window.innerHeight * 0.92));
|
|
12575
12818
|
panel.classList.add('ttp-panel-expanded');
|
|
@@ -12599,14 +12842,10 @@ var DesktopPresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
|
12599
12842
|
}
|
|
12600
12843
|
}, {
|
|
12601
12844
|
key: "updateCart",
|
|
12602
|
-
value: function updateCart(
|
|
12603
|
-
this._ensureReady();
|
|
12604
|
-
this.cartSummary.update(cartInfo.itemCount, cartInfo.total, cartInfo.currency);
|
|
12605
|
-
}
|
|
12845
|
+
value: function updateCart(_cartInfo) {}
|
|
12606
12846
|
}, {
|
|
12607
12847
|
key: "destroy",
|
|
12608
12848
|
value: function destroy() {
|
|
12609
|
-
var _this$cartSummary$ele;
|
|
12610
12849
|
if (this.flyToCart) this.flyToCart.destroy();
|
|
12611
12850
|
this.productPicker.hide();
|
|
12612
12851
|
this.cartToast.hide();
|
|
@@ -12615,9 +12854,6 @@ var DesktopPresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
|
12615
12854
|
if (this.voiceActiveState) {
|
|
12616
12855
|
this.voiceActiveState.classList.remove('ttp-products-visible');
|
|
12617
12856
|
}
|
|
12618
|
-
if ((_this$cartSummary$ele = this.cartSummary.element) !== null && _this$cartSummary$ele !== void 0 && _this$cartSummary$ele.parentNode) {
|
|
12619
|
-
this.cartSummary.element.parentNode.removeChild(this.cartSummary.element);
|
|
12620
|
-
}
|
|
12621
12857
|
if (this.container && this.container.parentNode) {
|
|
12622
12858
|
this.container.parentNode.removeChild(this.container);
|
|
12623
12859
|
}
|
|
@@ -12638,7 +12874,7 @@ var DesktopPresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
|
12638
12874
|
}, {
|
|
12639
12875
|
key: "_injectStyles",
|
|
12640
12876
|
value: function _injectStyles() {
|
|
12641
|
-
var css = (0,
|
|
12877
|
+
var css = (0,_ecommerceStyles_js__WEBPACK_IMPORTED_MODULE_4__.generateEcommerceCSS)();
|
|
12642
12878
|
var root = this.shadowRoot;
|
|
12643
12879
|
if (!root) return;
|
|
12644
12880
|
|
|
@@ -12683,18 +12919,6 @@ var DesktopPresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
|
12683
12919
|
} else {
|
|
12684
12920
|
this.voiceActiveState.appendChild(this.container);
|
|
12685
12921
|
}
|
|
12686
|
-
|
|
12687
|
-
/* Cart inside input area — unified section (like mobile) */
|
|
12688
|
-
var inputArea = this.voiceActiveState.querySelector('.voice-text-input-area');
|
|
12689
|
-
if (inputArea) {
|
|
12690
|
-
var cartEl = this.cartSummary.render();
|
|
12691
|
-
cartEl.classList.add('ttp-cart-summary-inline');
|
|
12692
|
-
inputArea.appendChild(cartEl);
|
|
12693
|
-
} else {
|
|
12694
|
-
var _cartEl = this.cartSummary.render();
|
|
12695
|
-
_cartEl.classList.add('ttp-cart-summary-bottom');
|
|
12696
|
-
this.voiceActiveState.appendChild(_cartEl);
|
|
12697
|
-
}
|
|
12698
12922
|
this.isAttached = true;
|
|
12699
12923
|
}
|
|
12700
12924
|
|
|
@@ -12716,26 +12940,20 @@ var DesktopPresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
|
12716
12940
|
this.container.style.overflowY = '';
|
|
12717
12941
|
if (this.voiceActiveState) this.voiceActiveState.classList.remove('ttp-products-visible');
|
|
12718
12942
|
}
|
|
12719
|
-
|
|
12720
|
-
/* Cart: visible only in products mode (fly target) or when items > 0 */
|
|
12721
|
-
if (this.cartSummary.element) {
|
|
12722
|
-
var show = mode === 'products' || mode === 'summary' && this.cartSummary.itemCount > 0;
|
|
12723
|
-
this.cartSummary.element.classList.toggle('ttp-cart-empty', !show);
|
|
12724
|
-
}
|
|
12725
12943
|
}
|
|
12726
12944
|
}, {
|
|
12727
12945
|
key: "_expandPanelToFit",
|
|
12728
12946
|
value: function _expandPanelToFit(productCount, layout) {
|
|
12729
|
-
var
|
|
12947
|
+
var _this4 = this;
|
|
12730
12948
|
if (!this._widgetDomRoot()) return;
|
|
12731
12949
|
var panel = this._queryId('text-chat-panel');
|
|
12732
12950
|
if (!panel) return;
|
|
12733
12951
|
panel.classList.add('ttp-panel-expanded');
|
|
12734
12952
|
var activeState = this._queryId('voiceActiveState');
|
|
12735
12953
|
requestAnimationFrame(function () {
|
|
12736
|
-
var
|
|
12954
|
+
var _this4$container;
|
|
12737
12955
|
var compact = activeState === null || activeState === void 0 ? void 0 : activeState.querySelector('.compact-voice-section');
|
|
12738
|
-
var picker = (
|
|
12956
|
+
var picker = (_this4$container = _this4.container) === null || _this4$container === void 0 ? void 0 : _this4$container.querySelector('.ttp-product-picker');
|
|
12739
12957
|
var transcript = activeState === null || activeState === void 0 ? void 0 : activeState.querySelector('.desktop-conversation-panel');
|
|
12740
12958
|
var inputRow = activeState === null || activeState === void 0 ? void 0 : activeState.querySelector('.voice-text-input-area');
|
|
12741
12959
|
var needed = 0;
|
|
@@ -13142,65 +13360,470 @@ var EcommercePresenter = /*#__PURE__*/function () {
|
|
|
13142
13360
|
|
|
13143
13361
|
/***/ }),
|
|
13144
13362
|
|
|
13145
|
-
/***/ "./src/ecommerce/
|
|
13146
|
-
|
|
13147
|
-
!*** ./src/ecommerce/
|
|
13148
|
-
|
|
13363
|
+
/***/ "./src/ecommerce/EcommerceVoiceStrip.js":
|
|
13364
|
+
/*!**********************************************!*\
|
|
13365
|
+
!*** ./src/ecommerce/EcommerceVoiceStrip.js ***!
|
|
13366
|
+
\**********************************************/
|
|
13149
13367
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
13150
13368
|
|
|
13151
13369
|
"use strict";
|
|
13152
13370
|
__webpack_require__.r(__webpack_exports__);
|
|
13153
13371
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
13154
|
-
/* harmony export */
|
|
13372
|
+
/* harmony export */ EcommerceVoiceStrip: () => (/* binding */ EcommerceVoiceStrip)
|
|
13155
13373
|
/* harmony export */ });
|
|
13156
|
-
/* harmony import */ var
|
|
13157
|
-
/* harmony import */ var html2canvas__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(html2canvas__WEBPACK_IMPORTED_MODULE_0__);
|
|
13374
|
+
/* harmony import */ var _widget_voice_rtlText_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../widget/voice/rtlText.js */ "./src/widget/voice/rtlText.js");
|
|
13158
13375
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
13159
|
-
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
13160
|
-
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
13161
|
-
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
13162
13376
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
13163
13377
|
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
13164
13378
|
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
13165
13379
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
13166
13380
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
13167
13381
|
/**
|
|
13168
|
-
*
|
|
13382
|
+
* EcommerceVoiceStrip
|
|
13169
13383
|
*
|
|
13170
|
-
*
|
|
13171
|
-
*
|
|
13384
|
+
* Fixed bottom-strip voice UI for desktop sessions with flavor.callView === 'minimized'.
|
|
13385
|
+
* (Historically ecommerce-only; now any flavored widget may opt in.) Requires
|
|
13386
|
+
* window.innerWidth > 768.
|
|
13172
13387
|
*
|
|
13173
|
-
*
|
|
13174
|
-
*
|
|
13388
|
+
* Appended to document.body (outside shadow DOM). Control actions delegate to
|
|
13389
|
+
* VoiceInterface via callbacks; optional getVoiceUiState() keeps icons in sync.
|
|
13175
13390
|
*
|
|
13176
|
-
*
|
|
13177
|
-
*
|
|
13178
|
-
*
|
|
13179
|
-
*
|
|
13180
|
-
*
|
|
13181
|
-
*
|
|
13391
|
+
* Public API:
|
|
13392
|
+
* strip.show(opts?) – opts.reset false = reveal without clearing transcript
|
|
13393
|
+
* strip.hide()
|
|
13394
|
+
* strip.syncFromVoice() – refresh mute / pause / speaker icons from getVoiceUiState
|
|
13395
|
+
* strip.updateTranscript(text) – assistant line (replaces any user text on the strip)
|
|
13396
|
+
* strip.updateUserTranscript(text, isFinal) – user STT on the same line (replaces agent text); no "You:" prefix; warm text color
|
|
13397
|
+
* strip.updateTimer(text)
|
|
13398
|
+
* strip.setMuted(bool) – legacy sync (prefer syncFromVoice)
|
|
13399
|
+
* strip.setHeld(bool) – legacy pause visual (maps to paused)
|
|
13400
|
+
* strip.destroy()
|
|
13182
13401
|
*/
|
|
13183
13402
|
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13195
|
-
|
|
13196
|
-
|
|
13197
|
-
|
|
13198
|
-
|
|
13199
|
-
|
|
13403
|
+
var EcommerceVoiceStrip = /*#__PURE__*/function () {
|
|
13404
|
+
/**
|
|
13405
|
+
* @param {Object} opts
|
|
13406
|
+
* @param {Object} [opts.theme] – match floating pill: pillGradient, pillTextColor, pillDotColor, endCallButtonColor
|
|
13407
|
+
* @param {() => { isMicMuted: boolean, isPaused: boolean, isSpeakerMuted: boolean }} [opts.getVoiceUiState]
|
|
13408
|
+
* @param {Function} opts.onHangup
|
|
13409
|
+
* @param {Function} opts.onToggleMute
|
|
13410
|
+
* @param {Function} opts.onTogglePause
|
|
13411
|
+
* @param {Function} opts.onToggleSpeaker
|
|
13412
|
+
* @param {Function} opts.onSendText
|
|
13413
|
+
* @param {string} [opts.language] – Widget language (BCP-47); with `opts.direction` drives RTL transcript/input.
|
|
13414
|
+
* @param {string} [opts.direction] – Widget `direction` (`ltr` | `rtl`); Hebrew/Arabic `language` also enables RTL text.
|
|
13415
|
+
* @param {{ brandUrl: string, brandName: string } | null} [opts.footer] – "Powered by" row at bottom of strip
|
|
13416
|
+
*/
|
|
13417
|
+
function EcommerceVoiceStrip() {
|
|
13418
|
+
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
13419
|
+
_classCallCheck(this, EcommerceVoiceStrip);
|
|
13420
|
+
this.theme = opts.theme || {};
|
|
13421
|
+
this.footer = opts.footer || null;
|
|
13422
|
+
this.getVoiceUiState = typeof opts.getVoiceUiState === 'function' ? opts.getVoiceUiState : null;
|
|
13423
|
+
this._language = typeof opts.language === 'string' ? opts.language : '';
|
|
13424
|
+
this._direction = opts.direction === 'rtl' || opts.direction === 'ltr' ? opts.direction : '';
|
|
13425
|
+
this.onHangup = opts.onHangup || function () {};
|
|
13426
|
+
this.onToggleMute = opts.onToggleMute || function () {};
|
|
13427
|
+
this.onTogglePause = opts.onTogglePause || function () {};
|
|
13428
|
+
this.onToggleSpeaker = opts.onToggleSpeaker || function () {};
|
|
13429
|
+
this.onSendText = opts.onSendText || function () {};
|
|
13430
|
+
this._kbdOpen = false;
|
|
13431
|
+
this._el = null;
|
|
13432
|
+
this._build();
|
|
13200
13433
|
}
|
|
13201
|
-
return _createClass(
|
|
13202
|
-
key: "
|
|
13203
|
-
|
|
13434
|
+
return _createClass(EcommerceVoiceStrip, [{
|
|
13435
|
+
key: "_applyStripTextDirection",
|
|
13436
|
+
value: function _applyStripTextDirection() {
|
|
13437
|
+
if (!this._el) return;
|
|
13438
|
+
var stripCfg = {
|
|
13439
|
+
language: this._language,
|
|
13440
|
+
direction: this._direction
|
|
13441
|
+
};
|
|
13442
|
+
var rtlChrome = (0,_widget_voice_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.configPrefersRtlText)(stripCfg);
|
|
13443
|
+
var transcript = this._el.querySelector('#ttp-ecom-transcript');
|
|
13444
|
+
var center = this._el.querySelector('#ttp-ecom-center');
|
|
13445
|
+
var inputRow = this._el.querySelector('#ttp-ecom-input-row');
|
|
13446
|
+
var input = this._el.querySelector('#ttp-ecom-input');
|
|
13447
|
+
var preferredLang = this._language && this._language.trim();
|
|
13448
|
+
if (transcript) {
|
|
13449
|
+
transcript.setAttribute('dir', (0,_widget_voice_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(transcript.textContent || '', stripCfg));
|
|
13450
|
+
if (rtlChrome && preferredLang) transcript.setAttribute('lang', preferredLang);else transcript.removeAttribute('lang');
|
|
13451
|
+
}
|
|
13452
|
+
if (center) {
|
|
13453
|
+
if (rtlChrome) {
|
|
13454
|
+
center.setAttribute('dir', 'rtl');
|
|
13455
|
+
if (preferredLang) center.setAttribute('lang', preferredLang);else center.removeAttribute('lang');
|
|
13456
|
+
} else {
|
|
13457
|
+
center.removeAttribute('dir');
|
|
13458
|
+
center.removeAttribute('lang');
|
|
13459
|
+
}
|
|
13460
|
+
}
|
|
13461
|
+
if (rtlChrome) {
|
|
13462
|
+
if (inputRow) {
|
|
13463
|
+
inputRow.setAttribute('dir', 'rtl');
|
|
13464
|
+
if (preferredLang) inputRow.setAttribute('lang', preferredLang);else inputRow.removeAttribute('lang');
|
|
13465
|
+
}
|
|
13466
|
+
if (input) {
|
|
13467
|
+
input.setAttribute('dir', 'rtl');
|
|
13468
|
+
if (preferredLang) input.setAttribute('lang', preferredLang);else input.removeAttribute('lang');
|
|
13469
|
+
}
|
|
13470
|
+
} else {
|
|
13471
|
+
if (inputRow) {
|
|
13472
|
+
inputRow.removeAttribute('dir');
|
|
13473
|
+
inputRow.removeAttribute('lang');
|
|
13474
|
+
}
|
|
13475
|
+
if (input) {
|
|
13476
|
+
input.setAttribute('dir', 'auto');
|
|
13477
|
+
input.removeAttribute('lang');
|
|
13478
|
+
}
|
|
13479
|
+
}
|
|
13480
|
+
}
|
|
13481
|
+
}, {
|
|
13482
|
+
key: "_build",
|
|
13483
|
+
value: function _build() {
|
|
13484
|
+
this._injectStyles();
|
|
13485
|
+
var wrapper = document.createElement('div');
|
|
13486
|
+
wrapper.id = 'ttp-ecom-strip-wrapper';
|
|
13487
|
+
wrapper.innerHTML = this._html();
|
|
13488
|
+
document.body.appendChild(wrapper);
|
|
13489
|
+
this._el = wrapper;
|
|
13490
|
+
var strip = this._el.querySelector('#ttp-ecom-strip');
|
|
13491
|
+
if (strip) this._applyTheme(strip);
|
|
13492
|
+
this._applyStripTextDirection();
|
|
13493
|
+
this._wireEvents();
|
|
13494
|
+
this._el.style.display = 'none';
|
|
13495
|
+
}
|
|
13496
|
+
}, {
|
|
13497
|
+
key: "_applyTheme",
|
|
13498
|
+
value: function _applyTheme(strip) {
|
|
13499
|
+
var t = this.theme;
|
|
13500
|
+
var g = t.pillGradient || 'linear-gradient(135deg, #581c87, #312e81, #1e1b4b)';
|
|
13501
|
+
var fg = t.pillTextColor || '#ffffff';
|
|
13502
|
+
var accent = t.pillDotColor || '#4ade80';
|
|
13503
|
+
var endC = t.endCallButtonColor || '#ef4444';
|
|
13504
|
+
strip.style.setProperty('--ecom-strip-bg', g);
|
|
13505
|
+
strip.style.setProperty('--ecom-fg', fg);
|
|
13506
|
+
strip.style.setProperty('--ecom-accent', accent);
|
|
13507
|
+
strip.style.setProperty('--ecom-end', endC);
|
|
13508
|
+
}
|
|
13509
|
+
}, {
|
|
13510
|
+
key: "_html",
|
|
13511
|
+
value: function _html() {
|
|
13512
|
+
return "\n <div id=\"ttp-ecom-strip\" role=\"region\" aria-label=\"Voice call\">\n <div id=\"ttp-ecom-live-edge\" aria-live=\"polite\">\n <div id=\"ttp-ecom-live-row\">\n <span id=\"ttp-ecom-dot\" class=\"ttp-ecom-live-dot\"></span>\n <span id=\"ttp-ecom-live-label\">LIVE \xB7 00:00</span>\n </div>\n </div>\n <div id=\"ttp-ecom-main-row\">\n <div id=\"ttp-ecom-left-controls\" class=\"ttp-ecom-controls\">\n <button type=\"button\" class=\"ttp-ecom-ctrl\" id=\"ttp-ecom-btn-mic\" title=\"Mute / Unmute\" aria-pressed=\"false\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"ttp-ecom-mic-on\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n </svg>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"ttp-ecom-mic-off\" style=\"display:none\" aria-hidden=\"true\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#fca5a5\" stroke-width=\"1.6\" stroke-linecap=\"round\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-ecom-ctrl\" id=\"ttp-ecom-btn-pause\" title=\"Pause / Resume\" aria-pressed=\"false\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" class=\"ttp-ecom-pause-ico\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" class=\"ttp-ecom-play-ico\" style=\"display:none\" aria-hidden=\"true\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-ecom-ctrl\" id=\"ttp-ecom-btn-speaker\" title=\"Speaker\" aria-pressed=\"false\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"ttp-ecom-spk-on\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"ttp-ecom-spk-off\" style=\"display:none\" aria-hidden=\"true\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#fca5a5\" stroke-width=\"1.6\" stroke-linecap=\"round\"/>\n </svg>\n </button>\n </div>\n\n <div id=\"ttp-ecom-center\">\n <div id=\"ttp-ecom-transcript\" aria-live=\"polite\">Connecting\u2026</div>\n </div>\n\n <div id=\"ttp-ecom-right-cluster\" class=\"ttp-ecom-controls\">\n <div class=\"ttp-ecom-mini-wave\" aria-hidden=\"true\">\n <span class=\"ttp-ecom-wb\"></span><span class=\"ttp-ecom-wb\"></span><span class=\"ttp-ecom-wb\"></span>\n <span class=\"ttp-ecom-wb\"></span><span class=\"ttp-ecom-wb\"></span>\n </div>\n <button type=\"button\" class=\"ttp-ecom-ctrl\" id=\"ttp-ecom-btn-kbd\" title=\"Type a message\" aria-pressed=\"false\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"2\" y=\"4\" width=\"20\" height=\"16\" rx=\"2\"/>\n <path d=\"M6 8h.01M10 8h.01M14 8h.01M18 8h.01M6 12h.01M10 12h.01M14 12h.01M18 12h.01M8 16h8\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-ecom-ctrl ttp-ecom-ctrl-end\" id=\"ttp-ecom-btn-end\" title=\"End call\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.99 12 19.79 19.79 0 0 1 1.97 3.4 2 2 0 0 1 3.95 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <div id=\"ttp-ecom-input-row\">\n <input type=\"text\" id=\"ttp-ecom-input\" placeholder=\"Type your message\u2026\" autocomplete=\"off\" />\n <button type=\"button\" id=\"ttp-ecom-send\" title=\"Send\" class=\"ttp-ecom-send\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>\n </svg>\n </button>\n </div>\n ".concat(this._footerHtml(), "\n </div>\n ");
|
|
13513
|
+
}
|
|
13514
|
+
}, {
|
|
13515
|
+
key: "_footerHtml",
|
|
13516
|
+
value: function _footerHtml() {
|
|
13517
|
+
if (!this.footer) return '';
|
|
13518
|
+
var _this$footer = this.footer,
|
|
13519
|
+
brandUrl = _this$footer.brandUrl,
|
|
13520
|
+
brandName = _this$footer.brandName;
|
|
13521
|
+
return "\n <div id=\"ttp-ecom-footer\" class=\"ttp-ecom-footer\">\n <span>Powered by <a href=\"".concat(brandUrl, "\" target=\"_blank\" rel=\"noopener noreferrer\"><b>").concat(brandName, "</b></a></span>\n </div>");
|
|
13522
|
+
}
|
|
13523
|
+
}, {
|
|
13524
|
+
key: "_wireEvents",
|
|
13525
|
+
value: function _wireEvents() {
|
|
13526
|
+
var _this = this;
|
|
13527
|
+
var $ = function $(id) {
|
|
13528
|
+
return _this._el.querySelector('#' + id);
|
|
13529
|
+
};
|
|
13530
|
+
$('ttp-ecom-btn-mic').addEventListener('click', function () {
|
|
13531
|
+
_this.onToggleMute();
|
|
13532
|
+
_this.syncFromVoice();
|
|
13533
|
+
});
|
|
13534
|
+
$('ttp-ecom-btn-pause').addEventListener('click', function () {
|
|
13535
|
+
_this.onTogglePause();
|
|
13536
|
+
_this.syncFromVoice();
|
|
13537
|
+
});
|
|
13538
|
+
$('ttp-ecom-btn-speaker').addEventListener('click', function () {
|
|
13539
|
+
_this.onToggleSpeaker();
|
|
13540
|
+
_this.syncFromVoice();
|
|
13541
|
+
});
|
|
13542
|
+
$('ttp-ecom-btn-kbd').addEventListener('click', function () {
|
|
13543
|
+
_this._kbdOpen = !_this._kbdOpen;
|
|
13544
|
+
$('ttp-ecom-btn-kbd').classList.toggle('ttp-ecom-ctrl-active', _this._kbdOpen);
|
|
13545
|
+
$('ttp-ecom-input-row').classList.toggle('open', _this._kbdOpen);
|
|
13546
|
+
if (_this._kbdOpen) setTimeout(function () {
|
|
13547
|
+
return $('ttp-ecom-input').focus();
|
|
13548
|
+
}, 50);
|
|
13549
|
+
});
|
|
13550
|
+
$('ttp-ecom-btn-end').addEventListener('click', function () {
|
|
13551
|
+
return _this.onHangup();
|
|
13552
|
+
});
|
|
13553
|
+
var sendFn = function sendFn() {
|
|
13554
|
+
var input = $('ttp-ecom-input');
|
|
13555
|
+
var text = input.value.trim();
|
|
13556
|
+
if (!text) return;
|
|
13557
|
+
_this.onSendText(text);
|
|
13558
|
+
_this.updateUserTranscript(text, true);
|
|
13559
|
+
input.value = '';
|
|
13560
|
+
};
|
|
13561
|
+
$('ttp-ecom-send').addEventListener('click', sendFn);
|
|
13562
|
+
$('ttp-ecom-input').addEventListener('keydown', function (e) {
|
|
13563
|
+
if (e.key === 'Enter') sendFn();
|
|
13564
|
+
});
|
|
13565
|
+
}
|
|
13566
|
+
}, {
|
|
13567
|
+
key: "show",
|
|
13568
|
+
value: function show() {
|
|
13569
|
+
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
13570
|
+
if (!this._el) return;
|
|
13571
|
+
if (opts.reset !== false) this._reset();
|
|
13572
|
+
this._el.style.display = '';
|
|
13573
|
+
this.syncFromVoice();
|
|
13574
|
+
}
|
|
13575
|
+
}, {
|
|
13576
|
+
key: "hide",
|
|
13577
|
+
value: function hide() {
|
|
13578
|
+
if (!this._el) return;
|
|
13579
|
+
this._el.style.display = 'none';
|
|
13580
|
+
}
|
|
13581
|
+
}, {
|
|
13582
|
+
key: "syncFromVoice",
|
|
13583
|
+
value: function syncFromVoice() {
|
|
13584
|
+
if (!this._el || !this.getVoiceUiState) return;
|
|
13585
|
+
var s;
|
|
13586
|
+
try {
|
|
13587
|
+
s = this.getVoiceUiState();
|
|
13588
|
+
} catch (_) {
|
|
13589
|
+
return;
|
|
13590
|
+
}
|
|
13591
|
+
var micMuted = !!s.isMicMuted;
|
|
13592
|
+
var paused = !!s.isPaused;
|
|
13593
|
+
var spkMuted = !!s.isSpeakerMuted;
|
|
13594
|
+
var micBtn = this._el.querySelector('#ttp-ecom-btn-mic');
|
|
13595
|
+
var micOn = this._el.querySelector('.ttp-ecom-mic-on');
|
|
13596
|
+
var micOff = this._el.querySelector('.ttp-ecom-mic-off');
|
|
13597
|
+
if (micBtn && micOn && micOff) {
|
|
13598
|
+
micBtn.classList.toggle('ttp-ecom-ctrl-warn', micMuted);
|
|
13599
|
+
micBtn.setAttribute('aria-pressed', micMuted ? 'true' : 'false');
|
|
13600
|
+
micOn.style.display = micMuted ? 'none' : 'block';
|
|
13601
|
+
micOff.style.display = micMuted ? 'block' : 'none';
|
|
13602
|
+
}
|
|
13603
|
+
var pauseBtn = this._el.querySelector('#ttp-ecom-btn-pause');
|
|
13604
|
+
var pauseIco = this._el.querySelector('.ttp-ecom-pause-ico');
|
|
13605
|
+
var playIco = this._el.querySelector('.ttp-ecom-play-ico');
|
|
13606
|
+
if (pauseBtn && pauseIco && playIco) {
|
|
13607
|
+
pauseBtn.classList.toggle('ttp-ecom-ctrl-warn', paused);
|
|
13608
|
+
pauseBtn.setAttribute('aria-pressed', paused ? 'true' : 'false');
|
|
13609
|
+
pauseIco.style.display = paused ? 'none' : 'block';
|
|
13610
|
+
playIco.style.display = paused ? 'block' : 'none';
|
|
13611
|
+
}
|
|
13612
|
+
var spkBtn = this._el.querySelector('#ttp-ecom-btn-speaker');
|
|
13613
|
+
var spkOn = this._el.querySelector('.ttp-ecom-spk-on');
|
|
13614
|
+
var spkOff = this._el.querySelector('.ttp-ecom-spk-off');
|
|
13615
|
+
if (spkBtn && spkOn && spkOff) {
|
|
13616
|
+
spkBtn.classList.toggle('ttp-ecom-ctrl-warn', spkMuted);
|
|
13617
|
+
spkBtn.setAttribute('aria-pressed', spkMuted ? 'true' : 'false');
|
|
13618
|
+
spkOn.style.display = spkMuted ? 'none' : 'block';
|
|
13619
|
+
spkOff.style.display = spkMuted ? 'block' : 'none';
|
|
13620
|
+
}
|
|
13621
|
+
var dot = this._el.querySelector('#ttp-ecom-dot');
|
|
13622
|
+
if (dot) dot.classList.toggle('paused', paused);
|
|
13623
|
+
this._syncMiniWave(micMuted || paused || spkMuted);
|
|
13624
|
+
}
|
|
13625
|
+
}, {
|
|
13626
|
+
key: "_syncMiniWave",
|
|
13627
|
+
value: function _syncMiniWave(frozen) {
|
|
13628
|
+
var _this$_el;
|
|
13629
|
+
(_this$_el = this._el) === null || _this$_el === void 0 || _this$_el.querySelectorAll('.ttp-ecom-mini-wave .ttp-ecom-wb').forEach(function (b) {
|
|
13630
|
+
b.classList.toggle('paused', frozen);
|
|
13631
|
+
});
|
|
13632
|
+
}
|
|
13633
|
+
}, {
|
|
13634
|
+
key: "updateTranscript",
|
|
13635
|
+
value: function updateTranscript(text) {
|
|
13636
|
+
var _this$_language;
|
|
13637
|
+
if (!this._el) return;
|
|
13638
|
+
var el = this._el.querySelector('#ttp-ecom-transcript');
|
|
13639
|
+
if (!el) return;
|
|
13640
|
+
var t = text != null ? String(text) : '';
|
|
13641
|
+
if (!t.trim()) return;
|
|
13642
|
+
var stripCfg = {
|
|
13643
|
+
language: this._language,
|
|
13644
|
+
direction: this._direction
|
|
13645
|
+
};
|
|
13646
|
+
el.classList.remove('ttp-ecom-transcript--user');
|
|
13647
|
+
el.textContent = t;
|
|
13648
|
+
el.style.color = '';
|
|
13649
|
+
el.setAttribute('dir', (0,_widget_voice_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(t, stripCfg));
|
|
13650
|
+
if ((0,_widget_voice_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.configPrefersRtlText)(stripCfg) && (_this$_language = this._language) !== null && _this$_language !== void 0 && _this$_language.trim()) el.setAttribute('lang', this._language.trim());else el.removeAttribute('lang');
|
|
13651
|
+
}
|
|
13652
|
+
|
|
13653
|
+
/**
|
|
13654
|
+
* User STT (interim + final) on the same line as the assistant transcript; replaces agent text while user is speaking.
|
|
13655
|
+
* No "You:" prefix — color distinguishes user vs agent.
|
|
13656
|
+
* @param {string} text
|
|
13657
|
+
* @param {boolean} [isFinal]
|
|
13658
|
+
*/
|
|
13659
|
+
}, {
|
|
13660
|
+
key: "updateUserTranscript",
|
|
13661
|
+
value: function updateUserTranscript(text, isFinal) {
|
|
13662
|
+
if (!this._el) return;
|
|
13663
|
+
var el = this._el.querySelector('#ttp-ecom-transcript');
|
|
13664
|
+
if (!el) return;
|
|
13665
|
+
var stripCfg = {
|
|
13666
|
+
language: this._language,
|
|
13667
|
+
direction: this._direction
|
|
13668
|
+
};
|
|
13669
|
+
var utter = text != null ? String(text) : '';
|
|
13670
|
+
var trimmed = utter.trim();
|
|
13671
|
+
if (!trimmed) {
|
|
13672
|
+
el.textContent = '';
|
|
13673
|
+
el.classList.remove('ttp-ecom-transcript--user');
|
|
13674
|
+
el.style.color = '';
|
|
13675
|
+
el.removeAttribute('dir');
|
|
13676
|
+
el.removeAttribute('lang');
|
|
13677
|
+
return;
|
|
13678
|
+
}
|
|
13679
|
+
el.classList.add('ttp-ecom-transcript--user');
|
|
13680
|
+
el.textContent = utter;
|
|
13681
|
+
el.style.color = '';
|
|
13682
|
+
el.setAttribute('dir', (0,_widget_voice_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(utter, stripCfg));
|
|
13683
|
+
var preferredLang = this._language && this._language.trim();
|
|
13684
|
+
if ((0,_widget_voice_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.configPrefersRtlText)(stripCfg) && preferredLang) el.setAttribute('lang', preferredLang);else el.removeAttribute('lang');
|
|
13685
|
+
}
|
|
13686
|
+
}, {
|
|
13687
|
+
key: "updateTimer",
|
|
13688
|
+
value: function updateTimer(timerText) {
|
|
13689
|
+
if (!this._el) return;
|
|
13690
|
+
var el = this._el.querySelector('#ttp-ecom-live-label');
|
|
13691
|
+
if (el) el.textContent = 'LIVE · ' + timerText;
|
|
13692
|
+
}
|
|
13693
|
+
}, {
|
|
13694
|
+
key: "setMuted",
|
|
13695
|
+
value: function setMuted(muted) {
|
|
13696
|
+
if (!this.getVoiceUiState) return;
|
|
13697
|
+
this.syncFromVoice();
|
|
13698
|
+
}
|
|
13699
|
+
}, {
|
|
13700
|
+
key: "setHeld",
|
|
13701
|
+
value: function setHeld(held) {
|
|
13702
|
+
if (!this.getVoiceUiState) return;
|
|
13703
|
+
this.syncFromVoice();
|
|
13704
|
+
}
|
|
13705
|
+
}, {
|
|
13706
|
+
key: "destroy",
|
|
13707
|
+
value: function destroy() {
|
|
13708
|
+
if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
|
|
13709
|
+
this._el = null;
|
|
13710
|
+
var style = document.getElementById('ttp-ecom-strip-styles');
|
|
13711
|
+
if (style) style.parentNode.removeChild(style);
|
|
13712
|
+
}
|
|
13713
|
+
}, {
|
|
13714
|
+
key: "_reset",
|
|
13715
|
+
value: function _reset() {
|
|
13716
|
+
var _this2 = this,
|
|
13717
|
+
_$,
|
|
13718
|
+
_$2;
|
|
13719
|
+
this._kbdOpen = false;
|
|
13720
|
+
var $ = function $(id) {
|
|
13721
|
+
return _this2._el.querySelector('#' + id);
|
|
13722
|
+
};
|
|
13723
|
+
var tx = $('ttp-ecom-transcript');
|
|
13724
|
+
if (tx) {
|
|
13725
|
+
var _this$_language2;
|
|
13726
|
+
tx.textContent = 'Connecting…';
|
|
13727
|
+
tx.classList.remove('ttp-ecom-transcript--user');
|
|
13728
|
+
tx.style.color = '';
|
|
13729
|
+
var stripCfg = {
|
|
13730
|
+
language: this._language,
|
|
13731
|
+
direction: this._direction
|
|
13732
|
+
};
|
|
13733
|
+
tx.setAttribute('dir', (0,_widget_voice_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(tx.textContent, stripCfg));
|
|
13734
|
+
if ((0,_widget_voice_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.configPrefersRtlText)(stripCfg) && (_this$_language2 = this._language) !== null && _this$_language2 !== void 0 && _this$_language2.trim()) tx.setAttribute('lang', this._language.trim());else tx.removeAttribute('lang');
|
|
13735
|
+
}
|
|
13736
|
+
var live = $('ttp-ecom-live-label');
|
|
13737
|
+
if (live) {
|
|
13738
|
+
live.textContent = 'LIVE · 00:00';
|
|
13739
|
+
live.style.color = '';
|
|
13740
|
+
}
|
|
13741
|
+
var dot = $('ttp-ecom-dot');
|
|
13742
|
+
if (dot) {
|
|
13743
|
+
dot.style.background = '';
|
|
13744
|
+
dot.classList.remove('paused');
|
|
13745
|
+
}
|
|
13746
|
+
(_$ = $('ttp-ecom-input-row')) === null || _$ === void 0 || _$.classList.remove('open');
|
|
13747
|
+
(_$2 = $('ttp-ecom-btn-kbd')) === null || _$2 === void 0 || _$2.classList.remove('ttp-ecom-ctrl-active');
|
|
13748
|
+
this._syncMiniWave(false);
|
|
13749
|
+
this.syncFromVoice();
|
|
13750
|
+
}
|
|
13751
|
+
}, {
|
|
13752
|
+
key: "_injectStyles",
|
|
13753
|
+
value: function _injectStyles() {
|
|
13754
|
+
if (document.getElementById('ttp-ecom-strip-styles')) return;
|
|
13755
|
+
var staleClip = document.getElementById('ttp-ecom-tab-clip-defs');
|
|
13756
|
+
if (staleClip) staleClip.remove();
|
|
13757
|
+
var style = document.createElement('style');
|
|
13758
|
+
style.id = 'ttp-ecom-strip-styles';
|
|
13759
|
+
style.textContent = EcommerceVoiceStrip._CSS;
|
|
13760
|
+
document.head.appendChild(style);
|
|
13761
|
+
}
|
|
13762
|
+
}]);
|
|
13763
|
+
}();
|
|
13764
|
+
EcommerceVoiceStrip._CSS = "\n#ttp-ecom-strip-wrapper {\n position: fixed;\n left: 50%;\n bottom: 0;\n transform: translateX(-50%);\n width: min(720px, calc(100vw - 40px));\n max-width: calc(100vw - 40px);\n z-index: 2147483647;\n box-sizing: border-box;\n padding: 0 14px max(14px, env(safe-area-inset-bottom, 0px));\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n font-feature-settings: \"kern\" 1, \"liga\" 1;\n pointer-events: none;\n}\n#ttp-ecom-strip-wrapper * { box-sizing: border-box; }\n\n/* Layered glass dock: rim light, soft lift, optional backdrop tint */\n#ttp-ecom-strip {\n pointer-events: auto;\n position: relative;\n isolation: isolate;\n margin: 0;\n border-radius: 26px;\n border: 1px solid rgba(255, 255, 255, 0.18);\n background: var(--ecom-strip-bg, linear-gradient(145deg, #5b21b6 0%, #3730a3 42%, #1e1b4b 100%));\n background-clip: padding-box;\n color: var(--ecom-fg, #ffffff);\n box-shadow:\n 0 0 0 1px rgba(0, 0, 0, 0.18) inset,\n 0 1px 0 rgba(255, 255, 255, 0.14) inset,\n 0 -20px 40px -8px rgba(0, 0, 0, 0.22) inset,\n 0 12px 32px rgba(0, 0, 0, 0.22),\n 0 28px 56px -16px rgba(0, 0, 0, 0.45),\n 0 0 80px -20px rgba(99, 102, 241, 0.35);\n overflow: visible;\n}\n\n#ttp-ecom-strip::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n z-index: 0;\n background: linear-gradient(\n 165deg,\n rgba(255, 255, 255, 0.08) 0%,\n rgba(255, 255, 255, 0) 42%,\n rgba(0, 0, 0, 0.12) 100%\n );\n}\n\n/* LIVE chip \u2014 glass capsule (backdrop blur), straddles top edge */\n#ttp-ecom-live-edge {\n position: absolute;\n left: 50%;\n top: 0;\n transform: translate(-50%, -50%);\n z-index: 4;\n max-width: calc(100% - 52px);\n pointer-events: none;\n padding: 3px 14px 4px;\n border-radius: 999px;\n background: rgba(8, 6, 24, 0.78);\n border: 1px solid rgba(255, 255, 255, 0.28);\n box-shadow:\n 0 0 0 1px rgba(0, 0, 0, 0.35),\n 0 4px 16px rgba(0, 0, 0, 0.35),\n inset 0 1px 0 rgba(255, 255, 255, 0.12);\n}\n@supports (backdrop-filter: blur(1px)) {\n #ttp-ecom-live-edge {\n backdrop-filter: blur(14px) saturate(180%);\n -webkit-backdrop-filter: blur(14px) saturate(180%);\n }\n}\n\n#ttp-ecom-main-row {\n position: relative;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 18px 16px 12px;\n min-height: 48px;\n}\n\n#ttp-ecom-footer {\n position: relative;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 16px 10px;\n margin-top: -4px;\n border-top: 1px solid rgba(255, 255, 255, 0.06);\n padding-top: 6px;\n}\n#ttp-ecom-footer span {\n font-size: 10px;\n color: rgba(255, 255, 255, 0.35);\n white-space: nowrap;\n}\n#ttp-ecom-footer a {\n color: rgba(167, 139, 250, 0.95);\n text-decoration: none;\n font-weight: 600;\n}\n#ttp-ecom-footer a:hover { opacity: 0.85; }\n#ttp-ecom-footer a b { font-weight: 700; }\n\n#ttp-ecom-left-controls,\n#ttp-ecom-right-cluster {\n display: flex;\n align-items: center;\n gap: 7px;\n flex-shrink: 0;\n}\n\n/* Inset \u201Cmeter bridge\u201D for transcript \u2014 keeps one-line layout, adds depth */\n#ttp-ecom-center {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n justify-content: center;\n text-align: start;\n padding: 7px 12px 7px 14px;\n border-radius: 16px;\n background: rgba(0, 0, 0, 0.16);\n border: 1px solid rgba(255, 255, 255, 0.08);\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.07),\n inset 0 -1px 0 rgba(0, 0, 0, 0.12);\n}\n\n#ttp-ecom-live-row {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n min-width: 0;\n}\n\n#ttp-ecom-live-label {\n font-size: 9px;\n font-weight: 800;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--ecom-accent, #4ade80);\n opacity: 0.98;\n white-space: nowrap;\n}\n\n#ttp-ecom-dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--ecom-accent, #4ade80);\n flex-shrink: 0;\n box-shadow: 0 0 14px rgba(74, 222, 128, 0.45);\n animation: ttp-ecom-blink 1.35s ease-in-out infinite;\n}\n#ttp-ecom-dot.paused {\n animation: none;\n opacity: 0.45;\n background: rgba(255,255,255,0.35);\n box-shadow: none;\n}\n@keyframes ttp-ecom-blink {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.25; transform: scale(0.92); }\n}\n\n#ttp-ecom-transcript {\n font-size: 12px;\n line-height: 1.42;\n letter-spacing: 0.015em;\n color: rgba(255, 255, 255, 0.94);\n width: 100%;\n max-width: 100%;\n overflow: hidden;\n word-break: break-word;\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n}\n\n#ttp-ecom-transcript.ttp-ecom-transcript--user {\n color: rgba(255, 244, 214, 0.98);\n font-weight: 600;\n letter-spacing: 0.02em;\n text-shadow: 0 0 28px rgba(251, 191, 36, 0.22);\n}\n\n/* Squircle glass controls */\n#ttp-ecom-strip .ttp-ecom-ctrl {\n width: 36px;\n height: 36px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.16);\n background: linear-gradient(165deg, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0.05) 100%);\n color: rgba(255, 255, 255, 0.92);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n position: relative;\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.18),\n 0 2px 8px rgba(0, 0, 0, 0.15);\n transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease, transform 0.14s ease;\n outline: none;\n}\n#ttp-ecom-strip .ttp-ecom-ctrl:hover {\n background: linear-gradient(165deg, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0.08) 100%);\n border-color: rgba(255, 255, 255, 0.32);\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.22),\n 0 4px 14px rgba(0, 0, 0, 0.2);\n}\n#ttp-ecom-strip .ttp-ecom-ctrl:active { transform: scale(0.94); }\n#ttp-ecom-strip .ttp-ecom-ctrl:focus-visible {\n outline: 2px solid rgba(165, 180, 252, 0.95);\n outline-offset: 2px;\n}\n#ttp-ecom-strip .ttp-ecom-ctrl-active {\n background: linear-gradient(165deg, rgba(129, 140, 248, 0.45) 0%, rgba(99, 102, 241, 0.28) 100%) !important;\n border-color: rgba(196, 181, 253, 0.65) !important;\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.2),\n 0 0 0 1px rgba(99, 102, 241, 0.35),\n 0 6px 18px rgba(79, 70, 229, 0.35) !important;\n}\n#ttp-ecom-strip .ttp-ecom-ctrl-warn {\n background: linear-gradient(165deg, rgba(252, 165, 165, 0.22) 0%, rgba(239, 68, 68, 0.2) 100%) !important;\n border-color: rgba(252, 165, 165, 0.55) !important;\n}\n\n#ttp-ecom-strip .ttp-ecom-ctrl-end {\n width: 40px;\n height: 40px;\n border-radius: 14px;\n background: linear-gradient(165deg, #fb7185 0%, var(--ecom-end, #ef4444) 52%, #991b1b 100%) !important;\n border: none !important;\n color: #fff !important;\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.25),\n 0 0 0 2px rgba(0, 0, 0, 0.12),\n 0 6px 22px rgba(239, 68, 68, 0.48);\n transform: rotate(135deg);\n transition: filter 0.18s ease, transform 0.14s ease, box-shadow 0.2s ease;\n}\n#ttp-ecom-strip .ttp-ecom-ctrl-end:hover {\n filter: brightness(1.06);\n transform: rotate(135deg) scale(1.05);\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.28),\n 0 0 0 2px rgba(0, 0, 0, 0.12),\n 0 10px 28px rgba(239, 68, 68, 0.52);\n}\n#ttp-ecom-strip .ttp-ecom-ctrl-end:active {\n transform: rotate(135deg) scale(0.94);\n}\n\n/* Mini waveform */\n#ttp-ecom-strip .ttp-ecom-mini-wave {\n display: flex;\n align-items: center;\n gap: 3px;\n height: 24px;\n padding: 0 4px;\n opacity: 0.9;\n}\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb {\n width: 3px;\n border-radius: 3px;\n background: linear-gradient(180deg, #ddd6fe 0%, var(--ecom-accent, #a78bfa) 100%);\n animation: ttp-ecom-wa 0.78s ease-in-out infinite;\n}\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(1) { height: 6px; animation-delay: 0s; }\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(2) { height: 11px; animation-delay: 0.1s; }\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(3) { height: 16px; animation-delay: 0.2s; }\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(4) { height: 9px; animation-delay: 0.15s; }\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb:nth-child(5) { height: 12px; animation-delay: 0.05s; }\n@keyframes ttp-ecom-wa {\n 0%, 100% { transform: scaleY(0.38); opacity: 0.5; }\n 50% { transform: scaleY(1); opacity: 1; }\n}\n#ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb.paused {\n animation-play-state: paused;\n transform: scaleY(0.32);\n opacity: 0.28;\n}\n\n/* Input row */\n#ttp-ecom-input-row {\n display: none;\n position: relative;\n z-index: 1;\n align-items: center;\n gap: 10px;\n padding: 0 14px 12px;\n margin-top: -2px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n}\n#ttp-ecom-input-row.open {\n display: flex;\n}\n#ttp-ecom-input {\n flex: 1;\n height: 40px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.18);\n background: rgba(0, 0, 0, 0.22);\n color: #fff;\n font-size: 14px;\n padding: 0 14px;\n outline: none;\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);\n transition: border-color 0.18s ease, box-shadow 0.18s ease;\n}\n#ttp-ecom-input::placeholder { color: rgba(255, 255, 255, 0.42); }\n#ttp-ecom-input:focus {\n border-color: rgba(165, 180, 252, 0.65);\n box-shadow:\n inset 0 1px 2px rgba(0, 0, 0, 0.2),\n 0 0 0 3px rgba(99, 102, 241, 0.25);\n}\n\n#ttp-ecom-strip .ttp-ecom-send {\n width: 36px;\n height: 36px;\n border-radius: 12px;\n border: none;\n background: linear-gradient(165deg, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0.1) 100%);\n color: #fff;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2);\n transition: background 0.18s ease, transform 0.12s ease;\n}\n#ttp-ecom-strip .ttp-ecom-send:hover {\n background: linear-gradient(165deg, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0.14) 100%);\n}\n#ttp-ecom-strip .ttp-ecom-send:active { transform: scale(0.94); }\n\n@media (max-width: 520px) {\n #ttp-ecom-strip .ttp-ecom-mini-wave { display: none; }\n #ttp-ecom-main-row { gap: 7px; padding: 16px 12px 10px; }\n #ttp-ecom-strip .ttp-ecom-ctrl { width: 36px; height: 36px; }\n #ttp-ecom-strip .ttp-ecom-ctrl-end { width: 40px; height: 40px; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n #ttp-ecom-dot { animation: none; opacity: 0.9; }\n #ttp-ecom-strip .ttp-ecom-mini-wave .ttp-ecom-wb { animation: none; opacity: 0.65; }\n}\n";
|
|
13765
|
+
|
|
13766
|
+
/***/ }),
|
|
13767
|
+
|
|
13768
|
+
/***/ "./src/ecommerce/FlyToCart.js":
|
|
13769
|
+
/*!************************************!*\
|
|
13770
|
+
!*** ./src/ecommerce/FlyToCart.js ***!
|
|
13771
|
+
\************************************/
|
|
13772
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
13773
|
+
|
|
13774
|
+
"use strict";
|
|
13775
|
+
__webpack_require__.r(__webpack_exports__);
|
|
13776
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
13777
|
+
/* harmony export */ FlyToCart: () => (/* binding */ FlyToCart)
|
|
13778
|
+
/* harmony export */ });
|
|
13779
|
+
/* harmony import */ var html2canvas__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! html2canvas */ "./node_modules/html2canvas/dist/html2canvas.js");
|
|
13780
|
+
/* harmony import */ var html2canvas__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(html2canvas__WEBPACK_IMPORTED_MODULE_0__);
|
|
13781
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
13782
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
13783
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
13784
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
13785
|
+
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
13786
|
+
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
13787
|
+
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
13788
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
13789
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
13790
|
+
/**
|
|
13791
|
+
* FlyToCart — Fly-to-cart animation with tornado funnel effect.
|
|
13792
|
+
*
|
|
13793
|
+
* When the user taps "Add to Cart", the product card flies straight down
|
|
13794
|
+
* into the cart icon with a 3D tornado distortion, particle trail, and impact effects.
|
|
13795
|
+
*
|
|
13796
|
+
* Uses html2canvas to capture the card as an image — avoids DOM stacking/overflow
|
|
13797
|
+
* clipping when the widget is in an iframe or a container with overflow:hidden.
|
|
13798
|
+
*
|
|
13799
|
+
* Usage:
|
|
13800
|
+
* const flyToCart = new FlyToCart({
|
|
13801
|
+
* getCartTarget: () => document.querySelector('.ttp-cart-icon'),
|
|
13802
|
+
* onCartBump: () => { ... },
|
|
13803
|
+
* });
|
|
13804
|
+
* flyToCart.triggerFly(cardElement, product, () => { /* add to cart API *\/ });
|
|
13805
|
+
*/
|
|
13806
|
+
|
|
13807
|
+
|
|
13808
|
+
var DURATION = 1000;
|
|
13809
|
+
var FlyToCart = /*#__PURE__*/function () {
|
|
13810
|
+
function FlyToCart() {
|
|
13811
|
+
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
13812
|
+
_classCallCheck(this, FlyToCart);
|
|
13813
|
+
this.getCartTarget = options.getCartTarget || function () {
|
|
13814
|
+
return null;
|
|
13815
|
+
};
|
|
13816
|
+
this.onCartBump = options.onCartBump || function () {};
|
|
13817
|
+
this.onRipple = options.onRipple || function () {};
|
|
13818
|
+
this.animatingId = null;
|
|
13819
|
+
this.trailParticles = [];
|
|
13820
|
+
this.canvas = null;
|
|
13821
|
+
this.rafId = null;
|
|
13822
|
+
this._canvasInitialized = false;
|
|
13823
|
+
}
|
|
13824
|
+
return _createClass(FlyToCart, [{
|
|
13825
|
+
key: "isAnimating",
|
|
13826
|
+
get: function get() {
|
|
13204
13827
|
return this.animatingId !== null;
|
|
13205
13828
|
}
|
|
13206
13829
|
}, {
|
|
@@ -13567,13 +14190,10 @@ var MobilePresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
|
13567
14190
|
if (this.carousel && this.carousel.container) {
|
|
13568
14191
|
this.carousel.showToast(product.name, cartInfo);
|
|
13569
14192
|
}
|
|
13570
|
-
this._updateMobileCartRow(cartInfo);
|
|
13571
14193
|
}
|
|
13572
14194
|
}, {
|
|
13573
14195
|
key: "updateCart",
|
|
13574
|
-
value: function updateCart(
|
|
13575
|
-
this._updateMobileCartRow(cartInfo);
|
|
13576
|
-
}
|
|
14196
|
+
value: function updateCart(_cartInfo) {}
|
|
13577
14197
|
}, {
|
|
13578
14198
|
key: "destroy",
|
|
13579
14199
|
value: function destroy() {
|
|
@@ -13583,27 +14203,6 @@ var MobilePresenter = /*#__PURE__*/function (_EcommercePresenter) {
|
|
|
13583
14203
|
this.carousel = null;
|
|
13584
14204
|
}
|
|
13585
14205
|
}
|
|
13586
|
-
|
|
13587
|
-
// ═══════════════════════════════════════════
|
|
13588
|
-
// Mobile cart row in the minimized voice bar
|
|
13589
|
-
// ═══════════════════════════════════════════
|
|
13590
|
-
}, {
|
|
13591
|
-
key: "_updateMobileCartRow",
|
|
13592
|
-
value: function _updateMobileCartRow(_ref) {
|
|
13593
|
-
var itemCount = _ref.itemCount,
|
|
13594
|
-
total = _ref.total,
|
|
13595
|
-
currency = _ref.currency;
|
|
13596
|
-
var row = document.getElementById('mobileCartRow');
|
|
13597
|
-
var textEl = document.getElementById('mobileCartText');
|
|
13598
|
-
if (!row || !textEl) return;
|
|
13599
|
-
if (itemCount <= 0) {
|
|
13600
|
-
row.style.display = 'none';
|
|
13601
|
-
return;
|
|
13602
|
-
}
|
|
13603
|
-
var itemText = "".concat(itemCount, " item").concat(itemCount !== 1 ? 's' : '');
|
|
13604
|
-
textEl.textContent = "".concat(itemText, " \xB7 ").concat(currency).concat(total.toFixed(2));
|
|
13605
|
-
row.style.display = 'flex';
|
|
13606
|
-
}
|
|
13607
14206
|
}]);
|
|
13608
14207
|
}(_EcommercePresenter_js__WEBPACK_IMPORTED_MODULE_0__.EcommercePresenter);
|
|
13609
14208
|
|
|
@@ -14192,14 +14791,11 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
14192
14791
|
/* harmony export */ PartnerScriptRunner: () => (/* binding */ PartnerScriptRunner),
|
|
14193
14792
|
/* harmony export */ buildRunPartnerScriptTool: () => (/* binding */ buildRunPartnerScriptTool)
|
|
14194
14793
|
/* harmony export */ });
|
|
14195
|
-
function
|
|
14196
|
-
function
|
|
14197
|
-
function
|
|
14198
|
-
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
|
|
14794
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
14795
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
14796
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
14199
14797
|
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
14200
14798
|
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
14201
|
-
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
14202
|
-
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
14203
14799
|
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
14204
14800
|
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
14205
14801
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
@@ -14207,11 +14803,18 @@ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o =
|
|
|
14207
14803
|
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
14208
14804
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
14209
14805
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
14210
|
-
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
14211
14806
|
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
14212
14807
|
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
14213
14808
|
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
14214
14809
|
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
14810
|
+
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
|
|
14811
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
14812
|
+
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
|
|
14813
|
+
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
|
|
14814
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
14815
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
14816
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
14817
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
14215
14818
|
/**
|
|
14216
14819
|
* PartnerScriptRunner
|
|
14217
14820
|
*
|
|
@@ -14219,7 +14822,8 @@ function _asyncToGenerator(n) { return function () { var t = this, e = arguments
|
|
|
14219
14822
|
*
|
|
14220
14823
|
* Backend flow:
|
|
14221
14824
|
* 1. On session start (after flavor is applied), backend sends
|
|
14222
|
-
* `{ t: 'partner_bundle', partner_id, platform,
|
|
14825
|
+
* `{ t: 'partner_bundle', partner_id, platform,
|
|
14826
|
+
* adapters: { action: {...} }, autoRun: { action: {...} } }`.
|
|
14223
14827
|
* Adapters are object-keyed because action is a unique key within a
|
|
14224
14828
|
* partner's bundle — the DB constraint enforces it, the voice LLM's
|
|
14225
14829
|
* tool schema assumes it.
|
|
@@ -14245,10 +14849,85 @@ function _asyncToGenerator(n) { return function () { var t = this, e = arguments
|
|
|
14245
14849
|
* - refreshUI : platform-aware UI nudge
|
|
14246
14850
|
* - normalizeCart: raw-cart → canonical cart (platform-dispatched)
|
|
14247
14851
|
* - runAdapter : (action, args) → Promise<result> (invokes another adapter)
|
|
14852
|
+
* - emit : (msg) → void — send an unsolicited message back to the backend
|
|
14853
|
+
*
|
|
14854
|
+
* Orchestration (DB columns before_actions / after_actions on each adapter row):
|
|
14855
|
+
* runAdapter(action) runs, in order:
|
|
14856
|
+
* 1. each before_actions[] entry (full runAdapter — hooks can chain)
|
|
14857
|
+
* 2. code_js body (category event + adapter fn)
|
|
14858
|
+
* 3. each after_actions[] entry when the body returned ok !== false
|
|
14859
|
+
* (args include _parentResult with the body return value)
|
|
14860
|
+
* before-hook failures are logged and do not block the main body.
|
|
14861
|
+
*
|
|
14862
|
+
* Inline pre/post hooks (entry-level pre_code_js / post_code_js):
|
|
14863
|
+
* Some bundles (notably the reserved `__client_tools__` client-tool bundle)
|
|
14864
|
+
* ship a 3-step entry: an optional `pre_code_js`, the required `code_js`
|
|
14865
|
+
* (main), and an optional `post_code_js`. These compile alongside `code_js`
|
|
14866
|
+
* and run synchronously in order around the main body:
|
|
14867
|
+
* - pre is best-effort: an error is logged and main still runs;
|
|
14868
|
+
* - main's return value is the result sent back; a main error → ok:false;
|
|
14869
|
+
* - post runs only if main succeeded (mirrors after-hook gating); post
|
|
14870
|
+
* errors are logged best-effort.
|
|
14871
|
+
* A bad pre/post poisons only its own entry (same isolation as code_js).
|
|
14872
|
+
*
|
|
14873
|
+
* Combined ordering when an entry has both named hooks and inline hooks:
|
|
14874
|
+
* named before_actions → inline pre → main → inline post → named after_actions
|
|
14875
|
+
*
|
|
14876
|
+
* Category-based event suppression:
|
|
14877
|
+
* When an adapter entry has a non-null `category` field, runAdapter fires a
|
|
14878
|
+
* CustomEvent on window named `netcost:partner-action:{category}` BEFORE
|
|
14879
|
+
* executing the adapter. Subscribers (e.g. cart.subscribe) listen for this
|
|
14880
|
+
* event to suppress their own duplicate notifications.
|
|
14248
14881
|
*/
|
|
14249
14882
|
|
|
14250
14883
|
var LOG = '[PartnerScriptRunner]';
|
|
14251
14884
|
|
|
14885
|
+
/** Max nested runAdapter depth (before/after hooks calling other hooked actions). */
|
|
14886
|
+
var MAX_ORCHESTRATION_DEPTH = 16;
|
|
14887
|
+
function adapterResultOk(result) {
|
|
14888
|
+
if (!result || _typeof(result) !== 'object') return true;
|
|
14889
|
+
if (!('ok' in result)) return true;
|
|
14890
|
+
return !!result.ok;
|
|
14891
|
+
}
|
|
14892
|
+
|
|
14893
|
+
/**
|
|
14894
|
+
* Best-effort IDs from run_partner_script args for diagnostics (cart.add, Shopify, etc.).
|
|
14895
|
+
* @param {Record<string, unknown>} raw
|
|
14896
|
+
* @returns {string[]}
|
|
14897
|
+
*/
|
|
14898
|
+
function extractProductIdsFromPartnerArgs(raw) {
|
|
14899
|
+
if (!raw || _typeof(raw) !== 'object') return [];
|
|
14900
|
+
var out = [];
|
|
14901
|
+
var push = function push(v) {
|
|
14902
|
+
if (v == null || v === '') return;
|
|
14903
|
+
var s = String(v);
|
|
14904
|
+
if (s) out.push(s);
|
|
14905
|
+
};
|
|
14906
|
+
push(raw.productId);
|
|
14907
|
+
push(raw.sku);
|
|
14908
|
+
push(raw.retailerProductId);
|
|
14909
|
+
push(raw.variantId);
|
|
14910
|
+
if (Array.isArray(raw.items)) {
|
|
14911
|
+
var _iterator = _createForOfIteratorHelper(raw.items),
|
|
14912
|
+
_step;
|
|
14913
|
+
try {
|
|
14914
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
14915
|
+
var i = _step.value;
|
|
14916
|
+
if (!i || _typeof(i) !== 'object') continue;
|
|
14917
|
+
push(i.productId);
|
|
14918
|
+
push(i.sku);
|
|
14919
|
+
push(i.retailerProductId);
|
|
14920
|
+
push(i.variantId);
|
|
14921
|
+
}
|
|
14922
|
+
} catch (err) {
|
|
14923
|
+
_iterator.e(err);
|
|
14924
|
+
} finally {
|
|
14925
|
+
_iterator.f();
|
|
14926
|
+
}
|
|
14927
|
+
}
|
|
14928
|
+
return _toConsumableArray(new Set(out));
|
|
14929
|
+
}
|
|
14930
|
+
|
|
14252
14931
|
// ---------------------------------------------------------------------------
|
|
14253
14932
|
// Compilation
|
|
14254
14933
|
// ---------------------------------------------------------------------------
|
|
@@ -14262,13 +14941,33 @@ var AsyncFunction = Object.getPrototypeOf(/*#__PURE__*/_asyncToGenerator(/*#__PU
|
|
|
14262
14941
|
}, _callee);
|
|
14263
14942
|
}))).constructor;
|
|
14264
14943
|
|
|
14265
|
-
// Wrap and compile an adapter body.
|
|
14266
|
-
//
|
|
14944
|
+
// Wrap and compile an adapter body. Two authoring styles are accepted:
|
|
14945
|
+
//
|
|
14946
|
+
// 1. Function expression (how DB partner adapters are stored):
|
|
14947
|
+
// async (ctx) => { ...; return {...}; }
|
|
14948
|
+
// 2. Raw statement body (how client-tool scripts are typed in the UI):
|
|
14949
|
+
// alert("hi"); return { ok: true };
|
|
14950
|
+
// The body runs inside an AsyncFunction whose single parameter is `ctx`,
|
|
14951
|
+
// so `ctx.args` etc. are directly in scope.
|
|
14952
|
+
//
|
|
14953
|
+
// Expression style is detected by shape (starts with `async`, `function`, `(`,
|
|
14954
|
+
// or `name =>`) and tried first; if it doesn't parse as an expression we fall
|
|
14955
|
+
// back to compiling the code as a plain body. AsyncFunction (not
|
|
14267
14956
|
// `new Function`) enforces an async execution context — no accidental sync
|
|
14268
14957
|
// adapter bodies.
|
|
14958
|
+
var FN_EXPR_RE = /^\s*(async[\s(])|^\s*function\b|^\s*\(|^\s*[A-Za-z_$][\w$]*\s*=>/;
|
|
14269
14959
|
function compileAdapter(codeJs) {
|
|
14960
|
+
if (FN_EXPR_RE.test(codeJs)) {
|
|
14961
|
+
try {
|
|
14962
|
+
// eslint-disable-next-line no-new-func
|
|
14963
|
+
return new AsyncFunction('ctx', "\"use strict\"; return (".concat(codeJs, ").call(null, ctx);"));
|
|
14964
|
+
} catch (e) {
|
|
14965
|
+
// Looked like an expression but doesn't parse as one (e.g. an
|
|
14966
|
+
// IIFE-opening statement body) — fall through to body compilation.
|
|
14967
|
+
}
|
|
14968
|
+
}
|
|
14270
14969
|
// eslint-disable-next-line no-new-func
|
|
14271
|
-
return new AsyncFunction('ctx', "\"use strict\";
|
|
14970
|
+
return new AsyncFunction('ctx', "\"use strict\"; ".concat(codeJs));
|
|
14272
14971
|
}
|
|
14273
14972
|
|
|
14274
14973
|
// Cheap 32-bit string hash, rendered as 12 hex chars. NOT cryptographic —
|
|
@@ -14377,20 +15076,42 @@ function refreshUIForPlatform(platform) {
|
|
|
14377
15076
|
// ---------------------------------------------------------------------------
|
|
14378
15077
|
|
|
14379
15078
|
var PartnerScriptRunner = /*#__PURE__*/function () {
|
|
15079
|
+
/**
|
|
15080
|
+
* @param {{ genericCtx?: boolean }} [opts]
|
|
15081
|
+
* genericCtx: when true, ctx omits the ecommerce-specific helpers
|
|
15082
|
+
* (platform / normalizeCart / refreshUI). Used by the flavor-independent
|
|
15083
|
+
* `__client_tools__` runner. Default false preserves the ecommerce ctx.
|
|
15084
|
+
*/
|
|
14380
15085
|
function PartnerScriptRunner() {
|
|
15086
|
+
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
14381
15087
|
_classCallCheck(this, PartnerScriptRunner);
|
|
14382
|
-
|
|
15088
|
+
this.genericCtx = !!(opts && opts.genericCtx);
|
|
15089
|
+
/** @type {Map<string, {fn?: Function, version?: number, codeSha256?: string, meta?: object, category?: string|null, beforeActions?: string[], afterActions?: string[], poisoned?: boolean, error?: string}>} */
|
|
14383
15090
|
this.adapters = new Map();
|
|
14384
15091
|
this.partnerId = null;
|
|
14385
15092
|
this.platform = null;
|
|
14386
15093
|
this.bundleVersion = 0; // monotonic, bumped on every setBundle
|
|
15094
|
+
/** @type {((msg: object) => void) | null} */
|
|
15095
|
+
this._emitCallback = null;
|
|
14387
15096
|
}
|
|
14388
15097
|
|
|
14389
15098
|
/**
|
|
14390
|
-
*
|
|
14391
|
-
*
|
|
15099
|
+
* Register the callback that adapters can use to send unsolicited messages
|
|
15100
|
+
* back to the backend via ctx.emit(msg). Called by EcommerceFlavor after it
|
|
15101
|
+
* loads the bundle so the callback is always fresh for the active SDK instance.
|
|
15102
|
+
* @param {(msg: object) => void} fn
|
|
14392
15103
|
*/
|
|
14393
15104
|
return _createClass(PartnerScriptRunner, [{
|
|
15105
|
+
key: "setEmitCallback",
|
|
15106
|
+
value: function setEmitCallback(fn) {
|
|
15107
|
+
this._emitCallback = typeof fn === 'function' ? fn : null;
|
|
15108
|
+
}
|
|
15109
|
+
|
|
15110
|
+
/**
|
|
15111
|
+
* Replace the cached bundle from a `partner_bundle` message.
|
|
15112
|
+
* @param {{partner_id?: string, platform?: string|null, adapters?: Record<string,{version?:number,code_js:string,code_sha256?:string,meta?:object,before_actions?:string[],after_actions?:string[]}>}} msg
|
|
15113
|
+
*/
|
|
15114
|
+
}, {
|
|
14394
15115
|
key: "setBundle",
|
|
14395
15116
|
value: function setBundle(msg) {
|
|
14396
15117
|
var adapters = (msg === null || msg === void 0 ? void 0 : msg.adapters) || {};
|
|
@@ -14415,22 +15136,33 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
|
|
|
14415
15136
|
continue;
|
|
14416
15137
|
}
|
|
14417
15138
|
try {
|
|
14418
|
-
var _entry$version2;
|
|
15139
|
+
var _entry$version2, _entry$category;
|
|
14419
15140
|
var fn = compileAdapter(entry.code_js);
|
|
15141
|
+
// Inline pre/post hooks (optional). Compiled with the same
|
|
15142
|
+
// AsyncFunction strict-mode wrapper as the main body so a syntax
|
|
15143
|
+
// error in a hook poisons only this entry (caught below).
|
|
15144
|
+
var preFn = typeof entry.pre_code_js === 'string' ? compileAdapter(entry.pre_code_js) : null;
|
|
15145
|
+
var postFn = typeof entry.post_code_js === 'string' ? compileAdapter(entry.post_code_js) : null;
|
|
14420
15146
|
this.adapters.set(action, {
|
|
14421
15147
|
fn: fn,
|
|
15148
|
+
preFn: preFn,
|
|
15149
|
+
postFn: postFn,
|
|
14422
15150
|
version: (_entry$version2 = entry.version) !== null && _entry$version2 !== void 0 ? _entry$version2 : null,
|
|
14423
15151
|
codeSha256: entry.code_sha256 || null,
|
|
14424
|
-
|
|
15152
|
+
category: (_entry$category = entry.category) !== null && _entry$category !== void 0 ? _entry$category : null,
|
|
15153
|
+
meta: entry.meta || {},
|
|
15154
|
+
beforeActions: Array.isArray(entry.before_actions) ? entry.before_actions : [],
|
|
15155
|
+
afterActions: Array.isArray(entry.after_actions) ? entry.after_actions : []
|
|
14425
15156
|
});
|
|
14426
15157
|
compiled++;
|
|
14427
15158
|
} catch (err) {
|
|
14428
|
-
var _entry$version3;
|
|
15159
|
+
var _entry$version3, _entry$category2;
|
|
14429
15160
|
console.error(LOG, "compile failed for '".concat(action, "':"), err);
|
|
14430
15161
|
this.adapters.set(action, {
|
|
14431
15162
|
poisoned: true,
|
|
14432
15163
|
error: String((err === null || err === void 0 ? void 0 : err.message) || err),
|
|
14433
|
-
version: (_entry$version3 = entry.version) !== null && _entry$version3 !== void 0 ? _entry$version3 : null
|
|
15164
|
+
version: (_entry$version3 = entry.version) !== null && _entry$version3 !== void 0 ? _entry$version3 : null,
|
|
15165
|
+
category: (_entry$category2 = entry.category) !== null && _entry$category2 !== void 0 ? _entry$category2 : null
|
|
14434
15166
|
});
|
|
14435
15167
|
poisoned++;
|
|
14436
15168
|
}
|
|
@@ -14466,24 +15198,62 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
|
|
|
14466
15198
|
/**
|
|
14467
15199
|
* Execute an adapter by action name. Used both by the run_partner_script
|
|
14468
15200
|
* tool handler and by ctx.runAdapter for intra-bundle chaining.
|
|
15201
|
+
*
|
|
15202
|
+
* When the adapter row defines before_actions / after_actions (from
|
|
15203
|
+
* ttp.partner_adapters), those run automatically around code_js.
|
|
15204
|
+
*
|
|
15205
|
+
* If the adapter has a non-null `category`, fires a window CustomEvent
|
|
15206
|
+
* named `netcost:partner-action:{category}` BEFORE executing code_js.
|
|
15207
|
+
* Subscribers (e.g. cart.subscribe) use this to suppress their own
|
|
15208
|
+
* duplicate notifications for the same mutation.
|
|
15209
|
+
*
|
|
15210
|
+
* @param {string} action
|
|
15211
|
+
* @param {Record<string, unknown>} [args]
|
|
15212
|
+
* @param {number} [depth] internal recursion guard
|
|
14469
15213
|
*/
|
|
14470
15214
|
}, {
|
|
14471
15215
|
key: "runAdapter",
|
|
14472
15216
|
value: (function () {
|
|
14473
15217
|
var _runAdapter = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(action) {
|
|
14474
|
-
var _entry$version4;
|
|
14475
15218
|
var args,
|
|
15219
|
+
depth,
|
|
14476
15220
|
entry,
|
|
14477
|
-
|
|
15221
|
+
safeArgs,
|
|
15222
|
+
beforeActions,
|
|
15223
|
+
afterActions,
|
|
15224
|
+
_iterator2,
|
|
15225
|
+
_step2,
|
|
15226
|
+
_hookAction,
|
|
15227
|
+
_hookResult,
|
|
15228
|
+
result,
|
|
15229
|
+
afterArgs,
|
|
15230
|
+
_iterator3,
|
|
15231
|
+
_step3,
|
|
15232
|
+
hookAction,
|
|
15233
|
+
hookResult,
|
|
14478
15234
|
_args2 = arguments,
|
|
14479
|
-
_t
|
|
15235
|
+
_t,
|
|
15236
|
+
_t2;
|
|
14480
15237
|
return _regenerator().w(function (_context2) {
|
|
14481
15238
|
while (1) switch (_context2.p = _context2.n) {
|
|
14482
15239
|
case 0:
|
|
14483
15240
|
args = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : {};
|
|
15241
|
+
depth = _args2.length > 2 && _args2[2] !== undefined ? _args2[2] : 0;
|
|
15242
|
+
if (!(depth > MAX_ORCHESTRATION_DEPTH)) {
|
|
15243
|
+
_context2.n = 1;
|
|
15244
|
+
break;
|
|
15245
|
+
}
|
|
15246
|
+
console.error("".concat(LOG, " orchestration depth exceeded for '").concat(action, "' (max ").concat(MAX_ORCHESTRATION_DEPTH, ")"));
|
|
15247
|
+
return _context2.a(2, {
|
|
15248
|
+
ok: false,
|
|
15249
|
+
reason: 'orchestration_depth_exceeded',
|
|
15250
|
+
action: action,
|
|
15251
|
+
depth: depth
|
|
15252
|
+
});
|
|
15253
|
+
case 1:
|
|
14484
15254
|
entry = this.adapters.get(action);
|
|
14485
15255
|
if (entry) {
|
|
14486
|
-
_context2.n =
|
|
15256
|
+
_context2.n = 2;
|
|
14487
15257
|
break;
|
|
14488
15258
|
}
|
|
14489
15259
|
return _context2.a(2, {
|
|
@@ -14491,9 +15261,9 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
|
|
|
14491
15261
|
reason: 'adapter_not_in_bundle',
|
|
14492
15262
|
action: action
|
|
14493
15263
|
});
|
|
14494
|
-
case
|
|
15264
|
+
case 2:
|
|
14495
15265
|
if (!entry.poisoned) {
|
|
14496
|
-
_context2.n =
|
|
15266
|
+
_context2.n = 3;
|
|
14497
15267
|
break;
|
|
14498
15268
|
}
|
|
14499
15269
|
return _context2.a(2, {
|
|
@@ -14502,40 +15272,225 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
|
|
|
14502
15272
|
action: action,
|
|
14503
15273
|
detail: entry.error
|
|
14504
15274
|
});
|
|
14505
|
-
case
|
|
14506
|
-
|
|
14507
|
-
|
|
14508
|
-
|
|
14509
|
-
|
|
14510
|
-
|
|
14511
|
-
|
|
14512
|
-
return _context2.a(2, _context2.v);
|
|
15275
|
+
case 3:
|
|
15276
|
+
safeArgs = args && _typeof(args) === 'object' ? args : {};
|
|
15277
|
+
beforeActions = entry.beforeActions || [];
|
|
15278
|
+
afterActions = entry.afterActions || [];
|
|
15279
|
+
_iterator2 = _createForOfIteratorHelper(beforeActions);
|
|
15280
|
+
_context2.p = 4;
|
|
15281
|
+
_iterator2.s();
|
|
14513
15282
|
case 5:
|
|
14514
|
-
|
|
14515
|
-
|
|
14516
|
-
|
|
14517
|
-
|
|
14518
|
-
|
|
14519
|
-
|
|
14520
|
-
|
|
14521
|
-
|
|
14522
|
-
|
|
14523
|
-
|
|
14524
|
-
|
|
14525
|
-
|
|
14526
|
-
|
|
14527
|
-
|
|
14528
|
-
|
|
15283
|
+
if ((_step2 = _iterator2.n()).done) {
|
|
15284
|
+
_context2.n = 9;
|
|
15285
|
+
break;
|
|
15286
|
+
}
|
|
15287
|
+
_hookAction = _step2.value;
|
|
15288
|
+
if (!(!_hookAction || typeof _hookAction !== 'string')) {
|
|
15289
|
+
_context2.n = 6;
|
|
15290
|
+
break;
|
|
15291
|
+
}
|
|
15292
|
+
return _context2.a(3, 8);
|
|
15293
|
+
case 6:
|
|
15294
|
+
console.log("".concat(LOG, " before hook ").concat(action, " \u2192 ").concat(_hookAction));
|
|
15295
|
+
_context2.n = 7;
|
|
15296
|
+
return this.runAdapter(_hookAction, safeArgs, depth + 1);
|
|
15297
|
+
case 7:
|
|
15298
|
+
_hookResult = _context2.v;
|
|
15299
|
+
if (!adapterResultOk(_hookResult)) {
|
|
15300
|
+
console.warn("".concat(LOG, " before hook '").concat(_hookAction, "' for '").concat(action, "' returned ok=false \u2014 continuing with main body:"), _hookResult);
|
|
15301
|
+
}
|
|
15302
|
+
case 8:
|
|
15303
|
+
_context2.n = 5;
|
|
15304
|
+
break;
|
|
15305
|
+
case 9:
|
|
15306
|
+
_context2.n = 11;
|
|
15307
|
+
break;
|
|
15308
|
+
case 10:
|
|
15309
|
+
_context2.p = 10;
|
|
15310
|
+
_t = _context2.v;
|
|
15311
|
+
_iterator2.e(_t);
|
|
15312
|
+
case 11:
|
|
15313
|
+
_context2.p = 11;
|
|
15314
|
+
_iterator2.f();
|
|
15315
|
+
return _context2.f(11);
|
|
15316
|
+
case 12:
|
|
15317
|
+
_context2.n = 13;
|
|
15318
|
+
return this._runAdapterBody(action, safeArgs, entry);
|
|
15319
|
+
case 13:
|
|
15320
|
+
result = _context2.v;
|
|
15321
|
+
if (!(adapterResultOk(result) && afterActions.length > 0)) {
|
|
15322
|
+
_context2.n = 22;
|
|
15323
|
+
break;
|
|
15324
|
+
}
|
|
15325
|
+
afterArgs = _objectSpread(_objectSpread({}, safeArgs), {}, {
|
|
15326
|
+
_parentResult: result
|
|
15327
|
+
});
|
|
15328
|
+
_iterator3 = _createForOfIteratorHelper(afterActions);
|
|
15329
|
+
_context2.p = 14;
|
|
15330
|
+
_iterator3.s();
|
|
15331
|
+
case 15:
|
|
15332
|
+
if ((_step3 = _iterator3.n()).done) {
|
|
15333
|
+
_context2.n = 19;
|
|
15334
|
+
break;
|
|
15335
|
+
}
|
|
15336
|
+
hookAction = _step3.value;
|
|
15337
|
+
if (!(!hookAction || typeof hookAction !== 'string')) {
|
|
15338
|
+
_context2.n = 16;
|
|
15339
|
+
break;
|
|
15340
|
+
}
|
|
15341
|
+
return _context2.a(3, 18);
|
|
15342
|
+
case 16:
|
|
15343
|
+
console.log("".concat(LOG, " after hook ").concat(action, " \u2192 ").concat(hookAction));
|
|
15344
|
+
_context2.n = 17;
|
|
15345
|
+
return this.runAdapter(hookAction, afterArgs, depth + 1);
|
|
15346
|
+
case 17:
|
|
15347
|
+
hookResult = _context2.v;
|
|
15348
|
+
if (!adapterResultOk(hookResult)) {
|
|
15349
|
+
console.warn("".concat(LOG, " after hook '").concat(hookAction, "' for '").concat(action, "' returned ok=false:"), hookResult);
|
|
15350
|
+
}
|
|
15351
|
+
case 18:
|
|
15352
|
+
_context2.n = 15;
|
|
15353
|
+
break;
|
|
15354
|
+
case 19:
|
|
15355
|
+
_context2.n = 21;
|
|
15356
|
+
break;
|
|
15357
|
+
case 20:
|
|
15358
|
+
_context2.p = 20;
|
|
15359
|
+
_t2 = _context2.v;
|
|
15360
|
+
_iterator3.e(_t2);
|
|
15361
|
+
case 21:
|
|
15362
|
+
_context2.p = 21;
|
|
15363
|
+
_iterator3.f();
|
|
15364
|
+
return _context2.f(21);
|
|
15365
|
+
case 22:
|
|
15366
|
+
return _context2.a(2, result);
|
|
15367
|
+
}
|
|
15368
|
+
}, _callee2, this, [[14, 20, 21, 22], [4, 10, 11, 12]]);
|
|
15369
|
+
}));
|
|
15370
|
+
function runAdapter(_x) {
|
|
15371
|
+
return _runAdapter.apply(this, arguments);
|
|
15372
|
+
}
|
|
15373
|
+
return runAdapter;
|
|
15374
|
+
}()
|
|
15375
|
+
/**
|
|
15376
|
+
* Run code_js only — category event + compiled fn. No before/after hooks.
|
|
15377
|
+
* @private
|
|
15378
|
+
*/
|
|
15379
|
+
)
|
|
15380
|
+
}, {
|
|
15381
|
+
key: "_runAdapterBody",
|
|
15382
|
+
value: (function () {
|
|
15383
|
+
var _runAdapterBody2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(action, args, entry) {
|
|
15384
|
+
var _entry$version4;
|
|
15385
|
+
var eventName, safeArgs, productIds, _productIds$, idStr, _idStr, ctx, result, postCtx, _t3, _t4, _t5;
|
|
15386
|
+
return _regenerator().w(function (_context3) {
|
|
15387
|
+
while (1) switch (_context3.p = _context3.n) {
|
|
15388
|
+
case 0:
|
|
15389
|
+
// Fire category event so subscribers can suppress duplicate handling.
|
|
15390
|
+
if (entry.category) {
|
|
15391
|
+
eventName = "netcost:partner-action:".concat(entry.category);
|
|
15392
|
+
safeArgs = args || {};
|
|
15393
|
+
productIds = extractProductIdsFromPartnerArgs(safeArgs);
|
|
15394
|
+
try {
|
|
15395
|
+
window.dispatchEvent(new CustomEvent(eventName, {
|
|
15396
|
+
detail: {
|
|
15397
|
+
action: action,
|
|
15398
|
+
args: safeArgs
|
|
15399
|
+
}
|
|
15400
|
+
}));
|
|
15401
|
+
idStr = productIds.length ? productIds.join(',') : '(none)';
|
|
15402
|
+
console.log("".concat(LOG, " dispatched ").concat(eventName, " action=").concat(action, " productIds=").concat(idStr), {
|
|
15403
|
+
action: action,
|
|
15404
|
+
category: entry.category,
|
|
15405
|
+
productIds: productIds,
|
|
15406
|
+
productId: (_productIds$ = productIds[0]) !== null && _productIds$ !== void 0 ? _productIds$ : null
|
|
15407
|
+
});
|
|
15408
|
+
} catch (e) {
|
|
15409
|
+
_idStr = productIds.length ? productIds.join(',') : '(none)';
|
|
15410
|
+
console.warn("".concat(LOG, " failed to dispatch category event '").concat(eventName, "' for '").concat(action, "' productIds=").concat(_idStr, ":"), e);
|
|
15411
|
+
}
|
|
15412
|
+
}
|
|
15413
|
+
ctx = this._buildCtx(args, action, entry);
|
|
15414
|
+
console.log("".concat(LOG, " exec ").concat(action, " v").concat((_entry$version4 = entry.version) !== null && _entry$version4 !== void 0 ? _entry$version4 : '?', " ") + "sha=".concat((entry.codeSha256 || '').slice(0, 12)));
|
|
15415
|
+
|
|
15416
|
+
// Inline pre-hook (best-effort): an error is logged and main still runs.
|
|
15417
|
+
if (!entry.preFn) {
|
|
15418
|
+
_context3.n = 4;
|
|
15419
|
+
break;
|
|
15420
|
+
}
|
|
15421
|
+
_context3.p = 1;
|
|
15422
|
+
_context3.n = 2;
|
|
15423
|
+
return entry.preFn(ctx);
|
|
15424
|
+
case 2:
|
|
15425
|
+
_context3.n = 4;
|
|
15426
|
+
break;
|
|
15427
|
+
case 3:
|
|
15428
|
+
_context3.p = 3;
|
|
15429
|
+
_t3 = _context3.v;
|
|
15430
|
+
console.warn("".concat(LOG, " ").concat(action, " inline pre-hook threw (continuing with main body):"), _t3);
|
|
15431
|
+
case 4:
|
|
15432
|
+
_context3.p = 4;
|
|
15433
|
+
_context3.n = 5;
|
|
15434
|
+
return entry.fn(ctx);
|
|
15435
|
+
case 5:
|
|
15436
|
+
result = _context3.v;
|
|
15437
|
+
_context3.n = 7;
|
|
15438
|
+
break;
|
|
15439
|
+
case 6:
|
|
15440
|
+
_context3.p = 6;
|
|
15441
|
+
_t4 = _context3.v;
|
|
15442
|
+
console.error("".concat(LOG, " ").concat(action, " threw:"), _t4);
|
|
15443
|
+
return _context3.a(2, {
|
|
15444
|
+
ok: false,
|
|
15445
|
+
reason: 'adapter_threw',
|
|
15446
|
+
error: String((_t4 === null || _t4 === void 0 ? void 0 : _t4.message) || _t4)
|
|
15447
|
+
});
|
|
15448
|
+
case 7:
|
|
15449
|
+
if (!(entry.postFn && adapterResultOk(result))) {
|
|
15450
|
+
_context3.n = 11;
|
|
15451
|
+
break;
|
|
15452
|
+
}
|
|
15453
|
+
postCtx = this._buildCtx(_objectSpread(_objectSpread({}, args || {}), {}, {
|
|
15454
|
+
_mainResult: result
|
|
15455
|
+
}), action, entry);
|
|
15456
|
+
_context3.p = 8;
|
|
15457
|
+
_context3.n = 9;
|
|
15458
|
+
return entry.postFn(postCtx);
|
|
15459
|
+
case 9:
|
|
15460
|
+
_context3.n = 11;
|
|
15461
|
+
break;
|
|
15462
|
+
case 10:
|
|
15463
|
+
_context3.p = 10;
|
|
15464
|
+
_t5 = _context3.v;
|
|
15465
|
+
console.warn("".concat(LOG, " ").concat(action, " inline post-hook threw (ignored):"), _t5);
|
|
15466
|
+
case 11:
|
|
15467
|
+
return _context3.a(2, result);
|
|
15468
|
+
}
|
|
15469
|
+
}, _callee3, this, [[8, 10], [4, 6], [1, 3]]);
|
|
15470
|
+
}));
|
|
15471
|
+
function _runAdapterBody(_x2, _x3, _x4) {
|
|
15472
|
+
return _runAdapterBody2.apply(this, arguments);
|
|
15473
|
+
}
|
|
15474
|
+
return _runAdapterBody;
|
|
14529
15475
|
}())
|
|
14530
15476
|
}, {
|
|
14531
15477
|
key: "_buildCtx",
|
|
14532
15478
|
value: function _buildCtx(args, action /*, entry */) {
|
|
14533
15479
|
var _this = this;
|
|
14534
15480
|
var platform = this.platform;
|
|
14535
|
-
|
|
15481
|
+
var emitCallback = this._emitCallback;
|
|
15482
|
+
// Send an unsolicited message back to the backend (e.g. cart_change_event).
|
|
15483
|
+
// No-op if no emitCallback is registered.
|
|
15484
|
+
var emit = function emit(msg) {
|
|
15485
|
+
if (typeof emitCallback === 'function') {
|
|
15486
|
+
emitCallback(msg);
|
|
15487
|
+
} else {
|
|
15488
|
+
console.warn("".concat(LOG, " [adapter:").concat(action, "] ctx.emit called but no emitCallback registered"));
|
|
15489
|
+
}
|
|
15490
|
+
};
|
|
15491
|
+
var ctx = {
|
|
14536
15492
|
args: args || {},
|
|
14537
15493
|
host: window.location.hostname,
|
|
14538
|
-
platform: platform,
|
|
14539
15494
|
fetch: window.fetch.bind(window),
|
|
14540
15495
|
log: function log() {
|
|
14541
15496
|
var _console;
|
|
@@ -14544,14 +15499,22 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
|
|
|
14544
15499
|
}
|
|
14545
15500
|
return (_console = console).log.apply(_console, ["[adapter:".concat(action, "]")].concat(a));
|
|
14546
15501
|
},
|
|
14547
|
-
refreshUI: refreshUIForPlatform(platform),
|
|
14548
|
-
normalizeCart: function normalizeCart(raw) {
|
|
14549
|
-
return normalizeCartByPlatform(platform, raw);
|
|
14550
|
-
},
|
|
14551
15502
|
runAdapter: function runAdapter(a, subArgs) {
|
|
14552
15503
|
return _this.runAdapter(a, subArgs);
|
|
14553
|
-
}
|
|
15504
|
+
},
|
|
15505
|
+
emit: emit
|
|
14554
15506
|
};
|
|
15507
|
+
// Ecommerce-specific helpers are omitted for the flavor-independent
|
|
15508
|
+
// `__client_tools__` runner (genericCtx). Client-tool scripts get a
|
|
15509
|
+
// platform-agnostic ctx with no cart normalization or UI nudges.
|
|
15510
|
+
if (!this.genericCtx) {
|
|
15511
|
+
ctx.platform = platform;
|
|
15512
|
+
ctx.refreshUI = refreshUIForPlatform(platform);
|
|
15513
|
+
ctx.normalizeCart = function (raw) {
|
|
15514
|
+
return normalizeCartByPlatform(platform, raw);
|
|
15515
|
+
};
|
|
15516
|
+
}
|
|
15517
|
+
return ctx;
|
|
14555
15518
|
}
|
|
14556
15519
|
}]);
|
|
14557
15520
|
}();
|
|
@@ -14568,28 +15531,28 @@ function buildRunPartnerScriptTool(runner) {
|
|
|
14568
15531
|
return {
|
|
14569
15532
|
name: 'run_partner_script',
|
|
14570
15533
|
handler: function () {
|
|
14571
|
-
var _handler = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
15534
|
+
var _handler = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
|
|
14572
15535
|
var parameters,
|
|
14573
15536
|
action,
|
|
14574
15537
|
args,
|
|
14575
|
-
|
|
14576
|
-
return _regenerator().w(function (
|
|
14577
|
-
while (1) switch (
|
|
15538
|
+
_args4 = arguments;
|
|
15539
|
+
return _regenerator().w(function (_context4) {
|
|
15540
|
+
while (1) switch (_context4.n) {
|
|
14578
15541
|
case 0:
|
|
14579
|
-
parameters =
|
|
15542
|
+
parameters = _args4.length > 0 && _args4[0] !== undefined ? _args4[0] : {};
|
|
14580
15543
|
action = parameters.action, args = parameters.args;
|
|
14581
15544
|
if (action) {
|
|
14582
|
-
|
|
15545
|
+
_context4.n = 1;
|
|
14583
15546
|
break;
|
|
14584
15547
|
}
|
|
14585
|
-
return
|
|
15548
|
+
return _context4.a(2, {
|
|
14586
15549
|
ok: false,
|
|
14587
15550
|
reason: 'missing_action'
|
|
14588
15551
|
});
|
|
14589
15552
|
case 1:
|
|
14590
|
-
return
|
|
15553
|
+
return _context4.a(2, runner.runAdapter(action, args || {}));
|
|
14591
15554
|
}
|
|
14592
|
-
},
|
|
15555
|
+
}, _callee4);
|
|
14593
15556
|
}));
|
|
14594
15557
|
function handler() {
|
|
14595
15558
|
return _handler.apply(this, arguments);
|
|
@@ -15365,7 +16328,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
15365
16328
|
* Follows the same pattern as Styles.generateCSS().
|
|
15366
16329
|
*/
|
|
15367
16330
|
function generateEcommerceCSS() {
|
|
15368
|
-
return "\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n ECOMMERCE AREA\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n\n/*\n * When products are visible, expand the panel dynamically.\n * Actual height is set via inline style by EcommerceManager._expandPanelToFit().\n * When the class is removed, the panel returns to its CSS-defined default.\n */\n#text-chat-panel.ttp-panel-expanded {\n transition: height 0.25s ease;\n}\n\n/*\n * Layout overrides ONLY when ecommerce/hotels overlay is active.\n * Scoped under .ttp-products-visible / .ttp-rooms-visible so the\n * normal voice CSS takes effect when products/rooms are hidden.\n *\n * voiceActiveState is a flex column: [voice-controls] [ecommerce] [conversation].\n * Ecommerce area fills the middle and scrolls; conversation panel stays at bottom.\n */\n#voiceActiveState.ttp-products-visible,\n#voiceActiveState.ttp-rooms-visible {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow: hidden !important;\n}\n\n.ttp-products-visible > .desktop-voice-section,\n.ttp-rooms-visible > .desktop-voice-section {\n flex-shrink: 1 !important;\n}\n\n/* Conversation + input always stick to bottom; ecommerce area above grows to fill. */\n.ttp-products-visible > .desktop-conversation-panel,\n.ttp-rooms-visible > .desktop-conversation-panel {\n flex: 0 0 auto !important;\n margin-top: auto !important;\n display: flex !important;\n flex-direction: column !important;\n}\n\n.ttp-products-visible .desktop-conversation-panel .live-transcript-collapsed,\n.ttp-rooms-visible .desktop-conversation-panel .live-transcript-collapsed {\n flex: 0 0 auto !important;\n min-height: 40px !important;\n overflow: hidden !important;\n}\n\n.ttp-products-visible .desktop-conversation-panel .voice-text-input-area,\n.ttp-rooms-visible .desktop-conversation-panel .voice-text-input-area {\n margin-top: auto !important;\n padding: 3px 16px !important;\n}\n\n.ttp-products-visible .desktop-conversation-panel .voice-input-hint,\n.ttp-rooms-visible .desktop-conversation-panel .voice-input-hint {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .desktop-voice-section {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .compact-voice-section {\n display: flex !important;\n}\n\n#voiceActiveState.ttp-products-visible .desktop-conversation-panel {\n flex: 0 0 auto !important;\n}\n\n#voiceActiveState.ttp-products-visible .conversation-history {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .live-transcript-collapsed {\n flex: 0 0 auto !important;\n}\n\n.voice-text-input-area {\n flex-shrink: 0 !important;\n}\n\n.ttp-products-visible .conversation-history,\n.ttp-rooms-visible .conversation-history {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow-y: auto !important;\n}\n\n.ttp-ecommerce-area {\n display: flex !important;\n flex-direction: column !important;\n width: 100%;\n flex: 1 1 0 !important;\n min-height: 0 !important;\n position: relative !important;\n overflow-x: hidden !important;\n overflow-y: hidden !important;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT PICKER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-picker {\n background: rgba(255,255,255,0.04);\n border-radius: 14px;\n border: 1px solid rgba(255,255,255,0.08);\n box-shadow: none;\n margin: 8px 10px;\n overflow-x: hidden !important;\n overflow-y: auto !important;\n animation: ttpSlideUp 0.25s ease;\n display: flex !important;\n flex-direction: column;\n flex: 1;\n min-height: 0 !important;\n}\n\n@keyframes ttpSlideUp {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.ttp-picker-header {\n display: flex !important;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n border-bottom: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n}\n\n.ttp-picker-title {\n font-size: 12px;\n font-weight: 600;\n color: rgba(255,255,255,0.5);\n}\n\n.ttp-picker-close {\n background: none;\n border: none;\n cursor: pointer;\n color: rgba(255,255,255,0.4);\n font-size: 14px;\n padding: 2px 6px;\n border-radius: 4px;\n}\n.ttp-picker-close:hover {\n background: rgba(255,255,255,0.08);\n color: rgba(255,255,255,0.7);\n}\n\n/* Horizontal card layout */\n.ttp-products-horizontal {\n display: flex !important;\n align-items: flex-start;\n flex-shrink: 0;\n gap: 10px;\n padding: 12px;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: thin;\n}\n.ttp-products-horizontal::-webkit-scrollbar { height: 4px; }\n.ttp-products-horizontal::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 2px; }\n.ttp-products-horizontal::-webkit-scrollbar-track { background: transparent; }\n\n/* Vertical list layout */\n.ttp-products-vertical {\n padding: 8px 12px;\n overflow-y: auto !important;\n display: flex !important;\n flex-direction: column;\n gap: 6px;\n flex: 1;\n min-height: 0 !important;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT CARD (horizontal)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-card {\n width: 170px !important;\n flex-shrink: 0 !important;\n background: rgba(255,255,255,0.04);\n border-radius: 12px;\n border: 1px solid rgba(255,255,255,0.08);\n overflow: hidden;\n transition: border-color 0.15s;\n}\n.ttp-product-card:hover { border-color: rgba(109,86,245,0.5); }\n\n.ttp-product-image {\n height: 110px !important;\n background: rgba(0,0,0,0.2);\n display: flex !important;\n align-items: center;\n justify-content: center;\n position: relative;\n overflow: hidden;\n}\n.ttp-product-image img {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n object-fit: contain !important;\n display: block !important;\n}\n\n.ttp-stock-badge {\n position: absolute;\n top: 6px; right: 6px;\n background: rgba(239,68,68,0.15);\n color: #ef4444;\n font-size: 9px;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: 4px;\n}\n\n.ttp-product-details {\n padding: 10px 12px 14px;\n}\n\n.ttp-product-name {\n font-size: 13px;\n font-weight: 600;\n color: #e8e4f0;\n line-height: 1.3;\n min-height: 30px;\n}\n\n.ttp-product-page-link {\n display: inline-flex;\n align-items: center;\n margin-left: 5px;\n font-size: 10px;\n color: #a78bfa;\n text-decoration: none;\n opacity: 0.7;\n vertical-align: middle;\n}\n.ttp-product-page-link:hover { opacity: 1; text-decoration: underline; }\n\n.ttp-product-meta {\n font-size: 10px;\n color: rgba(255,255,255,0.35);\n margin-top: 2px;\n}\n\n.ttp-product-footer {\n display: flex !important;\n align-items: center;\n justify-content: space-between;\n margin-top: 6px;\n}\n\n.ttp-product-price {\n font-size: 14px;\n font-weight: 700;\n color: #e8e4f0;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT COMPACT (list view)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-compact {\n display: flex !important;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n background: rgba(255,255,255,0.04);\n border-radius: 10px;\n border: 1px solid rgba(255,255,255,0.08);\n transition: border-color 0.15s;\n}\n.ttp-product-compact:hover { border-color: rgba(109,86,245,0.5); }\n\n.ttp-product-image-sm {\n width: 72px !important; height: 72px !important;\n border-radius: 10px;\n background: rgba(0,0,0,0.2);\n display: flex !important;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: hidden;\n}\n.ttp-product-image-sm img {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n object-fit: contain !important;\n display: block !important;\n}\n\n.ttp-product-compact .ttp-product-info {\n flex: 1;\n min-width: 0;\n}\n.ttp-product-compact .ttp-product-name {\n min-height: auto;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: #e8e4f0;\n}\n.ttp-product-compact .ttp-product-action {\n text-align: right;\n flex-shrink: 0 !important;\n}\n.ttp-product-compact .ttp-product-price {\n font-size: 13px;\n}\n\n.ttp-out-of-stock {\n font-size: 10px;\n color: #f87171;\n font-weight: 500;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n ADD BUTTON\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-add-btn {\n padding: 6px 14px;\n border-radius: 8px;\n border: 1.5px solid rgba(109,86,245,0.6);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n background: transparent;\n color: #a78bfa;\n transition: all 0.15s;\n}\n.ttp-add-btn:hover { background: rgba(109,86,245,0.15); border-color: #a78bfa; }\n.ttp-add-btn:active { transform: scale(0.95); }\n.ttp-add-btn.ttp-added { background: rgba(109,86,245,0.6) !important; border-color: #7c3aed !important; color: #fff !important; }\n.ttp-add-btn:disabled { cursor: default; opacity: 0.5; }\n.ttp-add-btn.ttp-update-btn { background: rgba(245,158,11,0.15); border-color: rgba(245,158,11,0.5); color: #fbbf24; }\n.ttp-add-btn.ttp-update-btn:hover { background: rgba(245,158,11,0.25); border-color: #fbbf24; }\n\n.ttp-products-animating .ttp-add-btn {\n opacity: 0.4;\n pointer-events: none;\n cursor: not-allowed;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n CART TOAST\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/* Toast replaces waveform in desktop voice section */\n.ttp-cart-toast {\n display: flex !important;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n margin: 8px 12px;\n background: rgba(16,185,129,0.1);\n border-radius: 12px;\n border: 1px solid rgba(16,185,129,0.2);\n animation: ttpToastIn 0.25s ease;\n flex-shrink: 0;\n box-shadow: none;\n}\n\n@keyframes ttpToastIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ttp-cart-toast.ttp-toast-hiding {\n animation: ttpSlideDown 0.3s ease forwards;\n}\n\n@keyframes ttpSlideDown {\n to { opacity: 0; transform: translateY(-8px); height: 0; padding: 0; margin: 0; overflow: hidden; }\n}\n\n.ttp-toast-icon {\n width: 24px !important; height: 24px !important;\n border-radius: 50%;\n background: #10b981;\n color: #fff;\n display: flex !important;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 700;\n flex-shrink: 0;\n}\n\n.ttp-toast-content { flex: 1; min-width: 0; }\n.ttp-toast-title { font-size: 12px; font-weight: 600; color: #34d399; }\n.ttp-toast-product {\n font-size: 11px;\n color: rgba(52,211,153,0.7);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ttp-toast-undo {\n background: none;\n border: 1px solid rgba(52,211,153,0.3);\n border-radius: 6px;\n padding: 3px 8px;\n font-size: 10px;\n color: #34d399;\n cursor: pointer;\n font-weight: 600;\n flex-shrink: 0;\n}\n.ttp-toast-undo:hover { background: rgba(52,211,153,0.1); }\n\n/* Fly-to-cart desktop target (when cart summary is hidden) */\n.ttp-desktop-fly-target {\n position: absolute;\n bottom: 12px;\n right: 80px;\n width: 24px;\n height: 24px;\n pointer-events: none;\n}\n\n/* Fly-to-cart ripple effect */\n.ttp-fly-ripple {\n position: absolute;\n left: 10px;\n top: 50%;\n width: 30px;\n height: 30px;\n margin-top: -15px;\n border-radius: 50%;\n border: 2px solid rgba(74,222,128,0.7);\n animation: ttp-ripple-expand 0.6s ease-out forwards;\n pointer-events: none;\n}\n\n@keyframes ttp-ripple-expand {\n 0% { transform: scale(0.5); opacity: 0.8; }\n 100% { transform: scale(3); opacity: 0; }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n CART SUMMARY BAR\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-cart-summary {\n display: flex !important;\n justify-content: space-between;\n align-items: center;\n padding: 8px 14px;\n margin: 4px 12px 0;\n margin-right: 20px;\n margin-top: auto !important;\n flex-shrink: 0 !important;\n background: rgba(255,255,255,0.04);\n border-radius: 12px;\n border: 1px solid rgba(255,255,255,0.08);\n}\n\n/* Hide cart when empty \u2014 overrides display:flex !important */\n.ttp-cart-summary.ttp-cart-empty {\n display: none !important;\n}\n\n/* Desktop: cart below input (fallback when input area not found) */\n.ttp-cart-summary-bottom {\n margin-top: 0 !important;\n margin: 8px 12px 12px !important;\n flex-shrink: 0 !important;\n}\n\n/* Desktop: cart inside input area \u2014 unified section (like mobile) */\n.ttp-cart-summary-inline {\n margin: 0 !important;\n padding: 0 !important;\n padding-top: 8px !important;\n flex-shrink: 0 !important;\n background: transparent !important;\n border: none !important;\n border-top: 1px solid rgba(255, 255, 255, 0.06) !important;\n border-radius: 0 !important;\n width: 100% !important;\n flex-basis: 100% !important;\n}\n\n.ttp-cart-info {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.ttp-cart-icon { font-size: 14px; }\n.ttp-cart-count { font-size: 12px; font-weight: 600; color: rgba(255,255,255,0.6); }\n.ttp-cart-separator { font-size: 12px; color: rgba(255,255,255,0.25); }\n.ttp-cart-total { font-size: 13px; font-weight: 700; color: #e8e4f0; }\n.ttp-cart-view {\n font-size: 12px;\n font-weight: 600;\n color: #a78bfa;\n text-decoration: none;\n cursor: pointer;\n white-space: nowrap;\n}\n.ttp-cart-view:hover {\n color: #c4b5fd;\n text-decoration: underline;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n QUANTITY/WEIGHT STEPPER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-stepper {\n display: flex !important;\n align-items: center;\n gap: 2px;\n margin: 4px 0;\n}\n\n.ttp-stepper-btn {\n width: 24px !important;\n height: 24px !important;\n border-radius: 6px;\n border: 1px solid rgba(255,255,255,0.15);\n background: rgba(255,255,255,0.06);\n color: rgba(255,255,255,0.6);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: flex !important;\n align-items: center;\n justify-content: center;\n padding: 0;\n line-height: 1;\n transition: all 0.15s;\n flex-shrink: 0;\n}\n\n.ttp-stepper-btn:hover:not(:disabled) {\n background: rgba(255,255,255,0.12);\n border-color: rgba(255,255,255,0.25);\n}\n\n.ttp-stepper-btn:active:not(:disabled) {\n transform: scale(0.9);\n}\n\n.ttp-stepper-btn:disabled,\n.ttp-stepper-disabled {\n opacity: 0.35;\n cursor: default;\n}\n\n.ttp-stepper-value {\n font-size: 12px;\n font-weight: 600;\n color: #e8e4f0;\n min-width: 36px;\n text-align: center;\n user-select: none;\n}\n\n.ttp-product-compact .ttp-stepper {\n justify-content: flex-end;\n}\n\n.ttp-product-compact .ttp-product-action {\n display: flex !important;\n flex-direction: column;\n align-items: flex-end;\n gap: 2px;\n}\n\n.ttp-product-card .ttp-stepper {\n justify-content: center;\n margin: 4px 0;\n}\n\n.ttp-product-card .ttp-product-footer {\n display: flex !important;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n VARIATION DESKTOP PICKER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.picker-relative {\n position: relative !important;\n flex: 1 1 0 !important;\n min-height: 0 !important;\n display: flex !important;\n flex-direction: column !important;\n overflow: hidden;\n}\n\n/* When variant picker is shown, center it horizontally and vertically */\n.picker-relative.ttp-var-picker-active {\n justify-content: center !important;\n align-items: center !important;\n}\n\n.ttp-var-picker {\n background: rgba(255,255,255,0.04);\n border-radius: 14px;\n border: 1px solid rgba(255,255,255,0.08);\n display: flex !important;\n flex-direction: column;\n animation: ttpVarFadeIn 0.2s ease;\n overflow: hidden;\n width: 100%;\n flex-shrink: 0 !important;\n}\n\n@keyframes ttpVarFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.ttp-var-header {\n display: flex !important;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n}\n\n.ttp-var-back-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: rgba(255,255,255,0.5);\n font-size: 16px;\n padding: 0;\n display: flex;\n align-items: center;\n line-height: 1;\n font-family: inherit;\n}\n.ttp-var-back-btn:hover { color: #e8e4f0; }\n\n.ttp-var-title {\n font-size: 13px;\n font-weight: 700;\n color: #e8e4f0;\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ttp-var-page-link {\n font-size: 11px;\n color: #a78bfa;\n text-decoration: none;\n font-weight: 500;\n white-space: nowrap;\n flex-shrink: 0;\n}\n.ttp-var-page-link:hover { text-decoration: underline; }\n\n.ttp-var-body {\n flex: 1;\n overflow-y: auto;\n padding: 12px 14px;\n display: flex !important;\n flex-direction: column;\n gap: 14px;\n}\n\n.ttp-var-preview {\n display: flex !important;\n gap: 12px;\n align-items: flex-start;\n}\n\n.ttp-var-preview-img {\n width: 72px !important;\n height: 72px !important;\n border-radius: 12px;\n background: rgba(0,0,0,0.2);\n border: 1px solid rgba(255,255,255,0.08);\n display: flex !important;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: hidden;\n transition: all 0.2s ease;\n}\n\n.ttp-var-preview-img img {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n object-fit: contain !important;\n display: block !important;\n padding: 6px;\n}\n\n.ttp-var-preview-info { flex: 1; min-width: 0; }\n\n.ttp-var-preview-brand {\n font-size: 10px;\n color: rgba(255,255,255,0.4);\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.ttp-var-preview-name {\n font-size: 14px;\n font-weight: 700;\n color: #e8e4f0;\n margin: 2px 0 4px;\n line-height: 1.2;\n}\n\n.ttp-var-preview-price {\n font-size: 18px;\n font-weight: 700;\n color: #7c3aed;\n}\n\n.ttp-var-preview-selected {\n font-size: 11px;\n color: rgba(255,255,255,0.4);\n margin-top: 3px;\n}\n\n.ttp-var-section { display: flex !important; flex-direction: column; gap: 8px; }\n\n.ttp-var-section-label {\n font-size: 11px;\n font-weight: 700;\n color: rgba(255,255,255,0.5);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n}\n\n.ttp-var-swatches { display: flex !important; flex-wrap: wrap; gap: 8px; }\n\n.ttp-var-swatch {\n display: block !important;\n width: 28px !important;\n height: 28px !important;\n border-radius: 50%;\n cursor: pointer;\n border: 3px solid transparent;\n transition: all 0.15s;\n position: relative;\n flex-shrink: 0;\n}\n.ttp-var-swatch:hover { transform: scale(1.1); }\n.ttp-var-swatch.selected {\n border-color: #7c3aed;\n box-shadow: 0 0 0 2px #fff, 0 0 0 4px #7c3aed;\n}\n.ttp-var-swatch.oos {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.ttp-var-pills { display: flex !important; flex-wrap: wrap; gap: 6px; }\n\n.ttp-var-pill {\n padding: 5px 12px;\n border-radius: 8px;\n border: 1.5px solid rgba(255,255,255,0.15);\n background: rgba(255,255,255,0.04);\n font-size: 12px;\n font-weight: 600;\n color: rgba(255,255,255,0.6);\n cursor: pointer;\n transition: all 0.15s;\n font-family: inherit;\n}\n.ttp-var-pill:hover:not(:disabled) { border-color: #7c3aed; color: #a78bfa; }\n.ttp-var-pill.selected { border-color: #7c3aed; background: #7c3aed; color: #fff; }\n.ttp-var-pill.oos { opacity: 0.35; cursor: not-allowed; text-decoration: line-through; }\n\n.ttp-var-footer {\n padding: 10px 14px 12px;\n border-top: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n}\n\n.ttp-var-add-btn {\n width: 100%;\n padding: 10px;\n background: linear-gradient(135deg, #7c3aed, #6d28d9);\n color: #fff;\n border: none;\n border-radius: 10px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n font-family: inherit;\n box-shadow: 0 3px 10px rgba(124,58,237,0.3);\n transition: all 0.15s;\n}\n.ttp-var-add-btn:hover:not(:disabled) { transform: scale(0.99); }\n.ttp-var-add-btn:disabled {\n background: rgba(255,255,255,0.1);\n box-shadow: none;\n cursor: not-allowed;\n color: rgba(255,255,255,0.3);\n}\n\n.ttp-var-spinner {\n width: 24px;\n height: 24px;\n border: 3px solid rgba(255,255,255,0.1);\n border-top-color: #7c3aed;\n border-radius: 50%;\n animation: ttpVarSpin 0.7s linear infinite;\n}\n@keyframes ttpVarSpin {\n to { transform: rotate(360deg); }\n}\n\n/* Amber \"Choose\" button for variant products */\n.ttp-add-btn.ttp-choose-btn {\n background: linear-gradient(135deg, #f59e0b, #d97706);\n box-shadow: 0 2px 8px rgba(245,158,11,0.3);\n color: #fff;\n}\n\n ";
|
|
16331
|
+
return "\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n ECOMMERCE AREA\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n\n/*\n * When products are visible, expand the panel dynamically.\n * Actual height is set via inline style by EcommerceManager._expandPanelToFit().\n * When the class is removed, the panel returns to its CSS-defined default.\n */\n#text-chat-panel.ttp-panel-expanded {\n transition: height 0.25s ease;\n}\n\n/*\n * Layout overrides ONLY when ecommerce/hotels overlay is active.\n * Scoped under .ttp-products-visible / .ttp-rooms-visible so the\n * normal voice CSS takes effect when products/rooms are hidden.\n *\n * voiceActiveState is a flex column: [voice-controls] [ecommerce] [conversation].\n * Ecommerce area fills the middle and scrolls; conversation panel stays at bottom.\n */\n#voiceActiveState.ttp-products-visible,\n#voiceActiveState.ttp-rooms-visible {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow: hidden !important;\n}\n\n.ttp-products-visible > .desktop-voice-section,\n.ttp-rooms-visible > .desktop-voice-section {\n flex-shrink: 1 !important;\n}\n\n/* Conversation + input always stick to bottom; ecommerce area above grows to fill. */\n.ttp-products-visible > .desktop-conversation-panel,\n.ttp-rooms-visible > .desktop-conversation-panel {\n flex: 0 0 auto !important;\n margin-top: auto !important;\n display: flex !important;\n flex-direction: column !important;\n}\n\n.ttp-products-visible .desktop-conversation-panel .live-transcript-collapsed,\n.ttp-rooms-visible .desktop-conversation-panel .live-transcript-collapsed {\n flex: 0 0 auto !important;\n min-height: 40px !important;\n overflow: hidden !important;\n}\n\n.ttp-products-visible .desktop-conversation-panel .voice-text-input-area,\n.ttp-rooms-visible .desktop-conversation-panel .voice-text-input-area {\n margin-top: auto !important;\n padding: 3px 16px !important;\n}\n\n.ttp-products-visible .desktop-conversation-panel .voice-input-hint,\n.ttp-rooms-visible .desktop-conversation-panel .voice-input-hint {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .desktop-voice-section {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .compact-voice-section {\n display: flex !important;\n}\n\n#voiceActiveState.ttp-products-visible .desktop-conversation-panel {\n flex: 0 0 auto !important;\n}\n\n#voiceActiveState.ttp-products-visible .conversation-history {\n display: none !important;\n}\n\n#voiceActiveState.ttp-products-visible .live-transcript-collapsed {\n flex: 0 0 auto !important;\n}\n\n.voice-text-input-area {\n flex-shrink: 0 !important;\n}\n\n.ttp-products-visible .conversation-history,\n.ttp-rooms-visible .conversation-history {\n flex: 1 1 0 !important;\n min-height: 0 !important;\n overflow-y: auto !important;\n}\n\n.ttp-ecommerce-area {\n display: flex !important;\n flex-direction: column !important;\n width: 100%;\n flex: 1 1 0 !important;\n min-height: 0 !important;\n position: relative !important;\n overflow-x: hidden !important;\n overflow-y: hidden !important;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT PICKER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-picker {\n background: rgba(255,255,255,0.04);\n border-radius: 14px;\n border: 1px solid rgba(255,255,255,0.08);\n box-shadow: none;\n margin: 8px 10px;\n overflow-x: hidden !important;\n overflow-y: auto !important;\n animation: ttpSlideUp 0.25s ease;\n display: flex !important;\n flex-direction: column;\n flex: 1;\n min-height: 0 !important;\n}\n\n@keyframes ttpSlideUp {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.ttp-picker-header {\n display: flex !important;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n border-bottom: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n}\n\n.ttp-picker-title {\n font-size: 12px;\n font-weight: 600;\n color: rgba(255,255,255,0.5);\n}\n\n.ttp-picker-close {\n background: none;\n border: none;\n cursor: pointer;\n color: rgba(255,255,255,0.4);\n font-size: 14px;\n padding: 2px 6px;\n border-radius: 4px;\n}\n.ttp-picker-close:hover {\n background: rgba(255,255,255,0.08);\n color: rgba(255,255,255,0.7);\n}\n\n/* Horizontal card layout */\n.ttp-products-horizontal {\n display: flex !important;\n align-items: flex-start;\n flex-shrink: 0;\n gap: 10px;\n padding: 12px;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: thin;\n}\n.ttp-products-horizontal::-webkit-scrollbar { height: 4px; }\n.ttp-products-horizontal::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 2px; }\n.ttp-products-horizontal::-webkit-scrollbar-track { background: transparent; }\n\n/* Vertical list layout */\n.ttp-products-vertical {\n padding: 8px 12px;\n overflow-y: auto !important;\n display: flex !important;\n flex-direction: column;\n gap: 6px;\n flex: 1;\n min-height: 0 !important;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT CARD (horizontal)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-card {\n width: 170px !important;\n flex-shrink: 0 !important;\n background: rgba(255,255,255,0.04);\n border-radius: 12px;\n border: 1px solid rgba(255,255,255,0.08);\n overflow: hidden;\n transition: border-color 0.15s;\n}\n.ttp-product-card:hover { border-color: rgba(109,86,245,0.5); }\n\n.ttp-product-image {\n height: 110px !important;\n background: rgba(0,0,0,0.2);\n display: flex !important;\n align-items: center;\n justify-content: center;\n position: relative;\n overflow: hidden;\n}\n.ttp-product-image img {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n object-fit: contain !important;\n display: block !important;\n}\n\n.ttp-stock-badge {\n position: absolute;\n top: 6px; right: 6px;\n background: rgba(239,68,68,0.15);\n color: #ef4444;\n font-size: 9px;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: 4px;\n}\n\n.ttp-product-details {\n padding: 10px 12px 14px;\n}\n\n.ttp-product-name {\n font-size: 13px;\n font-weight: 600;\n color: #e8e4f0;\n line-height: 1.3;\n min-height: 30px;\n}\n\n.ttp-product-page-link {\n display: inline-flex;\n align-items: center;\n margin-left: 5px;\n font-size: 10px;\n color: #a78bfa;\n text-decoration: none;\n opacity: 0.7;\n vertical-align: middle;\n}\n.ttp-product-page-link:hover { opacity: 1; text-decoration: underline; }\n\n.ttp-product-meta {\n font-size: 10px;\n color: rgba(255,255,255,0.35);\n margin-top: 2px;\n}\n\n.ttp-product-footer {\n display: flex !important;\n align-items: center;\n justify-content: space-between;\n margin-top: 6px;\n}\n\n.ttp-product-price {\n font-size: 14px;\n font-weight: 700;\n color: #e8e4f0;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n PRODUCT COMPACT (list view)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-product-compact {\n display: flex !important;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n background: rgba(255,255,255,0.04);\n border-radius: 10px;\n border: 1px solid rgba(255,255,255,0.08);\n transition: border-color 0.15s;\n}\n.ttp-product-compact:hover { border-color: rgba(109,86,245,0.5); }\n\n.ttp-product-image-sm {\n width: 72px !important; height: 72px !important;\n border-radius: 10px;\n background: rgba(0,0,0,0.2);\n display: flex !important;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: hidden;\n}\n.ttp-product-image-sm img {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n object-fit: contain !important;\n display: block !important;\n}\n\n.ttp-product-compact .ttp-product-info {\n flex: 1;\n min-width: 0;\n}\n.ttp-product-compact .ttp-product-name {\n min-height: auto;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: #e8e4f0;\n}\n.ttp-product-compact .ttp-product-action {\n text-align: right;\n flex-shrink: 0 !important;\n}\n.ttp-product-compact .ttp-product-price {\n font-size: 13px;\n}\n\n.ttp-out-of-stock {\n font-size: 10px;\n color: #f87171;\n font-weight: 500;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n ADD BUTTON\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-add-btn {\n padding: 6px 14px;\n border-radius: 8px;\n border: 1.5px solid rgba(109,86,245,0.6);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n background: transparent;\n color: #a78bfa;\n transition: all 0.15s;\n}\n.ttp-add-btn:hover { background: rgba(109,86,245,0.15); border-color: #a78bfa; }\n.ttp-add-btn:active { transform: scale(0.95); }\n.ttp-add-btn.ttp-added { background: rgba(109,86,245,0.6) !important; border-color: #7c3aed !important; color: #fff !important; }\n.ttp-add-btn:disabled { cursor: default; opacity: 0.5; }\n.ttp-add-btn.ttp-update-btn { background: rgba(245,158,11,0.15); border-color: rgba(245,158,11,0.5); color: #fbbf24; }\n.ttp-add-btn.ttp-update-btn:hover { background: rgba(245,158,11,0.25); border-color: #fbbf24; }\n\n.ttp-products-animating .ttp-add-btn {\n opacity: 0.4;\n pointer-events: none;\n cursor: not-allowed;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n CART TOAST\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/* Toast replaces waveform in desktop voice section */\n.ttp-cart-toast {\n display: flex !important;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n margin: 8px 12px;\n background: rgba(16,185,129,0.1);\n border-radius: 12px;\n border: 1px solid rgba(16,185,129,0.2);\n animation: ttpToastIn 0.25s ease;\n flex-shrink: 0;\n box-shadow: none;\n}\n\n@keyframes ttpToastIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ttp-cart-toast.ttp-toast-hiding {\n animation: ttpSlideDown 0.3s ease forwards;\n}\n\n@keyframes ttpSlideDown {\n to { opacity: 0; transform: translateY(-8px); height: 0; padding: 0; margin: 0; overflow: hidden; }\n}\n\n.ttp-toast-icon {\n width: 24px !important; height: 24px !important;\n border-radius: 50%;\n background: #10b981;\n color: #fff;\n display: flex !important;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 700;\n flex-shrink: 0;\n}\n\n.ttp-toast-content { flex: 1; min-width: 0; }\n.ttp-toast-title { font-size: 12px; font-weight: 600; color: #34d399; }\n.ttp-toast-product {\n font-size: 11px;\n color: rgba(52,211,153,0.7);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ttp-toast-undo {\n background: none;\n border: 1px solid rgba(52,211,153,0.3);\n border-radius: 6px;\n padding: 3px 8px;\n font-size: 10px;\n color: #34d399;\n cursor: pointer;\n font-weight: 600;\n flex-shrink: 0;\n}\n.ttp-toast-undo:hover { background: rgba(52,211,153,0.1); }\n\n/* Fly-to-cart desktop target (when cart summary is hidden) */\n.ttp-desktop-fly-target {\n position: absolute;\n bottom: 12px;\n right: 80px;\n width: 24px;\n height: 24px;\n pointer-events: none;\n}\n\n/* Fly-to-cart ripple effect */\n.ttp-fly-ripple {\n position: absolute;\n left: 10px;\n top: 50%;\n width: 30px;\n height: 30px;\n margin-top: -15px;\n border-radius: 50%;\n border: 2px solid rgba(74,222,128,0.7);\n animation: ttp-ripple-expand 0.6s ease-out forwards;\n pointer-events: none;\n}\n\n@keyframes ttp-ripple-expand {\n 0% { transform: scale(0.5); opacity: 0.8; }\n 100% { transform: scale(3); opacity: 0; }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n QUANTITY/WEIGHT STEPPER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.ttp-stepper {\n display: flex !important;\n align-items: center;\n gap: 2px;\n margin: 4px 0;\n}\n\n.ttp-stepper-btn {\n width: 24px !important;\n height: 24px !important;\n border-radius: 6px;\n border: 1px solid rgba(255,255,255,0.15);\n background: rgba(255,255,255,0.06);\n color: rgba(255,255,255,0.6);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: flex !important;\n align-items: center;\n justify-content: center;\n padding: 0;\n line-height: 1;\n transition: all 0.15s;\n flex-shrink: 0;\n}\n\n.ttp-stepper-btn:hover:not(:disabled) {\n background: rgba(255,255,255,0.12);\n border-color: rgba(255,255,255,0.25);\n}\n\n.ttp-stepper-btn:active:not(:disabled) {\n transform: scale(0.9);\n}\n\n.ttp-stepper-btn:disabled,\n.ttp-stepper-disabled {\n opacity: 0.35;\n cursor: default;\n}\n\n.ttp-stepper-value {\n font-size: 12px;\n font-weight: 600;\n color: #e8e4f0;\n min-width: 36px;\n text-align: center;\n user-select: none;\n}\n\n.ttp-product-compact .ttp-stepper {\n justify-content: flex-end;\n}\n\n.ttp-product-compact .ttp-product-action {\n display: flex !important;\n flex-direction: column;\n align-items: flex-end;\n gap: 2px;\n}\n\n.ttp-product-card .ttp-stepper {\n justify-content: center;\n margin: 4px 0;\n}\n\n.ttp-product-card .ttp-product-footer {\n display: flex !important;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n VARIATION DESKTOP PICKER\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.picker-relative {\n position: relative !important;\n flex: 1 1 0 !important;\n min-height: 0 !important;\n display: flex !important;\n flex-direction: column !important;\n overflow: hidden;\n}\n\n/* When variant picker is shown, center it horizontally and vertically */\n.picker-relative.ttp-var-picker-active {\n justify-content: center !important;\n align-items: center !important;\n}\n\n.ttp-var-picker {\n background: rgba(255,255,255,0.04);\n border-radius: 14px;\n border: 1px solid rgba(255,255,255,0.08);\n display: flex !important;\n flex-direction: column;\n animation: ttpVarFadeIn 0.2s ease;\n overflow: hidden;\n width: 100%;\n flex-shrink: 0 !important;\n}\n\n@keyframes ttpVarFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.ttp-var-header {\n display: flex !important;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n}\n\n.ttp-var-back-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: rgba(255,255,255,0.5);\n font-size: 16px;\n padding: 0;\n display: flex;\n align-items: center;\n line-height: 1;\n font-family: inherit;\n}\n.ttp-var-back-btn:hover { color: #e8e4f0; }\n\n.ttp-var-title {\n font-size: 13px;\n font-weight: 700;\n color: #e8e4f0;\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ttp-var-page-link {\n font-size: 11px;\n color: #a78bfa;\n text-decoration: none;\n font-weight: 500;\n white-space: nowrap;\n flex-shrink: 0;\n}\n.ttp-var-page-link:hover { text-decoration: underline; }\n\n.ttp-var-body {\n flex: 1;\n overflow-y: auto;\n padding: 12px 14px;\n display: flex !important;\n flex-direction: column;\n gap: 14px;\n}\n\n.ttp-var-preview {\n display: flex !important;\n gap: 12px;\n align-items: flex-start;\n}\n\n.ttp-var-preview-img {\n width: 72px !important;\n height: 72px !important;\n border-radius: 12px;\n background: rgba(0,0,0,0.2);\n border: 1px solid rgba(255,255,255,0.08);\n display: flex !important;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n overflow: hidden;\n transition: all 0.2s ease;\n}\n\n.ttp-var-preview-img img {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n object-fit: contain !important;\n display: block !important;\n padding: 6px;\n}\n\n.ttp-var-preview-info { flex: 1; min-width: 0; }\n\n.ttp-var-preview-brand {\n font-size: 10px;\n color: rgba(255,255,255,0.4);\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.ttp-var-preview-name {\n font-size: 14px;\n font-weight: 700;\n color: #e8e4f0;\n margin: 2px 0 4px;\n line-height: 1.2;\n}\n\n.ttp-var-preview-price {\n font-size: 18px;\n font-weight: 700;\n color: #7c3aed;\n}\n\n.ttp-var-preview-selected {\n font-size: 11px;\n color: rgba(255,255,255,0.4);\n margin-top: 3px;\n}\n\n.ttp-var-section { display: flex !important; flex-direction: column; gap: 8px; }\n\n.ttp-var-section-label {\n font-size: 11px;\n font-weight: 700;\n color: rgba(255,255,255,0.5);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n}\n\n.ttp-var-swatches { display: flex !important; flex-wrap: wrap; gap: 8px; }\n\n.ttp-var-swatch {\n display: block !important;\n width: 28px !important;\n height: 28px !important;\n border-radius: 50%;\n cursor: pointer;\n border: 3px solid transparent;\n transition: all 0.15s;\n position: relative;\n flex-shrink: 0;\n}\n.ttp-var-swatch:hover { transform: scale(1.1); }\n.ttp-var-swatch.selected {\n border-color: #7c3aed;\n box-shadow: 0 0 0 2px #fff, 0 0 0 4px #7c3aed;\n}\n.ttp-var-swatch.oos {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.ttp-var-pills { display: flex !important; flex-wrap: wrap; gap: 6px; }\n\n.ttp-var-pill {\n padding: 5px 12px;\n border-radius: 8px;\n border: 1.5px solid rgba(255,255,255,0.15);\n background: rgba(255,255,255,0.04);\n font-size: 12px;\n font-weight: 600;\n color: rgba(255,255,255,0.6);\n cursor: pointer;\n transition: all 0.15s;\n font-family: inherit;\n}\n.ttp-var-pill:hover:not(:disabled) { border-color: #7c3aed; color: #a78bfa; }\n.ttp-var-pill.selected { border-color: #7c3aed; background: #7c3aed; color: #fff; }\n.ttp-var-pill.oos { opacity: 0.35; cursor: not-allowed; text-decoration: line-through; }\n\n.ttp-var-footer {\n padding: 10px 14px 12px;\n border-top: 1px solid rgba(255,255,255,0.06);\n flex-shrink: 0;\n}\n\n.ttp-var-add-btn {\n width: 100%;\n padding: 10px;\n background: linear-gradient(135deg, #7c3aed, #6d28d9);\n color: #fff;\n border: none;\n border-radius: 10px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n font-family: inherit;\n box-shadow: 0 3px 10px rgba(124,58,237,0.3);\n transition: all 0.15s;\n}\n.ttp-var-add-btn:hover:not(:disabled) { transform: scale(0.99); }\n.ttp-var-add-btn:disabled {\n background: rgba(255,255,255,0.1);\n box-shadow: none;\n cursor: not-allowed;\n color: rgba(255,255,255,0.3);\n}\n\n.ttp-var-spinner {\n width: 24px;\n height: 24px;\n border: 3px solid rgba(255,255,255,0.1);\n border-top-color: #7c3aed;\n border-radius: 50%;\n animation: ttpVarSpin 0.7s linear infinite;\n}\n@keyframes ttpVarSpin {\n to { transform: rotate(360deg); }\n}\n\n/* Amber \"Choose\" button for variant products */\n.ttp-add-btn.ttp-choose-btn {\n background: linear-gradient(135deg, #f59e0b, #d97706);\n box-shadow: 0 2px 8px rgba(245,158,11,0.3);\n color: #fff;\n}\n\n ";
|
|
15369
16332
|
}
|
|
15370
16333
|
|
|
15371
16334
|
/***/ }),
|
|
@@ -15960,13 +16923,13 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
15960
16923
|
/* harmony import */ var _ecommerce_EcommerceManager_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ecommerce/EcommerceManager.js */ "./src/ecommerce/EcommerceManager.js");
|
|
15961
16924
|
/* harmony import */ var _ecommerce_PartnerScriptRunner_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../ecommerce/PartnerScriptRunner.js */ "./src/ecommerce/PartnerScriptRunner.js");
|
|
15962
16925
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
15963
|
-
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
15964
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
15965
|
-
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
15966
16926
|
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
15967
16927
|
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
15968
16928
|
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
15969
16929
|
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
16930
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
16931
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
16932
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
15970
16933
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
15971
16934
|
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
15972
16935
|
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
@@ -16067,10 +17030,41 @@ var EcommerceFlavor = /*#__PURE__*/function (_BaseFlavor) {
|
|
|
16067
17030
|
// === DB-backed per-partner adapters (replaces CDN hooks) ===
|
|
16068
17031
|
// Backend pushes the full bundle once at session start; the widget then
|
|
16069
17032
|
// executes individual adapters via the 'run_partner_script' client tool.
|
|
16070
|
-
// Shape: { t: 'partner_bundle', partner_id, platform,
|
|
17033
|
+
// Shape: { t: 'partner_bundle', partner_id, platform,
|
|
17034
|
+
// adapters: { action: {...} }, ← on-demand, invoked by LLM
|
|
17035
|
+
// autoRun: { action: {...} } } ← executed immediately on load
|
|
16071
17036
|
'partner_bundle': function partner_bundle(data) {
|
|
16072
|
-
|
|
16073
|
-
|
|
17037
|
+
// The reserved `__client_tools__` bundle is owned by the
|
|
17038
|
+
// flavor-independent ClientScriptManager at the SDK level — skip it
|
|
17039
|
+
// here so the two bundles stay separate per partner id.
|
|
17040
|
+
if ((data === null || data === void 0 ? void 0 : data.partner_id) === '__client_tools__') return;
|
|
17041
|
+
var runner = _this2._partnerScriptRunner;
|
|
17042
|
+
if (!runner) return;
|
|
17043
|
+
|
|
17044
|
+
// Wire the emit callback BEFORE setBundle so auto-run adapters can use it.
|
|
17045
|
+
runner.setEmitCallback(function (msg) {
|
|
17046
|
+
return _this2.sendToVoiceSDK(msg);
|
|
17047
|
+
});
|
|
17048
|
+
|
|
17049
|
+
// Merge autoRun entries into the adapters map so the runner can look
|
|
17050
|
+
// them up by action name (e.g. for ctx.runAdapter chaining).
|
|
17051
|
+
var merged = _objectSpread(_objectSpread({}, data), {}, {
|
|
17052
|
+
adapters: _objectSpread(_objectSpread({}, (data === null || data === void 0 ? void 0 : data.adapters) || {}), (data === null || data === void 0 ? void 0 : data.autoRun) || {})
|
|
17053
|
+
});
|
|
17054
|
+
runner.setBundle(merged);
|
|
17055
|
+
|
|
17056
|
+
// Execute every autoRun adapter once immediately.
|
|
17057
|
+
var _loop = function _loop() {
|
|
17058
|
+
var action = _Object$keys[_i];
|
|
17059
|
+
runner.runAdapter(action, {}).then(function (r) {
|
|
17060
|
+
return console.log("[EcommerceFlavor] auto-run '".concat(action, "':"), r);
|
|
17061
|
+
}).catch(function (e) {
|
|
17062
|
+
return console.warn("[EcommerceFlavor] auto-run '".concat(action, "' failed:"), e);
|
|
17063
|
+
});
|
|
17064
|
+
};
|
|
17065
|
+
for (var _i = 0, _Object$keys = Object.keys((data === null || data === void 0 ? void 0 : data.autoRun) || {}); _i < _Object$keys.length; _i++) {
|
|
17066
|
+
_loop();
|
|
17067
|
+
}
|
|
16074
17068
|
},
|
|
16075
17069
|
// Shopify: see file header — add_to_store_cart uses /cart/add.js + /cart.js.
|
|
16076
17070
|
'add_to_store_cart': function add_to_store_cart(data) {
|
|
@@ -18925,6 +19919,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
18925
19919
|
/* harmony export */ TTPEcommerceWidget: () => (/* reexport safe */ _ecommerce_TTPEcommerceWidget_js__WEBPACK_IMPORTED_MODULE_7__.TTPEcommerceWidget),
|
|
18926
19920
|
/* harmony export */ TextChatSDK: () => (/* reexport safe */ _core_TextChatSDK_js__WEBPACK_IMPORTED_MODULE_1__["default"]),
|
|
18927
19921
|
/* harmony export */ VERSION: () => (/* binding */ VERSION),
|
|
19922
|
+
/* harmony export */ VoiceSDK: () => (/* binding */ VoiceSDK),
|
|
18928
19923
|
/* harmony export */ VoiceSDK_v2: () => (/* reexport safe */ _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"]),
|
|
18929
19924
|
/* harmony export */ WebSocketManager: () => (/* reexport safe */ _core_WebSocketManager_js__WEBPACK_IMPORTED_MODULE_2__["default"]),
|
|
18930
19925
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__),
|
|
@@ -18955,6 +19950,11 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
18955
19950
|
// Core SDK
|
|
18956
19951
|
|
|
18957
19952
|
|
|
19953
|
+
// Backward-compatible alias: the legacy `VoiceSDK` name (v1) now points at the
|
|
19954
|
+
// v2 implementation. The v2 class defaults to `protocolVersion: 2`, so existing
|
|
19955
|
+
// code and documentation that references `VoiceSDK` keeps working on v2.
|
|
19956
|
+
var VoiceSDK = _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"];
|
|
19957
|
+
|
|
18958
19958
|
|
|
18959
19959
|
|
|
18960
19960
|
|
|
@@ -18976,8 +19976,8 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
18976
19976
|
|
|
18977
19977
|
|
|
18978
19978
|
// Version - injected at build time from package.json via webpack DefinePlugin
|
|
18979
|
-
var VERSION = "2.
|
|
18980
|
-
var BUILD_TIME = "2026-
|
|
19979
|
+
var VERSION = "2.45.2";
|
|
19980
|
+
var BUILD_TIME = "2026-06-12T20:23:48.560Z";
|
|
18981
19981
|
console.log("%c TTP Agent SDK v".concat(VERSION, " (").concat(BUILD_TIME, ") "), 'background: #4f46e5; color: white; font-size: 12px; font-weight: bold; padding: 2px 6px; border-radius: 4px;');
|
|
18982
19982
|
|
|
18983
19983
|
// Named exports
|
|
@@ -18986,6 +19986,7 @@ console.log("%c TTP Agent SDK v".concat(VERSION, " (").concat(BUILD_TIME, ") "),
|
|
|
18986
19986
|
// Default export
|
|
18987
19987
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({
|
|
18988
19988
|
VoiceSDK_v2: _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"],
|
|
19989
|
+
VoiceSDK: VoiceSDK,
|
|
18989
19990
|
TextChatSDK: _core_TextChatSDK_js__WEBPACK_IMPORTED_MODULE_1__["default"],
|
|
18990
19991
|
WebSocketManager: _core_WebSocketManager_js__WEBPACK_IMPORTED_MODULE_2__["default"],
|
|
18991
19992
|
AudioRecorder: _core_AudioRecorder_js__WEBPACK_IMPORTED_MODULE_3__["default"],
|
|
@@ -23441,8 +24442,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23441
24442
|
console.log('🛑 AudioPlayer: Ignoring PCM chunk - playback was stopped (barge-in)');
|
|
23442
24443
|
return _context.a(2);
|
|
23443
24444
|
case 1:
|
|
23444
|
-
|
|
23445
|
-
|
|
24445
|
+
_context.n = 2;
|
|
24446
|
+
return this.prepareChunk(pcmData);
|
|
24447
|
+
case 2:
|
|
24448
|
+
preparedFrame = _context.v;
|
|
23446
24449
|
if (preparedFrame) {
|
|
23447
24450
|
// CRITICAL: Clear empty sentence timeout since chunks are arriving
|
|
23448
24451
|
// This resets the timer for the current sentence
|
|
@@ -23499,7 +24502,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23499
24502
|
// Emit error event
|
|
23500
24503
|
this.emit('playbackError', new Error('Failed to prepare PCM chunk for playback'));
|
|
23501
24504
|
}
|
|
23502
|
-
case
|
|
24505
|
+
case 3:
|
|
23503
24506
|
return _context.a(2);
|
|
23504
24507
|
}
|
|
23505
24508
|
}, _callee, this);
|
|
@@ -23518,87 +24521,99 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23518
24521
|
)
|
|
23519
24522
|
}, {
|
|
23520
24523
|
key: "prepareChunk",
|
|
23521
|
-
value: function
|
|
23522
|
-
|
|
23523
|
-
var _this$outputFormat2;
|
|
23524
|
-
|
|
23525
|
-
|
|
23526
|
-
|
|
23527
|
-
|
|
23528
|
-
|
|
23529
|
-
|
|
23530
|
-
|
|
23531
|
-
|
|
23532
|
-
|
|
23533
|
-
|
|
23534
|
-
|
|
23535
|
-
|
|
23536
|
-
|
|
23537
|
-
|
|
23538
|
-
|
|
23539
|
-
|
|
23540
|
-
|
|
23541
|
-
|
|
23542
|
-
|
|
23543
|
-
|
|
23544
|
-
|
|
23545
|
-
|
|
23546
|
-
|
|
23547
|
-
|
|
23548
|
-
|
|
23549
|
-
|
|
23550
|
-
|
|
23551
|
-
|
|
23552
|
-
|
|
23553
|
-
|
|
23554
|
-
|
|
23555
|
-
|
|
23556
|
-
|
|
23557
|
-
var NORMALIZATION = 1.0 / 32768.0;
|
|
23558
|
-
var length = int16Array.length;
|
|
23559
|
-
for (var i = 0; i < length; i++) {
|
|
23560
|
-
float32Array[i] = int16Array[i] * NORMALIZATION;
|
|
23561
|
-
}
|
|
23562
|
-
|
|
23563
|
-
// Create audio buffer with the ACTUAL sample rate of the audio data
|
|
24524
|
+
value: (function () {
|
|
24525
|
+
var _prepareChunk = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(pcmData) {
|
|
24526
|
+
var _this$outputFormat2, processedData, padded, int16Array, float32Array, NORMALIZATION, length, i, audioDataSampleRate, contextSampleRate, audioBuffer, chunkDuration, sampleCount, actualDuration, _t;
|
|
24527
|
+
return _regenerator().w(function (_context2) {
|
|
24528
|
+
while (1) switch (_context2.p = _context2.n) {
|
|
24529
|
+
case 0:
|
|
24530
|
+
_context2.p = 0;
|
|
24531
|
+
if (this.outputFormat) {
|
|
24532
|
+
_context2.n = 1;
|
|
24533
|
+
break;
|
|
24534
|
+
}
|
|
24535
|
+
console.error('❌ AudioPlayer: Cannot prepare chunk - outputFormat not set');
|
|
24536
|
+
console.error(' Call setOutputFormat() before playing audio chunks');
|
|
24537
|
+
return _context2.a(2, null);
|
|
24538
|
+
case 1:
|
|
24539
|
+
if (this.audioContext) {
|
|
24540
|
+
_context2.n = 2;
|
|
24541
|
+
break;
|
|
24542
|
+
}
|
|
24543
|
+
_context2.n = 2;
|
|
24544
|
+
return this.initializeAudioContext();
|
|
24545
|
+
case 2:
|
|
24546
|
+
if (this.audioContext) {
|
|
24547
|
+
_context2.n = 3;
|
|
24548
|
+
break;
|
|
24549
|
+
}
|
|
24550
|
+
console.error('❌ AudioPlayer: Cannot prepare chunk - AudioContext not available');
|
|
24551
|
+
return _context2.a(2, null);
|
|
24552
|
+
case 3:
|
|
24553
|
+
// Ensure even byte count for 16-bit PCM
|
|
24554
|
+
processedData = pcmData;
|
|
24555
|
+
if (pcmData.byteLength % 2 !== 0) {
|
|
24556
|
+
console.warn('⚠️ Odd PCM chunk size, padding:', pcmData.byteLength);
|
|
24557
|
+
padded = new Uint8Array(pcmData.byteLength + 1);
|
|
24558
|
+
padded.set(new Uint8Array(pcmData), 0);
|
|
24559
|
+
padded[pcmData.byteLength] = 0; // Silence padding
|
|
23564
24560
|
|
|
23565
|
-
|
|
23566
|
-
|
|
23567
|
-
var audioBuffer = this.audioContext.createBuffer(1,
|
|
23568
|
-
// mono
|
|
24561
|
+
processedData = padded.buffer;
|
|
24562
|
+
}
|
|
23569
24563
|
|
|
23570
|
-
|
|
23571
|
-
|
|
24564
|
+
// Convert Int16 PCM to Float32 (pre-processing)
|
|
24565
|
+
int16Array = new Int16Array(processedData);
|
|
24566
|
+
float32Array = new Float32Array(int16Array.length);
|
|
24567
|
+
NORMALIZATION = 1.0 / 32768.0;
|
|
24568
|
+
length = int16Array.length;
|
|
24569
|
+
for (i = 0; i < length; i++) {
|
|
24570
|
+
float32Array[i] = int16Array[i] * NORMALIZATION;
|
|
24571
|
+
}
|
|
23572
24572
|
|
|
23573
|
-
|
|
24573
|
+
// Create audio buffer with the ACTUAL sample rate of the audio data
|
|
24574
|
+
audioDataSampleRate = ((_this$outputFormat2 = this.outputFormat) === null || _this$outputFormat2 === void 0 ? void 0 : _this$outputFormat2.sampleRate) || this.audioContext.sampleRate;
|
|
24575
|
+
contextSampleRate = this.audioContext.sampleRate;
|
|
24576
|
+
audioBuffer = this.audioContext.createBuffer(1,
|
|
24577
|
+
// mono
|
|
23574
24578
|
|
|
23575
|
-
|
|
23576
|
-
|
|
23577
|
-
var actualDuration = chunkDuration;
|
|
23578
|
-
if (contextSampleRate !== audioDataSampleRate) {
|
|
23579
|
-
actualDuration = sampleCount / contextSampleRate;
|
|
23580
|
-
}
|
|
24579
|
+
float32Array.length, audioDataSampleRate);
|
|
24580
|
+
audioBuffer.getChannelData(0).set(float32Array);
|
|
23581
24581
|
|
|
23582
|
-
|
|
24582
|
+
// Calculate duration (handle browser resampling)
|
|
24583
|
+
chunkDuration = audioBuffer.duration;
|
|
24584
|
+
sampleCount = float32Array.length;
|
|
24585
|
+
actualDuration = chunkDuration;
|
|
24586
|
+
if (contextSampleRate !== audioDataSampleRate) {
|
|
24587
|
+
actualDuration = sampleCount / contextSampleRate;
|
|
24588
|
+
}
|
|
23583
24589
|
|
|
23584
|
-
|
|
23585
|
-
|
|
23586
|
-
|
|
23587
|
-
|
|
23588
|
-
|
|
23589
|
-
|
|
23590
|
-
|
|
23591
|
-
|
|
23592
|
-
|
|
24590
|
+
// Return prepared frame
|
|
24591
|
+
return _context2.a(2, {
|
|
24592
|
+
buffer: audioBuffer,
|
|
24593
|
+
duration: actualDuration,
|
|
24594
|
+
sampleRate: audioDataSampleRate,
|
|
24595
|
+
contextSampleRate: contextSampleRate
|
|
24596
|
+
});
|
|
24597
|
+
case 4:
|
|
24598
|
+
_context2.p = 4;
|
|
24599
|
+
_t = _context2.v;
|
|
24600
|
+
console.error('❌ AudioPlayer: Error preparing chunk:', _t);
|
|
24601
|
+
return _context2.a(2, null);
|
|
24602
|
+
}
|
|
24603
|
+
}, _callee2, this, [[0, 4]]);
|
|
24604
|
+
}));
|
|
24605
|
+
function prepareChunk(_x2) {
|
|
24606
|
+
return _prepareChunk.apply(this, arguments);
|
|
23593
24607
|
}
|
|
23594
|
-
|
|
23595
|
-
|
|
24608
|
+
return prepareChunk;
|
|
24609
|
+
}()
|
|
23596
24610
|
/**
|
|
23597
24611
|
* Apply fade-out to audio buffer (stub method to prevent errors)
|
|
23598
24612
|
* This method is called by some code paths but doesn't need to do anything
|
|
23599
24613
|
* @param {AudioBuffer} audioBuffer - Audio buffer to fade
|
|
23600
24614
|
* @returns {AudioBuffer} - Same audio buffer (no modification)
|
|
23601
24615
|
*/
|
|
24616
|
+
)
|
|
23602
24617
|
}, {
|
|
23603
24618
|
key: "applyFadeOut",
|
|
23604
24619
|
value: function applyFadeOut(audioBuffer) {
|
|
@@ -23614,24 +24629,24 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23614
24629
|
}, {
|
|
23615
24630
|
key: "schedulePreparedFrames",
|
|
23616
24631
|
value: (function () {
|
|
23617
|
-
var _schedulePreparedFrames = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
24632
|
+
var _schedulePreparedFrames = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
|
|
23618
24633
|
var _this4 = this;
|
|
23619
|
-
var queuedFrames, targetLookaheadFrames, scheduledCount, _loop, _ret,
|
|
23620
|
-
return _regenerator().w(function (
|
|
23621
|
-
while (1) switch (
|
|
24634
|
+
var queuedFrames, targetLookaheadFrames, scheduledCount, _loop, _ret, _t2;
|
|
24635
|
+
return _regenerator().w(function (_context4) {
|
|
24636
|
+
while (1) switch (_context4.p = _context4.n) {
|
|
23622
24637
|
case 0:
|
|
23623
24638
|
if (!this._isStopped) {
|
|
23624
|
-
|
|
24639
|
+
_context4.n = 1;
|
|
23625
24640
|
break;
|
|
23626
24641
|
}
|
|
23627
24642
|
console.log('🛑 AudioPlayer: Not scheduling frames - playback was stopped (barge-in)');
|
|
23628
|
-
return
|
|
24643
|
+
return _context4.a(2);
|
|
23629
24644
|
case 1:
|
|
23630
24645
|
if (!this.isSchedulingFrames) {
|
|
23631
|
-
|
|
24646
|
+
_context4.n = 2;
|
|
23632
24647
|
break;
|
|
23633
24648
|
}
|
|
23634
|
-
return
|
|
24649
|
+
return _context4.a(2);
|
|
23635
24650
|
case 2:
|
|
23636
24651
|
this.isSchedulingFrames = true;
|
|
23637
24652
|
|
|
@@ -23644,17 +24659,8 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23644
24659
|
if (targetLookaheadFrames === 0 && queuedFrames > 0) {
|
|
23645
24660
|
targetLookaheadFrames = 1; // At least schedule 1 frame
|
|
23646
24661
|
}
|
|
23647
|
-
|
|
23648
|
-
|
|
23649
|
-
|
|
23650
|
-
if (!this.audioContext) {
|
|
23651
|
-
this.initializeAudioContext();
|
|
23652
|
-
}
|
|
23653
|
-
|
|
23654
|
-
// ✅ NEW: Wait for AudioContext to be in 'running' state before proceeding
|
|
23655
|
-
// This is critical for mobile devices where AudioContext initialization takes time
|
|
23656
|
-
// Replaces fixed timeouts with event-driven waiting
|
|
23657
|
-
_context3.n = 4;
|
|
24662
|
+
_context4.p = 3;
|
|
24663
|
+
_context4.n = 4;
|
|
23658
24664
|
return this.waitForAudioContextReady();
|
|
23659
24665
|
case 4:
|
|
23660
24666
|
// Schedule frames up to target lookahead (ensures smooth playback)
|
|
@@ -23662,23 +24668,23 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23662
24668
|
scheduledCount = 0;
|
|
23663
24669
|
_loop = /*#__PURE__*/_regenerator().m(function _loop() {
|
|
23664
24670
|
var preparedFrame, source, currentTime, isMobile, startupBuffer, minStartTime, startTime;
|
|
23665
|
-
return _regenerator().w(function (
|
|
23666
|
-
while (1) switch (
|
|
24671
|
+
return _regenerator().w(function (_context3) {
|
|
24672
|
+
while (1) switch (_context3.n) {
|
|
23667
24673
|
case 0:
|
|
23668
24674
|
if (!_this4._isStopped) {
|
|
23669
|
-
|
|
24675
|
+
_context3.n = 1;
|
|
23670
24676
|
break;
|
|
23671
24677
|
}
|
|
23672
24678
|
console.log('🛑 AudioPlayer: Stopping frame scheduling - playback was stopped');
|
|
23673
|
-
return
|
|
24679
|
+
return _context3.a(2, 0);
|
|
23674
24680
|
case 1:
|
|
23675
24681
|
// Get next prepared frame
|
|
23676
24682
|
preparedFrame = _this4.preparedBuffer.shift();
|
|
23677
24683
|
if (preparedFrame) {
|
|
23678
|
-
|
|
24684
|
+
_context3.n = 2;
|
|
23679
24685
|
break;
|
|
23680
24686
|
}
|
|
23681
|
-
return
|
|
24687
|
+
return _context3.a(2, 0);
|
|
23682
24688
|
case 2:
|
|
23683
24689
|
// Create source and schedule playback
|
|
23684
24690
|
source = _this4.audioContext.createBufferSource();
|
|
@@ -23832,25 +24838,25 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23832
24838
|
_this4.emit('playbackStarted');
|
|
23833
24839
|
}
|
|
23834
24840
|
case 3:
|
|
23835
|
-
return
|
|
24841
|
+
return _context3.a(2);
|
|
23836
24842
|
}
|
|
23837
24843
|
}, _loop);
|
|
23838
24844
|
});
|
|
23839
24845
|
case 5:
|
|
23840
24846
|
if (!(this.preparedBuffer.length > 0 && scheduledCount < targetLookaheadFrames)) {
|
|
23841
|
-
|
|
24847
|
+
_context4.n = 8;
|
|
23842
24848
|
break;
|
|
23843
24849
|
}
|
|
23844
|
-
return
|
|
24850
|
+
return _context4.d(_regeneratorValues(_loop()), 6);
|
|
23845
24851
|
case 6:
|
|
23846
|
-
_ret =
|
|
24852
|
+
_ret = _context4.v;
|
|
23847
24853
|
if (!(_ret === 0)) {
|
|
23848
|
-
|
|
24854
|
+
_context4.n = 7;
|
|
23849
24855
|
break;
|
|
23850
24856
|
}
|
|
23851
|
-
return
|
|
24857
|
+
return _context4.a(3, 8);
|
|
23852
24858
|
case 7:
|
|
23853
|
-
|
|
24859
|
+
_context4.n = 5;
|
|
23854
24860
|
break;
|
|
23855
24861
|
case 8:
|
|
23856
24862
|
// All prepared frames scheduled, reset flag
|
|
@@ -23892,18 +24898,18 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23892
24898
|
}
|
|
23893
24899
|
}, 10); // Check every 10ms for new frames
|
|
23894
24900
|
}
|
|
23895
|
-
|
|
24901
|
+
_context4.n = 10;
|
|
23896
24902
|
break;
|
|
23897
24903
|
case 9:
|
|
23898
|
-
|
|
23899
|
-
|
|
23900
|
-
console.error('❌ AudioPlayer v2: Error scheduling frames:',
|
|
23901
|
-
this.emit('playbackError',
|
|
24904
|
+
_context4.p = 9;
|
|
24905
|
+
_t2 = _context4.v;
|
|
24906
|
+
console.error('❌ AudioPlayer v2: Error scheduling frames:', _t2);
|
|
24907
|
+
this.emit('playbackError', _t2);
|
|
23902
24908
|
this.isSchedulingFrames = false;
|
|
23903
24909
|
case 10:
|
|
23904
|
-
return
|
|
24910
|
+
return _context4.a(2);
|
|
23905
24911
|
}
|
|
23906
|
-
},
|
|
24912
|
+
}, _callee3, this, [[3, 9]]);
|
|
23907
24913
|
}));
|
|
23908
24914
|
function schedulePreparedFrames() {
|
|
23909
24915
|
return _schedulePreparedFrames.apply(this, arguments);
|
|
@@ -23918,56 +24924,47 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23918
24924
|
}, {
|
|
23919
24925
|
key: "processPcmQueue",
|
|
23920
24926
|
value: (function () {
|
|
23921
|
-
var _processPcmQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
24927
|
+
var _processPcmQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
|
|
23922
24928
|
var _this5 = this;
|
|
23923
|
-
var _this$outputFormat3, _this$outputFormat4, pcmData, processedData, padded, int16Array, float32Array, NORMALIZATION, length, i, audioDataSampleRate, contextSampleRate, audioBuffer, source, currentTime, chunkDuration, sampleCount, actualDuration, startTime,
|
|
23924
|
-
return _regenerator().w(function (
|
|
23925
|
-
while (1) switch (
|
|
24929
|
+
var _this$outputFormat3, _this$outputFormat4, pcmData, processedData, padded, int16Array, float32Array, NORMALIZATION, length, i, audioDataSampleRate, contextSampleRate, audioBuffer, source, currentTime, chunkDuration, sampleCount, actualDuration, startTime, _t3;
|
|
24930
|
+
return _regenerator().w(function (_context5) {
|
|
24931
|
+
while (1) switch (_context5.p = _context5.n) {
|
|
23926
24932
|
case 0:
|
|
23927
24933
|
if (!this._isStopped) {
|
|
23928
|
-
|
|
24934
|
+
_context5.n = 1;
|
|
23929
24935
|
break;
|
|
23930
24936
|
}
|
|
23931
24937
|
console.log('🛑 AudioPlayer: Not processing PCM queue - playback was stopped (barge-in)');
|
|
23932
|
-
return
|
|
24938
|
+
return _context5.a(2);
|
|
23933
24939
|
case 1:
|
|
23934
24940
|
if (!this.isProcessingPcmQueue) {
|
|
23935
|
-
|
|
24941
|
+
_context5.n = 2;
|
|
23936
24942
|
break;
|
|
23937
24943
|
}
|
|
23938
|
-
return
|
|
24944
|
+
return _context5.a(2);
|
|
23939
24945
|
case 2:
|
|
23940
24946
|
this.isProcessingPcmQueue = true;
|
|
23941
|
-
|
|
23942
|
-
|
|
23943
|
-
|
|
23944
|
-
if (!this.audioContext) {
|
|
23945
|
-
this.initializeAudioContext();
|
|
23946
|
-
}
|
|
23947
|
-
|
|
23948
|
-
// ✅ NEW: Wait for AudioContext to be in 'running' state before proceeding
|
|
23949
|
-
// This is critical for mobile devices where AudioContext initialization takes time
|
|
23950
|
-
// Replaces fixed timeouts with event-driven waiting
|
|
23951
|
-
_context4.n = 4;
|
|
24947
|
+
_context5.p = 3;
|
|
24948
|
+
_context5.n = 4;
|
|
23952
24949
|
return this.waitForAudioContextReady();
|
|
23953
24950
|
case 4:
|
|
23954
24951
|
if (!(this.pcmChunkQueue.length > 0)) {
|
|
23955
|
-
|
|
24952
|
+
_context5.n = 7;
|
|
23956
24953
|
break;
|
|
23957
24954
|
}
|
|
23958
24955
|
if (!this._isStopped) {
|
|
23959
|
-
|
|
24956
|
+
_context5.n = 5;
|
|
23960
24957
|
break;
|
|
23961
24958
|
}
|
|
23962
24959
|
console.log('🛑 AudioPlayer: Stopping PCM queue processing - playback was stopped');
|
|
23963
|
-
return
|
|
24960
|
+
return _context5.a(3, 7);
|
|
23964
24961
|
case 5:
|
|
23965
24962
|
pcmData = this.pcmChunkQueue.shift();
|
|
23966
24963
|
if (pcmData) {
|
|
23967
|
-
|
|
24964
|
+
_context5.n = 6;
|
|
23968
24965
|
break;
|
|
23969
24966
|
}
|
|
23970
|
-
return
|
|
24967
|
+
return _context5.a(3, 4);
|
|
23971
24968
|
case 6:
|
|
23972
24969
|
// Ensure even byte count for 16-bit PCM
|
|
23973
24970
|
processedData = pcmData;
|
|
@@ -24060,25 +25057,25 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24060
25057
|
this.isPlaying = true;
|
|
24061
25058
|
this.emit('playbackStarted');
|
|
24062
25059
|
}
|
|
24063
|
-
|
|
25060
|
+
_context5.n = 4;
|
|
24064
25061
|
break;
|
|
24065
25062
|
case 7:
|
|
24066
25063
|
// end while loop
|
|
24067
25064
|
|
|
24068
25065
|
// All chunks scheduled, reset processing flag
|
|
24069
25066
|
this.isProcessingPcmQueue = false;
|
|
24070
|
-
|
|
25067
|
+
_context5.n = 9;
|
|
24071
25068
|
break;
|
|
24072
25069
|
case 8:
|
|
24073
|
-
|
|
24074
|
-
|
|
24075
|
-
console.error('❌ AudioPlayer v2: Error playing chunk:',
|
|
24076
|
-
this.emit('playbackError',
|
|
25070
|
+
_context5.p = 8;
|
|
25071
|
+
_t3 = _context5.v;
|
|
25072
|
+
console.error('❌ AudioPlayer v2: Error playing chunk:', _t3);
|
|
25073
|
+
this.emit('playbackError', _t3);
|
|
24077
25074
|
this.isProcessingPcmQueue = false;
|
|
24078
25075
|
case 9:
|
|
24079
|
-
return
|
|
25076
|
+
return _context5.a(2);
|
|
24080
25077
|
}
|
|
24081
|
-
},
|
|
25078
|
+
}, _callee4, this, [[3, 8]]);
|
|
24082
25079
|
}));
|
|
24083
25080
|
function processPcmQueue() {
|
|
24084
25081
|
return _processPcmQueue.apply(this, arguments);
|
|
@@ -24463,6 +25460,40 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24463
25460
|
return header;
|
|
24464
25461
|
}
|
|
24465
25462
|
|
|
25463
|
+
/**
|
|
25464
|
+
* Drop stale instance/shared references to a closed AudioContext so
|
|
25465
|
+
* initializeAudioContext() cannot no-op while the context stays closed.
|
|
25466
|
+
* @private
|
|
25467
|
+
*/
|
|
25468
|
+
}, {
|
|
25469
|
+
key: "_invalidateClosedAudioContext",
|
|
25470
|
+
value: function _invalidateClosedAudioContext() {
|
|
25471
|
+
if (this._audioContextStateChangeHandler && this.audioContext) {
|
|
25472
|
+
try {
|
|
25473
|
+
this.audioContext.removeEventListener('statechange', this._audioContextStateChangeHandler);
|
|
25474
|
+
} catch (_) {/* already detached */}
|
|
25475
|
+
this._audioContextStateChangeHandler = null;
|
|
25476
|
+
}
|
|
25477
|
+
if (this._waitForReadyStateHandler && this.audioContext) {
|
|
25478
|
+
try {
|
|
25479
|
+
this.audioContext.removeEventListener('statechange', this._waitForReadyStateHandler);
|
|
25480
|
+
} catch (_) {/* already detached */}
|
|
25481
|
+
this._waitForReadyStateHandler = null;
|
|
25482
|
+
}
|
|
25483
|
+
if (this.gainNode) {
|
|
25484
|
+
try {
|
|
25485
|
+
this.gainNode.disconnect();
|
|
25486
|
+
} catch (_) {/* ignore */}
|
|
25487
|
+
this.gainNode = null;
|
|
25488
|
+
}
|
|
25489
|
+
if (this.audioContext === _sharedPlayerContext) {
|
|
25490
|
+
_sharedPlayerContext = null;
|
|
25491
|
+
_sharedPlayerSampleRate = null;
|
|
25492
|
+
}
|
|
25493
|
+
this.audioContext = null;
|
|
25494
|
+
this._audioContextPrimed = false;
|
|
25495
|
+
}
|
|
25496
|
+
|
|
24466
25497
|
/**
|
|
24467
25498
|
* Wait for AudioContext to be in 'running' state before proceeding
|
|
24468
25499
|
* This is critical for mobile devices where AudioContext initialization takes time
|
|
@@ -24472,41 +25503,85 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24472
25503
|
}, {
|
|
24473
25504
|
key: "waitForAudioContextReady",
|
|
24474
25505
|
value: (function () {
|
|
24475
|
-
var _waitForAudioContextReady = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
25506
|
+
var _waitForAudioContextReady = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
|
|
25507
|
+
return _regenerator().w(function (_context6) {
|
|
25508
|
+
while (1) switch (_context6.n) {
|
|
25509
|
+
case 0:
|
|
25510
|
+
return _context6.a(2, this._waitForAudioContextReadyImpl(0));
|
|
25511
|
+
}
|
|
25512
|
+
}, _callee5, this);
|
|
25513
|
+
}));
|
|
25514
|
+
function waitForAudioContextReady() {
|
|
25515
|
+
return _waitForAudioContextReady.apply(this, arguments);
|
|
25516
|
+
}
|
|
25517
|
+
return waitForAudioContextReady;
|
|
25518
|
+
}())
|
|
25519
|
+
}, {
|
|
25520
|
+
key: "_waitForAudioContextReadyImpl",
|
|
25521
|
+
value: function () {
|
|
25522
|
+
var _waitForAudioContextReadyImpl2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(attempt) {
|
|
24476
25523
|
var _this6 = this;
|
|
24477
|
-
var audioContext;
|
|
24478
|
-
return _regenerator().w(function (
|
|
24479
|
-
while (1) switch (
|
|
25524
|
+
var MAX_RECREATE_ATTEMPTS, _this$audioContext2, audioContext;
|
|
25525
|
+
return _regenerator().w(function (_context7) {
|
|
25526
|
+
while (1) switch (_context7.n) {
|
|
24480
25527
|
case 0:
|
|
24481
|
-
|
|
24482
|
-
|
|
25528
|
+
MAX_RECREATE_ATTEMPTS = 3;
|
|
25529
|
+
if (!(!this.audioContext || this.audioContext.state === 'closed')) {
|
|
25530
|
+
_context7.n = 1;
|
|
25531
|
+
break;
|
|
24483
25532
|
}
|
|
25533
|
+
if (((_this$audioContext2 = this.audioContext) === null || _this$audioContext2 === void 0 ? void 0 : _this$audioContext2.state) === 'closed') {
|
|
25534
|
+
this._invalidateClosedAudioContext();
|
|
25535
|
+
}
|
|
25536
|
+
_context7.n = 1;
|
|
25537
|
+
return this.initializeAudioContext();
|
|
25538
|
+
case 1:
|
|
24484
25539
|
if (this.audioContext) {
|
|
24485
|
-
|
|
25540
|
+
_context7.n = 2;
|
|
24486
25541
|
break;
|
|
24487
25542
|
}
|
|
24488
25543
|
console.error('❌ AudioPlayer: Failed to create AudioContext');
|
|
24489
|
-
return
|
|
24490
|
-
case
|
|
25544
|
+
return _context7.a(2);
|
|
25545
|
+
case 2:
|
|
24491
25546
|
audioContext = this.audioContext;
|
|
24492
25547
|
if (!(audioContext.state === 'running')) {
|
|
24493
|
-
|
|
25548
|
+
_context7.n = 3;
|
|
24494
25549
|
break;
|
|
24495
25550
|
}
|
|
24496
|
-
return
|
|
24497
|
-
case
|
|
25551
|
+
return _context7.a(2);
|
|
25552
|
+
case 3:
|
|
25553
|
+
if (!(audioContext.state === 'closed')) {
|
|
25554
|
+
_context7.n = 6;
|
|
25555
|
+
break;
|
|
25556
|
+
}
|
|
25557
|
+
if (!(attempt >= MAX_RECREATE_ATTEMPTS)) {
|
|
25558
|
+
_context7.n = 4;
|
|
25559
|
+
break;
|
|
25560
|
+
}
|
|
25561
|
+
console.error('❌ AudioPlayer: AudioContext still closed after recreation attempts — aborting playback setup');
|
|
25562
|
+
return _context7.a(2);
|
|
25563
|
+
case 4:
|
|
25564
|
+
if (attempt === 0) {
|
|
25565
|
+
console.warn('⚠️ AudioPlayer: AudioContext was closed, recreating...');
|
|
25566
|
+
}
|
|
25567
|
+
this._invalidateClosedAudioContext();
|
|
25568
|
+
_context7.n = 5;
|
|
25569
|
+
return this.initializeAudioContext();
|
|
25570
|
+
case 5:
|
|
25571
|
+
return _context7.a(2, this._waitForAudioContextReadyImpl(attempt + 1));
|
|
25572
|
+
case 6:
|
|
24498
25573
|
if (!(audioContext.state === 'suspended')) {
|
|
24499
|
-
|
|
25574
|
+
_context7.n = 8;
|
|
24500
25575
|
break;
|
|
24501
25576
|
}
|
|
24502
|
-
|
|
25577
|
+
_context7.n = 7;
|
|
24503
25578
|
return audioContext.resume();
|
|
24504
|
-
case
|
|
25579
|
+
case 7:
|
|
24505
25580
|
if (!(audioContext.state !== 'running')) {
|
|
24506
|
-
|
|
25581
|
+
_context7.n = 8;
|
|
24507
25582
|
break;
|
|
24508
25583
|
}
|
|
24509
|
-
return
|
|
25584
|
+
return _context7.a(2, new Promise(function (resolve) {
|
|
24510
25585
|
var timeout = setTimeout(function () {
|
|
24511
25586
|
if (audioContext && _this6._waitForReadyStateHandler) {
|
|
24512
25587
|
audioContext.removeEventListener('statechange', _this6._waitForReadyStateHandler);
|
|
@@ -24543,161 +25618,190 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24543
25618
|
resolve();
|
|
24544
25619
|
}
|
|
24545
25620
|
}));
|
|
24546
|
-
case
|
|
24547
|
-
|
|
24548
|
-
|
|
25621
|
+
case 8:
|
|
25622
|
+
return _context7.a(2);
|
|
25623
|
+
}
|
|
25624
|
+
}, _callee6, this);
|
|
25625
|
+
}));
|
|
25626
|
+
function _waitForAudioContextReadyImpl(_x3) {
|
|
25627
|
+
return _waitForAudioContextReadyImpl2.apply(this, arguments);
|
|
25628
|
+
}
|
|
25629
|
+
return _waitForAudioContextReadyImpl;
|
|
25630
|
+
}()
|
|
25631
|
+
/**
|
|
25632
|
+
* Initialize audio context with correct sample rate
|
|
25633
|
+
*/
|
|
25634
|
+
}, {
|
|
25635
|
+
key: "initializeAudioContext",
|
|
25636
|
+
value: (function () {
|
|
25637
|
+
var _initializeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7() {
|
|
25638
|
+
var _this$outputFormat5,
|
|
25639
|
+
_this7 = this,
|
|
25640
|
+
_this$outputFormat6;
|
|
25641
|
+
var desiredSampleRate, currentSampleRate, canReuseShared, setupAfterResume, _t4;
|
|
25642
|
+
return _regenerator().w(function (_context8) {
|
|
25643
|
+
while (1) switch (_context8.p = _context8.n) {
|
|
25644
|
+
case 0:
|
|
25645
|
+
// Use negotiated sample rate, default to 24kHz to match typical server/TTS output
|
|
25646
|
+
desiredSampleRate = ((_this$outputFormat5 = this.outputFormat) === null || _this$outputFormat5 === void 0 ? void 0 : _this$outputFormat5.sampleRate) || 24000;
|
|
25647
|
+
if (![24000, 44100, 48000].includes(desiredSampleRate)) {
|
|
25648
|
+
console.log("\u2139\uFE0F AudioPlayer: Backend requested ".concat(desiredSampleRate, "Hz, but browser may resample"));
|
|
25649
|
+
console.log(" Consider requesting 24000Hz, 44100Hz, or 48000Hz from backend to reduce resampling");
|
|
25650
|
+
}
|
|
25651
|
+
if (_sharedPlayerContext && _sharedPlayerContext.state === 'closed') {
|
|
25652
|
+
_sharedPlayerContext = null;
|
|
25653
|
+
_sharedPlayerSampleRate = null;
|
|
25654
|
+
}
|
|
25655
|
+
|
|
25656
|
+
// Check if current instance AudioContext exists and matches
|
|
25657
|
+
if (!this.audioContext) {
|
|
25658
|
+
_context8.n = 3;
|
|
25659
|
+
break;
|
|
25660
|
+
}
|
|
25661
|
+
if (!(this.audioContext.state === 'closed')) {
|
|
25662
|
+
_context8.n = 1;
|
|
25663
|
+
break;
|
|
25664
|
+
}
|
|
25665
|
+
this._invalidateClosedAudioContext();
|
|
25666
|
+
_context8.n = 3;
|
|
25667
|
+
break;
|
|
25668
|
+
case 1:
|
|
25669
|
+
currentSampleRate = this.audioContext.sampleRate;
|
|
25670
|
+
if (!(Math.abs(currentSampleRate - desiredSampleRate) > 100)) {
|
|
25671
|
+
_context8.n = 2;
|
|
24549
25672
|
break;
|
|
24550
25673
|
}
|
|
24551
|
-
console.warn(
|
|
24552
|
-
this.
|
|
24553
|
-
|
|
24554
|
-
|
|
24555
|
-
|
|
25674
|
+
console.warn("\u26A0\uFE0F AudioPlayer: AudioContext sample rate (".concat(currentSampleRate, "Hz) doesn't match format (").concat(desiredSampleRate, "Hz), recreating..."));
|
|
25675
|
+
this.stopImmediate();
|
|
25676
|
+
this._cleanupAudioContext();
|
|
25677
|
+
_context8.n = 3;
|
|
25678
|
+
break;
|
|
25679
|
+
case 2:
|
|
25680
|
+
return _context8.a(2);
|
|
25681
|
+
case 3:
|
|
25682
|
+
// iOS FIX: Reuse shared AudioContext if available and compatible.
|
|
25683
|
+
// iOS WebKit doesn't release audio hardware synchronously on AudioContext.close(),
|
|
25684
|
+
// causing newly created AudioContexts to fail silently.
|
|
25685
|
+
canReuseShared = _sharedPlayerContext && _sharedPlayerContext.state !== 'closed' && _sharedPlayerSampleRate === desiredSampleRate;
|
|
25686
|
+
if (!canReuseShared) {
|
|
25687
|
+
_context8.n = 8;
|
|
25688
|
+
break;
|
|
25689
|
+
}
|
|
25690
|
+
console.log("\u267B\uFE0F AudioPlayer: Reusing shared AudioContext at ".concat(desiredSampleRate, "Hz (iOS-safe)"));
|
|
25691
|
+
this.audioContext = _sharedPlayerContext;
|
|
25692
|
+
setupAfterResume = function setupAfterResume() {
|
|
25693
|
+
_this7.setupAudioContextStateMonitoring();
|
|
25694
|
+
if (_this7.gainNode) {
|
|
25695
|
+
try {
|
|
25696
|
+
_this7.gainNode.disconnect();
|
|
25697
|
+
} catch (e) {
|
|
25698
|
+
console.warn('⚠️ AudioPlayer: Error disconnecting old GainNode:', e);
|
|
25699
|
+
}
|
|
25700
|
+
}
|
|
25701
|
+
_this7.gainNode = _this7.audioContext.createGain();
|
|
25702
|
+
_this7.gainNode.gain.value = 1.0;
|
|
25703
|
+
_this7.gainNode.connect(_this7.audioContext.destination);
|
|
25704
|
+
if (!_this7._audioContextPrimed) {
|
|
25705
|
+
_this7._primeAudioContext();
|
|
25706
|
+
}
|
|
25707
|
+
};
|
|
25708
|
+
if (!(this.audioContext.state === 'suspended')) {
|
|
25709
|
+
_context8.n = 7;
|
|
25710
|
+
break;
|
|
25711
|
+
}
|
|
25712
|
+
_context8.p = 4;
|
|
25713
|
+
_context8.n = 5;
|
|
25714
|
+
return this.audioContext.resume();
|
|
25715
|
+
case 5:
|
|
25716
|
+
_context8.n = 7;
|
|
25717
|
+
break;
|
|
25718
|
+
case 6:
|
|
25719
|
+
_context8.p = 6;
|
|
25720
|
+
_t4 = _context8.v;
|
|
25721
|
+
console.warn('⚠️ AudioPlayer: Error resuming shared context:', _t4);
|
|
25722
|
+
case 7:
|
|
25723
|
+
setupAfterResume();
|
|
25724
|
+
return _context8.a(2);
|
|
25725
|
+
case 8:
|
|
25726
|
+
console.log("\uD83C\uDFB5 AudioPlayer: Creating AudioContext at ".concat(desiredSampleRate, "Hz (from outputFormat: ").concat(((_this$outputFormat6 = this.outputFormat) === null || _this$outputFormat6 === void 0 ? void 0 : _this$outputFormat6.sampleRate) || 'not set', ")"));
|
|
25727
|
+
|
|
25728
|
+
// Close old shared context if sample rate changed
|
|
25729
|
+
if (_sharedPlayerContext && _sharedPlayerContext.state !== 'closed') {
|
|
25730
|
+
console.log('🔄 AudioPlayer: Closing old shared AudioContext (sample rate changed)');
|
|
25731
|
+
try {
|
|
25732
|
+
_sharedPlayerContext.close();
|
|
25733
|
+
} catch (e) {
|
|
25734
|
+
console.warn('⚠️ AudioPlayer: Error closing old shared context:', e);
|
|
25735
|
+
}
|
|
25736
|
+
}
|
|
25737
|
+
try {
|
|
25738
|
+
this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
|
|
25739
|
+
sampleRate: desiredSampleRate,
|
|
25740
|
+
latencyHint: 'playback'
|
|
25741
|
+
});
|
|
25742
|
+
|
|
25743
|
+
// Store as shared context
|
|
25744
|
+
_sharedPlayerContext = this.audioContext;
|
|
25745
|
+
_sharedPlayerSampleRate = desiredSampleRate;
|
|
25746
|
+
console.log("\u2705 AudioContext created at ".concat(this.audioContext.sampleRate, "Hz (requested: ").concat(desiredSampleRate, "Hz)"));
|
|
25747
|
+
if (Math.abs(this.audioContext.sampleRate - desiredSampleRate) > 100) {
|
|
25748
|
+
console.error("\u274C CRITICAL: Browser sample rate mismatch!");
|
|
25749
|
+
console.error(" Requested: ".concat(desiredSampleRate, "Hz"));
|
|
25750
|
+
console.error(" Got: ".concat(this.audioContext.sampleRate, "Hz"));
|
|
25751
|
+
console.error(" This WILL cause audio distortion/noise!");
|
|
25752
|
+
console.error(" Solution: Backend should send ".concat(this.audioContext.sampleRate, "Hz audio instead"));
|
|
25753
|
+
} else if (this.audioContext.sampleRate !== desiredSampleRate) {
|
|
25754
|
+
console.warn("\u26A0\uFE0F Browser adjusted sample rate: ".concat(desiredSampleRate, "Hz \u2192 ").concat(this.audioContext.sampleRate, "Hz"));
|
|
25755
|
+
console.warn(" Browser will automatically resample audio.");
|
|
25756
|
+
}
|
|
25757
|
+
this.setupAudioContextStateMonitoring();
|
|
25758
|
+
this.gainNode = this.audioContext.createGain();
|
|
25759
|
+
this.gainNode.gain.value = 1.0;
|
|
25760
|
+
this.gainNode.connect(this.audioContext.destination);
|
|
25761
|
+
console.log('✅ AudioPlayer: GainNode created for volume control');
|
|
25762
|
+
this._primeAudioContext();
|
|
25763
|
+
} catch (error) {
|
|
25764
|
+
console.error("\u274C Failed to create AudioContext:", error);
|
|
25765
|
+
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
25766
|
+
_sharedPlayerContext = this.audioContext;
|
|
25767
|
+
_sharedPlayerSampleRate = this.audioContext.sampleRate;
|
|
25768
|
+
console.log("\u2139\uFE0F Using browser default: ".concat(this.audioContext.sampleRate, "Hz"));
|
|
25769
|
+
this.setupAudioContextStateMonitoring();
|
|
25770
|
+
this.gainNode = this.audioContext.createGain();
|
|
25771
|
+
this.gainNode.gain.value = 1.0;
|
|
25772
|
+
this.gainNode.connect(this.audioContext.destination);
|
|
25773
|
+
console.log('✅ AudioPlayer: GainNode created for volume control (fallback)');
|
|
25774
|
+
this._primeAudioContext();
|
|
25775
|
+
}
|
|
25776
|
+
case 9:
|
|
25777
|
+
return _context8.a(2);
|
|
24556
25778
|
}
|
|
24557
|
-
},
|
|
25779
|
+
}, _callee7, this, [[4, 6]]);
|
|
24558
25780
|
}));
|
|
24559
|
-
function
|
|
24560
|
-
return
|
|
25781
|
+
function initializeAudioContext() {
|
|
25782
|
+
return _initializeAudioContext.apply(this, arguments);
|
|
24561
25783
|
}
|
|
24562
|
-
return
|
|
25784
|
+
return initializeAudioContext;
|
|
24563
25785
|
}()
|
|
24564
|
-
/**
|
|
24565
|
-
/**
|
|
24566
|
-
*
|
|
24567
|
-
* Initialize audio context with correct sample rate
|
|
24568
|
-
*
|
|
24569
|
-
*/
|
|
24570
|
-
)
|
|
24571
|
-
}, {
|
|
24572
|
-
key: "initializeAudioContext",
|
|
24573
|
-
value: function initializeAudioContext() {
|
|
24574
|
-
var _this$outputFormat5,
|
|
24575
|
-
_this7 = this,
|
|
24576
|
-
_this$outputFormat6;
|
|
24577
|
-
// Use negotiated sample rate, default to 24kHz to match typical server/TTS output
|
|
24578
|
-
var desiredSampleRate = ((_this$outputFormat5 = this.outputFormat) === null || _this$outputFormat5 === void 0 ? void 0 : _this$outputFormat5.sampleRate) || 24000;
|
|
24579
|
-
if (![24000, 44100, 48000].includes(desiredSampleRate)) {
|
|
24580
|
-
console.log("\u2139\uFE0F AudioPlayer: Backend requested ".concat(desiredSampleRate, "Hz, but browser may resample"));
|
|
24581
|
-
console.log(" Consider requesting 24000Hz, 44100Hz, or 48000Hz from backend to reduce resampling");
|
|
24582
|
-
}
|
|
24583
|
-
|
|
24584
|
-
// Check if current instance AudioContext exists and matches
|
|
24585
|
-
if (this.audioContext) {
|
|
24586
|
-
var currentSampleRate = this.audioContext.sampleRate;
|
|
24587
|
-
if (Math.abs(currentSampleRate - desiredSampleRate) > 100) {
|
|
24588
|
-
console.warn("\u26A0\uFE0F AudioPlayer: AudioContext sample rate (".concat(currentSampleRate, "Hz) doesn't match format (").concat(desiredSampleRate, "Hz), recreating..."));
|
|
24589
|
-
this.stopImmediate();
|
|
24590
|
-
this._cleanupAudioContext();
|
|
24591
|
-
} else {
|
|
24592
|
-
return;
|
|
24593
|
-
}
|
|
24594
|
-
}
|
|
24595
|
-
|
|
24596
|
-
// iOS FIX: Reuse shared AudioContext if available and compatible.
|
|
24597
|
-
// iOS WebKit doesn't release audio hardware synchronously on AudioContext.close(),
|
|
24598
|
-
// causing newly created AudioContexts to fail silently.
|
|
24599
|
-
var canReuseShared = _sharedPlayerContext && _sharedPlayerContext.state !== 'closed' && _sharedPlayerSampleRate === desiredSampleRate;
|
|
24600
|
-
if (canReuseShared) {
|
|
24601
|
-
console.log("\u267B\uFE0F AudioPlayer: Reusing shared AudioContext at ".concat(desiredSampleRate, "Hz (iOS-safe)"));
|
|
24602
|
-
this.audioContext = _sharedPlayerContext;
|
|
24603
|
-
var setupAfterResume = function setupAfterResume() {
|
|
24604
|
-
_this7.setupAudioContextStateMonitoring();
|
|
24605
|
-
if (_this7.gainNode) {
|
|
24606
|
-
try {
|
|
24607
|
-
_this7.gainNode.disconnect();
|
|
24608
|
-
} catch (e) {
|
|
24609
|
-
console.warn('⚠️ AudioPlayer: Error disconnecting old GainNode:', e);
|
|
24610
|
-
}
|
|
24611
|
-
}
|
|
24612
|
-
_this7.gainNode = _this7.audioContext.createGain();
|
|
24613
|
-
_this7.gainNode.gain.value = 1.0;
|
|
24614
|
-
_this7.gainNode.connect(_this7.audioContext.destination);
|
|
24615
|
-
if (!_this7._audioContextPrimed) {
|
|
24616
|
-
_this7._primeAudioContext();
|
|
24617
|
-
}
|
|
24618
|
-
};
|
|
24619
|
-
if (this.audioContext.state === 'suspended') {
|
|
24620
|
-
this.audioContext.resume().then(function () {
|
|
24621
|
-
return setupAfterResume();
|
|
24622
|
-
}).catch(function (e) {
|
|
24623
|
-
console.warn('⚠️ AudioPlayer: Error resuming shared context:', e);
|
|
24624
|
-
setupAfterResume();
|
|
24625
|
-
});
|
|
24626
|
-
} else {
|
|
24627
|
-
setupAfterResume();
|
|
24628
|
-
}
|
|
24629
|
-
return;
|
|
24630
|
-
}
|
|
24631
|
-
console.log("\uD83C\uDFB5 AudioPlayer: Creating AudioContext at ".concat(desiredSampleRate, "Hz (from outputFormat: ").concat(((_this$outputFormat6 = this.outputFormat) === null || _this$outputFormat6 === void 0 ? void 0 : _this$outputFormat6.sampleRate) || 'not set', ")"));
|
|
24632
|
-
|
|
24633
|
-
// Close old shared context if sample rate changed
|
|
24634
|
-
if (_sharedPlayerContext && _sharedPlayerContext.state !== 'closed') {
|
|
24635
|
-
console.log('🔄 AudioPlayer: Closing old shared AudioContext (sample rate changed)');
|
|
24636
|
-
try {
|
|
24637
|
-
_sharedPlayerContext.close();
|
|
24638
|
-
} catch (e) {
|
|
24639
|
-
console.warn('⚠️ AudioPlayer: Error closing old shared context:', e);
|
|
24640
|
-
}
|
|
24641
|
-
}
|
|
24642
|
-
try {
|
|
24643
|
-
this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
|
|
24644
|
-
sampleRate: desiredSampleRate,
|
|
24645
|
-
latencyHint: 'playback'
|
|
24646
|
-
});
|
|
24647
|
-
|
|
24648
|
-
// Store as shared context
|
|
24649
|
-
_sharedPlayerContext = this.audioContext;
|
|
24650
|
-
_sharedPlayerSampleRate = desiredSampleRate;
|
|
24651
|
-
console.log("\u2705 AudioContext created at ".concat(this.audioContext.sampleRate, "Hz (requested: ").concat(desiredSampleRate, "Hz)"));
|
|
24652
|
-
if (Math.abs(this.audioContext.sampleRate - desiredSampleRate) > 100) {
|
|
24653
|
-
console.error("\u274C CRITICAL: Browser sample rate mismatch!");
|
|
24654
|
-
console.error(" Requested: ".concat(desiredSampleRate, "Hz"));
|
|
24655
|
-
console.error(" Got: ".concat(this.audioContext.sampleRate, "Hz"));
|
|
24656
|
-
console.error(" This WILL cause audio distortion/noise!");
|
|
24657
|
-
console.error(" Solution: Backend should send ".concat(this.audioContext.sampleRate, "Hz audio instead"));
|
|
24658
|
-
} else if (this.audioContext.sampleRate !== desiredSampleRate) {
|
|
24659
|
-
console.warn("\u26A0\uFE0F Browser adjusted sample rate: ".concat(desiredSampleRate, "Hz \u2192 ").concat(this.audioContext.sampleRate, "Hz"));
|
|
24660
|
-
console.warn(" Browser will automatically resample audio.");
|
|
24661
|
-
}
|
|
24662
|
-
this.setupAudioContextStateMonitoring();
|
|
24663
|
-
this.gainNode = this.audioContext.createGain();
|
|
24664
|
-
this.gainNode.gain.value = 1.0;
|
|
24665
|
-
this.gainNode.connect(this.audioContext.destination);
|
|
24666
|
-
console.log('✅ AudioPlayer: GainNode created for volume control');
|
|
24667
|
-
this._primeAudioContext();
|
|
24668
|
-
} catch (error) {
|
|
24669
|
-
console.error("\u274C Failed to create AudioContext:", error);
|
|
24670
|
-
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
24671
|
-
_sharedPlayerContext = this.audioContext;
|
|
24672
|
-
_sharedPlayerSampleRate = this.audioContext.sampleRate;
|
|
24673
|
-
console.log("\u2139\uFE0F Using browser default: ".concat(this.audioContext.sampleRate, "Hz"));
|
|
24674
|
-
this.setupAudioContextStateMonitoring();
|
|
24675
|
-
this.gainNode = this.audioContext.createGain();
|
|
24676
|
-
this.gainNode.gain.value = 1.0;
|
|
24677
|
-
this.gainNode.connect(this.audioContext.destination);
|
|
24678
|
-
console.log('✅ AudioPlayer: GainNode created for volume control (fallback)');
|
|
24679
|
-
this._primeAudioContext();
|
|
24680
|
-
}
|
|
24681
|
-
}
|
|
24682
|
-
|
|
24683
25786
|
/**
|
|
24684
25787
|
* Prime AudioContext with silent buffer to initialize Android hardware
|
|
24685
25788
|
* This prevents the first audio frame from being cut off on Android devices
|
|
24686
25789
|
*/
|
|
25790
|
+
)
|
|
24687
25791
|
}, {
|
|
24688
25792
|
key: "_primeAudioContext",
|
|
24689
25793
|
value: (function () {
|
|
24690
|
-
var _primeAudioContext2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
24691
|
-
var sampleRate, silentBuffer, source,
|
|
24692
|
-
return _regenerator().w(function (
|
|
24693
|
-
while (1) switch (
|
|
25794
|
+
var _primeAudioContext2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
|
|
25795
|
+
var sampleRate, silentBuffer, source, _t5;
|
|
25796
|
+
return _regenerator().w(function (_context9) {
|
|
25797
|
+
while (1) switch (_context9.p = _context9.n) {
|
|
24694
25798
|
case 0:
|
|
24695
|
-
|
|
25799
|
+
_context9.p = 0;
|
|
24696
25800
|
if (!(!this.audioContext || this._audioContextPrimed)) {
|
|
24697
|
-
|
|
25801
|
+
_context9.n = 1;
|
|
24698
25802
|
break;
|
|
24699
25803
|
}
|
|
24700
|
-
return
|
|
25804
|
+
return _context9.a(2);
|
|
24701
25805
|
case 1:
|
|
24702
25806
|
sampleRate = this.audioContext.sampleRate;
|
|
24703
25807
|
silentBuffer = this.audioContext.createBuffer(1, sampleRate * 0.15, sampleRate);
|
|
@@ -24705,23 +25809,23 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24705
25809
|
source.buffer = silentBuffer;
|
|
24706
25810
|
source.connect(this.audioContext.destination);
|
|
24707
25811
|
source.start();
|
|
24708
|
-
|
|
25812
|
+
_context9.n = 2;
|
|
24709
25813
|
return new Promise(function (resolve) {
|
|
24710
25814
|
return setTimeout(resolve, 150);
|
|
24711
25815
|
});
|
|
24712
25816
|
case 2:
|
|
24713
25817
|
this._audioContextPrimed = true;
|
|
24714
25818
|
console.log('✅ AudioPlayer: Audio hardware primed with silent buffer');
|
|
24715
|
-
|
|
25819
|
+
_context9.n = 4;
|
|
24716
25820
|
break;
|
|
24717
25821
|
case 3:
|
|
24718
|
-
|
|
24719
|
-
|
|
24720
|
-
console.error('❌ AudioPlayer: Audio priming failed:',
|
|
25822
|
+
_context9.p = 3;
|
|
25823
|
+
_t5 = _context9.v;
|
|
25824
|
+
console.error('❌ AudioPlayer: Audio priming failed:', _t5);
|
|
24721
25825
|
case 4:
|
|
24722
|
-
return
|
|
25826
|
+
return _context9.a(2);
|
|
24723
25827
|
}
|
|
24724
|
-
},
|
|
25828
|
+
}, _callee8, this, [[0, 3]]);
|
|
24725
25829
|
}));
|
|
24726
25830
|
function _primeAudioContext() {
|
|
24727
25831
|
return _primeAudioContext2.apply(this, arguments);
|
|
@@ -24846,55 +25950,48 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24846
25950
|
}, {
|
|
24847
25951
|
key: "processQueue",
|
|
24848
25952
|
value: (function () {
|
|
24849
|
-
var _processQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
25953
|
+
var _processQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
|
|
24850
25954
|
var _this9 = this;
|
|
24851
|
-
var audioBlob, wasFirstPlay, audioContext, arrayBuffer, audioBuffer, shouldEmitStart, source,
|
|
24852
|
-
return _regenerator().w(function (
|
|
24853
|
-
while (1) switch (
|
|
25955
|
+
var audioBlob, wasFirstPlay, audioContext, arrayBuffer, audioBuffer, shouldEmitStart, source, _t6;
|
|
25956
|
+
return _regenerator().w(function (_context0) {
|
|
25957
|
+
while (1) switch (_context0.p = _context0.n) {
|
|
24854
25958
|
case 0:
|
|
24855
25959
|
if (!this._isStopped) {
|
|
24856
|
-
|
|
25960
|
+
_context0.n = 1;
|
|
24857
25961
|
break;
|
|
24858
25962
|
}
|
|
24859
25963
|
console.log('🛑 AudioPlayer: Not processing queue - playback was stopped (barge-in)');
|
|
24860
|
-
return
|
|
25964
|
+
return _context0.a(2);
|
|
24861
25965
|
case 1:
|
|
24862
25966
|
if (!(this.isProcessingQueue || this.audioQueue.length === 0)) {
|
|
24863
|
-
|
|
25967
|
+
_context0.n = 2;
|
|
24864
25968
|
break;
|
|
24865
25969
|
}
|
|
24866
|
-
return
|
|
25970
|
+
return _context0.a(2);
|
|
24867
25971
|
case 2:
|
|
24868
25972
|
this.isProcessingQueue = true;
|
|
24869
25973
|
audioBlob = this.audioQueue.shift();
|
|
24870
25974
|
if (audioBlob) {
|
|
24871
|
-
|
|
25975
|
+
_context0.n = 3;
|
|
24872
25976
|
break;
|
|
24873
25977
|
}
|
|
24874
25978
|
this.isProcessingQueue = false;
|
|
24875
|
-
return
|
|
25979
|
+
return _context0.a(2);
|
|
24876
25980
|
case 3:
|
|
24877
|
-
|
|
24878
|
-
wasFirstPlay = !this.isPlaying && this.currentSource === null; //
|
|
24879
|
-
|
|
24880
|
-
this.initializeAudioContext();
|
|
24881
|
-
}
|
|
24882
|
-
|
|
24883
|
-
// ✅ NEW: Wait for AudioContext to be in 'running' state before proceeding
|
|
24884
|
-
// This is critical for mobile devices where AudioContext initialization takes time
|
|
24885
|
-
// Replaces fixed timeouts with event-driven waiting
|
|
24886
|
-
_context7.n = 4;
|
|
25981
|
+
_context0.p = 3;
|
|
25982
|
+
wasFirstPlay = !this.isPlaying && this.currentSource === null; // Ensure context exists and is running (handles lazy init + suspended shared context)
|
|
25983
|
+
_context0.n = 4;
|
|
24887
25984
|
return this.waitForAudioContextReady();
|
|
24888
25985
|
case 4:
|
|
24889
25986
|
audioContext = this.audioContext; // Decode audio
|
|
24890
|
-
|
|
25987
|
+
_context0.n = 5;
|
|
24891
25988
|
return audioBlob.arrayBuffer();
|
|
24892
25989
|
case 5:
|
|
24893
|
-
arrayBuffer =
|
|
24894
|
-
|
|
25990
|
+
arrayBuffer = _context0.v;
|
|
25991
|
+
_context0.n = 6;
|
|
24895
25992
|
return audioContext.decodeAudioData(arrayBuffer);
|
|
24896
25993
|
case 6:
|
|
24897
|
-
audioBuffer =
|
|
25994
|
+
audioBuffer = _context0.v;
|
|
24898
25995
|
shouldEmitStart = wasFirstPlay && !this.isPlaying && this.currentSource === null; // Create source
|
|
24899
25996
|
source = audioContext.createBufferSource();
|
|
24900
25997
|
source.buffer = audioBuffer;
|
|
@@ -24937,14 +26034,14 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24937
26034
|
// Start playback - AudioContext should now be fully ready
|
|
24938
26035
|
|
|
24939
26036
|
source.start();
|
|
24940
|
-
|
|
26037
|
+
_context0.n = 8;
|
|
24941
26038
|
break;
|
|
24942
26039
|
case 7:
|
|
24943
|
-
|
|
24944
|
-
|
|
24945
|
-
console.error('❌ AudioPlayer v2: Error processing queue:',
|
|
26040
|
+
_context0.p = 7;
|
|
26041
|
+
_t6 = _context0.v;
|
|
26042
|
+
console.error('❌ AudioPlayer v2: Error processing queue:', _t6);
|
|
24946
26043
|
this.currentSource = null;
|
|
24947
|
-
this.emit('playbackError',
|
|
26044
|
+
this.emit('playbackError', _t6);
|
|
24948
26045
|
|
|
24949
26046
|
// Try next chunk
|
|
24950
26047
|
|
|
@@ -24959,9 +26056,9 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24959
26056
|
this.emit('playbackStopped');
|
|
24960
26057
|
}
|
|
24961
26058
|
case 8:
|
|
24962
|
-
return
|
|
26059
|
+
return _context0.a(2);
|
|
24963
26060
|
}
|
|
24964
|
-
},
|
|
26061
|
+
}, _callee9, this, [[3, 7]]);
|
|
24965
26062
|
}));
|
|
24966
26063
|
function processQueue() {
|
|
24967
26064
|
return _processQueue.apply(this, arguments);
|
|
@@ -25241,39 +26338,43 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
25241
26338
|
}, {
|
|
25242
26339
|
key: "resumeAudioContext",
|
|
25243
26340
|
value: (function () {
|
|
25244
|
-
var _resumeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
25245
|
-
var
|
|
25246
|
-
return _regenerator().w(function (
|
|
25247
|
-
while (1) switch (
|
|
26341
|
+
var _resumeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
|
|
26342
|
+
var _t7;
|
|
26343
|
+
return _regenerator().w(function (_context1) {
|
|
26344
|
+
while (1) switch (_context1.p = _context1.n) {
|
|
25248
26345
|
case 0:
|
|
25249
26346
|
if (!(!this.audioContext || this.audioContext.state === 'closed')) {
|
|
25250
|
-
|
|
26347
|
+
_context1.n = 2;
|
|
25251
26348
|
break;
|
|
25252
26349
|
}
|
|
25253
|
-
|
|
26350
|
+
_context1.n = 1;
|
|
25254
26351
|
return this.initializeAudioContext();
|
|
25255
26352
|
case 1:
|
|
25256
|
-
|
|
26353
|
+
_context1.n = 6;
|
|
26354
|
+
break;
|
|
25257
26355
|
case 2:
|
|
25258
26356
|
if (!(this.audioContext.state === 'suspended')) {
|
|
25259
|
-
|
|
26357
|
+
_context1.n = 6;
|
|
25260
26358
|
break;
|
|
25261
26359
|
}
|
|
25262
|
-
|
|
25263
|
-
|
|
26360
|
+
_context1.p = 3;
|
|
26361
|
+
_context1.n = 4;
|
|
25264
26362
|
return this.audioContext.resume();
|
|
25265
26363
|
case 4:
|
|
25266
26364
|
console.log('✅ AudioPlayer v2: AudioContext resumed after mic permission');
|
|
25267
|
-
|
|
26365
|
+
_context1.n = 6;
|
|
25268
26366
|
break;
|
|
25269
26367
|
case 5:
|
|
25270
|
-
|
|
25271
|
-
|
|
25272
|
-
console.warn('⚠️ AudioPlayer v2: Failed to resume AudioContext:',
|
|
26368
|
+
_context1.p = 5;
|
|
26369
|
+
_t7 = _context1.v;
|
|
26370
|
+
console.warn('⚠️ AudioPlayer v2: Failed to resume AudioContext:', _t7);
|
|
25273
26371
|
case 6:
|
|
25274
|
-
|
|
26372
|
+
_context1.n = 7;
|
|
26373
|
+
return this.waitForAudioContextReady();
|
|
26374
|
+
case 7:
|
|
26375
|
+
return _context1.a(2);
|
|
25275
26376
|
}
|
|
25276
|
-
},
|
|
26377
|
+
}, _callee0, this, [[3, 5]]);
|
|
25277
26378
|
}));
|
|
25278
26379
|
function resumeAudioContext() {
|
|
25279
26380
|
return _resumeAudioContext.apply(this, arguments);
|
|
@@ -25343,6 +26444,8 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
25343
26444
|
}
|
|
25344
26445
|
_sharedPlayerContext = null;
|
|
25345
26446
|
_sharedPlayerSampleRate = null;
|
|
26447
|
+
// Note: live AudioPlayer instances may still hold a reference to the now-closed
|
|
26448
|
+
// context until their next waitForAudioContextReady() / initializeAudioContext().
|
|
25346
26449
|
}
|
|
25347
26450
|
}]);
|
|
25348
26451
|
}(_shared_EventEmitter_js__WEBPACK_IMPORTED_MODULE_0__["default"]);
|
|
@@ -25366,8 +26469,9 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
25366
26469
|
/* harmony import */ var _core_AudioRecorder_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/AudioRecorder.js */ "./src/core/AudioRecorder.js");
|
|
25367
26470
|
/* harmony import */ var _utils_AudioFormatConverter_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./utils/AudioFormatConverter.js */ "./src/v2/utils/AudioFormatConverter.js");
|
|
25368
26471
|
/* harmony import */ var _core_ClientToolsRegistry_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../core/ClientToolsRegistry.js */ "./src/core/ClientToolsRegistry.js");
|
|
25369
|
-
/* harmony import */ var
|
|
25370
|
-
/* harmony import */ var
|
|
26472
|
+
/* harmony import */ var _core_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../core/ClientScriptManager.js */ "./src/core/ClientScriptManager.js");
|
|
26473
|
+
/* harmony import */ var _utils_screenshot_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../utils/screenshot.js */ "./src/utils/screenshot.js");
|
|
26474
|
+
/* harmony import */ var _utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../utils/visual-tools.js */ "./src/utils/visual-tools.js");
|
|
25371
26475
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
25372
26476
|
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
25373
26477
|
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
@@ -25401,6 +26505,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
|
|
|
25401
26505
|
|
|
25402
26506
|
|
|
25403
26507
|
|
|
26508
|
+
|
|
25404
26509
|
/**
|
|
25405
26510
|
|
|
25406
26511
|
* VoiceSDK v2 - Multi-codec speech-to-speech SDK
|
|
@@ -25578,6 +26683,18 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
25578
26683
|
return _this.conversationId;
|
|
25579
26684
|
}, config.sharedToolHandlers || null);
|
|
25580
26685
|
|
|
26686
|
+
// Flavor-independent client-tool script manager (reserved partner id
|
|
26687
|
+
// `__client_tools__`). Owns its own PartnerScriptRunner so client-tool
|
|
26688
|
+
// scripts run regardless of flavor, alongside (and separate from) the
|
|
26689
|
+
// ecommerce flavor's partner runner. Sends result/emit frames over the
|
|
26690
|
+
// voice WebSocket.
|
|
26691
|
+
_this.clientScriptManager = new _core_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_5__.ClientScriptManager({
|
|
26692
|
+
send: function send(message) {
|
|
26693
|
+
return _this.sendMessage(message);
|
|
26694
|
+
},
|
|
26695
|
+
label: 'voice'
|
|
26696
|
+
});
|
|
26697
|
+
|
|
25581
26698
|
// ===== REGISTER BUILT-IN SDK TOOLS =====
|
|
25582
26699
|
// These tools work automatically - users don't need to implement them
|
|
25583
26700
|
// Backend controls availability via agent configuration
|
|
@@ -25603,7 +26720,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
25603
26720
|
key: "_registerBuiltInTools",
|
|
25604
26721
|
value: function _registerBuiltInTools() {
|
|
25605
26722
|
try {
|
|
25606
|
-
(0,
|
|
26723
|
+
(0,_utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_7__.registerVisualTools)(this.clientToolsRegistry);
|
|
25607
26724
|
} catch (error) {
|
|
25608
26725
|
console.error('❌ VoiceSDK: Error registering built-in tools:', error);
|
|
25609
26726
|
console.error(' Error details:', error.message, error.stack);
|
|
@@ -26362,7 +27479,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
26362
27479
|
|
|
26363
27480
|
// Include SDK build time for debugging
|
|
26364
27481
|
if (true) {
|
|
26365
|
-
helloMessage.lastBuildTime = "2026-
|
|
27482
|
+
helloMessage.lastBuildTime = "2026-06-12T20:23:48.560Z";
|
|
26366
27483
|
}
|
|
26367
27484
|
|
|
26368
27485
|
// Page context is intentionally NOT attached to the hello message.
|
|
@@ -26551,6 +27668,16 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
26551
27668
|
// envelope which the backend dispatcher routes to the callback.
|
|
26552
27669
|
this._handleRunPartnerScript(message);
|
|
26553
27670
|
break;
|
|
27671
|
+
case 'partner_bundle':
|
|
27672
|
+
// Flavor-independent client-tool bundle (partner_id `__client_tools__`)
|
|
27673
|
+
// is loaded by the dedicated ClientScriptManager. Every other partner
|
|
27674
|
+
// id falls through to the flavor (via the emitted 'message' event), so
|
|
27675
|
+
// the ecommerce flavor's own bundle handling keeps working untouched.
|
|
27676
|
+
if (_core_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_5__.ClientScriptManager.isClientToolsBundle(message)) {
|
|
27677
|
+
this.clientScriptManager.setBundle(message);
|
|
27678
|
+
}
|
|
27679
|
+
this.emit('message', message);
|
|
27680
|
+
break;
|
|
26554
27681
|
case 'text_inject_ack':
|
|
26555
27682
|
this.handleTextInjectAck(message);
|
|
26556
27683
|
break;
|
|
@@ -26573,7 +27700,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
26573
27700
|
case 'get_page_context':
|
|
26574
27701
|
// Handle get_page_context request from backend
|
|
26575
27702
|
console.log('📖 [VISUAL ASSISTANT] Backend requested page context');
|
|
26576
|
-
(0,
|
|
27703
|
+
(0,_utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_7__.extractPageContext)().then(function (pageContext) {
|
|
26577
27704
|
// Use existing DOM scanner
|
|
26578
27705
|
_this6.sendMessage({
|
|
26579
27706
|
t: 'page_context',
|
|
@@ -26594,7 +27721,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
26594
27721
|
console.log('📸 [VISUAL ASSISTANT] Backend requested screenshot');
|
|
26595
27722
|
try {
|
|
26596
27723
|
// Use existing screenshot capture function
|
|
26597
|
-
(0,
|
|
27724
|
+
(0,_utils_screenshot_js__WEBPACK_IMPORTED_MODULE_6__.captureScreenshot)().then(function (screenshot) {
|
|
26598
27725
|
_this6.sendMessage({
|
|
26599
27726
|
t: 'screenshot',
|
|
26600
27727
|
screenshot: {
|
|
@@ -27074,9 +28201,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27074
28201
|
console.warn('⚠️ VoiceSDK v2: run_partner_script missing requestId; dropping', message);
|
|
27075
28202
|
return _context3.a(2);
|
|
27076
28203
|
case 1:
|
|
28204
|
+
if (!_core_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_5__.ClientScriptManager.isClientToolsScript(message)) {
|
|
28205
|
+
_context3.n = 2;
|
|
28206
|
+
break;
|
|
28207
|
+
}
|
|
28208
|
+
return _context3.a(2, this.clientScriptManager.handleRunPartnerScript(message));
|
|
28209
|
+
case 2:
|
|
27077
28210
|
handler = (_this$clientToolsRegi = this.clientToolsRegistry) === null || _this$clientToolsRegi === void 0 || (_this$clientToolsRegi = _this$clientToolsRegi.handlers) === null || _this$clientToolsRegi === void 0 ? void 0 : _this$clientToolsRegi.get('run_partner_script');
|
|
27078
28211
|
if (handler) {
|
|
27079
|
-
_context3.n =
|
|
28212
|
+
_context3.n = 3;
|
|
27080
28213
|
break;
|
|
27081
28214
|
}
|
|
27082
28215
|
console.error("\uD83E\uDDE9 VoiceSDK v2: no run_partner_script handler registered (ecommerce flavor not loaded?); requestId=".concat(requestId, " partner=").concat(partnerId, " action=").concat(action));
|
|
@@ -27089,16 +28222,16 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27089
28222
|
error: 'no_handler_registered'
|
|
27090
28223
|
});
|
|
27091
28224
|
return _context3.a(2);
|
|
27092
|
-
case
|
|
28225
|
+
case 3:
|
|
27093
28226
|
startedAt = Date.now();
|
|
27094
28227
|
console.log("\uD83E\uDDE9 VoiceSDK v2: run_partner_script dispatch partner=".concat(partnerId, " action=").concat(action, " requestId=").concat(requestId));
|
|
27095
|
-
_context3.p =
|
|
27096
|
-
_context3.n =
|
|
28228
|
+
_context3.p = 4;
|
|
28229
|
+
_context3.n = 5;
|
|
27097
28230
|
return handler({
|
|
27098
28231
|
action: action,
|
|
27099
28232
|
args: args
|
|
27100
28233
|
});
|
|
27101
|
-
case
|
|
28234
|
+
case 5:
|
|
27102
28235
|
result = _context3.v;
|
|
27103
28236
|
elapsedMs = Date.now() - startedAt; // Adapters return { ok, ... } themselves. Honor that flag if present;
|
|
27104
28237
|
// otherwise treat presence-of-result as success.
|
|
@@ -27126,10 +28259,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27126
28259
|
ok: ok,
|
|
27127
28260
|
result: result
|
|
27128
28261
|
});
|
|
27129
|
-
_context3.n =
|
|
28262
|
+
_context3.n = 7;
|
|
27130
28263
|
break;
|
|
27131
|
-
case
|
|
27132
|
-
_context3.p =
|
|
28264
|
+
case 6:
|
|
28265
|
+
_context3.p = 6;
|
|
27133
28266
|
_t2 = _context3.v;
|
|
27134
28267
|
_elapsedMs = Date.now() - startedAt;
|
|
27135
28268
|
console.error("\uD83E\uDDE9 VoiceSDK v2: run_partner_script threw partner=".concat(partnerId, " action=").concat(action, " requestId=").concat(requestId, " (").concat(_elapsedMs, "ms):"), _t2);
|
|
@@ -27141,10 +28274,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27141
28274
|
ok: false,
|
|
27142
28275
|
error: _t2 && _t2.message || String(_t2)
|
|
27143
28276
|
});
|
|
27144
|
-
case
|
|
28277
|
+
case 7:
|
|
27145
28278
|
return _context3.a(2);
|
|
27146
28279
|
}
|
|
27147
|
-
}, _callee3, this, [[
|
|
28280
|
+
}, _callee3, this, [[4, 6]]);
|
|
27148
28281
|
}));
|
|
27149
28282
|
function _handleRunPartnerScript(_x) {
|
|
27150
28283
|
return _handleRunPartnerScript2.apply(this, arguments);
|
|
@@ -30668,6 +31801,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
30668
31801
|
/* harmony import */ var _TextInterface_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./TextInterface.js */ "./src/widget/TextInterface.js");
|
|
30669
31802
|
/* harmony import */ var _widget_translations_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./widget-translations.js */ "./src/widget/widget-translations.js");
|
|
30670
31803
|
/* harmony import */ var _flavors_index_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../flavors/index.js */ "./src/flavors/index.js");
|
|
31804
|
+
/* harmony import */ var _ecommerce_EcommerceVoiceStrip_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../ecommerce/EcommerceVoiceStrip.js */ "./src/ecommerce/EcommerceVoiceStrip.js");
|
|
30671
31805
|
var _excluded = ["agentName"];
|
|
30672
31806
|
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
|
|
30673
31807
|
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
@@ -30695,6 +31829,7 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
|
|
|
30695
31829
|
|
|
30696
31830
|
|
|
30697
31831
|
|
|
31832
|
+
|
|
30698
31833
|
/** Fallback for `buildWebSocketUrl()` when no optional `websocketUrl` override is set (VoiceSDK v2 uses the same default). */
|
|
30699
31834
|
var DEFAULT_VOICE_WEBSOCKET_URL = 'wss://speech.talktopc.com/ws/conv';
|
|
30700
31835
|
function isNonEmptyConfigString(v) {
|
|
@@ -30789,6 +31924,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
30789
31924
|
function TTPChatWidget() {
|
|
30790
31925
|
var _this$config$behavior,
|
|
30791
31926
|
_this$config$panel,
|
|
31927
|
+
_this$config$flavor,
|
|
30792
31928
|
_this$config$text,
|
|
30793
31929
|
_this$config$text2,
|
|
30794
31930
|
_this$config$voice,
|
|
@@ -30841,6 +31977,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
30841
31977
|
var showVoiceUi = widgetModeForVoice === 'unified' || widgetModeForVoice === 'voice-only';
|
|
30842
31978
|
var panelBgForBrand = ((_this$config$panel = this.config.panel) === null || _this$config$panel === void 0 ? void 0 : _this$config$panel.backgroundColor) || '#16161e';
|
|
30843
31979
|
var talkToPcLink = talkToPcLinkColorForPanel(computeIsLightPanel(panelBgForBrand), this.config.voice, this._rawConfig, showVoiceUi);
|
|
31980
|
+
var desktopMinimizedEcommerceStripUi = typeof window !== 'undefined' && window.innerWidth > 768 && ((_this$config$flavor = this.config.flavor) === null || _this$config$flavor === void 0 ? void 0 : _this$config$flavor.callView) === 'minimized';
|
|
30844
31981
|
var voiceConfig = _objectSpread(_objectSpread(_objectSpread({}, this.config), this.config.voice), {}, {
|
|
30845
31982
|
agentName: this.config.agentName,
|
|
30846
31983
|
_talkToPcLinkColor: talkToPcLink,
|
|
@@ -30864,8 +32001,24 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
30864
32001
|
outputSampleRate: ((_this$config$voice4 = this.config.voice) === null || _this$config$voice4 === void 0 ? void 0 : _this$config$voice4.outputSampleRate) || 24000,
|
|
30865
32002
|
outputChannels: ((_this$config$voice5 = this.config.voice) === null || _this$config$voice5 === void 0 ? void 0 : _this$config$voice5.outputChannels) || 1,
|
|
30866
32003
|
outputBitDepth: ((_this$config$voice6 = this.config.voice) === null || _this$config$voice6 === void 0 ? void 0 : _this$config$voice6.outputBitDepth) || 16,
|
|
30867
|
-
|
|
30868
|
-
|
|
32004
|
+
desktopMinimizedEcommerceStripUi: desktopMinimizedEcommerceStripUi,
|
|
32005
|
+
onDesktopMinimizedStripCallStart: desktopMinimizedEcommerceStripUi ? function () {
|
|
32006
|
+
var _this$_ecommerceVoice;
|
|
32007
|
+
(_this$_ecommerceVoice = _this._ecommerceVoiceStrip) === null || _this$_ecommerceVoice === void 0 || _this$_ecommerceVoice.show();
|
|
32008
|
+
if (_this.isOpen) _this.minimize();
|
|
32009
|
+
_this._setPanelFooterVisible(false);
|
|
32010
|
+
_this._setEcommerceStripLauncherVisible(false);
|
|
32011
|
+
} : undefined,
|
|
32012
|
+
onDesktopMinimizedStripLauncherRestore: desktopMinimizedEcommerceStripUi ? function () {
|
|
32013
|
+
return _this._restoreDesktopEcommerceStripChrome();
|
|
32014
|
+
} : undefined,
|
|
32015
|
+
// Pass event callbacks to voice interface (forward only; panel is not auto-minimized on call start)
|
|
32016
|
+
onConversationStart: function onConversationStart() {
|
|
32017
|
+
if (typeof _this.config.onConversationStart === 'function') {
|
|
32018
|
+
var _this$config;
|
|
32019
|
+
(_this$config = _this.config).onConversationStart.apply(_this$config, arguments);
|
|
32020
|
+
}
|
|
32021
|
+
},
|
|
30869
32022
|
onConversationEnd: this.config.onConversationEnd,
|
|
30870
32023
|
onBargeIn: this.config.onBargeIn,
|
|
30871
32024
|
onAudioStartPlaying: this.config.onAudioStartPlaying,
|
|
@@ -30933,7 +32086,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
30933
32086
|
},
|
|
30934
32087
|
onMobileError: function onMobileError(message) {
|
|
30935
32088
|
_this.showMobileLandingError(message);
|
|
30936
|
-
}
|
|
32089
|
+
},
|
|
32090
|
+
// Root layout direction — must be last so `...this.config.voice` cannot overwrite with `undefined`
|
|
32091
|
+
direction: this.getEffectiveTextDirection()
|
|
30937
32092
|
});
|
|
30938
32093
|
this.voiceInterface = new _VoiceInterface_js__WEBPACK_IMPORTED_MODULE_1__.VoiceInterface(voiceConfig);
|
|
30939
32094
|
|
|
@@ -30981,11 +32136,12 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
30981
32136
|
// Check for resume state and auto-resume if needed
|
|
30982
32137
|
this.checkAndHandleResume();
|
|
30983
32138
|
|
|
30984
|
-
// Initialize flavor module if configured
|
|
32139
|
+
// Initialize flavor module if configured (use merged config so flavor matches this.config)
|
|
30985
32140
|
this._flavor = null;
|
|
30986
|
-
this._flavorObj = config.flavor && _typeof(config.flavor) === 'object' ? config.flavor : null;
|
|
32141
|
+
this._flavorObj = this.config.flavor && _typeof(this.config.flavor) === 'object' ? this.config.flavor : null;
|
|
30987
32142
|
if (this._flavorObj && this._flavorObj.type) {
|
|
30988
32143
|
this._initFlavor(this._flavorObj.type);
|
|
32144
|
+
this._initEcommerceVoiceStrip();
|
|
30989
32145
|
} else if (this._flavorObj && !this._flavorObj.type) {
|
|
30990
32146
|
console.error('[TTPChatWidget] flavor object is set but missing required "type" field. Widget will run in vanilla mode.');
|
|
30991
32147
|
}
|
|
@@ -31147,8 +32303,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31147
32303
|
}, {
|
|
31148
32304
|
key: "getEffectiveTextDirection",
|
|
31149
32305
|
value: function getEffectiveTextDirection() {
|
|
31150
|
-
var _this$
|
|
31151
|
-
return ((_this$
|
|
32306
|
+
var _this$config2;
|
|
32307
|
+
return ((_this$config2 = this.config) === null || _this$config2 === void 0 ? void 0 : _this$config2.direction) === 'rtl' ? 'rtl' : 'ltr';
|
|
31152
32308
|
}
|
|
31153
32309
|
|
|
31154
32310
|
/**
|
|
@@ -31200,6 +32356,52 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31200
32356
|
this._doTogglePanel();
|
|
31201
32357
|
}
|
|
31202
32358
|
|
|
32359
|
+
/**
|
|
32360
|
+
* DOM root that contains the launcher (`#text-chat-button-container`), matching createWidget.
|
|
32361
|
+
* @returns {ShadowRoot|Document}
|
|
32362
|
+
*/
|
|
32363
|
+
}, {
|
|
32364
|
+
key: "_launcherDomRoot",
|
|
32365
|
+
value: function _launcherDomRoot() {
|
|
32366
|
+
if (this.config.useShadowDOM !== false && this.shadowRoot && this.shadowRoot !== document) {
|
|
32367
|
+
return this.shadowRoot;
|
|
32368
|
+
}
|
|
32369
|
+
return document;
|
|
32370
|
+
}
|
|
32371
|
+
|
|
32372
|
+
/**
|
|
32373
|
+
* Show or hide the floating launcher for desktop callView:minimized (bottom strip mode).
|
|
32374
|
+
* Widget CSS uses `display: flex !important` on the container — hide with `none !important`.
|
|
32375
|
+
* @param {boolean} visible
|
|
32376
|
+
*/
|
|
32377
|
+
}, {
|
|
32378
|
+
key: "_setEcommerceStripLauncherVisible",
|
|
32379
|
+
value: function _setEcommerceStripLauncherVisible(visible) {
|
|
32380
|
+
var _this$config$behavior4;
|
|
32381
|
+
if ((_this$config$behavior4 = this.config.behavior) !== null && _this$config$behavior4 !== void 0 && _this$config$behavior4.hidden) return;
|
|
32382
|
+
var root = this._launcherDomRoot();
|
|
32383
|
+
var container = root.getElementById('text-chat-button-container');
|
|
32384
|
+
var btn = root.getElementById('text-chat-button');
|
|
32385
|
+
if (visible) {
|
|
32386
|
+
if (container) container.style.removeProperty('display');
|
|
32387
|
+
if (btn) btn.style.removeProperty('display');
|
|
32388
|
+
} else if (container) {
|
|
32389
|
+
container.style.setProperty('display', 'none', 'important');
|
|
32390
|
+
} else if (btn) {
|
|
32391
|
+
btn.style.setProperty('display', 'none', 'important');
|
|
32392
|
+
}
|
|
32393
|
+
}
|
|
32394
|
+
|
|
32395
|
+
/** Hide bottom strip and restore floating launcher (desktop minimized call UI). */
|
|
32396
|
+
}, {
|
|
32397
|
+
key: "_restoreDesktopEcommerceStripChrome",
|
|
32398
|
+
value: function _restoreDesktopEcommerceStripChrome() {
|
|
32399
|
+
var _this$_ecommerceVoice2;
|
|
32400
|
+
(_this$_ecommerceVoice2 = this._ecommerceVoiceStrip) === null || _this$_ecommerceVoice2 === void 0 || _this$_ecommerceVoice2.hide();
|
|
32401
|
+
this._setPanelFooterVisible(true);
|
|
32402
|
+
this._setEcommerceStripLauncherVisible(true);
|
|
32403
|
+
}
|
|
32404
|
+
|
|
31203
32405
|
/**
|
|
31204
32406
|
* Wire the universal widget-control message listener on both the
|
|
31205
32407
|
* text-chat SDK and the voice SDK. Two channels, one set of message types
|
|
@@ -31269,7 +32471,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31269
32471
|
}, {
|
|
31270
32472
|
key: "mergeWithDefaults",
|
|
31271
32473
|
value: function mergeWithDefaults(userConfig) {
|
|
31272
|
-
var _userConfig$button, _userConfig$button2, _userConfig$icon, _userConfig$icon2, _userConfig$icon3, _userConfig$icon4, _userConfig$icon5, _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$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;
|
|
32474
|
+
var _userConfig$button, _userConfig$button2, _userConfig$icon, _userConfig$icon2, _userConfig$icon3, _userConfig$icon4, _userConfig$icon5, _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$footer, _userConfig$footer2, _userConfig$footer3, _userConfig$footer4, _userConfig$footer5, _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;
|
|
31273
32475
|
// Handle legacy position string format
|
|
31274
32476
|
var positionConfig = userConfig.position || 'bottom-right';
|
|
31275
32477
|
if (typeof positionConfig === 'string') {
|
|
@@ -31535,7 +32737,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31535
32737
|
// Default true
|
|
31536
32738
|
backgroundColor: ((_userConfig$footer2 = userConfig.footer) === null || _userConfig$footer2 === void 0 ? void 0 : _userConfig$footer2.backgroundColor) || '#f9fafb',
|
|
31537
32739
|
textColor: ((_userConfig$footer3 = userConfig.footer) === null || _userConfig$footer3 === void 0 ? void 0 : _userConfig$footer3.textColor) || '#9ca3af',
|
|
31538
|
-
hoverColor: ((_userConfig$footer4 = userConfig.footer) === null || _userConfig$footer4 === void 0 ? void 0 : _userConfig$footer4.hoverColor) || '#7C3AED'
|
|
32740
|
+
hoverColor: ((_userConfig$footer4 = userConfig.footer) === null || _userConfig$footer4 === void 0 ? void 0 : _userConfig$footer4.hoverColor) || '#7C3AED',
|
|
32741
|
+
// Brand shown in "Powered by" footer. 'talktopc' (default) or 'speacart'.
|
|
32742
|
+
brand: ((_userConfig$footer5 = userConfig.footer) === null || _userConfig$footer5 === void 0 ? void 0 : _userConfig$footer5.brand) === 'speacart' ? 'speacart' : 'talktopc'
|
|
31539
32743
|
}, userConfig.footer),
|
|
31540
32744
|
// Tooltips Configuration
|
|
31541
32745
|
tooltips: _objectSpread({
|
|
@@ -31767,8 +32971,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31767
32971
|
|
|
31768
32972
|
// On desktop, ensure voice hero is shown for unified/voice-only (in-panel mode picker removed)
|
|
31769
32973
|
if (window.innerWidth > 768) {
|
|
31770
|
-
var _this$config$
|
|
31771
|
-
var widgetMode = ((_this$config$
|
|
32974
|
+
var _this$config$behavior5;
|
|
32975
|
+
var widgetMode = ((_this$config$behavior5 = this.config.behavior) === null || _this$config$behavior5 === void 0 ? void 0 : _this$config$behavior5.mode) || 'unified';
|
|
31772
32976
|
var preferVoice = widgetMode === 'unified' || widgetMode === 'voice-only';
|
|
31773
32977
|
if (preferVoice && this.currentView !== 'text') {
|
|
31774
32978
|
this.showVoice();
|
|
@@ -31866,7 +33070,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31866
33070
|
}, {
|
|
31867
33071
|
key: "generateWidgetHTML",
|
|
31868
33072
|
value: function generateWidgetHTML() {
|
|
31869
|
-
var _this$config$
|
|
33073
|
+
var _this$config$behavior6,
|
|
31870
33074
|
_this7 = this;
|
|
31871
33075
|
var pos = this.config.position;
|
|
31872
33076
|
var btn = this.config.button;
|
|
@@ -31895,7 +33099,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31895
33099
|
|
|
31896
33100
|
// Generate position styles
|
|
31897
33101
|
var positionStyles = this.generatePositionStyles();
|
|
31898
|
-
var widgetMode = ((_this$config$
|
|
33102
|
+
var widgetMode = ((_this$config$behavior6 = this.config.behavior) === null || _this$config$behavior6 === void 0 ? void 0 : _this$config$behavior6.mode) || 'unified';
|
|
31899
33103
|
var showVoice = widgetMode === 'unified' || widgetMode === 'voice-only';
|
|
31900
33104
|
var showText = widgetMode === 'unified' || widgetMode === 'text-only';
|
|
31901
33105
|
var voiceEnabled = showVoice;
|
|
@@ -31947,7 +33151,24 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31947
33151
|
var pillWaveformBars = '<span class="bar"></span><span class="bar"></span><span class="bar"></span><span class="bar"></span><span class="bar"></span>';
|
|
31948
33152
|
var pillIconInner = pillCustomImage ? "<img class=\"ttp-launcher-logo\" src=\"".concat(pillCustomImage, "\" alt=\"\" />") : "<div class=\"ttp-pill-waveform\" aria-hidden=\"true\">".concat(pillWaveformBars, "</div>");
|
|
31949
33153
|
var pillIconWrapClass = pillCustomImage ? 'ttp-pill-icon' : 'ttp-pill-icon ttp-pill-icon--wave';
|
|
31950
|
-
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 <!-- Desktop pill launcher (hidden on mobile) -->\n <button type=\"button\" id=\"text-chat-button\" aria-label=\"Open chat with ").concat(pillTitle, "\">\n <div class=\"").concat(pillIconWrapClass, "\">\n ").concat(pillIconInner, "\n </div>\n <div class=\"ttp-pill-text\">\n <span class=\"ttp-pill-title\">").concat(pillTitle, "</span>\n <span class=\"ttp-pill-status\">\n <span class=\"ttp-launcher-dot\"></span>\n ").concat(header.onlineIndicatorText !== undefined && header.onlineIndicatorText !== null ? header.onlineIndicatorText : t('online'), "\n </span>\n </div>\n </button>\n <!-- Mobile FAB (hidden on desktop) -->\n <button class=\"ttp-mobile-fab\" aria-label=\"").concat(this.config.accessibility.ariaLabel, "\">\n <div class=\"ttp-mobile-fab__icon-wrap\">\n <div class=\"ttp-mobile-fab__waveform\">\n <span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span>\n </div>\n </div>\n <div class=\"ttp-mobile-fab__label-wrap\">\n <span class=\"ttp-mobile-fab__label\">").concat(mobileFabLabel, "</span>\n <span class=\"ttp-mobile-fab__status\">\n <span class=\"ttp-mobile-fab__dot\"></span>\n ").concat(header.onlineIndicatorText !== undefined && header.onlineIndicatorText !== null ? header.onlineIndicatorText : t('online'), "\n </span>\n </div>\n </button>\n </div>\n "), "\n\n ").concat(this.config.behavior.hidden ? '' : "\n <div id=\"ttpMobileLanding\" class=\"ttp-mobile-landing\">\n <div class=\"ttp-mobile-landing__header\">\n <div class=\"ttp-mobile-landing__avatar\">\n <div class=\"ttp-mobile-landing__waveform\">\n <span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span>\n </div>\n </div>\n <div class=\"ttp-mobile-landing__info\">\n <div class=\"ttp-mobile-landing__name\">".concat(mobileLandingHeroName, "</div>\n <div class=\"ttp-mobile-landing__status\">\n <span class=\"ttp-mobile-landing__dot\"></span>\n ").concat(this.config.landing.statusText || t('online') + ' • ' + (this.config.landing.subtitle || 'Ready to help'), "\n </div>\n </div>\n <button class=\"ttp-mobile-landing__close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n <div class=\"ttp-mobile-landing__error\" id=\"ttpMobileLandingError\" style=\"display:none;\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/>\n </svg>\n <span class=\"ttp-mobile-landing__error-text\"></span>\n </div>\n <div class=\"ttp-mobile-landing__actions\">\n ").concat(showVoice ? "\n <button class=\"ttp-mobile-landing__btn ttp-mobile-landing__btn--call\" id=\"ttpMobileCallBtn\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z\"/>\n </svg>\n ".concat(this.config.landing.callButtonText || this.config.landing.voiceCardTitle || t('voiceCall'), "\n </button>\n ") : '', "\n ").concat(showText ? "\n <button class=\"ttp-mobile-landing__btn ttp-mobile-landing__btn--text\" id=\"ttpMobileChatBtn\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/>\n </svg>\n ".concat(this.config.landing.chatButtonText || this.config.landing.textCardTitle || t('textChat'), "\n </button>\n ") : '', "\n </div>\n ").concat(this.config.footer.show !== false ? "\n <div class=\"ttp-mobile-landing__footer\">\n <div class=\"ttp-mobile-landing__powered\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"10\" height=\"10\">\n <polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/>\n </svg>\n Powered by <a href=\"https://talktopc.com\" target=\"_blank\" rel=\"noopener\">TalkToPC</a>\n </div>\n </div>\n " : '', "\n </div>\n "), "\n \n <div id=\"text-chat-panel\">\n <div class=\"widget-shell\">\n <div class=\"panel-inner widget-container\" style=\"direction: ").concat(this.config.direction, ";\">\n\n ").concat(showVoice ? this.voiceInterface.generateHTML() : '', "\n ").concat(showText ? this.textInterface.generateHTML() : '', "\n ").concat(this.generateFooterHTML(), "\n </div>\n </div>\n </div>\n ");
|
|
33154
|
+
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 <!-- Desktop pill launcher (hidden on mobile) -->\n <button type=\"button\" id=\"text-chat-button\" aria-label=\"Open chat with ").concat(pillTitle, "\">\n <div class=\"").concat(pillIconWrapClass, "\">\n ").concat(pillIconInner, "\n </div>\n <div class=\"ttp-pill-text\">\n <span class=\"ttp-pill-title\">").concat(pillTitle, "</span>\n <span class=\"ttp-pill-status\">\n <span class=\"ttp-launcher-dot\"></span>\n ").concat(header.onlineIndicatorText !== undefined && header.onlineIndicatorText !== null ? header.onlineIndicatorText : t('online'), "\n </span>\n </div>\n </button>\n <!-- Mobile FAB (hidden on desktop) -->\n <button class=\"ttp-mobile-fab\" aria-label=\"").concat(this.config.accessibility.ariaLabel, "\">\n <div class=\"ttp-mobile-fab__icon-wrap\">\n <div class=\"ttp-mobile-fab__waveform\">\n <span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span>\n </div>\n </div>\n <div class=\"ttp-mobile-fab__label-wrap\">\n <span class=\"ttp-mobile-fab__label\">").concat(mobileFabLabel, "</span>\n <span class=\"ttp-mobile-fab__status\">\n <span class=\"ttp-mobile-fab__dot\"></span>\n ").concat(header.onlineIndicatorText !== undefined && header.onlineIndicatorText !== null ? header.onlineIndicatorText : t('online'), "\n </span>\n </div>\n </button>\n </div>\n "), "\n\n ").concat(this.config.behavior.hidden ? '' : "\n <div id=\"ttpMobileLanding\" class=\"ttp-mobile-landing\">\n <div class=\"ttp-mobile-landing__header\">\n <div class=\"ttp-mobile-landing__avatar\">\n <div class=\"ttp-mobile-landing__waveform\">\n <span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span><span class=\"bar\"></span>\n </div>\n </div>\n <div class=\"ttp-mobile-landing__info\">\n <div class=\"ttp-mobile-landing__name\">".concat(mobileLandingHeroName, "</div>\n <div class=\"ttp-mobile-landing__status\">\n <span class=\"ttp-mobile-landing__dot\"></span>\n ").concat(this.config.landing.statusText || t('online') + ' • ' + (this.config.landing.subtitle || 'Ready to help'), "\n </div>\n </div>\n <button class=\"ttp-mobile-landing__close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n <div class=\"ttp-mobile-landing__error\" id=\"ttpMobileLandingError\" style=\"display:none;\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/>\n </svg>\n <span class=\"ttp-mobile-landing__error-text\"></span>\n </div>\n <div class=\"ttp-mobile-landing__actions\">\n ").concat(showVoice ? "\n <button class=\"ttp-mobile-landing__btn ttp-mobile-landing__btn--call\" id=\"ttpMobileCallBtn\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z\"/>\n </svg>\n ".concat(this.config.landing.callButtonText || this.config.landing.voiceCardTitle || t('voiceCall'), "\n </button>\n ") : '', "\n ").concat(showText ? "\n <button class=\"ttp-mobile-landing__btn ttp-mobile-landing__btn--text\" id=\"ttpMobileChatBtn\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/>\n </svg>\n ".concat(this.config.landing.chatButtonText || this.config.landing.textCardTitle || t('textChat'), "\n </button>\n ") : '', "\n </div>\n ").concat(this.config.footer.show !== false ? "\n <div class=\"ttp-mobile-landing__footer\">\n <div class=\"ttp-mobile-landing__powered\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"10\" height=\"10\">\n <polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/>\n </svg>\n Powered by <a href=\"".concat(this.config.footer.brand === 'speacart' ? 'https://speacart.com' : 'https://talktopc.com', "\" target=\"_blank\" rel=\"noopener\">").concat(this.config.footer.brand === 'speacart' ? 'SpeaCart' : 'TalkToPC', "</a>\n </div>\n </div>\n ") : '', "\n </div>\n "), "\n \n <div id=\"text-chat-panel\">\n <div class=\"widget-shell\">\n <div class=\"panel-inner widget-container\" style=\"direction: ").concat(this.config.direction, ";\">\n\n ").concat(showVoice ? this.voiceInterface.generateHTML() : '', "\n ").concat(showText ? this.textInterface.generateHTML() : '', "\n ").concat(this.generateFooterHTML(), "\n </div>\n </div>\n </div>\n ");
|
|
33155
|
+
}
|
|
33156
|
+
|
|
33157
|
+
/**
|
|
33158
|
+
* Resolved footer branding for panel + desktop voice strip.
|
|
33159
|
+
* @returns {{ brandUrl: string, brandName: string } | null}
|
|
33160
|
+
*/
|
|
33161
|
+
}, {
|
|
33162
|
+
key: "getFooterBranding",
|
|
33163
|
+
value: function getFooterBranding() {
|
|
33164
|
+
var footer = this.config.footer;
|
|
33165
|
+
if (!(footer !== null && footer !== void 0 && footer.show)) return null;
|
|
33166
|
+
var refSource = encodeURIComponent(window.location.hostname || 'widget');
|
|
33167
|
+
var isSpeacart = footer.brand === 'speacart';
|
|
33168
|
+
return {
|
|
33169
|
+
brandUrl: isSpeacart ? 'https://speacart.com' : "https://talktopc.com?ref=widget&source=".concat(refSource),
|
|
33170
|
+
brandName: isSpeacart ? 'SpeaCart' : 'TalkToPC'
|
|
33171
|
+
};
|
|
31951
33172
|
}
|
|
31952
33173
|
|
|
31953
33174
|
/**
|
|
@@ -31956,11 +33177,24 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31956
33177
|
}, {
|
|
31957
33178
|
key: "generateFooterHTML",
|
|
31958
33179
|
value: function generateFooterHTML() {
|
|
31959
|
-
var
|
|
31960
|
-
if (!
|
|
31961
|
-
var
|
|
31962
|
-
|
|
31963
|
-
return "\n <div class=\"widget-footer ttp-footer\">\n <span>Powered by <a href=\"".concat(brandUrl, "\" target=\"_blank\" rel=\"noopener noreferrer\"><b>
|
|
33180
|
+
var branding = this.getFooterBranding();
|
|
33181
|
+
if (!branding) return '';
|
|
33182
|
+
var brandUrl = branding.brandUrl,
|
|
33183
|
+
brandName = branding.brandName;
|
|
33184
|
+
return "\n <div class=\"widget-footer ttp-footer\">\n <span>Powered by <a href=\"".concat(brandUrl, "\" target=\"_blank\" rel=\"noopener noreferrer\"><b>").concat(brandName, "</b></a></span>\n </div>\n ");
|
|
33185
|
+
}
|
|
33186
|
+
|
|
33187
|
+
/**
|
|
33188
|
+
* Hide in-panel footer while the desktop bottom strip owns the active call UI.
|
|
33189
|
+
* @param {boolean} visible
|
|
33190
|
+
*/
|
|
33191
|
+
}, {
|
|
33192
|
+
key: "_setPanelFooterVisible",
|
|
33193
|
+
value: function _setPanelFooterVisible(visible) {
|
|
33194
|
+
var _root$querySelector;
|
|
33195
|
+
var root = this.shadowRoot || document;
|
|
33196
|
+
var footer = (_root$querySelector = root.querySelector) === null || _root$querySelector === void 0 ? void 0 : _root$querySelector.call(root, '.ttp-footer');
|
|
33197
|
+
if (footer) footer.style.display = visible ? '' : 'none';
|
|
31964
33198
|
}
|
|
31965
33199
|
}, {
|
|
31966
33200
|
key: "generatePositionStyles",
|
|
@@ -32056,7 +33290,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
32056
33290
|
}, {
|
|
32057
33291
|
key: "generateCSS",
|
|
32058
33292
|
value: function generateCSS(positionStyles, buttonSize, iconSize) {
|
|
32059
|
-
var _this$config$
|
|
33293
|
+
var _this$config$behavior7, _this$config$text3, _this$config$position, _this$config$position2, _this$config$position3, _this$config$position4;
|
|
32060
33294
|
var btn = this.config.button;
|
|
32061
33295
|
var icon = this.config.icon;
|
|
32062
33296
|
var panel = this.config.panel;
|
|
@@ -32065,7 +33299,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
32065
33299
|
var anim = this.config.animation;
|
|
32066
33300
|
|
|
32067
33301
|
// Determine which interfaces to show
|
|
32068
|
-
var widgetMode = ((_this$config$
|
|
33302
|
+
var widgetMode = ((_this$config$behavior7 = this.config.behavior) === null || _this$config$behavior7 === void 0 ? void 0 : _this$config$behavior7.mode) || 'unified';
|
|
32069
33303
|
var showVoice = widgetMode === 'unified' || widgetMode === 'voice-only';
|
|
32070
33304
|
var showText = widgetMode === 'unified' || widgetMode === 'text-only';
|
|
32071
33305
|
|
|
@@ -32099,7 +33333,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
32099
33333
|
key: "setupWidgetEvents",
|
|
32100
33334
|
value: function setupWidgetEvents() {
|
|
32101
33335
|
var _this8 = this,
|
|
32102
|
-
_this$config$
|
|
33336
|
+
_this$config$behavior8;
|
|
32103
33337
|
if (!this.shadowRoot) return;
|
|
32104
33338
|
|
|
32105
33339
|
// Setup prompt animation visibility and interactions
|
|
@@ -32169,7 +33403,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
32169
33403
|
}
|
|
32170
33404
|
|
|
32171
33405
|
// Voice selection events (based on widget mode)
|
|
32172
|
-
var widgetMode = ((_this$config$
|
|
33406
|
+
var widgetMode = ((_this$config$behavior8 = this.config.behavior) === null || _this$config$behavior8 === void 0 ? void 0 : _this$config$behavior8.mode) || 'unified';
|
|
32173
33407
|
var isUnified = widgetMode === 'unified';
|
|
32174
33408
|
var showVoice = widgetMode === 'unified' || widgetMode === 'voice-only';
|
|
32175
33409
|
var showText = widgetMode === 'unified' || widgetMode === 'text-only';
|
|
@@ -32956,8 +34190,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
32956
34190
|
}, {
|
|
32957
34191
|
key: "updateConfig",
|
|
32958
34192
|
value: function updateConfig(newConfig) {
|
|
32959
|
-
var _this$
|
|
32960
|
-
_this$
|
|
34193
|
+
var _this$config3,
|
|
34194
|
+
_this$config4,
|
|
34195
|
+
_this$config$flavor2,
|
|
32961
34196
|
_this$config$voice8,
|
|
32962
34197
|
_this$config$voice9,
|
|
32963
34198
|
_this$config$voice0,
|
|
@@ -33081,9 +34316,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33081
34316
|
});
|
|
33082
34317
|
|
|
33083
34318
|
// Store current language before merge to detect changes
|
|
33084
|
-
var oldLanguage = ((_this$
|
|
34319
|
+
var oldLanguage = ((_this$config3 = this.config) === null || _this$config3 === void 0 ? void 0 : _this$config3.language) || 'en';
|
|
33085
34320
|
// Store current useShadowDOM setting
|
|
33086
|
-
var oldUseShadowDOM = ((_this$
|
|
34321
|
+
var oldUseShadowDOM = ((_this$config4 = this.config) === null || _this$config4 === void 0 ? void 0 : _this$config4.useShadowDOM) !== false;
|
|
33087
34322
|
this.config = this.mergeWithDefaults(mergedConfig);
|
|
33088
34323
|
|
|
33089
34324
|
// Check if useShadowDOM changed - if so, need to recreate container
|
|
@@ -33161,6 +34396,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33161
34396
|
// Recreate interfaces with new config (after mergeWithDefaults, so language is current)
|
|
33162
34397
|
// Ensure language is correctly passed - use the merged config value
|
|
33163
34398
|
var currentLanguage = this.config.language || 'en';
|
|
34399
|
+
var desktopMinimizedEcommerceStripUi = typeof window !== 'undefined' && window.innerWidth > 768 && ((_this$config$flavor2 = this.config.flavor) === null || _this$config$flavor2 === void 0 ? void 0 : _this$config$flavor2.callView) === 'minimized';
|
|
33164
34400
|
var voiceConfig = _objectSpread(_objectSpread(_objectSpread({}, this.config), this.config.voice), {}, {
|
|
33165
34401
|
agentName: this.config.agentName,
|
|
33166
34402
|
language: currentLanguage,
|
|
@@ -33178,8 +34414,24 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33178
34414
|
outputSampleRate: ((_this$config$voice0 = this.config.voice) === null || _this$config$voice0 === void 0 ? void 0 : _this$config$voice0.outputSampleRate) || 24000,
|
|
33179
34415
|
outputChannels: ((_this$config$voice1 = this.config.voice) === null || _this$config$voice1 === void 0 ? void 0 : _this$config$voice1.outputChannels) || 1,
|
|
33180
34416
|
outputBitDepth: ((_this$config$voice10 = this.config.voice) === null || _this$config$voice10 === void 0 ? void 0 : _this$config$voice10.outputBitDepth) || 16,
|
|
33181
|
-
|
|
33182
|
-
|
|
34417
|
+
desktopMinimizedEcommerceStripUi: desktopMinimizedEcommerceStripUi,
|
|
34418
|
+
onDesktopMinimizedStripCallStart: desktopMinimizedEcommerceStripUi ? function () {
|
|
34419
|
+
var _this13$_ecommerceVoi;
|
|
34420
|
+
(_this13$_ecommerceVoi = _this13._ecommerceVoiceStrip) === null || _this13$_ecommerceVoi === void 0 || _this13$_ecommerceVoi.show();
|
|
34421
|
+
if (_this13.isOpen) _this13.minimize();
|
|
34422
|
+
_this13._setPanelFooterVisible(false);
|
|
34423
|
+
_this13._setEcommerceStripLauncherVisible(false);
|
|
34424
|
+
} : undefined,
|
|
34425
|
+
onDesktopMinimizedStripLauncherRestore: desktopMinimizedEcommerceStripUi ? function () {
|
|
34426
|
+
return _this13._restoreDesktopEcommerceStripChrome();
|
|
34427
|
+
} : undefined,
|
|
34428
|
+
// Pass event callbacks to voice interface (forward only; panel is not auto-minimized on call start)
|
|
34429
|
+
onConversationStart: function onConversationStart() {
|
|
34430
|
+
if (typeof _this13.config.onConversationStart === 'function') {
|
|
34431
|
+
var _this13$config;
|
|
34432
|
+
(_this13$config = _this13.config).onConversationStart.apply(_this13$config, arguments);
|
|
34433
|
+
}
|
|
34434
|
+
},
|
|
33183
34435
|
onConversationEnd: this.config.onConversationEnd,
|
|
33184
34436
|
onBargeIn: this.config.onBargeIn,
|
|
33185
34437
|
onAudioStartPlaying: this.config.onAudioStartPlaying,
|
|
@@ -33229,7 +34481,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33229
34481
|
} else {
|
|
33230
34482
|
_this13.showLanding();
|
|
33231
34483
|
}
|
|
33232
|
-
}
|
|
34484
|
+
},
|
|
34485
|
+
direction: this.getEffectiveTextDirection()
|
|
33233
34486
|
});
|
|
33234
34487
|
this.voiceInterface = new _VoiceInterface_js__WEBPACK_IMPORTED_MODULE_1__.VoiceInterface(voiceConfig);
|
|
33235
34488
|
|
|
@@ -33252,6 +34505,136 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33252
34505
|
if (this.textInterface && this.textInterface.updateInputAttributes) {
|
|
33253
34506
|
this.textInterface.updateInputAttributes();
|
|
33254
34507
|
}
|
|
34508
|
+
|
|
34509
|
+
// Recreated VoiceInterface = new AgentSDK; re-wire ecommerce bottom strip to onConversationStart / transcript.
|
|
34510
|
+
this._flavorObj = this.config.flavor && _typeof(this.config.flavor) === 'object' ? this.config.flavor : null;
|
|
34511
|
+
if (this._ecommerceVoiceStrip) {
|
|
34512
|
+
this._ecommerceVoiceStrip.destroy();
|
|
34513
|
+
this._ecommerceVoiceStrip = null;
|
|
34514
|
+
}
|
|
34515
|
+
this._initEcommerceVoiceStrip();
|
|
34516
|
+
}
|
|
34517
|
+
|
|
34518
|
+
/**
|
|
34519
|
+
* Initialize the bottom-strip voice UI for desktop + flavor.callView:"minimized".
|
|
34520
|
+
*
|
|
34521
|
+
* Activates when flavor.callView === "minimized" and Desktop (window.innerWidth > 768).
|
|
34522
|
+
*
|
|
34523
|
+
* The strip renders as a fixed bar at the bottom of the page and replaces the
|
|
34524
|
+
* in-panel voice active state during a call. Landing and idle views are unchanged.
|
|
34525
|
+
*/
|
|
34526
|
+
}, {
|
|
34527
|
+
key: "_initEcommerceVoiceStrip",
|
|
34528
|
+
value: function _initEcommerceVoiceStrip() {
|
|
34529
|
+
var _this14 = this,
|
|
34530
|
+
_this$voiceInterface6;
|
|
34531
|
+
var flavor = this.config.flavor && _typeof(this.config.flavor) === 'object' ? this.config.flavor : null;
|
|
34532
|
+
if (window.innerWidth <= 768) return;
|
|
34533
|
+
if (!flavor || flavor.callView !== 'minimized') return;
|
|
34534
|
+
if (this._ecommerceVoiceStrip) {
|
|
34535
|
+
this._ecommerceVoiceStrip.destroy();
|
|
34536
|
+
this._ecommerceVoiceStrip = null;
|
|
34537
|
+
}
|
|
34538
|
+
var v = this.config.voice || {};
|
|
34539
|
+
var strip;
|
|
34540
|
+
strip = new _ecommerce_EcommerceVoiceStrip_js__WEBPACK_IMPORTED_MODULE_5__.EcommerceVoiceStrip({
|
|
34541
|
+
language: this.config.language || 'en',
|
|
34542
|
+
direction: this.getEffectiveTextDirection(),
|
|
34543
|
+
footer: this.getFooterBranding(),
|
|
34544
|
+
theme: {
|
|
34545
|
+
pillGradient: v.pillGradient || 'linear-gradient(135deg, #581c87, #312e81, #1e1b4b)',
|
|
34546
|
+
pillTextColor: v.pillTextColor || '#ffffff',
|
|
34547
|
+
pillDotColor: v.pillDotColor || '#4ade80',
|
|
34548
|
+
endCallButtonColor: v.endCallButtonColor || '#ef4444'
|
|
34549
|
+
},
|
|
34550
|
+
getVoiceUiState: function getVoiceUiState() {
|
|
34551
|
+
var _this14$voiceInterfac, _this14$voiceInterfac2, _this14$voiceInterfac3;
|
|
34552
|
+
return {
|
|
34553
|
+
isMicMuted: !!((_this14$voiceInterfac = _this14.voiceInterface) !== null && _this14$voiceInterfac !== void 0 && _this14$voiceInterfac.isMicMuted),
|
|
34554
|
+
isPaused: !!((_this14$voiceInterfac2 = _this14.voiceInterface) !== null && _this14$voiceInterfac2 !== void 0 && _this14$voiceInterfac2.isPaused),
|
|
34555
|
+
isSpeakerMuted: !!((_this14$voiceInterfac3 = _this14.voiceInterface) !== null && _this14$voiceInterfac3 !== void 0 && _this14$voiceInterfac3.isSpeakerMuted)
|
|
34556
|
+
};
|
|
34557
|
+
},
|
|
34558
|
+
onHangup: function onHangup() {
|
|
34559
|
+
if (_this14.voiceInterface) _this14.voiceInterface.endVoiceCall();
|
|
34560
|
+
},
|
|
34561
|
+
onToggleMute: function onToggleMute() {
|
|
34562
|
+
if (_this14.voiceInterface) _this14.voiceInterface.toggleMute();
|
|
34563
|
+
strip.syncFromVoice();
|
|
34564
|
+
},
|
|
34565
|
+
onTogglePause: function onTogglePause() {
|
|
34566
|
+
if (_this14.voiceInterface) _this14.voiceInterface.togglePause();
|
|
34567
|
+
strip.syncFromVoice();
|
|
34568
|
+
},
|
|
34569
|
+
onToggleSpeaker: function onToggleSpeaker() {
|
|
34570
|
+
if (_this14.voiceInterface) _this14.voiceInterface.toggleSpeaker();
|
|
34571
|
+
strip.syncFromVoice();
|
|
34572
|
+
},
|
|
34573
|
+
onSendText: function onSendText(text) {
|
|
34574
|
+
var _this14$voiceInterfac4;
|
|
34575
|
+
var voiceSDK = (_this14$voiceInterfac4 = _this14.voiceInterface) === null || _this14$voiceInterfac4 === void 0 || (_this14$voiceInterfac4 = _this14$voiceInterfac4.sdk) === null || _this14$voiceInterfac4 === void 0 ? void 0 : _this14$voiceInterfac4.voiceSDK;
|
|
34576
|
+
if (voiceSDK && typeof voiceSDK.sendMessage === 'function') {
|
|
34577
|
+
voiceSDK.sendMessage({
|
|
34578
|
+
t: 'text_inject',
|
|
34579
|
+
text: text
|
|
34580
|
+
});
|
|
34581
|
+
}
|
|
34582
|
+
}
|
|
34583
|
+
});
|
|
34584
|
+
this._ecommerceVoiceStrip = strip;
|
|
34585
|
+
|
|
34586
|
+
// Show strip when conversation starts
|
|
34587
|
+
var agentSDK = (_this$voiceInterface6 = this.voiceInterface) === null || _this$voiceInterface6 === void 0 ? void 0 : _this$voiceInterface6.sdk;
|
|
34588
|
+
if (agentSDK) {
|
|
34589
|
+
var origConvStart = agentSDK.onConversationStart;
|
|
34590
|
+
agentSDK.onConversationStart = function () {
|
|
34591
|
+
strip.show({
|
|
34592
|
+
reset: false
|
|
34593
|
+
});
|
|
34594
|
+
strip.syncFromVoice();
|
|
34595
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
34596
|
+
args[_key] = arguments[_key];
|
|
34597
|
+
}
|
|
34598
|
+
if (typeof origConvStart === 'function') origConvStart.call.apply(origConvStart, [agentSDK].concat(args));
|
|
34599
|
+
};
|
|
34600
|
+
|
|
34601
|
+
// Feed transcript updates into the strip
|
|
34602
|
+
var origOnTranscript = agentSDK.onTranscript;
|
|
34603
|
+
agentSDK.onTranscript = function (text) {
|
|
34604
|
+
strip.updateTranscript(text);
|
|
34605
|
+
if (typeof origOnTranscript === 'function') origOnTranscript.call(agentSDK, text);
|
|
34606
|
+
};
|
|
34607
|
+
var origOnUserTranscript = agentSDK.onUserTranscript;
|
|
34608
|
+
agentSDK.onUserTranscript = function (utext, isFinal) {
|
|
34609
|
+
try {
|
|
34610
|
+
strip.updateUserTranscript(utext, isFinal);
|
|
34611
|
+
} catch (e) {
|
|
34612
|
+
console.warn('[TTPChatWidget] ecommerce strip user transcript:', e);
|
|
34613
|
+
}
|
|
34614
|
+
if (typeof origOnUserTranscript === 'function') {
|
|
34615
|
+
origOnUserTranscript.call(agentSDK, utext, isFinal);
|
|
34616
|
+
}
|
|
34617
|
+
};
|
|
34618
|
+
}
|
|
34619
|
+
|
|
34620
|
+
// Hide strip when call ends (config.onCallEnd is called by endVoiceCall())
|
|
34621
|
+
if (this.voiceInterface) {
|
|
34622
|
+
var _this$voiceInterface$2;
|
|
34623
|
+
var origOnCallEnd = this.voiceInterface.config.onCallEnd;
|
|
34624
|
+
this.voiceInterface.config.onCallEnd = function () {
|
|
34625
|
+
_this14._restoreDesktopEcommerceStripChrome();
|
|
34626
|
+
if (typeof origOnCallEnd === 'function') origOnCallEnd();
|
|
34627
|
+
};
|
|
34628
|
+
|
|
34629
|
+
// Feed timer ticks into the strip
|
|
34630
|
+
var origUpdateTimer = (_this$voiceInterface$2 = this.voiceInterface._updateTimerElements) === null || _this$voiceInterface$2 === void 0 ? void 0 : _this$voiceInterface$2.bind(this.voiceInterface);
|
|
34631
|
+
if (origUpdateTimer) {
|
|
34632
|
+
this.voiceInterface._updateTimerElements = function (timerText, elapsed) {
|
|
34633
|
+
strip.updateTimer(timerText);
|
|
34634
|
+
origUpdateTimer(timerText, elapsed);
|
|
34635
|
+
};
|
|
34636
|
+
}
|
|
34637
|
+
}
|
|
33255
34638
|
}
|
|
33256
34639
|
}, {
|
|
33257
34640
|
key: "destroy",
|
|
@@ -33289,6 +34672,11 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33289
34672
|
this._flavor.destroy();
|
|
33290
34673
|
this._flavor = null;
|
|
33291
34674
|
}
|
|
34675
|
+
// Clean up ecommerce voice strip
|
|
34676
|
+
if (this._ecommerceVoiceStrip) {
|
|
34677
|
+
this._ecommerceVoiceStrip.destroy();
|
|
34678
|
+
this._ecommerceVoiceStrip = null;
|
|
34679
|
+
}
|
|
33292
34680
|
}
|
|
33293
34681
|
|
|
33294
34682
|
// Delegated to VoiceInterface
|
|
@@ -33358,7 +34746,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33358
34746
|
key: "injectData",
|
|
33359
34747
|
value: (function () {
|
|
33360
34748
|
var _injectData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(data) {
|
|
33361
|
-
var _this$
|
|
34749
|
+
var _this$voiceInterface7;
|
|
33362
34750
|
var options,
|
|
33363
34751
|
voiceSDK,
|
|
33364
34752
|
ack,
|
|
@@ -33383,7 +34771,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33383
34771
|
throw new Error('Voice call is not active');
|
|
33384
34772
|
case 2:
|
|
33385
34773
|
// Check if SDK is available
|
|
33386
|
-
voiceSDK = (_this$
|
|
34774
|
+
voiceSDK = (_this$voiceInterface7 = this.voiceInterface) === null || _this$voiceInterface7 === void 0 ? void 0 : _this$voiceInterface7.getVoiceSDK();
|
|
33387
34775
|
if (voiceSDK) {
|
|
33388
34776
|
_context6.n = 3;
|
|
33389
34777
|
break;
|
|
@@ -34330,6 +35718,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
34330
35718
|
/* harmony import */ var _voice_handlers_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./voice/handlers.js */ "./src/widget/voice/handlers.js");
|
|
34331
35719
|
/* harmony import */ var _voice_desktop_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./voice/desktop.js */ "./src/widget/voice/desktop.js");
|
|
34332
35720
|
/* harmony import */ var _voice_mobile_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./voice/mobile.js */ "./src/widget/voice/mobile.js");
|
|
35721
|
+
/* harmony import */ var _voice_rtlText_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./voice/rtlText.js */ "./src/widget/voice/rtlText.js");
|
|
34333
35722
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
34334
35723
|
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
34335
35724
|
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
@@ -34358,6 +35747,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
|
|
|
34358
35747
|
|
|
34359
35748
|
|
|
34360
35749
|
|
|
35750
|
+
|
|
34361
35751
|
var VoiceInterface = /*#__PURE__*/function () {
|
|
34362
35752
|
function VoiceInterface(config) {
|
|
34363
35753
|
var _this$config$behavior, _config$agentSettings;
|
|
@@ -34772,13 +36162,20 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
34772
36162
|
return this.getText('userTranscriptPrefix', 'userTranscriptPrefix');
|
|
34773
36163
|
}
|
|
34774
36164
|
|
|
36165
|
+
/** Assistant / user live transcript and bubbles: RTL for `direction: 'rtl'` or Hebrew/Arabic `language`. */
|
|
36166
|
+
}, {
|
|
36167
|
+
key: "prefersRtlText",
|
|
36168
|
+
value: function prefersRtlText() {
|
|
36169
|
+
return (0,_voice_rtlText_js__WEBPACK_IMPORTED_MODULE_12__.configPrefersRtlText)(this.config);
|
|
36170
|
+
}
|
|
36171
|
+
|
|
34775
36172
|
/**
|
|
34776
36173
|
* Live user STT line: correct RTL/LTR order, visible gap after "אתה:", and sensible ? placement (utterance in dir=auto).
|
|
34777
36174
|
*/
|
|
34778
36175
|
}, {
|
|
34779
36176
|
key: "_renderUserLiveTranscriptSnippet",
|
|
34780
36177
|
value: function _renderUserLiveTranscriptSnippet(userPrefix, text) {
|
|
34781
|
-
var isRtl = this.
|
|
36178
|
+
var isRtl = this.prefersRtlText();
|
|
34782
36179
|
var raw = userPrefix !== undefined && userPrefix !== null ? String(userPrefix) : '';
|
|
34783
36180
|
var trimmedPrefix = raw.replace(/\s+$/, '');
|
|
34784
36181
|
var sepChar = isRtl ? "\xA0" : ' ';
|
|
@@ -35115,7 +36512,7 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
35115
36512
|
value: function () {
|
|
35116
36513
|
var _proceedWithVoiceCall = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
|
|
35117
36514
|
var _this3 = this;
|
|
35118
|
-
var isResumeCall, panel, header, toggleText, originalSection, compactSection, idleState, _idleState, activeState, voiceInterface, connected, serverRejected, originalOnError, originalOnDisconnected, attempts, _vs, vs, hasDisclaimers, sendDisclaimerAckAfterMobileMicReady, _vs$lastDisclaimerPay, disclaimerTexts, _this$config$inputFor, _this$config$connecti, micStream, primeSr, floatingButton, mobileFab, delayConfig, streamToUse, _this$sdk$voiceSDK, error, existingBar, _existingBar, _idleState2, _floatingButton, updateTimer, isDisclaimerDeclined, deviceInfo, _t, _t2, _t3, _t4, _t5;
|
|
36515
|
+
var isResumeCall, panel, header, toggleText, originalSection, compactSection, idleState, _idleState, activeState, stripOnly, voiceInterface, connected, serverRejected, originalOnError, originalOnDisconnected, attempts, _vs, vs, hasDisclaimers, sendDisclaimerAckAfterMobileMicReady, _vs$lastDisclaimerPay, disclaimerTexts, _this$config$inputFor, _this$config$connecti, micStream, primeSr, floatingButton, mobileFab, delayConfig, streamToUse, _this$sdk$voiceSDK, error, existingBar, _existingBar, _idleState2, _floatingButton, updateTimer, isDisclaimerDeclined, deviceInfo, _t, _t2, _t3, _t4, _t5;
|
|
35119
36516
|
return _regenerator().w(function (_context4) {
|
|
35120
36517
|
while (1) switch (_context4.p = _context4.n) {
|
|
35121
36518
|
case 0:
|
|
@@ -35163,12 +36560,22 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
35163
36560
|
this.showMobileCallBarAwaitingBrowserMic();
|
|
35164
36561
|
this.applyMobileBarConnectingCopy();
|
|
35165
36562
|
} else {
|
|
35166
|
-
// Desktop:
|
|
36563
|
+
// Desktop: full in-panel active UI, except flavor.callView:minimized (bottom strip only).
|
|
35167
36564
|
this.resetConnectingState();
|
|
35168
36565
|
_idleState = this.shadowRoot.getElementById('voiceIdleState');
|
|
35169
36566
|
activeState = this.shadowRoot.getElementById('voiceActiveState');
|
|
35170
36567
|
if (_idleState) _idleState.style.display = 'none';
|
|
35171
|
-
|
|
36568
|
+
stripOnly = this.config.desktopMinimizedEcommerceStripUi === true;
|
|
36569
|
+
if (activeState) {
|
|
36570
|
+
activeState.style.display = stripOnly ? 'none' : 'flex';
|
|
36571
|
+
}
|
|
36572
|
+
if (stripOnly && typeof this.config.onDesktopMinimizedStripCallStart === 'function') {
|
|
36573
|
+
try {
|
|
36574
|
+
this.config.onDesktopMinimizedStripCallStart();
|
|
36575
|
+
} catch (e) {
|
|
36576
|
+
console.warn('[VoiceInterface] onDesktopMinimizedStripCallStart:', e);
|
|
36577
|
+
}
|
|
36578
|
+
}
|
|
35172
36579
|
}
|
|
35173
36580
|
voiceInterface = this.shadowRoot.getElementById('voiceInterface'); // Adjust sizes based on container dimensions
|
|
35174
36581
|
setTimeout(function () {
|
|
@@ -35463,6 +36870,10 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
35463
36870
|
this._clearChatPanelDisplayLock();
|
|
35464
36871
|
_floatingButton = this.shadowRoot.getElementById('text-chat-button') || document.getElementById('text-chat-button');
|
|
35465
36872
|
if (_floatingButton) _floatingButton.style.display = '';
|
|
36873
|
+
} else if (this.config.desktopMinimizedEcommerceStripUi === true && typeof this.config.onDesktopMinimizedStripLauncherRestore === 'function') {
|
|
36874
|
+
try {
|
|
36875
|
+
this.config.onDesktopMinimizedStripLauncherRestore();
|
|
36876
|
+
} catch (_) {}
|
|
35466
36877
|
}
|
|
35467
36878
|
throw _t4;
|
|
35468
36879
|
case 29:
|
|
@@ -35627,6 +37038,13 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
35627
37038
|
var activeState = this.shadowRoot.getElementById('voiceActiveState');
|
|
35628
37039
|
if (idleState) idleState.style.display = 'flex';
|
|
35629
37040
|
if (activeState) activeState.style.display = 'none';
|
|
37041
|
+
if (typeof this.config.onDesktopMinimizedStripLauncherRestore === 'function') {
|
|
37042
|
+
try {
|
|
37043
|
+
this.config.onDesktopMinimizedStripLauncherRestore();
|
|
37044
|
+
} catch (e) {
|
|
37045
|
+
console.warn('[VoiceInterface] onDesktopMinimizedStripLauncherRestore:', e);
|
|
37046
|
+
}
|
|
37047
|
+
}
|
|
35630
37048
|
}
|
|
35631
37049
|
|
|
35632
37050
|
/**
|
|
@@ -38058,6 +39476,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
38058
39476
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
38059
39477
|
/* harmony export */ Mobile: () => (/* binding */ Mobile)
|
|
38060
39478
|
/* harmony export */ });
|
|
39479
|
+
/* harmony import */ var _rtlText_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./rtlText.js */ "./src/widget/voice/rtlText.js");
|
|
38061
39480
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
38062
39481
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
38063
39482
|
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
@@ -38081,6 +39500,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
|
|
|
38081
39500
|
* - _mobileCallStartTime: number | null
|
|
38082
39501
|
*/
|
|
38083
39502
|
|
|
39503
|
+
|
|
38084
39504
|
var Mobile = /*#__PURE__*/function () {
|
|
38085
39505
|
function Mobile(voiceInterface) {
|
|
38086
39506
|
_classCallCheck(this, Mobile);
|
|
@@ -38131,11 +39551,11 @@ var Mobile = /*#__PURE__*/function () {
|
|
|
38131
39551
|
// Create container div
|
|
38132
39552
|
var container = document.createElement('div');
|
|
38133
39553
|
container.id = 'mobile-voice-call-bar-container';
|
|
38134
|
-
var textDir = this.config
|
|
39554
|
+
var textDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.configPrefersRtlText)(this.config) ? 'rtl' : 'ltr';
|
|
38135
39555
|
container.setAttribute('dir', textDir);
|
|
38136
39556
|
container.innerHTML = "\n <!-- Duration Badge (hidden until first audio) -->\n <div class=\"mobile-duration-badge mobile-duration-badge--hidden\" id=\"mobileDurationBadge\">\n <div class=\"mobile-duration-dot\"></div>\n <span id=\"mobileDurationText\">00:00</span>\n </div>\n \n <!-- Main Bar -->\n <div class=\"mobile-voice-bar\">\n <!-- Top Row: Status, Waveform/Spinner, Controls -->\n <div class=\"mobile-top-row\">\n <!-- Status Indicator -->\n <div class=\"mobile-status-indicator\">\n <div class=\"mobile-status-dot\" id=\"mobileStatusDot\"></div>\n <span class=\"mobile-status-text\" id=\"mobileStatusText\">".concat(this.t('listening'), "</span>\n </div>\n \n <!-- Centered: Spinner (loading) or Waveform (after first audio) -->\n <div class=\"mobile-waveform-center\" id=\"mobileWaveform\">\n <div class=\"mobile-waveform-spinner\" id=\"mobileWaveformSpinner\" aria-hidden=\"false\">\n <div class=\"mobile-waveform-spinner-inner\"></div>\n </div>\n <div class=\"mobile-waveform-bars mobile-waveform-bars--hidden\" id=\"mobileWaveformBars\">\n ").concat(Array(7).fill(0).map(function (_, i) {
|
|
38137
39557
|
return "<div class=\"mobile-waveform-bar\" data-index=\"".concat(i, "\"></div>");
|
|
38138
|
-
}).join(''), "\n </div>\n </div>\n \n <!-- Controls -->\n <div class=\"mobile-controls\">\n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobileMuteBtn\" aria-label=\"Mute microphone\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-icon\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-off-icon\" style=\"display: none;\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n <line x1=\"4\" y1=\"4\" x2=\"20\" y2=\"20\" stroke=\"#ef4444\" stroke-width=\"1.5\" opacity=\"0.85\"/>\n </svg>\n </button>\n \n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobilePauseBtn\" aria-label=\"Pause call\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" class=\"mobile-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"#fff\" stroke=\"none\" class=\"mobile-play-icon\" style=\"display: none;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n \n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobileSpeakerBtn\" aria-label=\"Toggle speaker\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-off-icon\" style=\"display: none;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n <line x1=\"4\" y1=\"4\" x2=\"20\" y2=\"20\" stroke=\"#ef4444\" stroke-width=\"1.5\" opacity=\"0.85\"/>\n </svg>\n </button>\n \n <button type=\"button\" class=\"mobile-end-call-btn\" id=\"mobileEndCallBtn\" aria-label=\"End call\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.63A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91\"/>\n <line x1=\"22\" x2=\"2\" y1=\"2\" y2=\"22\"/>\n </svg>\n </button>\n </div>\n </div>\n \n <!-- Bottom Row: Transcript (expandable) -->\n <button type=\"button\" class=\"mobile-transcript-row\" id=\"mobileTranscriptRow\">\n <p class=\"mobile-transcript-text\" id=\"mobileTranscriptText\" dir=\"").concat(textDir, "\">").concat(this.t('transcriptWillAppear'), "</p>\n <div class=\"mobile-transcript-footer\">\n <span class=\"mobile-expand-hint\">tap to expand & type</span>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"opacity: 0.6;\">\n <path d=\"M7 17L17 7M17 7H7M17 7V17\"/>\n </svg>\n </div>\n </button>\n\n <!-- Fly-to-cart target: always visible for animation (positioned where cart icon appears) -->\n <div id=\"ttpFlyToCartTarget\" class=\"ttp-fly-target\" aria-hidden=\"true\"></div>\n
|
|
39558
|
+
}).join(''), "\n </div>\n </div>\n \n <!-- Controls -->\n <div class=\"mobile-controls\">\n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobileMuteBtn\" aria-label=\"Mute microphone\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-icon\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-off-icon\" style=\"display: none;\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n <line x1=\"4\" y1=\"4\" x2=\"20\" y2=\"20\" stroke=\"#ef4444\" stroke-width=\"1.5\" opacity=\"0.85\"/>\n </svg>\n </button>\n \n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobilePauseBtn\" aria-label=\"Pause call\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" class=\"mobile-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"#fff\" stroke=\"none\" class=\"mobile-play-icon\" style=\"display: none;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n \n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobileSpeakerBtn\" aria-label=\"Toggle speaker\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-off-icon\" style=\"display: none;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n <line x1=\"4\" y1=\"4\" x2=\"20\" y2=\"20\" stroke=\"#ef4444\" stroke-width=\"1.5\" opacity=\"0.85\"/>\n </svg>\n </button>\n \n <button type=\"button\" class=\"mobile-end-call-btn\" id=\"mobileEndCallBtn\" aria-label=\"End call\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.63A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91\"/>\n <line x1=\"22\" x2=\"2\" y1=\"2\" y2=\"22\"/>\n </svg>\n </button>\n </div>\n </div>\n \n <!-- Bottom Row: Transcript (expandable) -->\n <button type=\"button\" class=\"mobile-transcript-row\" id=\"mobileTranscriptRow\">\n <p class=\"mobile-transcript-text\" id=\"mobileTranscriptText\" dir=\"").concat(textDir, "\">").concat(this.t('transcriptWillAppear'), "</p>\n <div class=\"mobile-transcript-footer\">\n <span class=\"mobile-expand-hint\">tap to expand & type</span>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"opacity: 0.6;\">\n <path d=\"M7 17L17 7M17 7H7M17 7V17\"/>\n </svg>\n </div>\n </button>\n\n <!-- Fly-to-cart target: always visible for animation (positioned where cart icon appears) -->\n <div id=\"ttpFlyToCartTarget\" class=\"ttp-fly-target\" aria-hidden=\"true\"></div>\n </div>\n \n <!-- Expanded Transcript Overlay -->\n <div class=\"mobile-transcript-overlay\" id=\"mobileTranscriptOverlay\">\n <div class=\"mobile-expanded-transcript\" id=\"mobileExpandedTranscript\">\n <!-- Header -->\n <div class=\"mobile-transcript-header\">\n <div class=\"mobile-header-left\">\n <div class=\"mobile-header-status\">\n <div class=\"mobile-header-status-dot\"></div>\n <span class=\"mobile-header-status-text\" id=\"mobileHeaderStatusText\">").concat(this.t('listening'), "</span>\n <span class=\"mobile-header-duration\" id=\"mobileHeaderDuration\">00:00</span>\n </div>\n <span class=\"mobile-transcript-label\">CONVERSATION</span>\n </div>\n <button type=\"button\" class=\"mobile-close-transcript-btn\" id=\"mobileCloseTranscriptBtn\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n \n <!-- Messages Container -->\n <div class=\"mobile-messages-container\" id=\"mobileMessagesContainer\">\n <!-- Messages will be dynamically added here -->\n </div>\n \n <!-- Text Input Area -->\n <div class=\"mobile-input-area\">\n <div class=\"mobile-input-wrapper\">\n <input\n type=\"text\"\n placeholder=\"Type a message...\"\n id=\"mobileTextInput\"\n class=\"mobile-text-input\"\n dir=\"").concat(textDir, "\"\n />\n <button type=\"button\" class=\"mobile-send-btn\" id=\"mobileSendBtn\" disabled>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z\"/>\n </svg>\n </button>\n </div>\n <p class=\"mobile-input-hint\">\uD83C\uDF99\uFE0F ").concat(this.t('voiceActive') || 'Voice is still active', " \u2022 ").concat(this.t('typeOrSpeak') || 'Type or speak', "</p>\n </div>\n </div>\n </div>\n ");
|
|
38139
39559
|
|
|
38140
39560
|
// Append to body
|
|
38141
39561
|
document.body.appendChild(container);
|
|
@@ -38407,7 +39827,7 @@ var Mobile = /*#__PURE__*/function () {
|
|
|
38407
39827
|
// Render message
|
|
38408
39828
|
var messageEl = document.createElement('div');
|
|
38409
39829
|
messageEl.className = "mobile-message-bubble ".concat(type);
|
|
38410
|
-
messageEl.setAttribute('dir', this.config
|
|
39830
|
+
messageEl.setAttribute('dir', (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(text, this.config));
|
|
38411
39831
|
messageEl.textContent = text;
|
|
38412
39832
|
messagesContainer.appendChild(messageEl);
|
|
38413
39833
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
@@ -38475,6 +39895,68 @@ var Mobile = /*#__PURE__*/function () {
|
|
|
38475
39895
|
|
|
38476
39896
|
/***/ }),
|
|
38477
39897
|
|
|
39898
|
+
/***/ "./src/widget/voice/rtlText.js":
|
|
39899
|
+
/*!*************************************!*\
|
|
39900
|
+
!*** ./src/widget/voice/rtlText.js ***!
|
|
39901
|
+
\*************************************/
|
|
39902
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
39903
|
+
|
|
39904
|
+
"use strict";
|
|
39905
|
+
__webpack_require__.r(__webpack_exports__);
|
|
39906
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
39907
|
+
/* harmony export */ configPrefersRtlText: () => (/* binding */ configPrefersRtlText),
|
|
39908
|
+
/* harmony export */ dirForAssistantStreamingText: () => (/* binding */ dirForAssistantStreamingText),
|
|
39909
|
+
/* harmony export */ dirForTranscriptSnippet: () => (/* binding */ dirForTranscriptSnippet)
|
|
39910
|
+
/* harmony export */ });
|
|
39911
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
39912
|
+
/**
|
|
39913
|
+
* RTL for voice / transcript text when the widget is in a right-to-left locale.
|
|
39914
|
+
* Uses explicit `direction: 'rtl'` from merged config, or language base `he` / `ar`
|
|
39915
|
+
* when direction was not set (or was lost on nested config spreads).
|
|
39916
|
+
*
|
|
39917
|
+
* @param {{ direction?: string, language?: string } | null | undefined} config
|
|
39918
|
+
* @returns {boolean}
|
|
39919
|
+
*/
|
|
39920
|
+
function configPrefersRtlText(config) {
|
|
39921
|
+
if (!config || _typeof(config) !== 'object') return false;
|
|
39922
|
+
if (config.direction === 'rtl') return true;
|
|
39923
|
+
var base = String(config.language || '').toLowerCase().split(/[-_]/)[0];
|
|
39924
|
+
return base === 'he' || base === 'ar';
|
|
39925
|
+
}
|
|
39926
|
+
|
|
39927
|
+
/**
|
|
39928
|
+
* `dir` for elements showing streamed assistant / agent text (and similar dynamic strings).
|
|
39929
|
+
* Uses `rtl` when the widget is explicitly RTL; otherwise `auto` so Hebrew (or other RTL)
|
|
39930
|
+
* agent copy still lays out correctly when Language/Direction are English + LTR.
|
|
39931
|
+
*
|
|
39932
|
+
* @param {{ direction?: string, language?: string } | null | undefined} config
|
|
39933
|
+
* @returns {'rtl' | 'auto'}
|
|
39934
|
+
*/
|
|
39935
|
+
function dirForAssistantStreamingText(config) {
|
|
39936
|
+
return configPrefersRtlText(config) ? 'rtl' : 'auto';
|
|
39937
|
+
}
|
|
39938
|
+
var RE_HEBREW = /[\u0590-\u05FF]/;
|
|
39939
|
+
var RE_ARABIC = /[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/;
|
|
39940
|
+
|
|
39941
|
+
/**
|
|
39942
|
+
* `dir` for a single assistant / agent transcript string.
|
|
39943
|
+
* When the widget chrome is LTR but the line contains Hebrew or Arabic letters,
|
|
39944
|
+
* use explicit `rtl` so neutral punctuation (e.g. "?") stays at the logical sentence end.
|
|
39945
|
+
*
|
|
39946
|
+
* @param {string|null|undefined} text
|
|
39947
|
+
* @param {{ direction?: string, language?: string } | null | undefined} config
|
|
39948
|
+
* @returns {'rtl' | 'ltr' | 'auto'}
|
|
39949
|
+
*/
|
|
39950
|
+
function dirForTranscriptSnippet(text, config) {
|
|
39951
|
+
if (configPrefersRtlText(config)) return 'rtl';
|
|
39952
|
+
var s = text != null ? String(text) : '';
|
|
39953
|
+
if (!s.trim()) return 'auto';
|
|
39954
|
+
if (RE_HEBREW.test(s) || RE_ARABIC.test(s)) return 'rtl';
|
|
39955
|
+
return 'ltr';
|
|
39956
|
+
}
|
|
39957
|
+
|
|
39958
|
+
/***/ }),
|
|
39959
|
+
|
|
38478
39960
|
/***/ "./src/widget/voice/styles.js":
|
|
38479
39961
|
/*!************************************!*\
|
|
38480
39962
|
!*** ./src/widget/voice/styles.js ***!
|
|
@@ -38570,7 +40052,7 @@ var Styles = /*#__PURE__*/function () {
|
|
|
38570
40052
|
key: "generateMobileCSS",
|
|
38571
40053
|
value: function generateMobileCSS() {
|
|
38572
40054
|
var important = ' !important';
|
|
38573
|
-
return "\n /* Mobile Voice Call UI - Container positioning */\n #mobile-voice-call-bar-container {\n position: fixed !important;\n bottom: 0 !important;\n left: 0 !important;\n right: 0 !important;\n z-index: 99999 !important;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;\n padding: 0 12px 12px !important;\n pointer-events: none".concat(important, ";\n box-sizing: border-box !important;\n width: 100% !important;\n max-width: 100vw !important;\n margin: 0 !important;\n overflow: visible !important;\n isolation: isolate !important;\n }\n\n #mobile-voice-call-bar-container *,\n #mobile-voice-call-bar-container *::before,\n #mobile-voice-call-bar-container *::after {\n box-sizing: border-box !important;\n }\n\n /* Mobile loading: small spinner in waveform area until first audio */\n .mobile-waveform-spinner {\n position: absolute !important;\n left: 50% !important;\n top: 50% !important;\n transform: translate(-50%, -50%) !important;\n width: 16px !important;\n height: 16px !important;\n }\n\n .mobile-waveform-spinner-inner {\n width: 100% !important;\n height: 100% !important;\n border: 2px solid rgba(255,255,255,0.3) !important;\n border-top-color: #fff !important;\n border-radius: 50% !important;\n animation: mobileLoadingSpin 0.8s linear infinite !important;\n }\n\n .mobile-waveform-spinner--hidden {\n display: none !important;\n }\n\n @keyframes mobileLoadingSpin {\n to { transform: rotate(360deg); }\n }\n\n .mobile-waveform-bars--hidden {\n visibility: hidden !important;\n opacity: 0 !important;\n }\n\n .mobile-duration-badge--hidden {\n visibility: hidden !important;\n opacity: 0 !important;\n pointer-events: none !important;\n }\n\n .mobile-duration-badge {\n position: absolute !important;\n top: -28px !important;\n left: 50% !important;\n transform: translateX(-50%) !important;\n display: flex !important;\n align-items: center !important;\n gap: 6px !important;\n padding: 5px 14px !important;\n background: rgba(0,0,0,0.75) !important;\n border-radius: 20px !important;\n font-size: 12px !important;\n font-weight: 700 !important;\n color: #fff !important;\n backdrop-filter: blur(8px) !important;\n -webkit-backdrop-filter: blur(8px) !important;\n z-index: 10 !important;\n pointer-events: auto !important;\n visibility: visible !important;\n opacity: 1 !important;\n }\n\n /* Fly-to-cart target: invisible anchor for animation when cart row is hidden */\n .ttp-fly-target {\n position: absolute").concat(important, ";\n bottom: 14px").concat(important, ";\n left: 30px").concat(important, ";\n width: 24px").concat(important, ";\n height: 24px").concat(important, ";\n pointer-events: none").concat(important, ";\n }\n\n /*
|
|
40055
|
+
return "\n /* Mobile Voice Call UI - Container positioning */\n #mobile-voice-call-bar-container {\n position: fixed !important;\n bottom: 0 !important;\n left: 0 !important;\n right: 0 !important;\n z-index: 99999 !important;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;\n padding: 0 12px 12px !important;\n pointer-events: none".concat(important, ";\n box-sizing: border-box !important;\n width: 100% !important;\n max-width: 100vw !important;\n margin: 0 !important;\n overflow: visible !important;\n isolation: isolate !important;\n }\n\n #mobile-voice-call-bar-container *,\n #mobile-voice-call-bar-container *::before,\n #mobile-voice-call-bar-container *::after {\n box-sizing: border-box !important;\n }\n\n /* Mobile loading: small spinner in waveform area until first audio */\n .mobile-waveform-spinner {\n position: absolute !important;\n left: 50% !important;\n top: 50% !important;\n transform: translate(-50%, -50%) !important;\n width: 16px !important;\n height: 16px !important;\n }\n\n .mobile-waveform-spinner-inner {\n width: 100% !important;\n height: 100% !important;\n border: 2px solid rgba(255,255,255,0.3) !important;\n border-top-color: #fff !important;\n border-radius: 50% !important;\n animation: mobileLoadingSpin 0.8s linear infinite !important;\n }\n\n .mobile-waveform-spinner--hidden {\n display: none !important;\n }\n\n @keyframes mobileLoadingSpin {\n to { transform: rotate(360deg); }\n }\n\n .mobile-waveform-bars--hidden {\n visibility: hidden !important;\n opacity: 0 !important;\n }\n\n .mobile-duration-badge--hidden {\n visibility: hidden !important;\n opacity: 0 !important;\n pointer-events: none !important;\n }\n\n .mobile-duration-badge {\n position: absolute !important;\n top: -28px !important;\n left: 50% !important;\n transform: translateX(-50%) !important;\n display: flex !important;\n align-items: center !important;\n gap: 6px !important;\n padding: 5px 14px !important;\n background: rgba(0,0,0,0.75) !important;\n border-radius: 20px !important;\n font-size: 12px !important;\n font-weight: 700 !important;\n color: #fff !important;\n backdrop-filter: blur(8px) !important;\n -webkit-backdrop-filter: blur(8px) !important;\n z-index: 10 !important;\n pointer-events: auto !important;\n visibility: visible !important;\n opacity: 1 !important;\n }\n\n /* Fly-to-cart target: invisible anchor for animation when cart row is hidden */\n .ttp-fly-target {\n position: absolute").concat(important, ";\n bottom: 14px").concat(important, ";\n left: 30px").concat(important, ";\n width: 24px").concat(important, ";\n height: 24px").concat(important, ";\n pointer-events: none").concat(important, ";\n }\n\n /* Shared mobile styles */\n ").concat(this._getSharedMobileCSS(important), "\n ");
|
|
38574
40056
|
}
|
|
38575
40057
|
}]);
|
|
38576
40058
|
}();
|
|
@@ -38591,6 +40073,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
38591
40073
|
/* harmony export */ resolveIdleHeaderAvatarUrl: () => (/* binding */ resolveIdleHeaderAvatarUrl),
|
|
38592
40074
|
/* harmony export */ sanitizeHeaderAvatarImageUrl: () => (/* binding */ sanitizeHeaderAvatarImageUrl)
|
|
38593
40075
|
/* harmony export */ });
|
|
40076
|
+
/* harmony import */ var _rtlText_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./rtlText.js */ "./src/widget/voice/rtlText.js");
|
|
38594
40077
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
38595
40078
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
38596
40079
|
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
@@ -38611,6 +40094,8 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
|
|
|
38611
40094
|
* - None (stateless template generation)
|
|
38612
40095
|
*/
|
|
38613
40096
|
|
|
40097
|
+
|
|
40098
|
+
|
|
38614
40099
|
/** Allow only http(s) absolute URLs for idle header avatar images. */
|
|
38615
40100
|
function sanitizeHeaderAvatarImageUrl(raw) {
|
|
38616
40101
|
var s = raw != null && String(raw).trim() || '';
|
|
@@ -38686,7 +40171,7 @@ var Templates = /*#__PURE__*/function () {
|
|
|
38686
40171
|
}, {
|
|
38687
40172
|
key: "generateHTML",
|
|
38688
40173
|
value: function generateHTML() {
|
|
38689
|
-
var _this$config$whatsapp;
|
|
40174
|
+
var _this$config$behavior, _this$config$whatsapp;
|
|
38690
40175
|
// Mobile UI - minimized bar design
|
|
38691
40176
|
if (this.voiceInterface.isMobile) {
|
|
38692
40177
|
return this.generateMobileHTML();
|
|
@@ -38702,8 +40187,11 @@ var Templates = /*#__PURE__*/function () {
|
|
|
38702
40187
|
var headerAvatarUrl = resolveIdleHeaderAvatarUrl(this.config);
|
|
38703
40188
|
var idleAvatarInner = headerAvatarUrl ? "<img src=\"".concat(escapeHtmlAttr(headerAvatarUrl), "\" alt=\"\" class=\"ttp-avatar-img\" />") : avatarInitial;
|
|
38704
40189
|
var idleAvatarClass = headerAvatarUrl ? 'ttp-avatar ttp-avatar--image' : 'ttp-avatar';
|
|
38705
|
-
var textDir = this.config
|
|
38706
|
-
return "<div class=\"voice-interface\" id=\"voiceInterface\">\n <!-- Idle View -->\n <div id=\"voiceIdleState\">\n <div class=\"ttp-hero\">\n <div class=\"ttp-header\">\n <div class=\"ttp-agent\">\n <div class=\"ttp-avatar-wrap\">\n <div class=\"".concat(idleAvatarClass, "\">").concat(idleAvatarInner, "</div>\n <div class=\"ttp-online-dot\"></div>\n </div>\n <div>\n <div class=\"ttp-agent-name\">").concat(agentName, "</div>\n <div class=\"ttp-agent-role\">").concat(agentRole, "</div>\n </div>\n </div>\n <button type=\"button\" class=\"ttp-close-btn\" id=\"ttpCloseIdle\" aria-label=\"Close widget\">\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n </div>\n <div class=\"ttp-headline\">").concat(headline, "</div>\n <div class=\"ttp-subline\">").concat(subline, "</div>\n </div>\n\n <div class=\"ttp-body\">\n <button type=\"button\" class=\"ttp-btn-primary\" id=\"startCallBtn\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"").concat(this.config.startCallButtonTextColor || 'white', "\" stroke-width=\"2\">\n <path d=\"M12 2a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V5a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2M12 19v3M8 22h8\"/>\n </svg>\n ").concat(this.getText('startCallButtonText', 'startCall') || 'Start Voice Call', "\n </button>\n <button type=\"button\" class=\"ttp-btn-secondary\" id=\"startTextBtn\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/>\n </svg>\n ").concat(this.config.sendMessageText || this.getText('sendMessageText', 'sendMessage') || 'Send a Message', "\n </button>\n ").concat((_this$config$whatsapp = this.config.whatsapp) !== null && _this$config$whatsapp !== void 0 && _this$config$whatsapp.number ? "\n <a class=\"ttp-btn-wa\" id=\"ttpWhatsAppBtn\"\n href=\"https://wa.me/".concat(this.config.whatsapp.number).concat(this.config.whatsapp.text ? '?text=' + encodeURIComponent(this.config.whatsapp.text) : '', "\"\n target=\"_blank\" rel=\"noopener\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"#25d366\">\n <path d=\"M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.655-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z\"/>\n </svg>\n WhatsApp\n </a>\n ") : '', "\n </div>\n </div>\n\n <!-- Active Call View -->\n <div id=\"voiceActiveState\" style=\"display: none;\">\n <!-- Full voice section (hidden when items/rooms are shown) -->\n <div class=\"desktop-voice-section\" id=\"desktopVoiceSection\">\n <div class=\"ttp-call-hero\">\n <div class=\"ttp-call-topbar\">\n <div class=\"ttp-rec\">\n <div class=\"ttp-rec-dot\"></div>\n <span class=\"ttp-timer\" id=\"desktopTimerText\">00:00</span>\n </div>\n <span class=\"ttp-call-label\">Voice call active</span>\n <button type=\"button\" class=\"ttp-close-btn\" id=\"ttpCloseActive\" aria-label=\"Close widget\">\n <svg width=\"9\" height=\"9\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n </div>\n\n <div class=\"ttp-orb-wrap\">\n <div class=\"ttp-orb-rings\">\n <div class=\"ttp-ring ttp-ring-1\"></div>\n <div class=\"ttp-ring ttp-ring-2\"></div>\n <div class=\"ttp-orb\" id=\"desktopMainWaveform\">\n <div class=\"ttp-wave\" id=\"ttp-wave\"></div>\n </div>\n </div>\n <div class=\"ttp-listen-label\" id=\"desktopStatusText\">").concat(this.t('listening') || 'Listening to you...', "</div>\n </div>\n </div>\n\n <div class=\"ttp-controls\">\n <button type=\"button\" class=\"ttp-ctrl-btn\" id=\"desktopMuteBtn\" title=\"").concat(this.getTooltip('mute'), "\" aria-label=\"Mute microphone\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-mic-icon\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-mic-off-icon\" style=\"display: none; position: absolute;\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-ctrl-btn\" id=\"desktopPauseBtn\" title=\"Pause call\" aria-label=\"Pause call\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" class=\"desktop-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" class=\"desktop-play-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-ctrl-btn\" id=\"desktopSpeakerBtn\" title=\"").concat(this.getTooltip('speaker'), "\" aria-label=\"Toggle speaker\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-speaker-off-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-end-btn\" id=\"desktopEndCallBtn\" title=\"").concat(this.getTooltip('endCall'), "\" aria-label=\"End call\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.99 12 19.79 19.79 0 0 1 1.97 3.4 2 2 0 0 1 3.95 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Compact voice section (shown when items/rooms are visible) -->\n <div class=\"compact-voice-section\" id=\"compactVoiceSection\" style=\"display: none;\">\n <div class=\"compact-left\">\n <div class=\"compact-avatar\" id=\"desktopAvatarCompact\">\n <div class=\"ttp-orb compact-orb\" id=\"desktopMainWaveformCompact\">\n <div class=\"ttp-wave\"></div>\n </div>\n </div>\n <div class=\"compact-info\">\n <div class=\"compact-timer\" id=\"desktopTimerCompact\">\n <span class=\"ttp-rec-dot\"></span>\n <span id=\"compactTimerText\">00:00</span>\n </div>\n <div class=\"compact-status\" id=\"desktopStatusCompact\">\n <span id=\"compactStatusText\">").concat(this.t('listening') || 'Listening...', "</span>\n </div>\n </div>\n </div>\n <div class=\"compact-controls\">\n <button type=\"button\" class=\"compact-control-btn secondary\" id=\"desktopMuteBtnCompact\" title=\"").concat(this.getTooltip('mute'), "\" aria-label=\"Mute microphone\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-mic-icon\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-mic-off-icon\" style=\"display: none; position: absolute;\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"compact-control-btn secondary\" id=\"desktopPauseBtnCompact\" title=\"Pause call\" aria-label=\"Pause call\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" class=\"desktop-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" class=\"desktop-play-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"compact-control-btn secondary\" id=\"desktopSpeakerBtnCompact\" title=\"").concat(this.getTooltip('speaker'), "\" aria-label=\"Toggle speaker\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-speaker-off-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"compact-control-btn danger\" id=\"desktopEndCallBtnCompact\" title=\"").concat(this.getTooltip('endCall'), "\" aria-label=\"End call\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" style=\"overflow: visible;\">\n <path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.99 12 19.79 19.79 0 0 1 1.97 3.4 2 2 0 0 1 3.95 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\" transform=\"rotate(135 12 12)\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Transcript -->\n <div class=\"ttp-transcript desktop-conversation-panel\" id=\"desktopConversationPanel\" dir=\"").concat(textDir, "\">\n <div class=\"ttp-transcript-hdr\" id=\"conversationHeader\">\n <span class=\"ttp-live-label\">Live</span>\n <span class=\"ttp-toggle-text\" id=\"conversationToggleText\">Show history</span>\n <div class=\"ttp-dots\">\n <div class=\"ttp-dot\"></div>\n <div class=\"ttp-dot\"></div>\n <div class=\"ttp-dot\"></div>\n </div>\n </div>\n <div class=\"live-transcript-collapsed\" id=\"liveTranscriptCollapsed\">\n <div class=\"ttp-transcript-text\" id=\"liveTextCollapsed\">\n ").concat(this.t('transcriptWillAppear') || 'Transcript will appear here...', "<span class=\"ttp-cursor\"></span>\n </div>\n </div>\n <div class=\"conversation-history\" id=\"conversationHistory\">\n <div class=\"conversation-messages\" id=\"conversationMessages\"></div>\n <div class=\"live-message-row\" id=\"liveMessageRow\" style=\"display: none;\">\n <div class=\"message-bubble\" dir=\"").concat(textDir, "\">\n <span class=\"live-badge\">Live</span>\n <span id=\"liveMessageText\"></span><span class=\"ttp-cursor\"></span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Type row -->\n <div class=\"ttp-type-row voice-text-input-area\">\n <input class=\"ttp-input\" type=\"text\" id=\"voiceTextInput\" dir=\"").concat(textDir, "\" placeholder=\"").concat(this.getText('inputPlaceholder', 'typeMessage') || 'Or type here…', "\" aria-label=\"Type a message\" />\n <button type=\"button\" class=\"ttp-send-btn\" id=\"voiceSendBtn\" aria-label=\"Send message\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>\n </svg>\n </button>\n </div>\n </div>\n </div>");
|
|
40190
|
+
var textDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.configPrefersRtlText)(this.config) ? 'rtl' : 'ltr';
|
|
40191
|
+
var transcriptPlaceholder = this.t('transcriptWillAppear') || '';
|
|
40192
|
+
var transcriptDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(transcriptPlaceholder, this.config);
|
|
40193
|
+
var voiceInputDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForAssistantStreamingText)(this.config);
|
|
40194
|
+
return "<div class=\"voice-interface\" id=\"voiceInterface\">\n <!-- Idle View -->\n <div id=\"voiceIdleState\">\n <div class=\"ttp-hero\">\n <div class=\"ttp-header\">\n <div class=\"ttp-agent\">\n <div class=\"ttp-avatar-wrap\">\n <div class=\"".concat(idleAvatarClass, "\">").concat(idleAvatarInner, "</div>\n <div class=\"ttp-online-dot\"></div>\n </div>\n <div>\n <div class=\"ttp-agent-name\">").concat(agentName, "</div>\n <div class=\"ttp-agent-role\">").concat(agentRole, "</div>\n </div>\n </div>\n <button type=\"button\" class=\"ttp-close-btn\" id=\"ttpCloseIdle\" aria-label=\"Close widget\">\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n </div>\n <div class=\"ttp-headline\">").concat(headline, "</div>\n <div class=\"ttp-subline\">").concat(subline, "</div>\n </div>\n\n <div class=\"ttp-body\">\n <button type=\"button\" class=\"ttp-btn-primary\" id=\"startCallBtn\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"").concat(this.config.startCallButtonTextColor || 'white', "\" stroke-width=\"2\">\n <path d=\"M12 2a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V5a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2M12 19v3M8 22h8\"/>\n </svg>\n ").concat(this.getText('startCallButtonText', 'startCall') || 'Start Voice Call', "\n </button>\n ").concat((((_this$config$behavior = this.config.behavior) === null || _this$config$behavior === void 0 ? void 0 : _this$config$behavior.mode) || 'unified') !== 'voice-only' ? "\n <button type=\"button\" class=\"ttp-btn-secondary\" id=\"startTextBtn\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/>\n </svg>\n ".concat(this.config.sendMessageText || this.getText('sendMessageText', 'sendMessage') || 'Send a Message', "\n </button>\n ") : '', "\n ").concat((_this$config$whatsapp = this.config.whatsapp) !== null && _this$config$whatsapp !== void 0 && _this$config$whatsapp.number ? "\n <a class=\"ttp-btn-wa\" id=\"ttpWhatsAppBtn\"\n href=\"https://wa.me/".concat(this.config.whatsapp.number).concat(this.config.whatsapp.text ? '?text=' + encodeURIComponent(this.config.whatsapp.text) : '', "\"\n target=\"_blank\" rel=\"noopener\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"#25d366\">\n <path d=\"M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.655-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z\"/>\n </svg>\n WhatsApp\n </a>\n ") : '', "\n </div>\n </div>\n\n <!-- Active Call View -->\n <div id=\"voiceActiveState\" style=\"display: none;\">\n <!-- Full voice section (hidden when items/rooms are shown) -->\n <div class=\"desktop-voice-section\" id=\"desktopVoiceSection\">\n <div class=\"ttp-call-hero\">\n <div class=\"ttp-call-topbar\">\n <div class=\"ttp-rec\">\n <div class=\"ttp-rec-dot\"></div>\n <span class=\"ttp-timer\" id=\"desktopTimerText\">00:00</span>\n </div>\n <span class=\"ttp-call-label\">Voice call active</span>\n <button type=\"button\" class=\"ttp-close-btn\" id=\"ttpCloseActive\" aria-label=\"Close widget\">\n <svg width=\"9\" height=\"9\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n </div>\n\n <div class=\"ttp-orb-wrap\">\n <div class=\"ttp-orb-rings\">\n <div class=\"ttp-ring ttp-ring-1\"></div>\n <div class=\"ttp-ring ttp-ring-2\"></div>\n <div class=\"ttp-orb\" id=\"desktopMainWaveform\">\n <div class=\"ttp-wave\" id=\"ttp-wave\"></div>\n </div>\n </div>\n <div class=\"ttp-listen-label\" id=\"desktopStatusText\">").concat(this.t('listening') || 'Listening to you...', "</div>\n </div>\n </div>\n\n <div class=\"ttp-controls\">\n <button type=\"button\" class=\"ttp-ctrl-btn\" id=\"desktopMuteBtn\" title=\"").concat(this.getTooltip('mute'), "\" aria-label=\"Mute microphone\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-mic-icon\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-mic-off-icon\" style=\"display: none; position: absolute;\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-ctrl-btn\" id=\"desktopPauseBtn\" title=\"Pause call\" aria-label=\"Pause call\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" class=\"desktop-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" class=\"desktop-play-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-ctrl-btn\" id=\"desktopSpeakerBtn\" title=\"").concat(this.getTooltip('speaker'), "\" aria-label=\"Toggle speaker\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-speaker-off-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"ttp-end-btn\" id=\"desktopEndCallBtn\" title=\"").concat(this.getTooltip('endCall'), "\" aria-label=\"End call\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.99 12 19.79 19.79 0 0 1 1.97 3.4 2 2 0 0 1 3.95 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Compact voice section (shown when items/rooms are visible) -->\n <div class=\"compact-voice-section\" id=\"compactVoiceSection\" style=\"display: none;\">\n <div class=\"compact-left\">\n <div class=\"compact-avatar\" id=\"desktopAvatarCompact\">\n <div class=\"ttp-orb compact-orb\" id=\"desktopMainWaveformCompact\">\n <div class=\"ttp-wave\"></div>\n </div>\n </div>\n <div class=\"compact-info\">\n <div class=\"compact-timer\" id=\"desktopTimerCompact\">\n <span class=\"ttp-rec-dot\"></span>\n <span id=\"compactTimerText\">00:00</span>\n </div>\n <div class=\"compact-status\" id=\"desktopStatusCompact\">\n <span id=\"compactStatusText\">").concat(this.t('listening') || 'Listening...', "</span>\n </div>\n </div>\n </div>\n <div class=\"compact-controls\">\n <button type=\"button\" class=\"compact-control-btn secondary\" id=\"desktopMuteBtnCompact\" title=\"").concat(this.getTooltip('mute'), "\" aria-label=\"Mute microphone\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-mic-icon\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-mic-off-icon\" style=\"display: none; position: absolute;\">\n <path d=\"M12 1a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V4a3 3 0 0 1 3-3z\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"compact-control-btn secondary\" id=\"desktopPauseBtnCompact\" title=\"Pause call\" aria-label=\"Pause call\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" class=\"desktop-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" class=\"desktop-play-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"compact-control-btn secondary\" id=\"desktopSpeakerBtnCompact\" title=\"").concat(this.getTooltip('speaker'), "\" aria-label=\"Toggle speaker\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" class=\"desktop-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"desktop-speaker-off-icon\" style=\"display: none; position: absolute;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n <line x1=\"5\" y1=\"5\" x2=\"19\" y2=\"19\" stroke=\"#ef4444\" stroke-width=\"1.5\" stroke-linecap=\"round\" opacity=\"0.85\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"compact-control-btn danger\" id=\"desktopEndCallBtnCompact\" title=\"").concat(this.getTooltip('endCall'), "\" aria-label=\"End call\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" style=\"overflow: visible;\">\n <path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.99 12 19.79 19.79 0 0 1 1.97 3.4 2 2 0 0 1 3.95 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\" transform=\"rotate(135 12 12)\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Transcript -->\n <div class=\"ttp-transcript desktop-conversation-panel\" id=\"desktopConversationPanel\" dir=\"").concat(textDir, "\">\n <div class=\"ttp-transcript-hdr\" id=\"conversationHeader\">\n <span class=\"ttp-live-label\">Live</span>\n <span class=\"ttp-toggle-text\" id=\"conversationToggleText\">Show history</span>\n <div class=\"ttp-dots\">\n <div class=\"ttp-dot\"></div>\n <div class=\"ttp-dot\"></div>\n <div class=\"ttp-dot\"></div>\n </div>\n </div>\n <div class=\"live-transcript-collapsed\" id=\"liveTranscriptCollapsed\">\n <div class=\"ttp-transcript-text\" id=\"liveTextCollapsed\" dir=\"").concat(transcriptDir, "\">\n ").concat(this.t('transcriptWillAppear') || 'Transcript will appear here...', "<span class=\"ttp-cursor\"></span>\n </div>\n </div>\n <div class=\"conversation-history\" id=\"conversationHistory\">\n <div class=\"conversation-messages\" id=\"conversationMessages\"></div>\n <div class=\"live-message-row\" id=\"liveMessageRow\" style=\"display: none;\">\n <div class=\"message-bubble\" dir=\"").concat(transcriptDir, "\">\n <span class=\"live-badge\">Live</span>\n <span id=\"liveMessageText\"></span><span class=\"ttp-cursor\"></span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Type row -->\n <div class=\"ttp-type-row voice-text-input-area\">\n <input class=\"ttp-input\" type=\"text\" id=\"voiceTextInput\" dir=\"").concat(voiceInputDir, "\" placeholder=\"").concat(this.getText('inputPlaceholder', 'typeMessage') || 'Or type here…', "\" aria-label=\"Type a message\" />\n <button type=\"button\" class=\"ttp-send-btn\" id=\"voiceSendBtn\" aria-label=\"Send message\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/>\n <polygon points=\"22 2 15 22 11 13 2 9 22 2\"/>\n </svg>\n </button>\n </div>\n </div>\n </div>");
|
|
38707
40195
|
}
|
|
38708
40196
|
|
|
38709
40197
|
/**
|
|
@@ -38713,13 +40201,16 @@ var Templates = /*#__PURE__*/function () {
|
|
|
38713
40201
|
key: "generateMobileHTML",
|
|
38714
40202
|
value: function generateMobileHTML() {
|
|
38715
40203
|
// For mobile, idle state goes in widget, but active state will be appended to body
|
|
38716
|
-
var textDir = this.config
|
|
40204
|
+
var textDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.configPrefersRtlText)(this.config) ? 'rtl' : 'ltr';
|
|
40205
|
+
var transcriptPlaceholder = this.t('transcriptWillAppear') || '';
|
|
40206
|
+
var transcriptDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(transcriptPlaceholder, this.config);
|
|
40207
|
+
var voiceInputDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForAssistantStreamingText)(this.config);
|
|
38717
40208
|
var headerAvatarUrl = resolveIdleHeaderAvatarUrl(this.config);
|
|
38718
40209
|
var mobileAvatarInner = headerAvatarUrl ? "<img src=\"".concat(escapeHtmlAttr(headerAvatarUrl), "\" alt=\"\" class=\"voice-avatar-img\" />") : '🤖';
|
|
38719
40210
|
var mobileAvatarClass = headerAvatarUrl ? 'voice-avatar voice-avatar--image' : 'voice-avatar';
|
|
38720
40211
|
return "<div class=\"voice-interface mobile-voice-interface\" id=\"voiceInterface\" dir=\"".concat(textDir, "\">\n <!-- Before Call State (same as desktop) -->\n <div id=\"voiceIdleState\">\n <div class=\"").concat(mobileAvatarClass, "\" id=\"voiceAvatar\">").concat(mobileAvatarInner, "</div>\n <div class=\"voice-status\">\n <div class=\"voice-status-title\">").concat(this.getText('startCallTitle', 'clickToStartCall'), "</div>\n <div class=\"voice-status-subtitle\">").concat(this.getText('startCallSubtitle', 'realTimeVoice'), "</div>\n </div>\n <button type=\"button\" class=\"start-call-btn\" id=\"startCallBtn\">\n <svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z\"/>\n </svg>\n <span>").concat(this.getText('startCallButtonText', 'startCall'), "</span>\n </button>\n </div>\n \n <!-- Mobile Active Call State - Hidden placeholder (actual bar will be appended to body) -->\n <div id=\"voiceActiveState\" class=\"mobile-active-state\" style=\"display: none;\">\n <!-- Duration Badge -->\n <div class=\"mobile-duration-badge\" id=\"mobileDurationBadge\">\n <div class=\"mobile-duration-dot\"></div>\n <span id=\"mobileDurationText\">00:00</span>\n </div>\n \n <!-- Main Bar -->\n <div class=\"mobile-voice-bar\">\n <!-- Top Row: Status, Waveform, Controls -->\n <div class=\"mobile-top-row\">\n <!-- Status Indicator -->\n <div class=\"mobile-status-indicator\">\n <div class=\"mobile-status-dot\" id=\"mobileStatusDot\"></div>\n <span class=\"mobile-status-text\" id=\"mobileStatusText\">Listening...</span>\n </div>\n \n <!-- Centered Waveform -->\n <div class=\"mobile-waveform-center\" id=\"mobileWaveform\">\n ").concat(Array(7).fill(0).map(function (_, i) {
|
|
38721
40212
|
return "<div class=\"mobile-waveform-bar\" data-index=\"".concat(i, "\"></div>");
|
|
38722
|
-
}).join(''), "\n </div>\n \n <!-- Controls -->\n <div class=\"mobile-controls\">\n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobileMuteBtn\" aria-label=\"Mute microphone\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-icon\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-off-icon\" style=\"display: none;\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n <line x1=\"4\" y1=\"4\" x2=\"20\" y2=\"20\" stroke=\"#ef4444\" stroke-width=\"1.5\" opacity=\"0.85\"/>\n </svg>\n </button>\n \n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobilePauseBtn\" aria-label=\"Pause call\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" class=\"mobile-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"#fff\" stroke=\"none\" class=\"mobile-play-icon\" style=\"display: none;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n\n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobileSpeakerBtn\" aria-label=\"Toggle speaker\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-off-icon\" style=\"display: none;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n <line x1=\"4\" y1=\"4\" x2=\"20\" y2=\"20\" stroke=\"#ef4444\" stroke-width=\"1.5\" opacity=\"0.85\"/>\n </svg>\n </button>\n\n <button type=\"button\" class=\"mobile-end-call-btn\" id=\"mobileEndCallBtn\" aria-label=\"End call\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.63A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91\"/>\n <line x1=\"22\" x2=\"2\" y1=\"2\" y2=\"22\"/>\n </svg>\n </button>\n </div>\n </div>\n \n <!-- Bottom Row: Transcript (expandable) -->\n <button type=\"button\" class=\"mobile-transcript-row\" id=\"mobileTranscriptRow\">\n <p class=\"mobile-transcript-text\" id=\"mobileTranscriptText\" dir=\"").concat(
|
|
40213
|
+
}).join(''), "\n </div>\n \n <!-- Controls -->\n <div class=\"mobile-controls\">\n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobileMuteBtn\" aria-label=\"Mute microphone\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-icon\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-mic-off-icon\" style=\"display: none;\">\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/>\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/>\n <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/>\n <line x1=\"4\" y1=\"4\" x2=\"20\" y2=\"20\" stroke=\"#ef4444\" stroke-width=\"1.5\" opacity=\"0.85\"/>\n </svg>\n </button>\n \n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobilePauseBtn\" aria-label=\"Pause call\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" class=\"mobile-pause-icon\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"#fff\" stroke=\"none\" class=\"mobile-play-icon\" style=\"display: none;\">\n <polygon points=\"6,4 20,12 6,20\"/>\n </svg>\n </button>\n\n <button type=\"button\" class=\"mobile-control-btn\" id=\"mobileSpeakerBtn\" aria-label=\"Toggle speaker\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-icon\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n </svg>\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"mobile-speaker-off-icon\" style=\"display: none;\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\"/>\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\"/>\n <line x1=\"4\" y1=\"4\" x2=\"20\" y2=\"20\" stroke=\"#ef4444\" stroke-width=\"1.5\" opacity=\"0.85\"/>\n </svg>\n </button>\n\n <button type=\"button\" class=\"mobile-end-call-btn\" id=\"mobileEndCallBtn\" aria-label=\"End call\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.63A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91\"/>\n <line x1=\"22\" x2=\"2\" y1=\"2\" y2=\"22\"/>\n </svg>\n </button>\n </div>\n </div>\n \n <!-- Bottom Row: Transcript (expandable) -->\n <button type=\"button\" class=\"mobile-transcript-row\" id=\"mobileTranscriptRow\">\n <p class=\"mobile-transcript-text\" id=\"mobileTranscriptText\" dir=\"").concat(transcriptDir, "\">").concat(this.t('transcriptWillAppear'), "</p>\n <div class=\"mobile-transcript-footer\">\n <span class=\"mobile-expand-hint\">tap to expand & type</span>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"opacity: 0.6;\">\n <path d=\"M7 17L17 7M17 7H7M17 7V17\"/>\n </svg>\n </div>\n </button>\n </div>\n </div>\n \n <!-- Expanded Transcript Overlay -->\n <div class=\"mobile-transcript-overlay\" id=\"mobileTranscriptOverlay\">\n <div class=\"mobile-expanded-transcript\" id=\"mobileExpandedTranscript\">\n <!-- Header -->\n <div class=\"mobile-transcript-header\">\n <div class=\"mobile-header-left\">\n <div class=\"mobile-header-status\">\n <div class=\"mobile-header-status-dot\"></div>\n <span class=\"mobile-header-status-text\" id=\"mobileHeaderStatusText\">Listening...</span>\n <span class=\"mobile-header-duration\" id=\"mobileHeaderDuration\">00:00</span>\n </div>\n <span class=\"mobile-transcript-label\">CONVERSATION</span>\n </div>\n <button type=\"button\" class=\"mobile-close-transcript-btn\" id=\"mobileCloseTranscriptBtn\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n \n <!-- Messages Container -->\n <div class=\"mobile-messages-container\" id=\"mobileMessagesContainer\">\n <!-- Messages will be dynamically added here -->\n </div>\n \n <!-- Text Input Area -->\n <div class=\"mobile-input-area\">\n <div class=\"mobile-input-wrapper\">\n <input\n type=\"text\"\n placeholder=\"Type a message...\"\n id=\"mobileTextInput\"\n class=\"mobile-text-input\"\n dir=\"").concat(voiceInputDir, "\"\n />\n <button type=\"button\" class=\"mobile-send-btn\" id=\"mobileSendBtn\" disabled>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z\"/>\n </svg>\n </button>\n </div>\n <p class=\"mobile-input-hint\">\uD83C\uDF99\uFE0F Voice is still active \u2022 Type or speak</p>\n </div>\n </div>\n </div>\n </div>");
|
|
38723
40214
|
}
|
|
38724
40215
|
}]);
|
|
38725
40216
|
}();
|
|
@@ -39142,6 +40633,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
39142
40633
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
39143
40634
|
/* harmony export */ Transcript: () => (/* binding */ Transcript)
|
|
39144
40635
|
/* harmony export */ });
|
|
40636
|
+
/* harmony import */ var _rtlText_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./rtlText.js */ "./src/widget/voice/rtlText.js");
|
|
39145
40637
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
39146
40638
|
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
|
|
39147
40639
|
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
@@ -39154,6 +40646,8 @@ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o =
|
|
|
39154
40646
|
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
39155
40647
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
39156
40648
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
40649
|
+
|
|
40650
|
+
|
|
39157
40651
|
/**
|
|
39158
40652
|
* Transcript - Manages transcript display and conversation history
|
|
39159
40653
|
*
|
|
@@ -39206,12 +40700,17 @@ var Transcript = /*#__PURE__*/function () {
|
|
|
39206
40700
|
key: "updateTranscript",
|
|
39207
40701
|
value: function updateTranscript(text) {
|
|
39208
40702
|
console.log('📝 [TRANSCRIPT] updateTranscript() called with text:', text ? text.substring(0, 50) + '...' : 'null/empty');
|
|
40703
|
+
var cfg = this.voiceInterface.config;
|
|
40704
|
+
var hasContent = !!(text && text.trim());
|
|
40705
|
+
var snippetForDir = hasContent ? text : this.t('transcriptWillAppear') || '';
|
|
40706
|
+
var assistantDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(snippetForDir, cfg);
|
|
39209
40707
|
|
|
39210
40708
|
// Mobile transcript (use container-scoped lookup for Shopify/embedded contexts)
|
|
39211
40709
|
var mobileBar = document.getElementById('mobile-voice-call-bar-container');
|
|
39212
40710
|
if (mobileBar) {
|
|
39213
40711
|
var mobileTranscriptText = mobileBar.querySelector('#mobileTranscriptText');
|
|
39214
40712
|
if (mobileTranscriptText) {
|
|
40713
|
+
mobileTranscriptText.setAttribute('dir', assistantDir);
|
|
39215
40714
|
mobileTranscriptText.textContent = text;
|
|
39216
40715
|
mobileTranscriptText.style.color = '';
|
|
39217
40716
|
}
|
|
@@ -39232,6 +40731,7 @@ var Transcript = /*#__PURE__*/function () {
|
|
|
39232
40731
|
var lastMessage = messagesContainer.querySelector('.mobile-message-bubble.assistant:last-child');
|
|
39233
40732
|
if (lastMessage) {
|
|
39234
40733
|
lastMessage.textContent = text;
|
|
40734
|
+
lastMessage.setAttribute('dir', (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(text, cfg));
|
|
39235
40735
|
} else {
|
|
39236
40736
|
this.voiceInterface.addMobileMessage('assistant', text);
|
|
39237
40737
|
}
|
|
@@ -39242,6 +40742,7 @@ var Transcript = /*#__PURE__*/function () {
|
|
|
39242
40742
|
// Desktop transcript - Update collapsed live text (2 lines)
|
|
39243
40743
|
var liveTextCollapsed = this.voiceInterface.shadowRoot.getElementById('liveTextCollapsed');
|
|
39244
40744
|
if (liveTextCollapsed) {
|
|
40745
|
+
liveTextCollapsed.setAttribute('dir', assistantDir);
|
|
39245
40746
|
liveTextCollapsed.style.color = '';
|
|
39246
40747
|
if (text && text.trim()) {
|
|
39247
40748
|
liveTextCollapsed.textContent = text;
|
|
@@ -39430,6 +40931,10 @@ var Transcript = /*#__PURE__*/function () {
|
|
|
39430
40931
|
// Update message text
|
|
39431
40932
|
liveMessageText.textContent = this.currentLiveMessage.content;
|
|
39432
40933
|
liveMessageRow.style.display = 'flex';
|
|
40934
|
+
var bubble = liveMessageRow.querySelector('.message-bubble');
|
|
40935
|
+
if (bubble) {
|
|
40936
|
+
bubble.setAttribute('dir', (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(this.currentLiveMessage.content, this.voiceInterface.config));
|
|
40937
|
+
}
|
|
39433
40938
|
|
|
39434
40939
|
// Re-add cursor
|
|
39435
40940
|
var cursor = liveMessageText.querySelector('.typing-cursor') || document.createElement('span');
|
|
@@ -39485,11 +40990,11 @@ var Transcript = /*#__PURE__*/function () {
|
|
|
39485
40990
|
messagesToRender = messagesToRender.slice(0, -1);
|
|
39486
40991
|
}
|
|
39487
40992
|
}
|
|
39488
|
-
var
|
|
40993
|
+
var bubbleDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(msg.content, this.config);
|
|
39489
40994
|
console.log('📝 [TRANSCRIPT] Rendering', messagesToRender.length, 'messages (live message excluded)');
|
|
39490
40995
|
container.innerHTML = messagesToRender.map(function (msg) {
|
|
39491
40996
|
var isUser = msg.role === 'user';
|
|
39492
|
-
return "\n <div class=\"message-row ".concat(msg.role, "\">\n <div class=\"message-avatar ").concat(msg.role, "\">\n ").concat(isUser ? 'U' : '<div class="orb"></div>', "\n </div>\n <div class=\"message-bubble\" dir=\"").concat(
|
|
40997
|
+
return "\n <div class=\"message-row ".concat(msg.role, "\">\n <div class=\"message-avatar ").concat(msg.role, "\">\n ").concat(isUser ? 'U' : '<div class="orb"></div>', "\n </div>\n <div class=\"message-bubble\" dir=\"").concat(bubbleDir, "\">").concat(_this.escapeHtml(msg.content), "</div>\n </div>\n ");
|
|
39493
40998
|
}).join('');
|
|
39494
40999
|
|
|
39495
41000
|
// Scroll to bottom
|