ttp-agent-sdk 2.44.0 → 2.45.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-widget.dev.js +2131 -646
- 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/ocado-cart-network-probe.js +1 -0
- package/dist/examples/sobeys-cart-dispatch-probe.js +1 -0
- package/dist/examples/sobeys-cart-read-test.html +112 -0
- package/dist/examples/sobeys-cart-read-test.js +1 -0
- package/dist/examples/sobeys-cart-subscribe-probe.js +1 -0
- package/dist/examples/sobeys-cart-subscribe-test.html +168 -0
- package/dist/examples/sobeys-cart-subscribe-test.js +1 -0
- package/dist/examples/sobeys-inject-probe-min.js +1 -0
- package/dist/examples/sobeys-inject-probe.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-12T15:13:11.602Z";
|
|
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
|
// ---------------------------------------------------------------------------
|
|
@@ -14377,20 +15056,42 @@ function refreshUIForPlatform(platform) {
|
|
|
14377
15056
|
// ---------------------------------------------------------------------------
|
|
14378
15057
|
|
|
14379
15058
|
var PartnerScriptRunner = /*#__PURE__*/function () {
|
|
15059
|
+
/**
|
|
15060
|
+
* @param {{ genericCtx?: boolean }} [opts]
|
|
15061
|
+
* genericCtx: when true, ctx omits the ecommerce-specific helpers
|
|
15062
|
+
* (platform / normalizeCart / refreshUI). Used by the flavor-independent
|
|
15063
|
+
* `__client_tools__` runner. Default false preserves the ecommerce ctx.
|
|
15064
|
+
*/
|
|
14380
15065
|
function PartnerScriptRunner() {
|
|
15066
|
+
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
14381
15067
|
_classCallCheck(this, PartnerScriptRunner);
|
|
14382
|
-
|
|
15068
|
+
this.genericCtx = !!(opts && opts.genericCtx);
|
|
15069
|
+
/** @type {Map<string, {fn?: Function, version?: number, codeSha256?: string, meta?: object, category?: string|null, beforeActions?: string[], afterActions?: string[], poisoned?: boolean, error?: string}>} */
|
|
14383
15070
|
this.adapters = new Map();
|
|
14384
15071
|
this.partnerId = null;
|
|
14385
15072
|
this.platform = null;
|
|
14386
15073
|
this.bundleVersion = 0; // monotonic, bumped on every setBundle
|
|
15074
|
+
/** @type {((msg: object) => void) | null} */
|
|
15075
|
+
this._emitCallback = null;
|
|
14387
15076
|
}
|
|
14388
15077
|
|
|
14389
15078
|
/**
|
|
14390
|
-
*
|
|
14391
|
-
*
|
|
15079
|
+
* Register the callback that adapters can use to send unsolicited messages
|
|
15080
|
+
* back to the backend via ctx.emit(msg). Called by EcommerceFlavor after it
|
|
15081
|
+
* loads the bundle so the callback is always fresh for the active SDK instance.
|
|
15082
|
+
* @param {(msg: object) => void} fn
|
|
14392
15083
|
*/
|
|
14393
15084
|
return _createClass(PartnerScriptRunner, [{
|
|
15085
|
+
key: "setEmitCallback",
|
|
15086
|
+
value: function setEmitCallback(fn) {
|
|
15087
|
+
this._emitCallback = typeof fn === 'function' ? fn : null;
|
|
15088
|
+
}
|
|
15089
|
+
|
|
15090
|
+
/**
|
|
15091
|
+
* Replace the cached bundle from a `partner_bundle` message.
|
|
15092
|
+
* @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
|
|
15093
|
+
*/
|
|
15094
|
+
}, {
|
|
14394
15095
|
key: "setBundle",
|
|
14395
15096
|
value: function setBundle(msg) {
|
|
14396
15097
|
var adapters = (msg === null || msg === void 0 ? void 0 : msg.adapters) || {};
|
|
@@ -14415,22 +15116,33 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
|
|
|
14415
15116
|
continue;
|
|
14416
15117
|
}
|
|
14417
15118
|
try {
|
|
14418
|
-
var _entry$version2;
|
|
15119
|
+
var _entry$version2, _entry$category;
|
|
14419
15120
|
var fn = compileAdapter(entry.code_js);
|
|
15121
|
+
// Inline pre/post hooks (optional). Compiled with the same
|
|
15122
|
+
// AsyncFunction strict-mode wrapper as the main body so a syntax
|
|
15123
|
+
// error in a hook poisons only this entry (caught below).
|
|
15124
|
+
var preFn = typeof entry.pre_code_js === 'string' ? compileAdapter(entry.pre_code_js) : null;
|
|
15125
|
+
var postFn = typeof entry.post_code_js === 'string' ? compileAdapter(entry.post_code_js) : null;
|
|
14420
15126
|
this.adapters.set(action, {
|
|
14421
15127
|
fn: fn,
|
|
15128
|
+
preFn: preFn,
|
|
15129
|
+
postFn: postFn,
|
|
14422
15130
|
version: (_entry$version2 = entry.version) !== null && _entry$version2 !== void 0 ? _entry$version2 : null,
|
|
14423
15131
|
codeSha256: entry.code_sha256 || null,
|
|
14424
|
-
|
|
15132
|
+
category: (_entry$category = entry.category) !== null && _entry$category !== void 0 ? _entry$category : null,
|
|
15133
|
+
meta: entry.meta || {},
|
|
15134
|
+
beforeActions: Array.isArray(entry.before_actions) ? entry.before_actions : [],
|
|
15135
|
+
afterActions: Array.isArray(entry.after_actions) ? entry.after_actions : []
|
|
14425
15136
|
});
|
|
14426
15137
|
compiled++;
|
|
14427
15138
|
} catch (err) {
|
|
14428
|
-
var _entry$version3;
|
|
15139
|
+
var _entry$version3, _entry$category2;
|
|
14429
15140
|
console.error(LOG, "compile failed for '".concat(action, "':"), err);
|
|
14430
15141
|
this.adapters.set(action, {
|
|
14431
15142
|
poisoned: true,
|
|
14432
15143
|
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
|
|
15144
|
+
version: (_entry$version3 = entry.version) !== null && _entry$version3 !== void 0 ? _entry$version3 : null,
|
|
15145
|
+
category: (_entry$category2 = entry.category) !== null && _entry$category2 !== void 0 ? _entry$category2 : null
|
|
14434
15146
|
});
|
|
14435
15147
|
poisoned++;
|
|
14436
15148
|
}
|
|
@@ -14466,24 +15178,62 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
|
|
|
14466
15178
|
/**
|
|
14467
15179
|
* Execute an adapter by action name. Used both by the run_partner_script
|
|
14468
15180
|
* tool handler and by ctx.runAdapter for intra-bundle chaining.
|
|
15181
|
+
*
|
|
15182
|
+
* When the adapter row defines before_actions / after_actions (from
|
|
15183
|
+
* ttp.partner_adapters), those run automatically around code_js.
|
|
15184
|
+
*
|
|
15185
|
+
* If the adapter has a non-null `category`, fires a window CustomEvent
|
|
15186
|
+
* named `netcost:partner-action:{category}` BEFORE executing code_js.
|
|
15187
|
+
* Subscribers (e.g. cart.subscribe) use this to suppress their own
|
|
15188
|
+
* duplicate notifications for the same mutation.
|
|
15189
|
+
*
|
|
15190
|
+
* @param {string} action
|
|
15191
|
+
* @param {Record<string, unknown>} [args]
|
|
15192
|
+
* @param {number} [depth] internal recursion guard
|
|
14469
15193
|
*/
|
|
14470
15194
|
}, {
|
|
14471
15195
|
key: "runAdapter",
|
|
14472
15196
|
value: (function () {
|
|
14473
15197
|
var _runAdapter = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(action) {
|
|
14474
|
-
var _entry$version4;
|
|
14475
15198
|
var args,
|
|
15199
|
+
depth,
|
|
14476
15200
|
entry,
|
|
14477
|
-
|
|
15201
|
+
safeArgs,
|
|
15202
|
+
beforeActions,
|
|
15203
|
+
afterActions,
|
|
15204
|
+
_iterator2,
|
|
15205
|
+
_step2,
|
|
15206
|
+
_hookAction,
|
|
15207
|
+
_hookResult,
|
|
15208
|
+
result,
|
|
15209
|
+
afterArgs,
|
|
15210
|
+
_iterator3,
|
|
15211
|
+
_step3,
|
|
15212
|
+
hookAction,
|
|
15213
|
+
hookResult,
|
|
14478
15214
|
_args2 = arguments,
|
|
14479
|
-
_t
|
|
15215
|
+
_t,
|
|
15216
|
+
_t2;
|
|
14480
15217
|
return _regenerator().w(function (_context2) {
|
|
14481
15218
|
while (1) switch (_context2.p = _context2.n) {
|
|
14482
15219
|
case 0:
|
|
14483
15220
|
args = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : {};
|
|
15221
|
+
depth = _args2.length > 2 && _args2[2] !== undefined ? _args2[2] : 0;
|
|
15222
|
+
if (!(depth > MAX_ORCHESTRATION_DEPTH)) {
|
|
15223
|
+
_context2.n = 1;
|
|
15224
|
+
break;
|
|
15225
|
+
}
|
|
15226
|
+
console.error("".concat(LOG, " orchestration depth exceeded for '").concat(action, "' (max ").concat(MAX_ORCHESTRATION_DEPTH, ")"));
|
|
15227
|
+
return _context2.a(2, {
|
|
15228
|
+
ok: false,
|
|
15229
|
+
reason: 'orchestration_depth_exceeded',
|
|
15230
|
+
action: action,
|
|
15231
|
+
depth: depth
|
|
15232
|
+
});
|
|
15233
|
+
case 1:
|
|
14484
15234
|
entry = this.adapters.get(action);
|
|
14485
15235
|
if (entry) {
|
|
14486
|
-
_context2.n =
|
|
15236
|
+
_context2.n = 2;
|
|
14487
15237
|
break;
|
|
14488
15238
|
}
|
|
14489
15239
|
return _context2.a(2, {
|
|
@@ -14491,9 +15241,9 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
|
|
|
14491
15241
|
reason: 'adapter_not_in_bundle',
|
|
14492
15242
|
action: action
|
|
14493
15243
|
});
|
|
14494
|
-
case
|
|
15244
|
+
case 2:
|
|
14495
15245
|
if (!entry.poisoned) {
|
|
14496
|
-
_context2.n =
|
|
15246
|
+
_context2.n = 3;
|
|
14497
15247
|
break;
|
|
14498
15248
|
}
|
|
14499
15249
|
return _context2.a(2, {
|
|
@@ -14502,40 +15252,225 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
|
|
|
14502
15252
|
action: action,
|
|
14503
15253
|
detail: entry.error
|
|
14504
15254
|
});
|
|
14505
|
-
case
|
|
14506
|
-
|
|
14507
|
-
|
|
14508
|
-
|
|
14509
|
-
|
|
14510
|
-
|
|
14511
|
-
|
|
14512
|
-
return _context2.a(2, _context2.v);
|
|
15255
|
+
case 3:
|
|
15256
|
+
safeArgs = args && _typeof(args) === 'object' ? args : {};
|
|
15257
|
+
beforeActions = entry.beforeActions || [];
|
|
15258
|
+
afterActions = entry.afterActions || [];
|
|
15259
|
+
_iterator2 = _createForOfIteratorHelper(beforeActions);
|
|
15260
|
+
_context2.p = 4;
|
|
15261
|
+
_iterator2.s();
|
|
14513
15262
|
case 5:
|
|
14514
|
-
|
|
14515
|
-
|
|
14516
|
-
|
|
14517
|
-
|
|
14518
|
-
|
|
14519
|
-
|
|
14520
|
-
|
|
14521
|
-
|
|
14522
|
-
|
|
14523
|
-
|
|
14524
|
-
|
|
14525
|
-
|
|
14526
|
-
|
|
14527
|
-
|
|
14528
|
-
|
|
14529
|
-
|
|
14530
|
-
|
|
14531
|
-
|
|
14532
|
-
|
|
15263
|
+
if ((_step2 = _iterator2.n()).done) {
|
|
15264
|
+
_context2.n = 9;
|
|
15265
|
+
break;
|
|
15266
|
+
}
|
|
15267
|
+
_hookAction = _step2.value;
|
|
15268
|
+
if (!(!_hookAction || typeof _hookAction !== 'string')) {
|
|
15269
|
+
_context2.n = 6;
|
|
15270
|
+
break;
|
|
15271
|
+
}
|
|
15272
|
+
return _context2.a(3, 8);
|
|
15273
|
+
case 6:
|
|
15274
|
+
console.log("".concat(LOG, " before hook ").concat(action, " \u2192 ").concat(_hookAction));
|
|
15275
|
+
_context2.n = 7;
|
|
15276
|
+
return this.runAdapter(_hookAction, safeArgs, depth + 1);
|
|
15277
|
+
case 7:
|
|
15278
|
+
_hookResult = _context2.v;
|
|
15279
|
+
if (!adapterResultOk(_hookResult)) {
|
|
15280
|
+
console.warn("".concat(LOG, " before hook '").concat(_hookAction, "' for '").concat(action, "' returned ok=false \u2014 continuing with main body:"), _hookResult);
|
|
15281
|
+
}
|
|
15282
|
+
case 8:
|
|
15283
|
+
_context2.n = 5;
|
|
15284
|
+
break;
|
|
15285
|
+
case 9:
|
|
15286
|
+
_context2.n = 11;
|
|
15287
|
+
break;
|
|
15288
|
+
case 10:
|
|
15289
|
+
_context2.p = 10;
|
|
15290
|
+
_t = _context2.v;
|
|
15291
|
+
_iterator2.e(_t);
|
|
15292
|
+
case 11:
|
|
15293
|
+
_context2.p = 11;
|
|
15294
|
+
_iterator2.f();
|
|
15295
|
+
return _context2.f(11);
|
|
15296
|
+
case 12:
|
|
15297
|
+
_context2.n = 13;
|
|
15298
|
+
return this._runAdapterBody(action, safeArgs, entry);
|
|
15299
|
+
case 13:
|
|
15300
|
+
result = _context2.v;
|
|
15301
|
+
if (!(adapterResultOk(result) && afterActions.length > 0)) {
|
|
15302
|
+
_context2.n = 22;
|
|
15303
|
+
break;
|
|
15304
|
+
}
|
|
15305
|
+
afterArgs = _objectSpread(_objectSpread({}, safeArgs), {}, {
|
|
15306
|
+
_parentResult: result
|
|
15307
|
+
});
|
|
15308
|
+
_iterator3 = _createForOfIteratorHelper(afterActions);
|
|
15309
|
+
_context2.p = 14;
|
|
15310
|
+
_iterator3.s();
|
|
15311
|
+
case 15:
|
|
15312
|
+
if ((_step3 = _iterator3.n()).done) {
|
|
15313
|
+
_context2.n = 19;
|
|
15314
|
+
break;
|
|
15315
|
+
}
|
|
15316
|
+
hookAction = _step3.value;
|
|
15317
|
+
if (!(!hookAction || typeof hookAction !== 'string')) {
|
|
15318
|
+
_context2.n = 16;
|
|
15319
|
+
break;
|
|
15320
|
+
}
|
|
15321
|
+
return _context2.a(3, 18);
|
|
15322
|
+
case 16:
|
|
15323
|
+
console.log("".concat(LOG, " after hook ").concat(action, " \u2192 ").concat(hookAction));
|
|
15324
|
+
_context2.n = 17;
|
|
15325
|
+
return this.runAdapter(hookAction, afterArgs, depth + 1);
|
|
15326
|
+
case 17:
|
|
15327
|
+
hookResult = _context2.v;
|
|
15328
|
+
if (!adapterResultOk(hookResult)) {
|
|
15329
|
+
console.warn("".concat(LOG, " after hook '").concat(hookAction, "' for '").concat(action, "' returned ok=false:"), hookResult);
|
|
15330
|
+
}
|
|
15331
|
+
case 18:
|
|
15332
|
+
_context2.n = 15;
|
|
15333
|
+
break;
|
|
15334
|
+
case 19:
|
|
15335
|
+
_context2.n = 21;
|
|
15336
|
+
break;
|
|
15337
|
+
case 20:
|
|
15338
|
+
_context2.p = 20;
|
|
15339
|
+
_t2 = _context2.v;
|
|
15340
|
+
_iterator3.e(_t2);
|
|
15341
|
+
case 21:
|
|
15342
|
+
_context2.p = 21;
|
|
15343
|
+
_iterator3.f();
|
|
15344
|
+
return _context2.f(21);
|
|
15345
|
+
case 22:
|
|
15346
|
+
return _context2.a(2, result);
|
|
15347
|
+
}
|
|
15348
|
+
}, _callee2, this, [[14, 20, 21, 22], [4, 10, 11, 12]]);
|
|
15349
|
+
}));
|
|
15350
|
+
function runAdapter(_x) {
|
|
15351
|
+
return _runAdapter.apply(this, arguments);
|
|
15352
|
+
}
|
|
15353
|
+
return runAdapter;
|
|
15354
|
+
}()
|
|
15355
|
+
/**
|
|
15356
|
+
* Run code_js only — category event + compiled fn. No before/after hooks.
|
|
15357
|
+
* @private
|
|
15358
|
+
*/
|
|
15359
|
+
)
|
|
15360
|
+
}, {
|
|
15361
|
+
key: "_runAdapterBody",
|
|
15362
|
+
value: (function () {
|
|
15363
|
+
var _runAdapterBody2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(action, args, entry) {
|
|
15364
|
+
var _entry$version4;
|
|
15365
|
+
var eventName, safeArgs, productIds, _productIds$, idStr, _idStr, ctx, result, postCtx, _t3, _t4, _t5;
|
|
15366
|
+
return _regenerator().w(function (_context3) {
|
|
15367
|
+
while (1) switch (_context3.p = _context3.n) {
|
|
15368
|
+
case 0:
|
|
15369
|
+
// Fire category event so subscribers can suppress duplicate handling.
|
|
15370
|
+
if (entry.category) {
|
|
15371
|
+
eventName = "netcost:partner-action:".concat(entry.category);
|
|
15372
|
+
safeArgs = args || {};
|
|
15373
|
+
productIds = extractProductIdsFromPartnerArgs(safeArgs);
|
|
15374
|
+
try {
|
|
15375
|
+
window.dispatchEvent(new CustomEvent(eventName, {
|
|
15376
|
+
detail: {
|
|
15377
|
+
action: action,
|
|
15378
|
+
args: safeArgs
|
|
15379
|
+
}
|
|
15380
|
+
}));
|
|
15381
|
+
idStr = productIds.length ? productIds.join(',') : '(none)';
|
|
15382
|
+
console.log("".concat(LOG, " dispatched ").concat(eventName, " action=").concat(action, " productIds=").concat(idStr), {
|
|
15383
|
+
action: action,
|
|
15384
|
+
category: entry.category,
|
|
15385
|
+
productIds: productIds,
|
|
15386
|
+
productId: (_productIds$ = productIds[0]) !== null && _productIds$ !== void 0 ? _productIds$ : null
|
|
15387
|
+
});
|
|
15388
|
+
} catch (e) {
|
|
15389
|
+
_idStr = productIds.length ? productIds.join(',') : '(none)';
|
|
15390
|
+
console.warn("".concat(LOG, " failed to dispatch category event '").concat(eventName, "' for '").concat(action, "' productIds=").concat(_idStr, ":"), e);
|
|
15391
|
+
}
|
|
15392
|
+
}
|
|
15393
|
+
ctx = this._buildCtx(args, action, entry);
|
|
15394
|
+
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)));
|
|
15395
|
+
|
|
15396
|
+
// Inline pre-hook (best-effort): an error is logged and main still runs.
|
|
15397
|
+
if (!entry.preFn) {
|
|
15398
|
+
_context3.n = 4;
|
|
15399
|
+
break;
|
|
15400
|
+
}
|
|
15401
|
+
_context3.p = 1;
|
|
15402
|
+
_context3.n = 2;
|
|
15403
|
+
return entry.preFn(ctx);
|
|
15404
|
+
case 2:
|
|
15405
|
+
_context3.n = 4;
|
|
15406
|
+
break;
|
|
15407
|
+
case 3:
|
|
15408
|
+
_context3.p = 3;
|
|
15409
|
+
_t3 = _context3.v;
|
|
15410
|
+
console.warn("".concat(LOG, " ").concat(action, " inline pre-hook threw (continuing with main body):"), _t3);
|
|
15411
|
+
case 4:
|
|
15412
|
+
_context3.p = 4;
|
|
15413
|
+
_context3.n = 5;
|
|
15414
|
+
return entry.fn(ctx);
|
|
15415
|
+
case 5:
|
|
15416
|
+
result = _context3.v;
|
|
15417
|
+
_context3.n = 7;
|
|
15418
|
+
break;
|
|
15419
|
+
case 6:
|
|
15420
|
+
_context3.p = 6;
|
|
15421
|
+
_t4 = _context3.v;
|
|
15422
|
+
console.error("".concat(LOG, " ").concat(action, " threw:"), _t4);
|
|
15423
|
+
return _context3.a(2, {
|
|
15424
|
+
ok: false,
|
|
15425
|
+
reason: 'adapter_threw',
|
|
15426
|
+
error: String((_t4 === null || _t4 === void 0 ? void 0 : _t4.message) || _t4)
|
|
15427
|
+
});
|
|
15428
|
+
case 7:
|
|
15429
|
+
if (!(entry.postFn && adapterResultOk(result))) {
|
|
15430
|
+
_context3.n = 11;
|
|
15431
|
+
break;
|
|
15432
|
+
}
|
|
15433
|
+
postCtx = this._buildCtx(_objectSpread(_objectSpread({}, args || {}), {}, {
|
|
15434
|
+
_mainResult: result
|
|
15435
|
+
}), action, entry);
|
|
15436
|
+
_context3.p = 8;
|
|
15437
|
+
_context3.n = 9;
|
|
15438
|
+
return entry.postFn(postCtx);
|
|
15439
|
+
case 9:
|
|
15440
|
+
_context3.n = 11;
|
|
15441
|
+
break;
|
|
15442
|
+
case 10:
|
|
15443
|
+
_context3.p = 10;
|
|
15444
|
+
_t5 = _context3.v;
|
|
15445
|
+
console.warn("".concat(LOG, " ").concat(action, " inline post-hook threw (ignored):"), _t5);
|
|
15446
|
+
case 11:
|
|
15447
|
+
return _context3.a(2, result);
|
|
15448
|
+
}
|
|
15449
|
+
}, _callee3, this, [[8, 10], [4, 6], [1, 3]]);
|
|
15450
|
+
}));
|
|
15451
|
+
function _runAdapterBody(_x2, _x3, _x4) {
|
|
15452
|
+
return _runAdapterBody2.apply(this, arguments);
|
|
15453
|
+
}
|
|
15454
|
+
return _runAdapterBody;
|
|
15455
|
+
}())
|
|
15456
|
+
}, {
|
|
15457
|
+
key: "_buildCtx",
|
|
15458
|
+
value: function _buildCtx(args, action /*, entry */) {
|
|
14533
15459
|
var _this = this;
|
|
14534
15460
|
var platform = this.platform;
|
|
14535
|
-
|
|
15461
|
+
var emitCallback = this._emitCallback;
|
|
15462
|
+
// Send an unsolicited message back to the backend (e.g. cart_change_event).
|
|
15463
|
+
// No-op if no emitCallback is registered.
|
|
15464
|
+
var emit = function emit(msg) {
|
|
15465
|
+
if (typeof emitCallback === 'function') {
|
|
15466
|
+
emitCallback(msg);
|
|
15467
|
+
} else {
|
|
15468
|
+
console.warn("".concat(LOG, " [adapter:").concat(action, "] ctx.emit called but no emitCallback registered"));
|
|
15469
|
+
}
|
|
15470
|
+
};
|
|
15471
|
+
var ctx = {
|
|
14536
15472
|
args: args || {},
|
|
14537
15473
|
host: window.location.hostname,
|
|
14538
|
-
platform: platform,
|
|
14539
15474
|
fetch: window.fetch.bind(window),
|
|
14540
15475
|
log: function log() {
|
|
14541
15476
|
var _console;
|
|
@@ -14544,14 +15479,22 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
|
|
|
14544
15479
|
}
|
|
14545
15480
|
return (_console = console).log.apply(_console, ["[adapter:".concat(action, "]")].concat(a));
|
|
14546
15481
|
},
|
|
14547
|
-
refreshUI: refreshUIForPlatform(platform),
|
|
14548
|
-
normalizeCart: function normalizeCart(raw) {
|
|
14549
|
-
return normalizeCartByPlatform(platform, raw);
|
|
14550
|
-
},
|
|
14551
15482
|
runAdapter: function runAdapter(a, subArgs) {
|
|
14552
15483
|
return _this.runAdapter(a, subArgs);
|
|
14553
|
-
}
|
|
15484
|
+
},
|
|
15485
|
+
emit: emit
|
|
14554
15486
|
};
|
|
15487
|
+
// Ecommerce-specific helpers are omitted for the flavor-independent
|
|
15488
|
+
// `__client_tools__` runner (genericCtx). Client-tool scripts get a
|
|
15489
|
+
// platform-agnostic ctx with no cart normalization or UI nudges.
|
|
15490
|
+
if (!this.genericCtx) {
|
|
15491
|
+
ctx.platform = platform;
|
|
15492
|
+
ctx.refreshUI = refreshUIForPlatform(platform);
|
|
15493
|
+
ctx.normalizeCart = function (raw) {
|
|
15494
|
+
return normalizeCartByPlatform(platform, raw);
|
|
15495
|
+
};
|
|
15496
|
+
}
|
|
15497
|
+
return ctx;
|
|
14555
15498
|
}
|
|
14556
15499
|
}]);
|
|
14557
15500
|
}();
|
|
@@ -14568,28 +15511,28 @@ function buildRunPartnerScriptTool(runner) {
|
|
|
14568
15511
|
return {
|
|
14569
15512
|
name: 'run_partner_script',
|
|
14570
15513
|
handler: function () {
|
|
14571
|
-
var _handler = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
15514
|
+
var _handler = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
|
|
14572
15515
|
var parameters,
|
|
14573
15516
|
action,
|
|
14574
15517
|
args,
|
|
14575
|
-
|
|
14576
|
-
return _regenerator().w(function (
|
|
14577
|
-
while (1) switch (
|
|
15518
|
+
_args4 = arguments;
|
|
15519
|
+
return _regenerator().w(function (_context4) {
|
|
15520
|
+
while (1) switch (_context4.n) {
|
|
14578
15521
|
case 0:
|
|
14579
|
-
parameters =
|
|
15522
|
+
parameters = _args4.length > 0 && _args4[0] !== undefined ? _args4[0] : {};
|
|
14580
15523
|
action = parameters.action, args = parameters.args;
|
|
14581
15524
|
if (action) {
|
|
14582
|
-
|
|
15525
|
+
_context4.n = 1;
|
|
14583
15526
|
break;
|
|
14584
15527
|
}
|
|
14585
|
-
return
|
|
15528
|
+
return _context4.a(2, {
|
|
14586
15529
|
ok: false,
|
|
14587
15530
|
reason: 'missing_action'
|
|
14588
15531
|
});
|
|
14589
15532
|
case 1:
|
|
14590
|
-
return
|
|
15533
|
+
return _context4.a(2, runner.runAdapter(action, args || {}));
|
|
14591
15534
|
}
|
|
14592
|
-
},
|
|
15535
|
+
}, _callee4);
|
|
14593
15536
|
}));
|
|
14594
15537
|
function handler() {
|
|
14595
15538
|
return _handler.apply(this, arguments);
|
|
@@ -15365,7 +16308,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
15365
16308
|
* Follows the same pattern as Styles.generateCSS().
|
|
15366
16309
|
*/
|
|
15367
16310
|
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 ";
|
|
16311
|
+
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
16312
|
}
|
|
15370
16313
|
|
|
15371
16314
|
/***/ }),
|
|
@@ -15960,13 +16903,13 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
15960
16903
|
/* harmony import */ var _ecommerce_EcommerceManager_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ecommerce/EcommerceManager.js */ "./src/ecommerce/EcommerceManager.js");
|
|
15961
16904
|
/* harmony import */ var _ecommerce_PartnerScriptRunner_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../ecommerce/PartnerScriptRunner.js */ "./src/ecommerce/PartnerScriptRunner.js");
|
|
15962
16905
|
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
16906
|
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
16907
|
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
16908
|
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
16909
|
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); }); }; }
|
|
16910
|
+
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; }
|
|
16911
|
+
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; }
|
|
16912
|
+
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
16913
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
15971
16914
|
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
16915
|
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
@@ -16067,10 +17010,41 @@ var EcommerceFlavor = /*#__PURE__*/function (_BaseFlavor) {
|
|
|
16067
17010
|
// === DB-backed per-partner adapters (replaces CDN hooks) ===
|
|
16068
17011
|
// Backend pushes the full bundle once at session start; the widget then
|
|
16069
17012
|
// executes individual adapters via the 'run_partner_script' client tool.
|
|
16070
|
-
// Shape: { t: 'partner_bundle', partner_id, platform,
|
|
17013
|
+
// Shape: { t: 'partner_bundle', partner_id, platform,
|
|
17014
|
+
// adapters: { action: {...} }, ← on-demand, invoked by LLM
|
|
17015
|
+
// autoRun: { action: {...} } } ← executed immediately on load
|
|
16071
17016
|
'partner_bundle': function partner_bundle(data) {
|
|
16072
|
-
|
|
16073
|
-
|
|
17017
|
+
// The reserved `__client_tools__` bundle is owned by the
|
|
17018
|
+
// flavor-independent ClientScriptManager at the SDK level — skip it
|
|
17019
|
+
// here so the two bundles stay separate per partner id.
|
|
17020
|
+
if ((data === null || data === void 0 ? void 0 : data.partner_id) === '__client_tools__') return;
|
|
17021
|
+
var runner = _this2._partnerScriptRunner;
|
|
17022
|
+
if (!runner) return;
|
|
17023
|
+
|
|
17024
|
+
// Wire the emit callback BEFORE setBundle so auto-run adapters can use it.
|
|
17025
|
+
runner.setEmitCallback(function (msg) {
|
|
17026
|
+
return _this2.sendToVoiceSDK(msg);
|
|
17027
|
+
});
|
|
17028
|
+
|
|
17029
|
+
// Merge autoRun entries into the adapters map so the runner can look
|
|
17030
|
+
// them up by action name (e.g. for ctx.runAdapter chaining).
|
|
17031
|
+
var merged = _objectSpread(_objectSpread({}, data), {}, {
|
|
17032
|
+
adapters: _objectSpread(_objectSpread({}, (data === null || data === void 0 ? void 0 : data.adapters) || {}), (data === null || data === void 0 ? void 0 : data.autoRun) || {})
|
|
17033
|
+
});
|
|
17034
|
+
runner.setBundle(merged);
|
|
17035
|
+
|
|
17036
|
+
// Execute every autoRun adapter once immediately.
|
|
17037
|
+
var _loop = function _loop() {
|
|
17038
|
+
var action = _Object$keys[_i];
|
|
17039
|
+
runner.runAdapter(action, {}).then(function (r) {
|
|
17040
|
+
return console.log("[EcommerceFlavor] auto-run '".concat(action, "':"), r);
|
|
17041
|
+
}).catch(function (e) {
|
|
17042
|
+
return console.warn("[EcommerceFlavor] auto-run '".concat(action, "' failed:"), e);
|
|
17043
|
+
});
|
|
17044
|
+
};
|
|
17045
|
+
for (var _i = 0, _Object$keys = Object.keys((data === null || data === void 0 ? void 0 : data.autoRun) || {}); _i < _Object$keys.length; _i++) {
|
|
17046
|
+
_loop();
|
|
17047
|
+
}
|
|
16074
17048
|
},
|
|
16075
17049
|
// Shopify: see file header — add_to_store_cart uses /cart/add.js + /cart.js.
|
|
16076
17050
|
'add_to_store_cart': function add_to_store_cart(data) {
|
|
@@ -18925,6 +19899,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
18925
19899
|
/* harmony export */ TTPEcommerceWidget: () => (/* reexport safe */ _ecommerce_TTPEcommerceWidget_js__WEBPACK_IMPORTED_MODULE_7__.TTPEcommerceWidget),
|
|
18926
19900
|
/* harmony export */ TextChatSDK: () => (/* reexport safe */ _core_TextChatSDK_js__WEBPACK_IMPORTED_MODULE_1__["default"]),
|
|
18927
19901
|
/* harmony export */ VERSION: () => (/* binding */ VERSION),
|
|
19902
|
+
/* harmony export */ VoiceSDK: () => (/* binding */ VoiceSDK),
|
|
18928
19903
|
/* harmony export */ VoiceSDK_v2: () => (/* reexport safe */ _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"]),
|
|
18929
19904
|
/* harmony export */ WebSocketManager: () => (/* reexport safe */ _core_WebSocketManager_js__WEBPACK_IMPORTED_MODULE_2__["default"]),
|
|
18930
19905
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__),
|
|
@@ -18955,6 +19930,11 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
18955
19930
|
// Core SDK
|
|
18956
19931
|
|
|
18957
19932
|
|
|
19933
|
+
// Backward-compatible alias: the legacy `VoiceSDK` name (v1) now points at the
|
|
19934
|
+
// v2 implementation. The v2 class defaults to `protocolVersion: 2`, so existing
|
|
19935
|
+
// code and documentation that references `VoiceSDK` keeps working on v2.
|
|
19936
|
+
var VoiceSDK = _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"];
|
|
19937
|
+
|
|
18958
19938
|
|
|
18959
19939
|
|
|
18960
19940
|
|
|
@@ -18976,8 +19956,8 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
18976
19956
|
|
|
18977
19957
|
|
|
18978
19958
|
// Version - injected at build time from package.json via webpack DefinePlugin
|
|
18979
|
-
var VERSION = "2.
|
|
18980
|
-
var BUILD_TIME = "2026-
|
|
19959
|
+
var VERSION = "2.45.0";
|
|
19960
|
+
var BUILD_TIME = "2026-06-12T15:13:11.602Z";
|
|
18981
19961
|
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
19962
|
|
|
18983
19963
|
// Named exports
|
|
@@ -18986,6 +19966,7 @@ console.log("%c TTP Agent SDK v".concat(VERSION, " (").concat(BUILD_TIME, ") "),
|
|
|
18986
19966
|
// Default export
|
|
18987
19967
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({
|
|
18988
19968
|
VoiceSDK_v2: _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"],
|
|
19969
|
+
VoiceSDK: VoiceSDK,
|
|
18989
19970
|
TextChatSDK: _core_TextChatSDK_js__WEBPACK_IMPORTED_MODULE_1__["default"],
|
|
18990
19971
|
WebSocketManager: _core_WebSocketManager_js__WEBPACK_IMPORTED_MODULE_2__["default"],
|
|
18991
19972
|
AudioRecorder: _core_AudioRecorder_js__WEBPACK_IMPORTED_MODULE_3__["default"],
|
|
@@ -23441,8 +24422,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23441
24422
|
console.log('🛑 AudioPlayer: Ignoring PCM chunk - playback was stopped (barge-in)');
|
|
23442
24423
|
return _context.a(2);
|
|
23443
24424
|
case 1:
|
|
23444
|
-
|
|
23445
|
-
|
|
24425
|
+
_context.n = 2;
|
|
24426
|
+
return this.prepareChunk(pcmData);
|
|
24427
|
+
case 2:
|
|
24428
|
+
preparedFrame = _context.v;
|
|
23446
24429
|
if (preparedFrame) {
|
|
23447
24430
|
// CRITICAL: Clear empty sentence timeout since chunks are arriving
|
|
23448
24431
|
// This resets the timer for the current sentence
|
|
@@ -23499,7 +24482,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23499
24482
|
// Emit error event
|
|
23500
24483
|
this.emit('playbackError', new Error('Failed to prepare PCM chunk for playback'));
|
|
23501
24484
|
}
|
|
23502
|
-
case
|
|
24485
|
+
case 3:
|
|
23503
24486
|
return _context.a(2);
|
|
23504
24487
|
}
|
|
23505
24488
|
}, _callee, this);
|
|
@@ -23518,87 +24501,99 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23518
24501
|
)
|
|
23519
24502
|
}, {
|
|
23520
24503
|
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
|
|
24504
|
+
value: (function () {
|
|
24505
|
+
var _prepareChunk = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(pcmData) {
|
|
24506
|
+
var _this$outputFormat2, processedData, padded, int16Array, float32Array, NORMALIZATION, length, i, audioDataSampleRate, contextSampleRate, audioBuffer, chunkDuration, sampleCount, actualDuration, _t;
|
|
24507
|
+
return _regenerator().w(function (_context2) {
|
|
24508
|
+
while (1) switch (_context2.p = _context2.n) {
|
|
24509
|
+
case 0:
|
|
24510
|
+
_context2.p = 0;
|
|
24511
|
+
if (this.outputFormat) {
|
|
24512
|
+
_context2.n = 1;
|
|
24513
|
+
break;
|
|
24514
|
+
}
|
|
24515
|
+
console.error('❌ AudioPlayer: Cannot prepare chunk - outputFormat not set');
|
|
24516
|
+
console.error(' Call setOutputFormat() before playing audio chunks');
|
|
24517
|
+
return _context2.a(2, null);
|
|
24518
|
+
case 1:
|
|
24519
|
+
if (this.audioContext) {
|
|
24520
|
+
_context2.n = 2;
|
|
24521
|
+
break;
|
|
24522
|
+
}
|
|
24523
|
+
_context2.n = 2;
|
|
24524
|
+
return this.initializeAudioContext();
|
|
24525
|
+
case 2:
|
|
24526
|
+
if (this.audioContext) {
|
|
24527
|
+
_context2.n = 3;
|
|
24528
|
+
break;
|
|
24529
|
+
}
|
|
24530
|
+
console.error('❌ AudioPlayer: Cannot prepare chunk - AudioContext not available');
|
|
24531
|
+
return _context2.a(2, null);
|
|
24532
|
+
case 3:
|
|
24533
|
+
// Ensure even byte count for 16-bit PCM
|
|
24534
|
+
processedData = pcmData;
|
|
24535
|
+
if (pcmData.byteLength % 2 !== 0) {
|
|
24536
|
+
console.warn('⚠️ Odd PCM chunk size, padding:', pcmData.byteLength);
|
|
24537
|
+
padded = new Uint8Array(pcmData.byteLength + 1);
|
|
24538
|
+
padded.set(new Uint8Array(pcmData), 0);
|
|
24539
|
+
padded[pcmData.byteLength] = 0; // Silence padding
|
|
23564
24540
|
|
|
23565
|
-
|
|
23566
|
-
|
|
23567
|
-
var audioBuffer = this.audioContext.createBuffer(1,
|
|
23568
|
-
// mono
|
|
24541
|
+
processedData = padded.buffer;
|
|
24542
|
+
}
|
|
23569
24543
|
|
|
23570
|
-
|
|
23571
|
-
|
|
24544
|
+
// Convert Int16 PCM to Float32 (pre-processing)
|
|
24545
|
+
int16Array = new Int16Array(processedData);
|
|
24546
|
+
float32Array = new Float32Array(int16Array.length);
|
|
24547
|
+
NORMALIZATION = 1.0 / 32768.0;
|
|
24548
|
+
length = int16Array.length;
|
|
24549
|
+
for (i = 0; i < length; i++) {
|
|
24550
|
+
float32Array[i] = int16Array[i] * NORMALIZATION;
|
|
24551
|
+
}
|
|
23572
24552
|
|
|
23573
|
-
|
|
24553
|
+
// Create audio buffer with the ACTUAL sample rate of the audio data
|
|
24554
|
+
audioDataSampleRate = ((_this$outputFormat2 = this.outputFormat) === null || _this$outputFormat2 === void 0 ? void 0 : _this$outputFormat2.sampleRate) || this.audioContext.sampleRate;
|
|
24555
|
+
contextSampleRate = this.audioContext.sampleRate;
|
|
24556
|
+
audioBuffer = this.audioContext.createBuffer(1,
|
|
24557
|
+
// mono
|
|
23574
24558
|
|
|
23575
|
-
|
|
23576
|
-
|
|
23577
|
-
var actualDuration = chunkDuration;
|
|
23578
|
-
if (contextSampleRate !== audioDataSampleRate) {
|
|
23579
|
-
actualDuration = sampleCount / contextSampleRate;
|
|
23580
|
-
}
|
|
24559
|
+
float32Array.length, audioDataSampleRate);
|
|
24560
|
+
audioBuffer.getChannelData(0).set(float32Array);
|
|
23581
24561
|
|
|
23582
|
-
|
|
24562
|
+
// Calculate duration (handle browser resampling)
|
|
24563
|
+
chunkDuration = audioBuffer.duration;
|
|
24564
|
+
sampleCount = float32Array.length;
|
|
24565
|
+
actualDuration = chunkDuration;
|
|
24566
|
+
if (contextSampleRate !== audioDataSampleRate) {
|
|
24567
|
+
actualDuration = sampleCount / contextSampleRate;
|
|
24568
|
+
}
|
|
23583
24569
|
|
|
23584
|
-
|
|
23585
|
-
|
|
23586
|
-
|
|
23587
|
-
|
|
23588
|
-
|
|
23589
|
-
|
|
23590
|
-
|
|
23591
|
-
|
|
23592
|
-
|
|
24570
|
+
// Return prepared frame
|
|
24571
|
+
return _context2.a(2, {
|
|
24572
|
+
buffer: audioBuffer,
|
|
24573
|
+
duration: actualDuration,
|
|
24574
|
+
sampleRate: audioDataSampleRate,
|
|
24575
|
+
contextSampleRate: contextSampleRate
|
|
24576
|
+
});
|
|
24577
|
+
case 4:
|
|
24578
|
+
_context2.p = 4;
|
|
24579
|
+
_t = _context2.v;
|
|
24580
|
+
console.error('❌ AudioPlayer: Error preparing chunk:', _t);
|
|
24581
|
+
return _context2.a(2, null);
|
|
24582
|
+
}
|
|
24583
|
+
}, _callee2, this, [[0, 4]]);
|
|
24584
|
+
}));
|
|
24585
|
+
function prepareChunk(_x2) {
|
|
24586
|
+
return _prepareChunk.apply(this, arguments);
|
|
23593
24587
|
}
|
|
23594
|
-
|
|
23595
|
-
|
|
24588
|
+
return prepareChunk;
|
|
24589
|
+
}()
|
|
23596
24590
|
/**
|
|
23597
24591
|
* Apply fade-out to audio buffer (stub method to prevent errors)
|
|
23598
24592
|
* This method is called by some code paths but doesn't need to do anything
|
|
23599
24593
|
* @param {AudioBuffer} audioBuffer - Audio buffer to fade
|
|
23600
24594
|
* @returns {AudioBuffer} - Same audio buffer (no modification)
|
|
23601
24595
|
*/
|
|
24596
|
+
)
|
|
23602
24597
|
}, {
|
|
23603
24598
|
key: "applyFadeOut",
|
|
23604
24599
|
value: function applyFadeOut(audioBuffer) {
|
|
@@ -23614,24 +24609,24 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23614
24609
|
}, {
|
|
23615
24610
|
key: "schedulePreparedFrames",
|
|
23616
24611
|
value: (function () {
|
|
23617
|
-
var _schedulePreparedFrames = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
24612
|
+
var _schedulePreparedFrames = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
|
|
23618
24613
|
var _this4 = this;
|
|
23619
|
-
var queuedFrames, targetLookaheadFrames, scheduledCount, _loop, _ret,
|
|
23620
|
-
return _regenerator().w(function (
|
|
23621
|
-
while (1) switch (
|
|
24614
|
+
var queuedFrames, targetLookaheadFrames, scheduledCount, _loop, _ret, _t2;
|
|
24615
|
+
return _regenerator().w(function (_context4) {
|
|
24616
|
+
while (1) switch (_context4.p = _context4.n) {
|
|
23622
24617
|
case 0:
|
|
23623
24618
|
if (!this._isStopped) {
|
|
23624
|
-
|
|
24619
|
+
_context4.n = 1;
|
|
23625
24620
|
break;
|
|
23626
24621
|
}
|
|
23627
24622
|
console.log('🛑 AudioPlayer: Not scheduling frames - playback was stopped (barge-in)');
|
|
23628
|
-
return
|
|
24623
|
+
return _context4.a(2);
|
|
23629
24624
|
case 1:
|
|
23630
24625
|
if (!this.isSchedulingFrames) {
|
|
23631
|
-
|
|
24626
|
+
_context4.n = 2;
|
|
23632
24627
|
break;
|
|
23633
24628
|
}
|
|
23634
|
-
return
|
|
24629
|
+
return _context4.a(2);
|
|
23635
24630
|
case 2:
|
|
23636
24631
|
this.isSchedulingFrames = true;
|
|
23637
24632
|
|
|
@@ -23644,17 +24639,8 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23644
24639
|
if (targetLookaheadFrames === 0 && queuedFrames > 0) {
|
|
23645
24640
|
targetLookaheadFrames = 1; // At least schedule 1 frame
|
|
23646
24641
|
}
|
|
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;
|
|
24642
|
+
_context4.p = 3;
|
|
24643
|
+
_context4.n = 4;
|
|
23658
24644
|
return this.waitForAudioContextReady();
|
|
23659
24645
|
case 4:
|
|
23660
24646
|
// Schedule frames up to target lookahead (ensures smooth playback)
|
|
@@ -23662,23 +24648,23 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23662
24648
|
scheduledCount = 0;
|
|
23663
24649
|
_loop = /*#__PURE__*/_regenerator().m(function _loop() {
|
|
23664
24650
|
var preparedFrame, source, currentTime, isMobile, startupBuffer, minStartTime, startTime;
|
|
23665
|
-
return _regenerator().w(function (
|
|
23666
|
-
while (1) switch (
|
|
24651
|
+
return _regenerator().w(function (_context3) {
|
|
24652
|
+
while (1) switch (_context3.n) {
|
|
23667
24653
|
case 0:
|
|
23668
24654
|
if (!_this4._isStopped) {
|
|
23669
|
-
|
|
24655
|
+
_context3.n = 1;
|
|
23670
24656
|
break;
|
|
23671
24657
|
}
|
|
23672
24658
|
console.log('🛑 AudioPlayer: Stopping frame scheduling - playback was stopped');
|
|
23673
|
-
return
|
|
24659
|
+
return _context3.a(2, 0);
|
|
23674
24660
|
case 1:
|
|
23675
24661
|
// Get next prepared frame
|
|
23676
24662
|
preparedFrame = _this4.preparedBuffer.shift();
|
|
23677
24663
|
if (preparedFrame) {
|
|
23678
|
-
|
|
24664
|
+
_context3.n = 2;
|
|
23679
24665
|
break;
|
|
23680
24666
|
}
|
|
23681
|
-
return
|
|
24667
|
+
return _context3.a(2, 0);
|
|
23682
24668
|
case 2:
|
|
23683
24669
|
// Create source and schedule playback
|
|
23684
24670
|
source = _this4.audioContext.createBufferSource();
|
|
@@ -23832,25 +24818,25 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23832
24818
|
_this4.emit('playbackStarted');
|
|
23833
24819
|
}
|
|
23834
24820
|
case 3:
|
|
23835
|
-
return
|
|
24821
|
+
return _context3.a(2);
|
|
23836
24822
|
}
|
|
23837
24823
|
}, _loop);
|
|
23838
24824
|
});
|
|
23839
24825
|
case 5:
|
|
23840
24826
|
if (!(this.preparedBuffer.length > 0 && scheduledCount < targetLookaheadFrames)) {
|
|
23841
|
-
|
|
24827
|
+
_context4.n = 8;
|
|
23842
24828
|
break;
|
|
23843
24829
|
}
|
|
23844
|
-
return
|
|
24830
|
+
return _context4.d(_regeneratorValues(_loop()), 6);
|
|
23845
24831
|
case 6:
|
|
23846
|
-
_ret =
|
|
24832
|
+
_ret = _context4.v;
|
|
23847
24833
|
if (!(_ret === 0)) {
|
|
23848
|
-
|
|
24834
|
+
_context4.n = 7;
|
|
23849
24835
|
break;
|
|
23850
24836
|
}
|
|
23851
|
-
return
|
|
24837
|
+
return _context4.a(3, 8);
|
|
23852
24838
|
case 7:
|
|
23853
|
-
|
|
24839
|
+
_context4.n = 5;
|
|
23854
24840
|
break;
|
|
23855
24841
|
case 8:
|
|
23856
24842
|
// All prepared frames scheduled, reset flag
|
|
@@ -23892,18 +24878,18 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23892
24878
|
}
|
|
23893
24879
|
}, 10); // Check every 10ms for new frames
|
|
23894
24880
|
}
|
|
23895
|
-
|
|
24881
|
+
_context4.n = 10;
|
|
23896
24882
|
break;
|
|
23897
24883
|
case 9:
|
|
23898
|
-
|
|
23899
|
-
|
|
23900
|
-
console.error('❌ AudioPlayer v2: Error scheduling frames:',
|
|
23901
|
-
this.emit('playbackError',
|
|
24884
|
+
_context4.p = 9;
|
|
24885
|
+
_t2 = _context4.v;
|
|
24886
|
+
console.error('❌ AudioPlayer v2: Error scheduling frames:', _t2);
|
|
24887
|
+
this.emit('playbackError', _t2);
|
|
23902
24888
|
this.isSchedulingFrames = false;
|
|
23903
24889
|
case 10:
|
|
23904
|
-
return
|
|
24890
|
+
return _context4.a(2);
|
|
23905
24891
|
}
|
|
23906
|
-
},
|
|
24892
|
+
}, _callee3, this, [[3, 9]]);
|
|
23907
24893
|
}));
|
|
23908
24894
|
function schedulePreparedFrames() {
|
|
23909
24895
|
return _schedulePreparedFrames.apply(this, arguments);
|
|
@@ -23918,56 +24904,47 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
23918
24904
|
}, {
|
|
23919
24905
|
key: "processPcmQueue",
|
|
23920
24906
|
value: (function () {
|
|
23921
|
-
var _processPcmQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
24907
|
+
var _processPcmQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
|
|
23922
24908
|
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 (
|
|
24909
|
+
var _this$outputFormat3, _this$outputFormat4, pcmData, processedData, padded, int16Array, float32Array, NORMALIZATION, length, i, audioDataSampleRate, contextSampleRate, audioBuffer, source, currentTime, chunkDuration, sampleCount, actualDuration, startTime, _t3;
|
|
24910
|
+
return _regenerator().w(function (_context5) {
|
|
24911
|
+
while (1) switch (_context5.p = _context5.n) {
|
|
23926
24912
|
case 0:
|
|
23927
24913
|
if (!this._isStopped) {
|
|
23928
|
-
|
|
24914
|
+
_context5.n = 1;
|
|
23929
24915
|
break;
|
|
23930
24916
|
}
|
|
23931
24917
|
console.log('🛑 AudioPlayer: Not processing PCM queue - playback was stopped (barge-in)');
|
|
23932
|
-
return
|
|
24918
|
+
return _context5.a(2);
|
|
23933
24919
|
case 1:
|
|
23934
24920
|
if (!this.isProcessingPcmQueue) {
|
|
23935
|
-
|
|
24921
|
+
_context5.n = 2;
|
|
23936
24922
|
break;
|
|
23937
24923
|
}
|
|
23938
|
-
return
|
|
24924
|
+
return _context5.a(2);
|
|
23939
24925
|
case 2:
|
|
23940
24926
|
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;
|
|
24927
|
+
_context5.p = 3;
|
|
24928
|
+
_context5.n = 4;
|
|
23952
24929
|
return this.waitForAudioContextReady();
|
|
23953
24930
|
case 4:
|
|
23954
24931
|
if (!(this.pcmChunkQueue.length > 0)) {
|
|
23955
|
-
|
|
24932
|
+
_context5.n = 7;
|
|
23956
24933
|
break;
|
|
23957
24934
|
}
|
|
23958
24935
|
if (!this._isStopped) {
|
|
23959
|
-
|
|
24936
|
+
_context5.n = 5;
|
|
23960
24937
|
break;
|
|
23961
24938
|
}
|
|
23962
24939
|
console.log('🛑 AudioPlayer: Stopping PCM queue processing - playback was stopped');
|
|
23963
|
-
return
|
|
24940
|
+
return _context5.a(3, 7);
|
|
23964
24941
|
case 5:
|
|
23965
24942
|
pcmData = this.pcmChunkQueue.shift();
|
|
23966
24943
|
if (pcmData) {
|
|
23967
|
-
|
|
24944
|
+
_context5.n = 6;
|
|
23968
24945
|
break;
|
|
23969
24946
|
}
|
|
23970
|
-
return
|
|
24947
|
+
return _context5.a(3, 4);
|
|
23971
24948
|
case 6:
|
|
23972
24949
|
// Ensure even byte count for 16-bit PCM
|
|
23973
24950
|
processedData = pcmData;
|
|
@@ -24060,25 +25037,25 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24060
25037
|
this.isPlaying = true;
|
|
24061
25038
|
this.emit('playbackStarted');
|
|
24062
25039
|
}
|
|
24063
|
-
|
|
25040
|
+
_context5.n = 4;
|
|
24064
25041
|
break;
|
|
24065
25042
|
case 7:
|
|
24066
25043
|
// end while loop
|
|
24067
25044
|
|
|
24068
25045
|
// All chunks scheduled, reset processing flag
|
|
24069
25046
|
this.isProcessingPcmQueue = false;
|
|
24070
|
-
|
|
25047
|
+
_context5.n = 9;
|
|
24071
25048
|
break;
|
|
24072
25049
|
case 8:
|
|
24073
|
-
|
|
24074
|
-
|
|
24075
|
-
console.error('❌ AudioPlayer v2: Error playing chunk:',
|
|
24076
|
-
this.emit('playbackError',
|
|
25050
|
+
_context5.p = 8;
|
|
25051
|
+
_t3 = _context5.v;
|
|
25052
|
+
console.error('❌ AudioPlayer v2: Error playing chunk:', _t3);
|
|
25053
|
+
this.emit('playbackError', _t3);
|
|
24077
25054
|
this.isProcessingPcmQueue = false;
|
|
24078
25055
|
case 9:
|
|
24079
|
-
return
|
|
25056
|
+
return _context5.a(2);
|
|
24080
25057
|
}
|
|
24081
|
-
},
|
|
25058
|
+
}, _callee4, this, [[3, 8]]);
|
|
24082
25059
|
}));
|
|
24083
25060
|
function processPcmQueue() {
|
|
24084
25061
|
return _processPcmQueue.apply(this, arguments);
|
|
@@ -24463,6 +25440,40 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24463
25440
|
return header;
|
|
24464
25441
|
}
|
|
24465
25442
|
|
|
25443
|
+
/**
|
|
25444
|
+
* Drop stale instance/shared references to a closed AudioContext so
|
|
25445
|
+
* initializeAudioContext() cannot no-op while the context stays closed.
|
|
25446
|
+
* @private
|
|
25447
|
+
*/
|
|
25448
|
+
}, {
|
|
25449
|
+
key: "_invalidateClosedAudioContext",
|
|
25450
|
+
value: function _invalidateClosedAudioContext() {
|
|
25451
|
+
if (this._audioContextStateChangeHandler && this.audioContext) {
|
|
25452
|
+
try {
|
|
25453
|
+
this.audioContext.removeEventListener('statechange', this._audioContextStateChangeHandler);
|
|
25454
|
+
} catch (_) {/* already detached */}
|
|
25455
|
+
this._audioContextStateChangeHandler = null;
|
|
25456
|
+
}
|
|
25457
|
+
if (this._waitForReadyStateHandler && this.audioContext) {
|
|
25458
|
+
try {
|
|
25459
|
+
this.audioContext.removeEventListener('statechange', this._waitForReadyStateHandler);
|
|
25460
|
+
} catch (_) {/* already detached */}
|
|
25461
|
+
this._waitForReadyStateHandler = null;
|
|
25462
|
+
}
|
|
25463
|
+
if (this.gainNode) {
|
|
25464
|
+
try {
|
|
25465
|
+
this.gainNode.disconnect();
|
|
25466
|
+
} catch (_) {/* ignore */}
|
|
25467
|
+
this.gainNode = null;
|
|
25468
|
+
}
|
|
25469
|
+
if (this.audioContext === _sharedPlayerContext) {
|
|
25470
|
+
_sharedPlayerContext = null;
|
|
25471
|
+
_sharedPlayerSampleRate = null;
|
|
25472
|
+
}
|
|
25473
|
+
this.audioContext = null;
|
|
25474
|
+
this._audioContextPrimed = false;
|
|
25475
|
+
}
|
|
25476
|
+
|
|
24466
25477
|
/**
|
|
24467
25478
|
* Wait for AudioContext to be in 'running' state before proceeding
|
|
24468
25479
|
* This is critical for mobile devices where AudioContext initialization takes time
|
|
@@ -24472,41 +25483,85 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24472
25483
|
}, {
|
|
24473
25484
|
key: "waitForAudioContextReady",
|
|
24474
25485
|
value: (function () {
|
|
24475
|
-
var _waitForAudioContextReady = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
25486
|
+
var _waitForAudioContextReady = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
|
|
25487
|
+
return _regenerator().w(function (_context6) {
|
|
25488
|
+
while (1) switch (_context6.n) {
|
|
25489
|
+
case 0:
|
|
25490
|
+
return _context6.a(2, this._waitForAudioContextReadyImpl(0));
|
|
25491
|
+
}
|
|
25492
|
+
}, _callee5, this);
|
|
25493
|
+
}));
|
|
25494
|
+
function waitForAudioContextReady() {
|
|
25495
|
+
return _waitForAudioContextReady.apply(this, arguments);
|
|
25496
|
+
}
|
|
25497
|
+
return waitForAudioContextReady;
|
|
25498
|
+
}())
|
|
25499
|
+
}, {
|
|
25500
|
+
key: "_waitForAudioContextReadyImpl",
|
|
25501
|
+
value: function () {
|
|
25502
|
+
var _waitForAudioContextReadyImpl2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(attempt) {
|
|
24476
25503
|
var _this6 = this;
|
|
24477
|
-
var audioContext;
|
|
24478
|
-
return _regenerator().w(function (
|
|
24479
|
-
while (1) switch (
|
|
25504
|
+
var MAX_RECREATE_ATTEMPTS, _this$audioContext2, audioContext;
|
|
25505
|
+
return _regenerator().w(function (_context7) {
|
|
25506
|
+
while (1) switch (_context7.n) {
|
|
24480
25507
|
case 0:
|
|
24481
|
-
|
|
24482
|
-
|
|
25508
|
+
MAX_RECREATE_ATTEMPTS = 3;
|
|
25509
|
+
if (!(!this.audioContext || this.audioContext.state === 'closed')) {
|
|
25510
|
+
_context7.n = 1;
|
|
25511
|
+
break;
|
|
25512
|
+
}
|
|
25513
|
+
if (((_this$audioContext2 = this.audioContext) === null || _this$audioContext2 === void 0 ? void 0 : _this$audioContext2.state) === 'closed') {
|
|
25514
|
+
this._invalidateClosedAudioContext();
|
|
24483
25515
|
}
|
|
25516
|
+
_context7.n = 1;
|
|
25517
|
+
return this.initializeAudioContext();
|
|
25518
|
+
case 1:
|
|
24484
25519
|
if (this.audioContext) {
|
|
24485
|
-
|
|
25520
|
+
_context7.n = 2;
|
|
24486
25521
|
break;
|
|
24487
25522
|
}
|
|
24488
25523
|
console.error('❌ AudioPlayer: Failed to create AudioContext');
|
|
24489
|
-
return
|
|
24490
|
-
case
|
|
25524
|
+
return _context7.a(2);
|
|
25525
|
+
case 2:
|
|
24491
25526
|
audioContext = this.audioContext;
|
|
24492
25527
|
if (!(audioContext.state === 'running')) {
|
|
24493
|
-
|
|
25528
|
+
_context7.n = 3;
|
|
24494
25529
|
break;
|
|
24495
25530
|
}
|
|
24496
|
-
return
|
|
24497
|
-
case
|
|
25531
|
+
return _context7.a(2);
|
|
25532
|
+
case 3:
|
|
25533
|
+
if (!(audioContext.state === 'closed')) {
|
|
25534
|
+
_context7.n = 6;
|
|
25535
|
+
break;
|
|
25536
|
+
}
|
|
25537
|
+
if (!(attempt >= MAX_RECREATE_ATTEMPTS)) {
|
|
25538
|
+
_context7.n = 4;
|
|
25539
|
+
break;
|
|
25540
|
+
}
|
|
25541
|
+
console.error('❌ AudioPlayer: AudioContext still closed after recreation attempts — aborting playback setup');
|
|
25542
|
+
return _context7.a(2);
|
|
25543
|
+
case 4:
|
|
25544
|
+
if (attempt === 0) {
|
|
25545
|
+
console.warn('⚠️ AudioPlayer: AudioContext was closed, recreating...');
|
|
25546
|
+
}
|
|
25547
|
+
this._invalidateClosedAudioContext();
|
|
25548
|
+
_context7.n = 5;
|
|
25549
|
+
return this.initializeAudioContext();
|
|
25550
|
+
case 5:
|
|
25551
|
+
return _context7.a(2, this._waitForAudioContextReadyImpl(attempt + 1));
|
|
25552
|
+
case 6:
|
|
24498
25553
|
if (!(audioContext.state === 'suspended')) {
|
|
24499
|
-
|
|
25554
|
+
_context7.n = 8;
|
|
24500
25555
|
break;
|
|
24501
25556
|
}
|
|
24502
|
-
|
|
25557
|
+
_context7.n = 7;
|
|
24503
25558
|
return audioContext.resume();
|
|
24504
|
-
case
|
|
25559
|
+
case 7:
|
|
24505
25560
|
if (!(audioContext.state !== 'running')) {
|
|
24506
|
-
|
|
25561
|
+
_context7.n = 8;
|
|
24507
25562
|
break;
|
|
24508
25563
|
}
|
|
24509
|
-
return
|
|
25564
|
+
return _context7.a(2, new Promise(function (resolve) {
|
|
24510
25565
|
var timeout = setTimeout(function () {
|
|
24511
25566
|
if (audioContext && _this6._waitForReadyStateHandler) {
|
|
24512
25567
|
audioContext.removeEventListener('statechange', _this6._waitForReadyStateHandler);
|
|
@@ -24543,161 +25598,190 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24543
25598
|
resolve();
|
|
24544
25599
|
}
|
|
24545
25600
|
}));
|
|
24546
|
-
case
|
|
24547
|
-
|
|
24548
|
-
|
|
25601
|
+
case 8:
|
|
25602
|
+
return _context7.a(2);
|
|
25603
|
+
}
|
|
25604
|
+
}, _callee6, this);
|
|
25605
|
+
}));
|
|
25606
|
+
function _waitForAudioContextReadyImpl(_x3) {
|
|
25607
|
+
return _waitForAudioContextReadyImpl2.apply(this, arguments);
|
|
25608
|
+
}
|
|
25609
|
+
return _waitForAudioContextReadyImpl;
|
|
25610
|
+
}()
|
|
25611
|
+
/**
|
|
25612
|
+
* Initialize audio context with correct sample rate
|
|
25613
|
+
*/
|
|
25614
|
+
}, {
|
|
25615
|
+
key: "initializeAudioContext",
|
|
25616
|
+
value: (function () {
|
|
25617
|
+
var _initializeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7() {
|
|
25618
|
+
var _this$outputFormat5,
|
|
25619
|
+
_this7 = this,
|
|
25620
|
+
_this$outputFormat6;
|
|
25621
|
+
var desiredSampleRate, currentSampleRate, canReuseShared, setupAfterResume, _t4;
|
|
25622
|
+
return _regenerator().w(function (_context8) {
|
|
25623
|
+
while (1) switch (_context8.p = _context8.n) {
|
|
25624
|
+
case 0:
|
|
25625
|
+
// Use negotiated sample rate, default to 24kHz to match typical server/TTS output
|
|
25626
|
+
desiredSampleRate = ((_this$outputFormat5 = this.outputFormat) === null || _this$outputFormat5 === void 0 ? void 0 : _this$outputFormat5.sampleRate) || 24000;
|
|
25627
|
+
if (![24000, 44100, 48000].includes(desiredSampleRate)) {
|
|
25628
|
+
console.log("\u2139\uFE0F AudioPlayer: Backend requested ".concat(desiredSampleRate, "Hz, but browser may resample"));
|
|
25629
|
+
console.log(" Consider requesting 24000Hz, 44100Hz, or 48000Hz from backend to reduce resampling");
|
|
25630
|
+
}
|
|
25631
|
+
if (_sharedPlayerContext && _sharedPlayerContext.state === 'closed') {
|
|
25632
|
+
_sharedPlayerContext = null;
|
|
25633
|
+
_sharedPlayerSampleRate = null;
|
|
25634
|
+
}
|
|
25635
|
+
|
|
25636
|
+
// Check if current instance AudioContext exists and matches
|
|
25637
|
+
if (!this.audioContext) {
|
|
25638
|
+
_context8.n = 3;
|
|
25639
|
+
break;
|
|
25640
|
+
}
|
|
25641
|
+
if (!(this.audioContext.state === 'closed')) {
|
|
25642
|
+
_context8.n = 1;
|
|
25643
|
+
break;
|
|
25644
|
+
}
|
|
25645
|
+
this._invalidateClosedAudioContext();
|
|
25646
|
+
_context8.n = 3;
|
|
25647
|
+
break;
|
|
25648
|
+
case 1:
|
|
25649
|
+
currentSampleRate = this.audioContext.sampleRate;
|
|
25650
|
+
if (!(Math.abs(currentSampleRate - desiredSampleRate) > 100)) {
|
|
25651
|
+
_context8.n = 2;
|
|
25652
|
+
break;
|
|
25653
|
+
}
|
|
25654
|
+
console.warn("\u26A0\uFE0F AudioPlayer: AudioContext sample rate (".concat(currentSampleRate, "Hz) doesn't match format (").concat(desiredSampleRate, "Hz), recreating..."));
|
|
25655
|
+
this.stopImmediate();
|
|
25656
|
+
this._cleanupAudioContext();
|
|
25657
|
+
_context8.n = 3;
|
|
25658
|
+
break;
|
|
25659
|
+
case 2:
|
|
25660
|
+
return _context8.a(2);
|
|
25661
|
+
case 3:
|
|
25662
|
+
// iOS FIX: Reuse shared AudioContext if available and compatible.
|
|
25663
|
+
// iOS WebKit doesn't release audio hardware synchronously on AudioContext.close(),
|
|
25664
|
+
// causing newly created AudioContexts to fail silently.
|
|
25665
|
+
canReuseShared = _sharedPlayerContext && _sharedPlayerContext.state !== 'closed' && _sharedPlayerSampleRate === desiredSampleRate;
|
|
25666
|
+
if (!canReuseShared) {
|
|
25667
|
+
_context8.n = 8;
|
|
24549
25668
|
break;
|
|
24550
25669
|
}
|
|
24551
|
-
console.
|
|
24552
|
-
this.
|
|
24553
|
-
|
|
24554
|
-
|
|
24555
|
-
|
|
25670
|
+
console.log("\u267B\uFE0F AudioPlayer: Reusing shared AudioContext at ".concat(desiredSampleRate, "Hz (iOS-safe)"));
|
|
25671
|
+
this.audioContext = _sharedPlayerContext;
|
|
25672
|
+
setupAfterResume = function setupAfterResume() {
|
|
25673
|
+
_this7.setupAudioContextStateMonitoring();
|
|
25674
|
+
if (_this7.gainNode) {
|
|
25675
|
+
try {
|
|
25676
|
+
_this7.gainNode.disconnect();
|
|
25677
|
+
} catch (e) {
|
|
25678
|
+
console.warn('⚠️ AudioPlayer: Error disconnecting old GainNode:', e);
|
|
25679
|
+
}
|
|
25680
|
+
}
|
|
25681
|
+
_this7.gainNode = _this7.audioContext.createGain();
|
|
25682
|
+
_this7.gainNode.gain.value = 1.0;
|
|
25683
|
+
_this7.gainNode.connect(_this7.audioContext.destination);
|
|
25684
|
+
if (!_this7._audioContextPrimed) {
|
|
25685
|
+
_this7._primeAudioContext();
|
|
25686
|
+
}
|
|
25687
|
+
};
|
|
25688
|
+
if (!(this.audioContext.state === 'suspended')) {
|
|
25689
|
+
_context8.n = 7;
|
|
25690
|
+
break;
|
|
25691
|
+
}
|
|
25692
|
+
_context8.p = 4;
|
|
25693
|
+
_context8.n = 5;
|
|
25694
|
+
return this.audioContext.resume();
|
|
25695
|
+
case 5:
|
|
25696
|
+
_context8.n = 7;
|
|
25697
|
+
break;
|
|
25698
|
+
case 6:
|
|
25699
|
+
_context8.p = 6;
|
|
25700
|
+
_t4 = _context8.v;
|
|
25701
|
+
console.warn('⚠️ AudioPlayer: Error resuming shared context:', _t4);
|
|
25702
|
+
case 7:
|
|
25703
|
+
setupAfterResume();
|
|
25704
|
+
return _context8.a(2);
|
|
25705
|
+
case 8:
|
|
25706
|
+
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', ")"));
|
|
25707
|
+
|
|
25708
|
+
// Close old shared context if sample rate changed
|
|
25709
|
+
if (_sharedPlayerContext && _sharedPlayerContext.state !== 'closed') {
|
|
25710
|
+
console.log('🔄 AudioPlayer: Closing old shared AudioContext (sample rate changed)');
|
|
25711
|
+
try {
|
|
25712
|
+
_sharedPlayerContext.close();
|
|
25713
|
+
} catch (e) {
|
|
25714
|
+
console.warn('⚠️ AudioPlayer: Error closing old shared context:', e);
|
|
25715
|
+
}
|
|
25716
|
+
}
|
|
25717
|
+
try {
|
|
25718
|
+
this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
|
|
25719
|
+
sampleRate: desiredSampleRate,
|
|
25720
|
+
latencyHint: 'playback'
|
|
25721
|
+
});
|
|
25722
|
+
|
|
25723
|
+
// Store as shared context
|
|
25724
|
+
_sharedPlayerContext = this.audioContext;
|
|
25725
|
+
_sharedPlayerSampleRate = desiredSampleRate;
|
|
25726
|
+
console.log("\u2705 AudioContext created at ".concat(this.audioContext.sampleRate, "Hz (requested: ").concat(desiredSampleRate, "Hz)"));
|
|
25727
|
+
if (Math.abs(this.audioContext.sampleRate - desiredSampleRate) > 100) {
|
|
25728
|
+
console.error("\u274C CRITICAL: Browser sample rate mismatch!");
|
|
25729
|
+
console.error(" Requested: ".concat(desiredSampleRate, "Hz"));
|
|
25730
|
+
console.error(" Got: ".concat(this.audioContext.sampleRate, "Hz"));
|
|
25731
|
+
console.error(" This WILL cause audio distortion/noise!");
|
|
25732
|
+
console.error(" Solution: Backend should send ".concat(this.audioContext.sampleRate, "Hz audio instead"));
|
|
25733
|
+
} else if (this.audioContext.sampleRate !== desiredSampleRate) {
|
|
25734
|
+
console.warn("\u26A0\uFE0F Browser adjusted sample rate: ".concat(desiredSampleRate, "Hz \u2192 ").concat(this.audioContext.sampleRate, "Hz"));
|
|
25735
|
+
console.warn(" Browser will automatically resample audio.");
|
|
25736
|
+
}
|
|
25737
|
+
this.setupAudioContextStateMonitoring();
|
|
25738
|
+
this.gainNode = this.audioContext.createGain();
|
|
25739
|
+
this.gainNode.gain.value = 1.0;
|
|
25740
|
+
this.gainNode.connect(this.audioContext.destination);
|
|
25741
|
+
console.log('✅ AudioPlayer: GainNode created for volume control');
|
|
25742
|
+
this._primeAudioContext();
|
|
25743
|
+
} catch (error) {
|
|
25744
|
+
console.error("\u274C Failed to create AudioContext:", error);
|
|
25745
|
+
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
25746
|
+
_sharedPlayerContext = this.audioContext;
|
|
25747
|
+
_sharedPlayerSampleRate = this.audioContext.sampleRate;
|
|
25748
|
+
console.log("\u2139\uFE0F Using browser default: ".concat(this.audioContext.sampleRate, "Hz"));
|
|
25749
|
+
this.setupAudioContextStateMonitoring();
|
|
25750
|
+
this.gainNode = this.audioContext.createGain();
|
|
25751
|
+
this.gainNode.gain.value = 1.0;
|
|
25752
|
+
this.gainNode.connect(this.audioContext.destination);
|
|
25753
|
+
console.log('✅ AudioPlayer: GainNode created for volume control (fallback)');
|
|
25754
|
+
this._primeAudioContext();
|
|
25755
|
+
}
|
|
25756
|
+
case 9:
|
|
25757
|
+
return _context8.a(2);
|
|
24556
25758
|
}
|
|
24557
|
-
},
|
|
25759
|
+
}, _callee7, this, [[4, 6]]);
|
|
24558
25760
|
}));
|
|
24559
|
-
function
|
|
24560
|
-
return
|
|
25761
|
+
function initializeAudioContext() {
|
|
25762
|
+
return _initializeAudioContext.apply(this, arguments);
|
|
24561
25763
|
}
|
|
24562
|
-
return
|
|
25764
|
+
return initializeAudioContext;
|
|
24563
25765
|
}()
|
|
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
25766
|
/**
|
|
24684
25767
|
* Prime AudioContext with silent buffer to initialize Android hardware
|
|
24685
25768
|
* This prevents the first audio frame from being cut off on Android devices
|
|
24686
25769
|
*/
|
|
25770
|
+
)
|
|
24687
25771
|
}, {
|
|
24688
25772
|
key: "_primeAudioContext",
|
|
24689
25773
|
value: (function () {
|
|
24690
|
-
var _primeAudioContext2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
24691
|
-
var sampleRate, silentBuffer, source,
|
|
24692
|
-
return _regenerator().w(function (
|
|
24693
|
-
while (1) switch (
|
|
25774
|
+
var _primeAudioContext2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
|
|
25775
|
+
var sampleRate, silentBuffer, source, _t5;
|
|
25776
|
+
return _regenerator().w(function (_context9) {
|
|
25777
|
+
while (1) switch (_context9.p = _context9.n) {
|
|
24694
25778
|
case 0:
|
|
24695
|
-
|
|
25779
|
+
_context9.p = 0;
|
|
24696
25780
|
if (!(!this.audioContext || this._audioContextPrimed)) {
|
|
24697
|
-
|
|
25781
|
+
_context9.n = 1;
|
|
24698
25782
|
break;
|
|
24699
25783
|
}
|
|
24700
|
-
return
|
|
25784
|
+
return _context9.a(2);
|
|
24701
25785
|
case 1:
|
|
24702
25786
|
sampleRate = this.audioContext.sampleRate;
|
|
24703
25787
|
silentBuffer = this.audioContext.createBuffer(1, sampleRate * 0.15, sampleRate);
|
|
@@ -24705,23 +25789,23 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24705
25789
|
source.buffer = silentBuffer;
|
|
24706
25790
|
source.connect(this.audioContext.destination);
|
|
24707
25791
|
source.start();
|
|
24708
|
-
|
|
25792
|
+
_context9.n = 2;
|
|
24709
25793
|
return new Promise(function (resolve) {
|
|
24710
25794
|
return setTimeout(resolve, 150);
|
|
24711
25795
|
});
|
|
24712
25796
|
case 2:
|
|
24713
25797
|
this._audioContextPrimed = true;
|
|
24714
25798
|
console.log('✅ AudioPlayer: Audio hardware primed with silent buffer');
|
|
24715
|
-
|
|
25799
|
+
_context9.n = 4;
|
|
24716
25800
|
break;
|
|
24717
25801
|
case 3:
|
|
24718
|
-
|
|
24719
|
-
|
|
24720
|
-
console.error('❌ AudioPlayer: Audio priming failed:',
|
|
25802
|
+
_context9.p = 3;
|
|
25803
|
+
_t5 = _context9.v;
|
|
25804
|
+
console.error('❌ AudioPlayer: Audio priming failed:', _t5);
|
|
24721
25805
|
case 4:
|
|
24722
|
-
return
|
|
25806
|
+
return _context9.a(2);
|
|
24723
25807
|
}
|
|
24724
|
-
},
|
|
25808
|
+
}, _callee8, this, [[0, 3]]);
|
|
24725
25809
|
}));
|
|
24726
25810
|
function _primeAudioContext() {
|
|
24727
25811
|
return _primeAudioContext2.apply(this, arguments);
|
|
@@ -24846,55 +25930,48 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24846
25930
|
}, {
|
|
24847
25931
|
key: "processQueue",
|
|
24848
25932
|
value: (function () {
|
|
24849
|
-
var _processQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
25933
|
+
var _processQueue = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
|
|
24850
25934
|
var _this9 = this;
|
|
24851
|
-
var audioBlob, wasFirstPlay, audioContext, arrayBuffer, audioBuffer, shouldEmitStart, source,
|
|
24852
|
-
return _regenerator().w(function (
|
|
24853
|
-
while (1) switch (
|
|
25935
|
+
var audioBlob, wasFirstPlay, audioContext, arrayBuffer, audioBuffer, shouldEmitStart, source, _t6;
|
|
25936
|
+
return _regenerator().w(function (_context0) {
|
|
25937
|
+
while (1) switch (_context0.p = _context0.n) {
|
|
24854
25938
|
case 0:
|
|
24855
25939
|
if (!this._isStopped) {
|
|
24856
|
-
|
|
25940
|
+
_context0.n = 1;
|
|
24857
25941
|
break;
|
|
24858
25942
|
}
|
|
24859
25943
|
console.log('🛑 AudioPlayer: Not processing queue - playback was stopped (barge-in)');
|
|
24860
|
-
return
|
|
25944
|
+
return _context0.a(2);
|
|
24861
25945
|
case 1:
|
|
24862
25946
|
if (!(this.isProcessingQueue || this.audioQueue.length === 0)) {
|
|
24863
|
-
|
|
25947
|
+
_context0.n = 2;
|
|
24864
25948
|
break;
|
|
24865
25949
|
}
|
|
24866
|
-
return
|
|
25950
|
+
return _context0.a(2);
|
|
24867
25951
|
case 2:
|
|
24868
25952
|
this.isProcessingQueue = true;
|
|
24869
25953
|
audioBlob = this.audioQueue.shift();
|
|
24870
25954
|
if (audioBlob) {
|
|
24871
|
-
|
|
25955
|
+
_context0.n = 3;
|
|
24872
25956
|
break;
|
|
24873
25957
|
}
|
|
24874
25958
|
this.isProcessingQueue = false;
|
|
24875
|
-
return
|
|
25959
|
+
return _context0.a(2);
|
|
24876
25960
|
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;
|
|
25961
|
+
_context0.p = 3;
|
|
25962
|
+
wasFirstPlay = !this.isPlaying && this.currentSource === null; // Ensure context exists and is running (handles lazy init + suspended shared context)
|
|
25963
|
+
_context0.n = 4;
|
|
24887
25964
|
return this.waitForAudioContextReady();
|
|
24888
25965
|
case 4:
|
|
24889
25966
|
audioContext = this.audioContext; // Decode audio
|
|
24890
|
-
|
|
25967
|
+
_context0.n = 5;
|
|
24891
25968
|
return audioBlob.arrayBuffer();
|
|
24892
25969
|
case 5:
|
|
24893
|
-
arrayBuffer =
|
|
24894
|
-
|
|
25970
|
+
arrayBuffer = _context0.v;
|
|
25971
|
+
_context0.n = 6;
|
|
24895
25972
|
return audioContext.decodeAudioData(arrayBuffer);
|
|
24896
25973
|
case 6:
|
|
24897
|
-
audioBuffer =
|
|
25974
|
+
audioBuffer = _context0.v;
|
|
24898
25975
|
shouldEmitStart = wasFirstPlay && !this.isPlaying && this.currentSource === null; // Create source
|
|
24899
25976
|
source = audioContext.createBufferSource();
|
|
24900
25977
|
source.buffer = audioBuffer;
|
|
@@ -24937,14 +26014,14 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24937
26014
|
// Start playback - AudioContext should now be fully ready
|
|
24938
26015
|
|
|
24939
26016
|
source.start();
|
|
24940
|
-
|
|
26017
|
+
_context0.n = 8;
|
|
24941
26018
|
break;
|
|
24942
26019
|
case 7:
|
|
24943
|
-
|
|
24944
|
-
|
|
24945
|
-
console.error('❌ AudioPlayer v2: Error processing queue:',
|
|
26020
|
+
_context0.p = 7;
|
|
26021
|
+
_t6 = _context0.v;
|
|
26022
|
+
console.error('❌ AudioPlayer v2: Error processing queue:', _t6);
|
|
24946
26023
|
this.currentSource = null;
|
|
24947
|
-
this.emit('playbackError',
|
|
26024
|
+
this.emit('playbackError', _t6);
|
|
24948
26025
|
|
|
24949
26026
|
// Try next chunk
|
|
24950
26027
|
|
|
@@ -24959,9 +26036,9 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
24959
26036
|
this.emit('playbackStopped');
|
|
24960
26037
|
}
|
|
24961
26038
|
case 8:
|
|
24962
|
-
return
|
|
26039
|
+
return _context0.a(2);
|
|
24963
26040
|
}
|
|
24964
|
-
},
|
|
26041
|
+
}, _callee9, this, [[3, 7]]);
|
|
24965
26042
|
}));
|
|
24966
26043
|
function processQueue() {
|
|
24967
26044
|
return _processQueue.apply(this, arguments);
|
|
@@ -25241,39 +26318,43 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
25241
26318
|
}, {
|
|
25242
26319
|
key: "resumeAudioContext",
|
|
25243
26320
|
value: (function () {
|
|
25244
|
-
var _resumeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function
|
|
25245
|
-
var
|
|
25246
|
-
return _regenerator().w(function (
|
|
25247
|
-
while (1) switch (
|
|
26321
|
+
var _resumeAudioContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
|
|
26322
|
+
var _t7;
|
|
26323
|
+
return _regenerator().w(function (_context1) {
|
|
26324
|
+
while (1) switch (_context1.p = _context1.n) {
|
|
25248
26325
|
case 0:
|
|
25249
26326
|
if (!(!this.audioContext || this.audioContext.state === 'closed')) {
|
|
25250
|
-
|
|
26327
|
+
_context1.n = 2;
|
|
25251
26328
|
break;
|
|
25252
26329
|
}
|
|
25253
|
-
|
|
26330
|
+
_context1.n = 1;
|
|
25254
26331
|
return this.initializeAudioContext();
|
|
25255
26332
|
case 1:
|
|
25256
|
-
|
|
26333
|
+
_context1.n = 6;
|
|
26334
|
+
break;
|
|
25257
26335
|
case 2:
|
|
25258
26336
|
if (!(this.audioContext.state === 'suspended')) {
|
|
25259
|
-
|
|
26337
|
+
_context1.n = 6;
|
|
25260
26338
|
break;
|
|
25261
26339
|
}
|
|
25262
|
-
|
|
25263
|
-
|
|
26340
|
+
_context1.p = 3;
|
|
26341
|
+
_context1.n = 4;
|
|
25264
26342
|
return this.audioContext.resume();
|
|
25265
26343
|
case 4:
|
|
25266
26344
|
console.log('✅ AudioPlayer v2: AudioContext resumed after mic permission');
|
|
25267
|
-
|
|
26345
|
+
_context1.n = 6;
|
|
25268
26346
|
break;
|
|
25269
26347
|
case 5:
|
|
25270
|
-
|
|
25271
|
-
|
|
25272
|
-
console.warn('⚠️ AudioPlayer v2: Failed to resume AudioContext:',
|
|
26348
|
+
_context1.p = 5;
|
|
26349
|
+
_t7 = _context1.v;
|
|
26350
|
+
console.warn('⚠️ AudioPlayer v2: Failed to resume AudioContext:', _t7);
|
|
25273
26351
|
case 6:
|
|
25274
|
-
|
|
26352
|
+
_context1.n = 7;
|
|
26353
|
+
return this.waitForAudioContextReady();
|
|
26354
|
+
case 7:
|
|
26355
|
+
return _context1.a(2);
|
|
25275
26356
|
}
|
|
25276
|
-
},
|
|
26357
|
+
}, _callee0, this, [[3, 5]]);
|
|
25277
26358
|
}));
|
|
25278
26359
|
function resumeAudioContext() {
|
|
25279
26360
|
return _resumeAudioContext.apply(this, arguments);
|
|
@@ -25343,6 +26424,8 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
|
|
|
25343
26424
|
}
|
|
25344
26425
|
_sharedPlayerContext = null;
|
|
25345
26426
|
_sharedPlayerSampleRate = null;
|
|
26427
|
+
// Note: live AudioPlayer instances may still hold a reference to the now-closed
|
|
26428
|
+
// context until their next waitForAudioContextReady() / initializeAudioContext().
|
|
25346
26429
|
}
|
|
25347
26430
|
}]);
|
|
25348
26431
|
}(_shared_EventEmitter_js__WEBPACK_IMPORTED_MODULE_0__["default"]);
|
|
@@ -25366,8 +26449,9 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
25366
26449
|
/* harmony import */ var _core_AudioRecorder_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/AudioRecorder.js */ "./src/core/AudioRecorder.js");
|
|
25367
26450
|
/* harmony import */ var _utils_AudioFormatConverter_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./utils/AudioFormatConverter.js */ "./src/v2/utils/AudioFormatConverter.js");
|
|
25368
26451
|
/* 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
|
|
26452
|
+
/* harmony import */ var _core_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../core/ClientScriptManager.js */ "./src/core/ClientScriptManager.js");
|
|
26453
|
+
/* harmony import */ var _utils_screenshot_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../utils/screenshot.js */ "./src/utils/screenshot.js");
|
|
26454
|
+
/* harmony import */ var _utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../utils/visual-tools.js */ "./src/utils/visual-tools.js");
|
|
25371
26455
|
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
26456
|
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
26457
|
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 +26485,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
|
|
|
25401
26485
|
|
|
25402
26486
|
|
|
25403
26487
|
|
|
26488
|
+
|
|
25404
26489
|
/**
|
|
25405
26490
|
|
|
25406
26491
|
* VoiceSDK v2 - Multi-codec speech-to-speech SDK
|
|
@@ -25578,6 +26663,18 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
25578
26663
|
return _this.conversationId;
|
|
25579
26664
|
}, config.sharedToolHandlers || null);
|
|
25580
26665
|
|
|
26666
|
+
// Flavor-independent client-tool script manager (reserved partner id
|
|
26667
|
+
// `__client_tools__`). Owns its own PartnerScriptRunner so client-tool
|
|
26668
|
+
// scripts run regardless of flavor, alongside (and separate from) the
|
|
26669
|
+
// ecommerce flavor's partner runner. Sends result/emit frames over the
|
|
26670
|
+
// voice WebSocket.
|
|
26671
|
+
_this.clientScriptManager = new _core_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_5__.ClientScriptManager({
|
|
26672
|
+
send: function send(message) {
|
|
26673
|
+
return _this.sendMessage(message);
|
|
26674
|
+
},
|
|
26675
|
+
label: 'voice'
|
|
26676
|
+
});
|
|
26677
|
+
|
|
25581
26678
|
// ===== REGISTER BUILT-IN SDK TOOLS =====
|
|
25582
26679
|
// These tools work automatically - users don't need to implement them
|
|
25583
26680
|
// Backend controls availability via agent configuration
|
|
@@ -25603,7 +26700,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
25603
26700
|
key: "_registerBuiltInTools",
|
|
25604
26701
|
value: function _registerBuiltInTools() {
|
|
25605
26702
|
try {
|
|
25606
|
-
(0,
|
|
26703
|
+
(0,_utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_7__.registerVisualTools)(this.clientToolsRegistry);
|
|
25607
26704
|
} catch (error) {
|
|
25608
26705
|
console.error('❌ VoiceSDK: Error registering built-in tools:', error);
|
|
25609
26706
|
console.error(' Error details:', error.message, error.stack);
|
|
@@ -26362,7 +27459,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
26362
27459
|
|
|
26363
27460
|
// Include SDK build time for debugging
|
|
26364
27461
|
if (true) {
|
|
26365
|
-
helloMessage.lastBuildTime = "2026-
|
|
27462
|
+
helloMessage.lastBuildTime = "2026-06-12T15:13:11.602Z";
|
|
26366
27463
|
}
|
|
26367
27464
|
|
|
26368
27465
|
// Page context is intentionally NOT attached to the hello message.
|
|
@@ -26551,6 +27648,16 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
26551
27648
|
// envelope which the backend dispatcher routes to the callback.
|
|
26552
27649
|
this._handleRunPartnerScript(message);
|
|
26553
27650
|
break;
|
|
27651
|
+
case 'partner_bundle':
|
|
27652
|
+
// Flavor-independent client-tool bundle (partner_id `__client_tools__`)
|
|
27653
|
+
// is loaded by the dedicated ClientScriptManager. Every other partner
|
|
27654
|
+
// id falls through to the flavor (via the emitted 'message' event), so
|
|
27655
|
+
// the ecommerce flavor's own bundle handling keeps working untouched.
|
|
27656
|
+
if (_core_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_5__.ClientScriptManager.isClientToolsBundle(message)) {
|
|
27657
|
+
this.clientScriptManager.setBundle(message);
|
|
27658
|
+
}
|
|
27659
|
+
this.emit('message', message);
|
|
27660
|
+
break;
|
|
26554
27661
|
case 'text_inject_ack':
|
|
26555
27662
|
this.handleTextInjectAck(message);
|
|
26556
27663
|
break;
|
|
@@ -26573,7 +27680,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
26573
27680
|
case 'get_page_context':
|
|
26574
27681
|
// Handle get_page_context request from backend
|
|
26575
27682
|
console.log('📖 [VISUAL ASSISTANT] Backend requested page context');
|
|
26576
|
-
(0,
|
|
27683
|
+
(0,_utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_7__.extractPageContext)().then(function (pageContext) {
|
|
26577
27684
|
// Use existing DOM scanner
|
|
26578
27685
|
_this6.sendMessage({
|
|
26579
27686
|
t: 'page_context',
|
|
@@ -26594,7 +27701,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
26594
27701
|
console.log('📸 [VISUAL ASSISTANT] Backend requested screenshot');
|
|
26595
27702
|
try {
|
|
26596
27703
|
// Use existing screenshot capture function
|
|
26597
|
-
(0,
|
|
27704
|
+
(0,_utils_screenshot_js__WEBPACK_IMPORTED_MODULE_6__.captureScreenshot)().then(function (screenshot) {
|
|
26598
27705
|
_this6.sendMessage({
|
|
26599
27706
|
t: 'screenshot',
|
|
26600
27707
|
screenshot: {
|
|
@@ -27074,9 +28181,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27074
28181
|
console.warn('⚠️ VoiceSDK v2: run_partner_script missing requestId; dropping', message);
|
|
27075
28182
|
return _context3.a(2);
|
|
27076
28183
|
case 1:
|
|
28184
|
+
if (!_core_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_5__.ClientScriptManager.isClientToolsScript(message)) {
|
|
28185
|
+
_context3.n = 2;
|
|
28186
|
+
break;
|
|
28187
|
+
}
|
|
28188
|
+
return _context3.a(2, this.clientScriptManager.handleRunPartnerScript(message));
|
|
28189
|
+
case 2:
|
|
27077
28190
|
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
28191
|
if (handler) {
|
|
27079
|
-
_context3.n =
|
|
28192
|
+
_context3.n = 3;
|
|
27080
28193
|
break;
|
|
27081
28194
|
}
|
|
27082
28195
|
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 +28202,16 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27089
28202
|
error: 'no_handler_registered'
|
|
27090
28203
|
});
|
|
27091
28204
|
return _context3.a(2);
|
|
27092
|
-
case
|
|
28205
|
+
case 3:
|
|
27093
28206
|
startedAt = Date.now();
|
|
27094
28207
|
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 =
|
|
28208
|
+
_context3.p = 4;
|
|
28209
|
+
_context3.n = 5;
|
|
27097
28210
|
return handler({
|
|
27098
28211
|
action: action,
|
|
27099
28212
|
args: args
|
|
27100
28213
|
});
|
|
27101
|
-
case
|
|
28214
|
+
case 5:
|
|
27102
28215
|
result = _context3.v;
|
|
27103
28216
|
elapsedMs = Date.now() - startedAt; // Adapters return { ok, ... } themselves. Honor that flag if present;
|
|
27104
28217
|
// otherwise treat presence-of-result as success.
|
|
@@ -27126,10 +28239,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27126
28239
|
ok: ok,
|
|
27127
28240
|
result: result
|
|
27128
28241
|
});
|
|
27129
|
-
_context3.n =
|
|
28242
|
+
_context3.n = 7;
|
|
27130
28243
|
break;
|
|
27131
|
-
case
|
|
27132
|
-
_context3.p =
|
|
28244
|
+
case 6:
|
|
28245
|
+
_context3.p = 6;
|
|
27133
28246
|
_t2 = _context3.v;
|
|
27134
28247
|
_elapsedMs = Date.now() - startedAt;
|
|
27135
28248
|
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 +28254,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
|
|
|
27141
28254
|
ok: false,
|
|
27142
28255
|
error: _t2 && _t2.message || String(_t2)
|
|
27143
28256
|
});
|
|
27144
|
-
case
|
|
28257
|
+
case 7:
|
|
27145
28258
|
return _context3.a(2);
|
|
27146
28259
|
}
|
|
27147
|
-
}, _callee3, this, [[
|
|
28260
|
+
}, _callee3, this, [[4, 6]]);
|
|
27148
28261
|
}));
|
|
27149
28262
|
function _handleRunPartnerScript(_x) {
|
|
27150
28263
|
return _handleRunPartnerScript2.apply(this, arguments);
|
|
@@ -30668,6 +31781,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
30668
31781
|
/* harmony import */ var _TextInterface_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./TextInterface.js */ "./src/widget/TextInterface.js");
|
|
30669
31782
|
/* harmony import */ var _widget_translations_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./widget-translations.js */ "./src/widget/widget-translations.js");
|
|
30670
31783
|
/* harmony import */ var _flavors_index_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../flavors/index.js */ "./src/flavors/index.js");
|
|
31784
|
+
/* harmony import */ var _ecommerce_EcommerceVoiceStrip_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../ecommerce/EcommerceVoiceStrip.js */ "./src/ecommerce/EcommerceVoiceStrip.js");
|
|
30671
31785
|
var _excluded = ["agentName"];
|
|
30672
31786
|
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
31787
|
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 +31809,7 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
|
|
|
30695
31809
|
|
|
30696
31810
|
|
|
30697
31811
|
|
|
31812
|
+
|
|
30698
31813
|
/** Fallback for `buildWebSocketUrl()` when no optional `websocketUrl` override is set (VoiceSDK v2 uses the same default). */
|
|
30699
31814
|
var DEFAULT_VOICE_WEBSOCKET_URL = 'wss://speech.talktopc.com/ws/conv';
|
|
30700
31815
|
function isNonEmptyConfigString(v) {
|
|
@@ -30789,6 +31904,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
30789
31904
|
function TTPChatWidget() {
|
|
30790
31905
|
var _this$config$behavior,
|
|
30791
31906
|
_this$config$panel,
|
|
31907
|
+
_this$config$flavor,
|
|
30792
31908
|
_this$config$text,
|
|
30793
31909
|
_this$config$text2,
|
|
30794
31910
|
_this$config$voice,
|
|
@@ -30841,6 +31957,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
30841
31957
|
var showVoiceUi = widgetModeForVoice === 'unified' || widgetModeForVoice === 'voice-only';
|
|
30842
31958
|
var panelBgForBrand = ((_this$config$panel = this.config.panel) === null || _this$config$panel === void 0 ? void 0 : _this$config$panel.backgroundColor) || '#16161e';
|
|
30843
31959
|
var talkToPcLink = talkToPcLinkColorForPanel(computeIsLightPanel(panelBgForBrand), this.config.voice, this._rawConfig, showVoiceUi);
|
|
31960
|
+
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
31961
|
var voiceConfig = _objectSpread(_objectSpread(_objectSpread({}, this.config), this.config.voice), {}, {
|
|
30845
31962
|
agentName: this.config.agentName,
|
|
30846
31963
|
_talkToPcLinkColor: talkToPcLink,
|
|
@@ -30864,8 +31981,24 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
30864
31981
|
outputSampleRate: ((_this$config$voice4 = this.config.voice) === null || _this$config$voice4 === void 0 ? void 0 : _this$config$voice4.outputSampleRate) || 24000,
|
|
30865
31982
|
outputChannels: ((_this$config$voice5 = this.config.voice) === null || _this$config$voice5 === void 0 ? void 0 : _this$config$voice5.outputChannels) || 1,
|
|
30866
31983
|
outputBitDepth: ((_this$config$voice6 = this.config.voice) === null || _this$config$voice6 === void 0 ? void 0 : _this$config$voice6.outputBitDepth) || 16,
|
|
30867
|
-
|
|
30868
|
-
|
|
31984
|
+
desktopMinimizedEcommerceStripUi: desktopMinimizedEcommerceStripUi,
|
|
31985
|
+
onDesktopMinimizedStripCallStart: desktopMinimizedEcommerceStripUi ? function () {
|
|
31986
|
+
var _this$_ecommerceVoice;
|
|
31987
|
+
(_this$_ecommerceVoice = _this._ecommerceVoiceStrip) === null || _this$_ecommerceVoice === void 0 || _this$_ecommerceVoice.show();
|
|
31988
|
+
if (_this.isOpen) _this.minimize();
|
|
31989
|
+
_this._setPanelFooterVisible(false);
|
|
31990
|
+
_this._setEcommerceStripLauncherVisible(false);
|
|
31991
|
+
} : undefined,
|
|
31992
|
+
onDesktopMinimizedStripLauncherRestore: desktopMinimizedEcommerceStripUi ? function () {
|
|
31993
|
+
return _this._restoreDesktopEcommerceStripChrome();
|
|
31994
|
+
} : undefined,
|
|
31995
|
+
// Pass event callbacks to voice interface (forward only; panel is not auto-minimized on call start)
|
|
31996
|
+
onConversationStart: function onConversationStart() {
|
|
31997
|
+
if (typeof _this.config.onConversationStart === 'function') {
|
|
31998
|
+
var _this$config;
|
|
31999
|
+
(_this$config = _this.config).onConversationStart.apply(_this$config, arguments);
|
|
32000
|
+
}
|
|
32001
|
+
},
|
|
30869
32002
|
onConversationEnd: this.config.onConversationEnd,
|
|
30870
32003
|
onBargeIn: this.config.onBargeIn,
|
|
30871
32004
|
onAudioStartPlaying: this.config.onAudioStartPlaying,
|
|
@@ -30933,7 +32066,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
30933
32066
|
},
|
|
30934
32067
|
onMobileError: function onMobileError(message) {
|
|
30935
32068
|
_this.showMobileLandingError(message);
|
|
30936
|
-
}
|
|
32069
|
+
},
|
|
32070
|
+
// Root layout direction — must be last so `...this.config.voice` cannot overwrite with `undefined`
|
|
32071
|
+
direction: this.getEffectiveTextDirection()
|
|
30937
32072
|
});
|
|
30938
32073
|
this.voiceInterface = new _VoiceInterface_js__WEBPACK_IMPORTED_MODULE_1__.VoiceInterface(voiceConfig);
|
|
30939
32074
|
|
|
@@ -30981,11 +32116,12 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
30981
32116
|
// Check for resume state and auto-resume if needed
|
|
30982
32117
|
this.checkAndHandleResume();
|
|
30983
32118
|
|
|
30984
|
-
// Initialize flavor module if configured
|
|
32119
|
+
// Initialize flavor module if configured (use merged config so flavor matches this.config)
|
|
30985
32120
|
this._flavor = null;
|
|
30986
|
-
this._flavorObj = config.flavor && _typeof(config.flavor) === 'object' ? config.flavor : null;
|
|
32121
|
+
this._flavorObj = this.config.flavor && _typeof(this.config.flavor) === 'object' ? this.config.flavor : null;
|
|
30987
32122
|
if (this._flavorObj && this._flavorObj.type) {
|
|
30988
32123
|
this._initFlavor(this._flavorObj.type);
|
|
32124
|
+
this._initEcommerceVoiceStrip();
|
|
30989
32125
|
} else if (this._flavorObj && !this._flavorObj.type) {
|
|
30990
32126
|
console.error('[TTPChatWidget] flavor object is set but missing required "type" field. Widget will run in vanilla mode.');
|
|
30991
32127
|
}
|
|
@@ -31147,8 +32283,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31147
32283
|
}, {
|
|
31148
32284
|
key: "getEffectiveTextDirection",
|
|
31149
32285
|
value: function getEffectiveTextDirection() {
|
|
31150
|
-
var _this$
|
|
31151
|
-
return ((_this$
|
|
32286
|
+
var _this$config2;
|
|
32287
|
+
return ((_this$config2 = this.config) === null || _this$config2 === void 0 ? void 0 : _this$config2.direction) === 'rtl' ? 'rtl' : 'ltr';
|
|
31152
32288
|
}
|
|
31153
32289
|
|
|
31154
32290
|
/**
|
|
@@ -31200,6 +32336,52 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31200
32336
|
this._doTogglePanel();
|
|
31201
32337
|
}
|
|
31202
32338
|
|
|
32339
|
+
/**
|
|
32340
|
+
* DOM root that contains the launcher (`#text-chat-button-container`), matching createWidget.
|
|
32341
|
+
* @returns {ShadowRoot|Document}
|
|
32342
|
+
*/
|
|
32343
|
+
}, {
|
|
32344
|
+
key: "_launcherDomRoot",
|
|
32345
|
+
value: function _launcherDomRoot() {
|
|
32346
|
+
if (this.config.useShadowDOM !== false && this.shadowRoot && this.shadowRoot !== document) {
|
|
32347
|
+
return this.shadowRoot;
|
|
32348
|
+
}
|
|
32349
|
+
return document;
|
|
32350
|
+
}
|
|
32351
|
+
|
|
32352
|
+
/**
|
|
32353
|
+
* Show or hide the floating launcher for desktop callView:minimized (bottom strip mode).
|
|
32354
|
+
* Widget CSS uses `display: flex !important` on the container — hide with `none !important`.
|
|
32355
|
+
* @param {boolean} visible
|
|
32356
|
+
*/
|
|
32357
|
+
}, {
|
|
32358
|
+
key: "_setEcommerceStripLauncherVisible",
|
|
32359
|
+
value: function _setEcommerceStripLauncherVisible(visible) {
|
|
32360
|
+
var _this$config$behavior4;
|
|
32361
|
+
if ((_this$config$behavior4 = this.config.behavior) !== null && _this$config$behavior4 !== void 0 && _this$config$behavior4.hidden) return;
|
|
32362
|
+
var root = this._launcherDomRoot();
|
|
32363
|
+
var container = root.getElementById('text-chat-button-container');
|
|
32364
|
+
var btn = root.getElementById('text-chat-button');
|
|
32365
|
+
if (visible) {
|
|
32366
|
+
if (container) container.style.removeProperty('display');
|
|
32367
|
+
if (btn) btn.style.removeProperty('display');
|
|
32368
|
+
} else if (container) {
|
|
32369
|
+
container.style.setProperty('display', 'none', 'important');
|
|
32370
|
+
} else if (btn) {
|
|
32371
|
+
btn.style.setProperty('display', 'none', 'important');
|
|
32372
|
+
}
|
|
32373
|
+
}
|
|
32374
|
+
|
|
32375
|
+
/** Hide bottom strip and restore floating launcher (desktop minimized call UI). */
|
|
32376
|
+
}, {
|
|
32377
|
+
key: "_restoreDesktopEcommerceStripChrome",
|
|
32378
|
+
value: function _restoreDesktopEcommerceStripChrome() {
|
|
32379
|
+
var _this$_ecommerceVoice2;
|
|
32380
|
+
(_this$_ecommerceVoice2 = this._ecommerceVoiceStrip) === null || _this$_ecommerceVoice2 === void 0 || _this$_ecommerceVoice2.hide();
|
|
32381
|
+
this._setPanelFooterVisible(true);
|
|
32382
|
+
this._setEcommerceStripLauncherVisible(true);
|
|
32383
|
+
}
|
|
32384
|
+
|
|
31203
32385
|
/**
|
|
31204
32386
|
* Wire the universal widget-control message listener on both the
|
|
31205
32387
|
* text-chat SDK and the voice SDK. Two channels, one set of message types
|
|
@@ -31269,7 +32451,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31269
32451
|
}, {
|
|
31270
32452
|
key: "mergeWithDefaults",
|
|
31271
32453
|
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;
|
|
32454
|
+
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
32455
|
// Handle legacy position string format
|
|
31274
32456
|
var positionConfig = userConfig.position || 'bottom-right';
|
|
31275
32457
|
if (typeof positionConfig === 'string') {
|
|
@@ -31535,7 +32717,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31535
32717
|
// Default true
|
|
31536
32718
|
backgroundColor: ((_userConfig$footer2 = userConfig.footer) === null || _userConfig$footer2 === void 0 ? void 0 : _userConfig$footer2.backgroundColor) || '#f9fafb',
|
|
31537
32719
|
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'
|
|
32720
|
+
hoverColor: ((_userConfig$footer4 = userConfig.footer) === null || _userConfig$footer4 === void 0 ? void 0 : _userConfig$footer4.hoverColor) || '#7C3AED',
|
|
32721
|
+
// Brand shown in "Powered by" footer. 'talktopc' (default) or 'speacart'.
|
|
32722
|
+
brand: ((_userConfig$footer5 = userConfig.footer) === null || _userConfig$footer5 === void 0 ? void 0 : _userConfig$footer5.brand) === 'speacart' ? 'speacart' : 'talktopc'
|
|
31539
32723
|
}, userConfig.footer),
|
|
31540
32724
|
// Tooltips Configuration
|
|
31541
32725
|
tooltips: _objectSpread({
|
|
@@ -31767,8 +32951,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31767
32951
|
|
|
31768
32952
|
// On desktop, ensure voice hero is shown for unified/voice-only (in-panel mode picker removed)
|
|
31769
32953
|
if (window.innerWidth > 768) {
|
|
31770
|
-
var _this$config$
|
|
31771
|
-
var widgetMode = ((_this$config$
|
|
32954
|
+
var _this$config$behavior5;
|
|
32955
|
+
var widgetMode = ((_this$config$behavior5 = this.config.behavior) === null || _this$config$behavior5 === void 0 ? void 0 : _this$config$behavior5.mode) || 'unified';
|
|
31772
32956
|
var preferVoice = widgetMode === 'unified' || widgetMode === 'voice-only';
|
|
31773
32957
|
if (preferVoice && this.currentView !== 'text') {
|
|
31774
32958
|
this.showVoice();
|
|
@@ -31866,7 +33050,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31866
33050
|
}, {
|
|
31867
33051
|
key: "generateWidgetHTML",
|
|
31868
33052
|
value: function generateWidgetHTML() {
|
|
31869
|
-
var _this$config$
|
|
33053
|
+
var _this$config$behavior6,
|
|
31870
33054
|
_this7 = this;
|
|
31871
33055
|
var pos = this.config.position;
|
|
31872
33056
|
var btn = this.config.button;
|
|
@@ -31895,7 +33079,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31895
33079
|
|
|
31896
33080
|
// Generate position styles
|
|
31897
33081
|
var positionStyles = this.generatePositionStyles();
|
|
31898
|
-
var widgetMode = ((_this$config$
|
|
33082
|
+
var widgetMode = ((_this$config$behavior6 = this.config.behavior) === null || _this$config$behavior6 === void 0 ? void 0 : _this$config$behavior6.mode) || 'unified';
|
|
31899
33083
|
var showVoice = widgetMode === 'unified' || widgetMode === 'voice-only';
|
|
31900
33084
|
var showText = widgetMode === 'unified' || widgetMode === 'text-only';
|
|
31901
33085
|
var voiceEnabled = showVoice;
|
|
@@ -31947,7 +33131,24 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31947
33131
|
var pillWaveformBars = '<span class="bar"></span><span class="bar"></span><span class="bar"></span><span class="bar"></span><span class="bar"></span>';
|
|
31948
33132
|
var pillIconInner = pillCustomImage ? "<img class=\"ttp-launcher-logo\" src=\"".concat(pillCustomImage, "\" alt=\"\" />") : "<div class=\"ttp-pill-waveform\" aria-hidden=\"true\">".concat(pillWaveformBars, "</div>");
|
|
31949
33133
|
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 ");
|
|
33134
|
+
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 ");
|
|
33135
|
+
}
|
|
33136
|
+
|
|
33137
|
+
/**
|
|
33138
|
+
* Resolved footer branding for panel + desktop voice strip.
|
|
33139
|
+
* @returns {{ brandUrl: string, brandName: string } | null}
|
|
33140
|
+
*/
|
|
33141
|
+
}, {
|
|
33142
|
+
key: "getFooterBranding",
|
|
33143
|
+
value: function getFooterBranding() {
|
|
33144
|
+
var footer = this.config.footer;
|
|
33145
|
+
if (!(footer !== null && footer !== void 0 && footer.show)) return null;
|
|
33146
|
+
var refSource = encodeURIComponent(window.location.hostname || 'widget');
|
|
33147
|
+
var isSpeacart = footer.brand === 'speacart';
|
|
33148
|
+
return {
|
|
33149
|
+
brandUrl: isSpeacart ? 'https://speacart.com' : "https://talktopc.com?ref=widget&source=".concat(refSource),
|
|
33150
|
+
brandName: isSpeacart ? 'SpeaCart' : 'TalkToPC'
|
|
33151
|
+
};
|
|
31951
33152
|
}
|
|
31952
33153
|
|
|
31953
33154
|
/**
|
|
@@ -31956,11 +33157,24 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
31956
33157
|
}, {
|
|
31957
33158
|
key: "generateFooterHTML",
|
|
31958
33159
|
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>
|
|
33160
|
+
var branding = this.getFooterBranding();
|
|
33161
|
+
if (!branding) return '';
|
|
33162
|
+
var brandUrl = branding.brandUrl,
|
|
33163
|
+
brandName = branding.brandName;
|
|
33164
|
+
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 ");
|
|
33165
|
+
}
|
|
33166
|
+
|
|
33167
|
+
/**
|
|
33168
|
+
* Hide in-panel footer while the desktop bottom strip owns the active call UI.
|
|
33169
|
+
* @param {boolean} visible
|
|
33170
|
+
*/
|
|
33171
|
+
}, {
|
|
33172
|
+
key: "_setPanelFooterVisible",
|
|
33173
|
+
value: function _setPanelFooterVisible(visible) {
|
|
33174
|
+
var _root$querySelector;
|
|
33175
|
+
var root = this.shadowRoot || document;
|
|
33176
|
+
var footer = (_root$querySelector = root.querySelector) === null || _root$querySelector === void 0 ? void 0 : _root$querySelector.call(root, '.ttp-footer');
|
|
33177
|
+
if (footer) footer.style.display = visible ? '' : 'none';
|
|
31964
33178
|
}
|
|
31965
33179
|
}, {
|
|
31966
33180
|
key: "generatePositionStyles",
|
|
@@ -32056,7 +33270,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
32056
33270
|
}, {
|
|
32057
33271
|
key: "generateCSS",
|
|
32058
33272
|
value: function generateCSS(positionStyles, buttonSize, iconSize) {
|
|
32059
|
-
var _this$config$
|
|
33273
|
+
var _this$config$behavior7, _this$config$text3, _this$config$position, _this$config$position2, _this$config$position3, _this$config$position4;
|
|
32060
33274
|
var btn = this.config.button;
|
|
32061
33275
|
var icon = this.config.icon;
|
|
32062
33276
|
var panel = this.config.panel;
|
|
@@ -32065,7 +33279,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
32065
33279
|
var anim = this.config.animation;
|
|
32066
33280
|
|
|
32067
33281
|
// Determine which interfaces to show
|
|
32068
|
-
var widgetMode = ((_this$config$
|
|
33282
|
+
var widgetMode = ((_this$config$behavior7 = this.config.behavior) === null || _this$config$behavior7 === void 0 ? void 0 : _this$config$behavior7.mode) || 'unified';
|
|
32069
33283
|
var showVoice = widgetMode === 'unified' || widgetMode === 'voice-only';
|
|
32070
33284
|
var showText = widgetMode === 'unified' || widgetMode === 'text-only';
|
|
32071
33285
|
|
|
@@ -32099,7 +33313,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
32099
33313
|
key: "setupWidgetEvents",
|
|
32100
33314
|
value: function setupWidgetEvents() {
|
|
32101
33315
|
var _this8 = this,
|
|
32102
|
-
_this$config$
|
|
33316
|
+
_this$config$behavior8;
|
|
32103
33317
|
if (!this.shadowRoot) return;
|
|
32104
33318
|
|
|
32105
33319
|
// Setup prompt animation visibility and interactions
|
|
@@ -32169,7 +33383,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
32169
33383
|
}
|
|
32170
33384
|
|
|
32171
33385
|
// Voice selection events (based on widget mode)
|
|
32172
|
-
var widgetMode = ((_this$config$
|
|
33386
|
+
var widgetMode = ((_this$config$behavior8 = this.config.behavior) === null || _this$config$behavior8 === void 0 ? void 0 : _this$config$behavior8.mode) || 'unified';
|
|
32173
33387
|
var isUnified = widgetMode === 'unified';
|
|
32174
33388
|
var showVoice = widgetMode === 'unified' || widgetMode === 'voice-only';
|
|
32175
33389
|
var showText = widgetMode === 'unified' || widgetMode === 'text-only';
|
|
@@ -32956,8 +34170,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
32956
34170
|
}, {
|
|
32957
34171
|
key: "updateConfig",
|
|
32958
34172
|
value: function updateConfig(newConfig) {
|
|
32959
|
-
var _this$
|
|
32960
|
-
_this$
|
|
34173
|
+
var _this$config3,
|
|
34174
|
+
_this$config4,
|
|
34175
|
+
_this$config$flavor2,
|
|
32961
34176
|
_this$config$voice8,
|
|
32962
34177
|
_this$config$voice9,
|
|
32963
34178
|
_this$config$voice0,
|
|
@@ -33081,9 +34296,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33081
34296
|
});
|
|
33082
34297
|
|
|
33083
34298
|
// Store current language before merge to detect changes
|
|
33084
|
-
var oldLanguage = ((_this$
|
|
34299
|
+
var oldLanguage = ((_this$config3 = this.config) === null || _this$config3 === void 0 ? void 0 : _this$config3.language) || 'en';
|
|
33085
34300
|
// Store current useShadowDOM setting
|
|
33086
|
-
var oldUseShadowDOM = ((_this$
|
|
34301
|
+
var oldUseShadowDOM = ((_this$config4 = this.config) === null || _this$config4 === void 0 ? void 0 : _this$config4.useShadowDOM) !== false;
|
|
33087
34302
|
this.config = this.mergeWithDefaults(mergedConfig);
|
|
33088
34303
|
|
|
33089
34304
|
// Check if useShadowDOM changed - if so, need to recreate container
|
|
@@ -33161,6 +34376,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33161
34376
|
// Recreate interfaces with new config (after mergeWithDefaults, so language is current)
|
|
33162
34377
|
// Ensure language is correctly passed - use the merged config value
|
|
33163
34378
|
var currentLanguage = this.config.language || 'en';
|
|
34379
|
+
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
34380
|
var voiceConfig = _objectSpread(_objectSpread(_objectSpread({}, this.config), this.config.voice), {}, {
|
|
33165
34381
|
agentName: this.config.agentName,
|
|
33166
34382
|
language: currentLanguage,
|
|
@@ -33178,8 +34394,24 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33178
34394
|
outputSampleRate: ((_this$config$voice0 = this.config.voice) === null || _this$config$voice0 === void 0 ? void 0 : _this$config$voice0.outputSampleRate) || 24000,
|
|
33179
34395
|
outputChannels: ((_this$config$voice1 = this.config.voice) === null || _this$config$voice1 === void 0 ? void 0 : _this$config$voice1.outputChannels) || 1,
|
|
33180
34396
|
outputBitDepth: ((_this$config$voice10 = this.config.voice) === null || _this$config$voice10 === void 0 ? void 0 : _this$config$voice10.outputBitDepth) || 16,
|
|
33181
|
-
|
|
33182
|
-
|
|
34397
|
+
desktopMinimizedEcommerceStripUi: desktopMinimizedEcommerceStripUi,
|
|
34398
|
+
onDesktopMinimizedStripCallStart: desktopMinimizedEcommerceStripUi ? function () {
|
|
34399
|
+
var _this13$_ecommerceVoi;
|
|
34400
|
+
(_this13$_ecommerceVoi = _this13._ecommerceVoiceStrip) === null || _this13$_ecommerceVoi === void 0 || _this13$_ecommerceVoi.show();
|
|
34401
|
+
if (_this13.isOpen) _this13.minimize();
|
|
34402
|
+
_this13._setPanelFooterVisible(false);
|
|
34403
|
+
_this13._setEcommerceStripLauncherVisible(false);
|
|
34404
|
+
} : undefined,
|
|
34405
|
+
onDesktopMinimizedStripLauncherRestore: desktopMinimizedEcommerceStripUi ? function () {
|
|
34406
|
+
return _this13._restoreDesktopEcommerceStripChrome();
|
|
34407
|
+
} : undefined,
|
|
34408
|
+
// Pass event callbacks to voice interface (forward only; panel is not auto-minimized on call start)
|
|
34409
|
+
onConversationStart: function onConversationStart() {
|
|
34410
|
+
if (typeof _this13.config.onConversationStart === 'function') {
|
|
34411
|
+
var _this13$config;
|
|
34412
|
+
(_this13$config = _this13.config).onConversationStart.apply(_this13$config, arguments);
|
|
34413
|
+
}
|
|
34414
|
+
},
|
|
33183
34415
|
onConversationEnd: this.config.onConversationEnd,
|
|
33184
34416
|
onBargeIn: this.config.onBargeIn,
|
|
33185
34417
|
onAudioStartPlaying: this.config.onAudioStartPlaying,
|
|
@@ -33229,7 +34461,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33229
34461
|
} else {
|
|
33230
34462
|
_this13.showLanding();
|
|
33231
34463
|
}
|
|
33232
|
-
}
|
|
34464
|
+
},
|
|
34465
|
+
direction: this.getEffectiveTextDirection()
|
|
33233
34466
|
});
|
|
33234
34467
|
this.voiceInterface = new _VoiceInterface_js__WEBPACK_IMPORTED_MODULE_1__.VoiceInterface(voiceConfig);
|
|
33235
34468
|
|
|
@@ -33252,6 +34485,136 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33252
34485
|
if (this.textInterface && this.textInterface.updateInputAttributes) {
|
|
33253
34486
|
this.textInterface.updateInputAttributes();
|
|
33254
34487
|
}
|
|
34488
|
+
|
|
34489
|
+
// Recreated VoiceInterface = new AgentSDK; re-wire ecommerce bottom strip to onConversationStart / transcript.
|
|
34490
|
+
this._flavorObj = this.config.flavor && _typeof(this.config.flavor) === 'object' ? this.config.flavor : null;
|
|
34491
|
+
if (this._ecommerceVoiceStrip) {
|
|
34492
|
+
this._ecommerceVoiceStrip.destroy();
|
|
34493
|
+
this._ecommerceVoiceStrip = null;
|
|
34494
|
+
}
|
|
34495
|
+
this._initEcommerceVoiceStrip();
|
|
34496
|
+
}
|
|
34497
|
+
|
|
34498
|
+
/**
|
|
34499
|
+
* Initialize the bottom-strip voice UI for desktop + flavor.callView:"minimized".
|
|
34500
|
+
*
|
|
34501
|
+
* Activates when flavor.callView === "minimized" and Desktop (window.innerWidth > 768).
|
|
34502
|
+
*
|
|
34503
|
+
* The strip renders as a fixed bar at the bottom of the page and replaces the
|
|
34504
|
+
* in-panel voice active state during a call. Landing and idle views are unchanged.
|
|
34505
|
+
*/
|
|
34506
|
+
}, {
|
|
34507
|
+
key: "_initEcommerceVoiceStrip",
|
|
34508
|
+
value: function _initEcommerceVoiceStrip() {
|
|
34509
|
+
var _this14 = this,
|
|
34510
|
+
_this$voiceInterface6;
|
|
34511
|
+
var flavor = this.config.flavor && _typeof(this.config.flavor) === 'object' ? this.config.flavor : null;
|
|
34512
|
+
if (window.innerWidth <= 768) return;
|
|
34513
|
+
if (!flavor || flavor.callView !== 'minimized') return;
|
|
34514
|
+
if (this._ecommerceVoiceStrip) {
|
|
34515
|
+
this._ecommerceVoiceStrip.destroy();
|
|
34516
|
+
this._ecommerceVoiceStrip = null;
|
|
34517
|
+
}
|
|
34518
|
+
var v = this.config.voice || {};
|
|
34519
|
+
var strip;
|
|
34520
|
+
strip = new _ecommerce_EcommerceVoiceStrip_js__WEBPACK_IMPORTED_MODULE_5__.EcommerceVoiceStrip({
|
|
34521
|
+
language: this.config.language || 'en',
|
|
34522
|
+
direction: this.getEffectiveTextDirection(),
|
|
34523
|
+
footer: this.getFooterBranding(),
|
|
34524
|
+
theme: {
|
|
34525
|
+
pillGradient: v.pillGradient || 'linear-gradient(135deg, #581c87, #312e81, #1e1b4b)',
|
|
34526
|
+
pillTextColor: v.pillTextColor || '#ffffff',
|
|
34527
|
+
pillDotColor: v.pillDotColor || '#4ade80',
|
|
34528
|
+
endCallButtonColor: v.endCallButtonColor || '#ef4444'
|
|
34529
|
+
},
|
|
34530
|
+
getVoiceUiState: function getVoiceUiState() {
|
|
34531
|
+
var _this14$voiceInterfac, _this14$voiceInterfac2, _this14$voiceInterfac3;
|
|
34532
|
+
return {
|
|
34533
|
+
isMicMuted: !!((_this14$voiceInterfac = _this14.voiceInterface) !== null && _this14$voiceInterfac !== void 0 && _this14$voiceInterfac.isMicMuted),
|
|
34534
|
+
isPaused: !!((_this14$voiceInterfac2 = _this14.voiceInterface) !== null && _this14$voiceInterfac2 !== void 0 && _this14$voiceInterfac2.isPaused),
|
|
34535
|
+
isSpeakerMuted: !!((_this14$voiceInterfac3 = _this14.voiceInterface) !== null && _this14$voiceInterfac3 !== void 0 && _this14$voiceInterfac3.isSpeakerMuted)
|
|
34536
|
+
};
|
|
34537
|
+
},
|
|
34538
|
+
onHangup: function onHangup() {
|
|
34539
|
+
if (_this14.voiceInterface) _this14.voiceInterface.endVoiceCall();
|
|
34540
|
+
},
|
|
34541
|
+
onToggleMute: function onToggleMute() {
|
|
34542
|
+
if (_this14.voiceInterface) _this14.voiceInterface.toggleMute();
|
|
34543
|
+
strip.syncFromVoice();
|
|
34544
|
+
},
|
|
34545
|
+
onTogglePause: function onTogglePause() {
|
|
34546
|
+
if (_this14.voiceInterface) _this14.voiceInterface.togglePause();
|
|
34547
|
+
strip.syncFromVoice();
|
|
34548
|
+
},
|
|
34549
|
+
onToggleSpeaker: function onToggleSpeaker() {
|
|
34550
|
+
if (_this14.voiceInterface) _this14.voiceInterface.toggleSpeaker();
|
|
34551
|
+
strip.syncFromVoice();
|
|
34552
|
+
},
|
|
34553
|
+
onSendText: function onSendText(text) {
|
|
34554
|
+
var _this14$voiceInterfac4;
|
|
34555
|
+
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;
|
|
34556
|
+
if (voiceSDK && typeof voiceSDK.sendMessage === 'function') {
|
|
34557
|
+
voiceSDK.sendMessage({
|
|
34558
|
+
t: 'text_inject',
|
|
34559
|
+
text: text
|
|
34560
|
+
});
|
|
34561
|
+
}
|
|
34562
|
+
}
|
|
34563
|
+
});
|
|
34564
|
+
this._ecommerceVoiceStrip = strip;
|
|
34565
|
+
|
|
34566
|
+
// Show strip when conversation starts
|
|
34567
|
+
var agentSDK = (_this$voiceInterface6 = this.voiceInterface) === null || _this$voiceInterface6 === void 0 ? void 0 : _this$voiceInterface6.sdk;
|
|
34568
|
+
if (agentSDK) {
|
|
34569
|
+
var origConvStart = agentSDK.onConversationStart;
|
|
34570
|
+
agentSDK.onConversationStart = function () {
|
|
34571
|
+
strip.show({
|
|
34572
|
+
reset: false
|
|
34573
|
+
});
|
|
34574
|
+
strip.syncFromVoice();
|
|
34575
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
34576
|
+
args[_key] = arguments[_key];
|
|
34577
|
+
}
|
|
34578
|
+
if (typeof origConvStart === 'function') origConvStart.call.apply(origConvStart, [agentSDK].concat(args));
|
|
34579
|
+
};
|
|
34580
|
+
|
|
34581
|
+
// Feed transcript updates into the strip
|
|
34582
|
+
var origOnTranscript = agentSDK.onTranscript;
|
|
34583
|
+
agentSDK.onTranscript = function (text) {
|
|
34584
|
+
strip.updateTranscript(text);
|
|
34585
|
+
if (typeof origOnTranscript === 'function') origOnTranscript.call(agentSDK, text);
|
|
34586
|
+
};
|
|
34587
|
+
var origOnUserTranscript = agentSDK.onUserTranscript;
|
|
34588
|
+
agentSDK.onUserTranscript = function (utext, isFinal) {
|
|
34589
|
+
try {
|
|
34590
|
+
strip.updateUserTranscript(utext, isFinal);
|
|
34591
|
+
} catch (e) {
|
|
34592
|
+
console.warn('[TTPChatWidget] ecommerce strip user transcript:', e);
|
|
34593
|
+
}
|
|
34594
|
+
if (typeof origOnUserTranscript === 'function') {
|
|
34595
|
+
origOnUserTranscript.call(agentSDK, utext, isFinal);
|
|
34596
|
+
}
|
|
34597
|
+
};
|
|
34598
|
+
}
|
|
34599
|
+
|
|
34600
|
+
// Hide strip when call ends (config.onCallEnd is called by endVoiceCall())
|
|
34601
|
+
if (this.voiceInterface) {
|
|
34602
|
+
var _this$voiceInterface$2;
|
|
34603
|
+
var origOnCallEnd = this.voiceInterface.config.onCallEnd;
|
|
34604
|
+
this.voiceInterface.config.onCallEnd = function () {
|
|
34605
|
+
_this14._restoreDesktopEcommerceStripChrome();
|
|
34606
|
+
if (typeof origOnCallEnd === 'function') origOnCallEnd();
|
|
34607
|
+
};
|
|
34608
|
+
|
|
34609
|
+
// Feed timer ticks into the strip
|
|
34610
|
+
var origUpdateTimer = (_this$voiceInterface$2 = this.voiceInterface._updateTimerElements) === null || _this$voiceInterface$2 === void 0 ? void 0 : _this$voiceInterface$2.bind(this.voiceInterface);
|
|
34611
|
+
if (origUpdateTimer) {
|
|
34612
|
+
this.voiceInterface._updateTimerElements = function (timerText, elapsed) {
|
|
34613
|
+
strip.updateTimer(timerText);
|
|
34614
|
+
origUpdateTimer(timerText, elapsed);
|
|
34615
|
+
};
|
|
34616
|
+
}
|
|
34617
|
+
}
|
|
33255
34618
|
}
|
|
33256
34619
|
}, {
|
|
33257
34620
|
key: "destroy",
|
|
@@ -33289,6 +34652,11 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33289
34652
|
this._flavor.destroy();
|
|
33290
34653
|
this._flavor = null;
|
|
33291
34654
|
}
|
|
34655
|
+
// Clean up ecommerce voice strip
|
|
34656
|
+
if (this._ecommerceVoiceStrip) {
|
|
34657
|
+
this._ecommerceVoiceStrip.destroy();
|
|
34658
|
+
this._ecommerceVoiceStrip = null;
|
|
34659
|
+
}
|
|
33292
34660
|
}
|
|
33293
34661
|
|
|
33294
34662
|
// Delegated to VoiceInterface
|
|
@@ -33358,7 +34726,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33358
34726
|
key: "injectData",
|
|
33359
34727
|
value: (function () {
|
|
33360
34728
|
var _injectData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(data) {
|
|
33361
|
-
var _this$
|
|
34729
|
+
var _this$voiceInterface7;
|
|
33362
34730
|
var options,
|
|
33363
34731
|
voiceSDK,
|
|
33364
34732
|
ack,
|
|
@@ -33383,7 +34751,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
|
|
|
33383
34751
|
throw new Error('Voice call is not active');
|
|
33384
34752
|
case 2:
|
|
33385
34753
|
// Check if SDK is available
|
|
33386
|
-
voiceSDK = (_this$
|
|
34754
|
+
voiceSDK = (_this$voiceInterface7 = this.voiceInterface) === null || _this$voiceInterface7 === void 0 ? void 0 : _this$voiceInterface7.getVoiceSDK();
|
|
33387
34755
|
if (voiceSDK) {
|
|
33388
34756
|
_context6.n = 3;
|
|
33389
34757
|
break;
|
|
@@ -34330,6 +35698,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
34330
35698
|
/* harmony import */ var _voice_handlers_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./voice/handlers.js */ "./src/widget/voice/handlers.js");
|
|
34331
35699
|
/* harmony import */ var _voice_desktop_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./voice/desktop.js */ "./src/widget/voice/desktop.js");
|
|
34332
35700
|
/* harmony import */ var _voice_mobile_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./voice/mobile.js */ "./src/widget/voice/mobile.js");
|
|
35701
|
+
/* harmony import */ var _voice_rtlText_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./voice/rtlText.js */ "./src/widget/voice/rtlText.js");
|
|
34333
35702
|
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
35703
|
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
35704
|
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 +35727,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
|
|
|
34358
35727
|
|
|
34359
35728
|
|
|
34360
35729
|
|
|
35730
|
+
|
|
34361
35731
|
var VoiceInterface = /*#__PURE__*/function () {
|
|
34362
35732
|
function VoiceInterface(config) {
|
|
34363
35733
|
var _this$config$behavior, _config$agentSettings;
|
|
@@ -34772,13 +36142,20 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
34772
36142
|
return this.getText('userTranscriptPrefix', 'userTranscriptPrefix');
|
|
34773
36143
|
}
|
|
34774
36144
|
|
|
36145
|
+
/** Assistant / user live transcript and bubbles: RTL for `direction: 'rtl'` or Hebrew/Arabic `language`. */
|
|
36146
|
+
}, {
|
|
36147
|
+
key: "prefersRtlText",
|
|
36148
|
+
value: function prefersRtlText() {
|
|
36149
|
+
return (0,_voice_rtlText_js__WEBPACK_IMPORTED_MODULE_12__.configPrefersRtlText)(this.config);
|
|
36150
|
+
}
|
|
36151
|
+
|
|
34775
36152
|
/**
|
|
34776
36153
|
* Live user STT line: correct RTL/LTR order, visible gap after "אתה:", and sensible ? placement (utterance in dir=auto).
|
|
34777
36154
|
*/
|
|
34778
36155
|
}, {
|
|
34779
36156
|
key: "_renderUserLiveTranscriptSnippet",
|
|
34780
36157
|
value: function _renderUserLiveTranscriptSnippet(userPrefix, text) {
|
|
34781
|
-
var isRtl = this.
|
|
36158
|
+
var isRtl = this.prefersRtlText();
|
|
34782
36159
|
var raw = userPrefix !== undefined && userPrefix !== null ? String(userPrefix) : '';
|
|
34783
36160
|
var trimmedPrefix = raw.replace(/\s+$/, '');
|
|
34784
36161
|
var sepChar = isRtl ? "\xA0" : ' ';
|
|
@@ -35115,7 +36492,7 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
35115
36492
|
value: function () {
|
|
35116
36493
|
var _proceedWithVoiceCall = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
|
|
35117
36494
|
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;
|
|
36495
|
+
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
36496
|
return _regenerator().w(function (_context4) {
|
|
35120
36497
|
while (1) switch (_context4.p = _context4.n) {
|
|
35121
36498
|
case 0:
|
|
@@ -35163,12 +36540,22 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
35163
36540
|
this.showMobileCallBarAwaitingBrowserMic();
|
|
35164
36541
|
this.applyMobileBarConnectingCopy();
|
|
35165
36542
|
} else {
|
|
35166
|
-
// Desktop:
|
|
36543
|
+
// Desktop: full in-panel active UI, except flavor.callView:minimized (bottom strip only).
|
|
35167
36544
|
this.resetConnectingState();
|
|
35168
36545
|
_idleState = this.shadowRoot.getElementById('voiceIdleState');
|
|
35169
36546
|
activeState = this.shadowRoot.getElementById('voiceActiveState');
|
|
35170
36547
|
if (_idleState) _idleState.style.display = 'none';
|
|
35171
|
-
|
|
36548
|
+
stripOnly = this.config.desktopMinimizedEcommerceStripUi === true;
|
|
36549
|
+
if (activeState) {
|
|
36550
|
+
activeState.style.display = stripOnly ? 'none' : 'flex';
|
|
36551
|
+
}
|
|
36552
|
+
if (stripOnly && typeof this.config.onDesktopMinimizedStripCallStart === 'function') {
|
|
36553
|
+
try {
|
|
36554
|
+
this.config.onDesktopMinimizedStripCallStart();
|
|
36555
|
+
} catch (e) {
|
|
36556
|
+
console.warn('[VoiceInterface] onDesktopMinimizedStripCallStart:', e);
|
|
36557
|
+
}
|
|
36558
|
+
}
|
|
35172
36559
|
}
|
|
35173
36560
|
voiceInterface = this.shadowRoot.getElementById('voiceInterface'); // Adjust sizes based on container dimensions
|
|
35174
36561
|
setTimeout(function () {
|
|
@@ -35463,6 +36850,10 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
35463
36850
|
this._clearChatPanelDisplayLock();
|
|
35464
36851
|
_floatingButton = this.shadowRoot.getElementById('text-chat-button') || document.getElementById('text-chat-button');
|
|
35465
36852
|
if (_floatingButton) _floatingButton.style.display = '';
|
|
36853
|
+
} else if (this.config.desktopMinimizedEcommerceStripUi === true && typeof this.config.onDesktopMinimizedStripLauncherRestore === 'function') {
|
|
36854
|
+
try {
|
|
36855
|
+
this.config.onDesktopMinimizedStripLauncherRestore();
|
|
36856
|
+
} catch (_) {}
|
|
35466
36857
|
}
|
|
35467
36858
|
throw _t4;
|
|
35468
36859
|
case 29:
|
|
@@ -35627,6 +37018,13 @@ var VoiceInterface = /*#__PURE__*/function () {
|
|
|
35627
37018
|
var activeState = this.shadowRoot.getElementById('voiceActiveState');
|
|
35628
37019
|
if (idleState) idleState.style.display = 'flex';
|
|
35629
37020
|
if (activeState) activeState.style.display = 'none';
|
|
37021
|
+
if (typeof this.config.onDesktopMinimizedStripLauncherRestore === 'function') {
|
|
37022
|
+
try {
|
|
37023
|
+
this.config.onDesktopMinimizedStripLauncherRestore();
|
|
37024
|
+
} catch (e) {
|
|
37025
|
+
console.warn('[VoiceInterface] onDesktopMinimizedStripLauncherRestore:', e);
|
|
37026
|
+
}
|
|
37027
|
+
}
|
|
35630
37028
|
}
|
|
35631
37029
|
|
|
35632
37030
|
/**
|
|
@@ -38058,6 +39456,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
38058
39456
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
38059
39457
|
/* harmony export */ Mobile: () => (/* binding */ Mobile)
|
|
38060
39458
|
/* harmony export */ });
|
|
39459
|
+
/* harmony import */ var _rtlText_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./rtlText.js */ "./src/widget/voice/rtlText.js");
|
|
38061
39460
|
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
39461
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
38063
39462
|
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 +39480,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
|
|
|
38081
39480
|
* - _mobileCallStartTime: number | null
|
|
38082
39481
|
*/
|
|
38083
39482
|
|
|
39483
|
+
|
|
38084
39484
|
var Mobile = /*#__PURE__*/function () {
|
|
38085
39485
|
function Mobile(voiceInterface) {
|
|
38086
39486
|
_classCallCheck(this, Mobile);
|
|
@@ -38131,11 +39531,11 @@ var Mobile = /*#__PURE__*/function () {
|
|
|
38131
39531
|
// Create container div
|
|
38132
39532
|
var container = document.createElement('div');
|
|
38133
39533
|
container.id = 'mobile-voice-call-bar-container';
|
|
38134
|
-
var textDir = this.config
|
|
39534
|
+
var textDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.configPrefersRtlText)(this.config) ? 'rtl' : 'ltr';
|
|
38135
39535
|
container.setAttribute('dir', textDir);
|
|
38136
39536
|
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
39537
|
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
|
|
39538
|
+
}).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
39539
|
|
|
38140
39540
|
// Append to body
|
|
38141
39541
|
document.body.appendChild(container);
|
|
@@ -38407,7 +39807,7 @@ var Mobile = /*#__PURE__*/function () {
|
|
|
38407
39807
|
// Render message
|
|
38408
39808
|
var messageEl = document.createElement('div');
|
|
38409
39809
|
messageEl.className = "mobile-message-bubble ".concat(type);
|
|
38410
|
-
messageEl.setAttribute('dir', this.config
|
|
39810
|
+
messageEl.setAttribute('dir', (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(text, this.config));
|
|
38411
39811
|
messageEl.textContent = text;
|
|
38412
39812
|
messagesContainer.appendChild(messageEl);
|
|
38413
39813
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
@@ -38475,6 +39875,68 @@ var Mobile = /*#__PURE__*/function () {
|
|
|
38475
39875
|
|
|
38476
39876
|
/***/ }),
|
|
38477
39877
|
|
|
39878
|
+
/***/ "./src/widget/voice/rtlText.js":
|
|
39879
|
+
/*!*************************************!*\
|
|
39880
|
+
!*** ./src/widget/voice/rtlText.js ***!
|
|
39881
|
+
\*************************************/
|
|
39882
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
39883
|
+
|
|
39884
|
+
"use strict";
|
|
39885
|
+
__webpack_require__.r(__webpack_exports__);
|
|
39886
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
39887
|
+
/* harmony export */ configPrefersRtlText: () => (/* binding */ configPrefersRtlText),
|
|
39888
|
+
/* harmony export */ dirForAssistantStreamingText: () => (/* binding */ dirForAssistantStreamingText),
|
|
39889
|
+
/* harmony export */ dirForTranscriptSnippet: () => (/* binding */ dirForTranscriptSnippet)
|
|
39890
|
+
/* harmony export */ });
|
|
39891
|
+
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); }
|
|
39892
|
+
/**
|
|
39893
|
+
* RTL for voice / transcript text when the widget is in a right-to-left locale.
|
|
39894
|
+
* Uses explicit `direction: 'rtl'` from merged config, or language base `he` / `ar`
|
|
39895
|
+
* when direction was not set (or was lost on nested config spreads).
|
|
39896
|
+
*
|
|
39897
|
+
* @param {{ direction?: string, language?: string } | null | undefined} config
|
|
39898
|
+
* @returns {boolean}
|
|
39899
|
+
*/
|
|
39900
|
+
function configPrefersRtlText(config) {
|
|
39901
|
+
if (!config || _typeof(config) !== 'object') return false;
|
|
39902
|
+
if (config.direction === 'rtl') return true;
|
|
39903
|
+
var base = String(config.language || '').toLowerCase().split(/[-_]/)[0];
|
|
39904
|
+
return base === 'he' || base === 'ar';
|
|
39905
|
+
}
|
|
39906
|
+
|
|
39907
|
+
/**
|
|
39908
|
+
* `dir` for elements showing streamed assistant / agent text (and similar dynamic strings).
|
|
39909
|
+
* Uses `rtl` when the widget is explicitly RTL; otherwise `auto` so Hebrew (or other RTL)
|
|
39910
|
+
* agent copy still lays out correctly when Language/Direction are English + LTR.
|
|
39911
|
+
*
|
|
39912
|
+
* @param {{ direction?: string, language?: string } | null | undefined} config
|
|
39913
|
+
* @returns {'rtl' | 'auto'}
|
|
39914
|
+
*/
|
|
39915
|
+
function dirForAssistantStreamingText(config) {
|
|
39916
|
+
return configPrefersRtlText(config) ? 'rtl' : 'auto';
|
|
39917
|
+
}
|
|
39918
|
+
var RE_HEBREW = /[\u0590-\u05FF]/;
|
|
39919
|
+
var RE_ARABIC = /[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/;
|
|
39920
|
+
|
|
39921
|
+
/**
|
|
39922
|
+
* `dir` for a single assistant / agent transcript string.
|
|
39923
|
+
* When the widget chrome is LTR but the line contains Hebrew or Arabic letters,
|
|
39924
|
+
* use explicit `rtl` so neutral punctuation (e.g. "?") stays at the logical sentence end.
|
|
39925
|
+
*
|
|
39926
|
+
* @param {string|null|undefined} text
|
|
39927
|
+
* @param {{ direction?: string, language?: string } | null | undefined} config
|
|
39928
|
+
* @returns {'rtl' | 'ltr' | 'auto'}
|
|
39929
|
+
*/
|
|
39930
|
+
function dirForTranscriptSnippet(text, config) {
|
|
39931
|
+
if (configPrefersRtlText(config)) return 'rtl';
|
|
39932
|
+
var s = text != null ? String(text) : '';
|
|
39933
|
+
if (!s.trim()) return 'auto';
|
|
39934
|
+
if (RE_HEBREW.test(s) || RE_ARABIC.test(s)) return 'rtl';
|
|
39935
|
+
return 'ltr';
|
|
39936
|
+
}
|
|
39937
|
+
|
|
39938
|
+
/***/ }),
|
|
39939
|
+
|
|
38478
39940
|
/***/ "./src/widget/voice/styles.js":
|
|
38479
39941
|
/*!************************************!*\
|
|
38480
39942
|
!*** ./src/widget/voice/styles.js ***!
|
|
@@ -38570,7 +40032,7 @@ var Styles = /*#__PURE__*/function () {
|
|
|
38570
40032
|
key: "generateMobileCSS",
|
|
38571
40033
|
value: function generateMobileCSS() {
|
|
38572
40034
|
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 /*
|
|
40035
|
+
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
40036
|
}
|
|
38575
40037
|
}]);
|
|
38576
40038
|
}();
|
|
@@ -38591,6 +40053,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
38591
40053
|
/* harmony export */ resolveIdleHeaderAvatarUrl: () => (/* binding */ resolveIdleHeaderAvatarUrl),
|
|
38592
40054
|
/* harmony export */ sanitizeHeaderAvatarImageUrl: () => (/* binding */ sanitizeHeaderAvatarImageUrl)
|
|
38593
40055
|
/* harmony export */ });
|
|
40056
|
+
/* harmony import */ var _rtlText_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./rtlText.js */ "./src/widget/voice/rtlText.js");
|
|
38594
40057
|
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
40058
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
38596
40059
|
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 +40074,8 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
|
|
|
38611
40074
|
* - None (stateless template generation)
|
|
38612
40075
|
*/
|
|
38613
40076
|
|
|
40077
|
+
|
|
40078
|
+
|
|
38614
40079
|
/** Allow only http(s) absolute URLs for idle header avatar images. */
|
|
38615
40080
|
function sanitizeHeaderAvatarImageUrl(raw) {
|
|
38616
40081
|
var s = raw != null && String(raw).trim() || '';
|
|
@@ -38686,7 +40151,7 @@ var Templates = /*#__PURE__*/function () {
|
|
|
38686
40151
|
}, {
|
|
38687
40152
|
key: "generateHTML",
|
|
38688
40153
|
value: function generateHTML() {
|
|
38689
|
-
var _this$config$whatsapp;
|
|
40154
|
+
var _this$config$behavior, _this$config$whatsapp;
|
|
38690
40155
|
// Mobile UI - minimized bar design
|
|
38691
40156
|
if (this.voiceInterface.isMobile) {
|
|
38692
40157
|
return this.generateMobileHTML();
|
|
@@ -38702,8 +40167,11 @@ var Templates = /*#__PURE__*/function () {
|
|
|
38702
40167
|
var headerAvatarUrl = resolveIdleHeaderAvatarUrl(this.config);
|
|
38703
40168
|
var idleAvatarInner = headerAvatarUrl ? "<img src=\"".concat(escapeHtmlAttr(headerAvatarUrl), "\" alt=\"\" class=\"ttp-avatar-img\" />") : avatarInitial;
|
|
38704
40169
|
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>");
|
|
40170
|
+
var textDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.configPrefersRtlText)(this.config) ? 'rtl' : 'ltr';
|
|
40171
|
+
var transcriptPlaceholder = this.t('transcriptWillAppear') || '';
|
|
40172
|
+
var transcriptDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(transcriptPlaceholder, this.config);
|
|
40173
|
+
var voiceInputDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForAssistantStreamingText)(this.config);
|
|
40174
|
+
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
40175
|
}
|
|
38708
40176
|
|
|
38709
40177
|
/**
|
|
@@ -38713,13 +40181,16 @@ var Templates = /*#__PURE__*/function () {
|
|
|
38713
40181
|
key: "generateMobileHTML",
|
|
38714
40182
|
value: function generateMobileHTML() {
|
|
38715
40183
|
// For mobile, idle state goes in widget, but active state will be appended to body
|
|
38716
|
-
var textDir = this.config
|
|
40184
|
+
var textDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.configPrefersRtlText)(this.config) ? 'rtl' : 'ltr';
|
|
40185
|
+
var transcriptPlaceholder = this.t('transcriptWillAppear') || '';
|
|
40186
|
+
var transcriptDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(transcriptPlaceholder, this.config);
|
|
40187
|
+
var voiceInputDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForAssistantStreamingText)(this.config);
|
|
38717
40188
|
var headerAvatarUrl = resolveIdleHeaderAvatarUrl(this.config);
|
|
38718
40189
|
var mobileAvatarInner = headerAvatarUrl ? "<img src=\"".concat(escapeHtmlAttr(headerAvatarUrl), "\" alt=\"\" class=\"voice-avatar-img\" />") : '🤖';
|
|
38719
40190
|
var mobileAvatarClass = headerAvatarUrl ? 'voice-avatar voice-avatar--image' : 'voice-avatar';
|
|
38720
40191
|
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
40192
|
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(
|
|
40193
|
+
}).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
40194
|
}
|
|
38724
40195
|
}]);
|
|
38725
40196
|
}();
|
|
@@ -39142,6 +40613,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
39142
40613
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
39143
40614
|
/* harmony export */ Transcript: () => (/* binding */ Transcript)
|
|
39144
40615
|
/* harmony export */ });
|
|
40616
|
+
/* harmony import */ var _rtlText_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./rtlText.js */ "./src/widget/voice/rtlText.js");
|
|
39145
40617
|
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
40618
|
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
|
|
39147
40619
|
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 +40626,8 @@ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o =
|
|
|
39154
40626
|
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
39155
40627
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
39156
40628
|
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); }
|
|
40629
|
+
|
|
40630
|
+
|
|
39157
40631
|
/**
|
|
39158
40632
|
* Transcript - Manages transcript display and conversation history
|
|
39159
40633
|
*
|
|
@@ -39206,12 +40680,17 @@ var Transcript = /*#__PURE__*/function () {
|
|
|
39206
40680
|
key: "updateTranscript",
|
|
39207
40681
|
value: function updateTranscript(text) {
|
|
39208
40682
|
console.log('📝 [TRANSCRIPT] updateTranscript() called with text:', text ? text.substring(0, 50) + '...' : 'null/empty');
|
|
40683
|
+
var cfg = this.voiceInterface.config;
|
|
40684
|
+
var hasContent = !!(text && text.trim());
|
|
40685
|
+
var snippetForDir = hasContent ? text : this.t('transcriptWillAppear') || '';
|
|
40686
|
+
var assistantDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(snippetForDir, cfg);
|
|
39209
40687
|
|
|
39210
40688
|
// Mobile transcript (use container-scoped lookup for Shopify/embedded contexts)
|
|
39211
40689
|
var mobileBar = document.getElementById('mobile-voice-call-bar-container');
|
|
39212
40690
|
if (mobileBar) {
|
|
39213
40691
|
var mobileTranscriptText = mobileBar.querySelector('#mobileTranscriptText');
|
|
39214
40692
|
if (mobileTranscriptText) {
|
|
40693
|
+
mobileTranscriptText.setAttribute('dir', assistantDir);
|
|
39215
40694
|
mobileTranscriptText.textContent = text;
|
|
39216
40695
|
mobileTranscriptText.style.color = '';
|
|
39217
40696
|
}
|
|
@@ -39232,6 +40711,7 @@ var Transcript = /*#__PURE__*/function () {
|
|
|
39232
40711
|
var lastMessage = messagesContainer.querySelector('.mobile-message-bubble.assistant:last-child');
|
|
39233
40712
|
if (lastMessage) {
|
|
39234
40713
|
lastMessage.textContent = text;
|
|
40714
|
+
lastMessage.setAttribute('dir', (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(text, cfg));
|
|
39235
40715
|
} else {
|
|
39236
40716
|
this.voiceInterface.addMobileMessage('assistant', text);
|
|
39237
40717
|
}
|
|
@@ -39242,6 +40722,7 @@ var Transcript = /*#__PURE__*/function () {
|
|
|
39242
40722
|
// Desktop transcript - Update collapsed live text (2 lines)
|
|
39243
40723
|
var liveTextCollapsed = this.voiceInterface.shadowRoot.getElementById('liveTextCollapsed');
|
|
39244
40724
|
if (liveTextCollapsed) {
|
|
40725
|
+
liveTextCollapsed.setAttribute('dir', assistantDir);
|
|
39245
40726
|
liveTextCollapsed.style.color = '';
|
|
39246
40727
|
if (text && text.trim()) {
|
|
39247
40728
|
liveTextCollapsed.textContent = text;
|
|
@@ -39430,6 +40911,10 @@ var Transcript = /*#__PURE__*/function () {
|
|
|
39430
40911
|
// Update message text
|
|
39431
40912
|
liveMessageText.textContent = this.currentLiveMessage.content;
|
|
39432
40913
|
liveMessageRow.style.display = 'flex';
|
|
40914
|
+
var bubble = liveMessageRow.querySelector('.message-bubble');
|
|
40915
|
+
if (bubble) {
|
|
40916
|
+
bubble.setAttribute('dir', (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(this.currentLiveMessage.content, this.voiceInterface.config));
|
|
40917
|
+
}
|
|
39433
40918
|
|
|
39434
40919
|
// Re-add cursor
|
|
39435
40920
|
var cursor = liveMessageText.querySelector('.typing-cursor') || document.createElement('span');
|
|
@@ -39485,11 +40970,11 @@ var Transcript = /*#__PURE__*/function () {
|
|
|
39485
40970
|
messagesToRender = messagesToRender.slice(0, -1);
|
|
39486
40971
|
}
|
|
39487
40972
|
}
|
|
39488
|
-
var
|
|
40973
|
+
var bubbleDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(msg.content, this.config);
|
|
39489
40974
|
console.log('📝 [TRANSCRIPT] Rendering', messagesToRender.length, 'messages (live message excluded)');
|
|
39490
40975
|
container.innerHTML = messagesToRender.map(function (msg) {
|
|
39491
40976
|
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(
|
|
40977
|
+
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
40978
|
}).join('');
|
|
39494
40979
|
|
|
39495
40980
|
// Scroll to bottom
|