ttp-agent-sdk 2.45.6 → 2.45.12

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.
@@ -10641,7 +10641,8 @@ __webpack_require__.r(__webpack_exports__);
10641
10641
  /* harmony export */ CLIENT_TOOLS_PARTNER_ID: () => (/* binding */ CLIENT_TOOLS_PARTNER_ID),
10642
10642
  /* harmony export */ ClientScriptManager: () => (/* binding */ ClientScriptManager)
10643
10643
  /* harmony export */ });
10644
- /* harmony import */ var _ecommerce_PartnerScriptRunner_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../ecommerce/PartnerScriptRunner.js */ "./src/ecommerce/PartnerScriptRunner.js");
10644
+ /* harmony import */ var _ClientToolsRegistry_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ClientToolsRegistry.js */ "./src/core/ClientToolsRegistry.js");
10645
+ /* harmony import */ var _ecommerce_PartnerScriptRunner_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ecommerce/PartnerScriptRunner.js */ "./src/ecommerce/PartnerScriptRunner.js");
10645
10646
  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); }
10646
10647
  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; }
10647
10648
  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; }
@@ -10688,11 +10689,10 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
10688
10689
  */
10689
10690
 
10690
10691
 
10692
+
10691
10693
  /** Reserved partner id for the flavor-independent client-tool bundle. */
10692
10694
  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;
10695
+ var MAX_RESULT_SIZE = _ClientToolsRegistry_js__WEBPACK_IMPORTED_MODULE_0__["default"].MAX_RESULT_SIZE;
10696
10696
  var LOG = '[ClientScriptManager]';
10697
10697
  var ClientScriptManager = /*#__PURE__*/function () {
10698
10698
  /**
@@ -10711,7 +10711,7 @@ var ClientScriptManager = /*#__PURE__*/function () {
10711
10711
  _classCallCheck(this, ClientScriptManager);
10712
10712
  this._send = typeof send === 'function' ? send : function () {};
10713
10713
  this._label = label;
10714
- this.runner = new _ecommerce_PartnerScriptRunner_js__WEBPACK_IMPORTED_MODULE_0__.PartnerScriptRunner({
10714
+ this.runner = new _ecommerce_PartnerScriptRunner_js__WEBPACK_IMPORTED_MODULE_1__.PartnerScriptRunner({
10715
10715
  genericCtx: true
10716
10716
  });
10717
10717
  // Unsolicited adapter pushes (ctx.emit) go back over the same channel.
@@ -10735,7 +10735,7 @@ var ClientScriptManager = /*#__PURE__*/function () {
10735
10735
  * {@link isClientToolsToolCall}) against the client-tool runner and answer
10736
10736
  * with the standard `client_tool_result` / `client_tool_error` envelopes —
10737
10737
  * the exact shapes ClientToolsRegistry sends, since the backend awaits the
10738
- * reply keyed by `toolCallId`. Enforces the 50KB result cap.
10738
+ * reply keyed by `toolCallId`. Enforces the result-size cap ({@link ClientToolsRegistry.MAX_RESULT_SIZE}).
10739
10739
  *
10740
10740
  * @param {object} message { toolCallId, toolName, parameters:{ action, args, partner_id } }
10741
10741
  * @param {string|null} conversationId the SDK's current conversation id
@@ -10875,7 +10875,7 @@ var ClientScriptManager = /*#__PURE__*/function () {
10875
10875
  /**
10876
10876
  * Execute a `run_partner_script` envelope against the client-tool runner and
10877
10877
  * post the `run_partner_script_result` back over the channel. Enforces the
10878
- * 50KB result-size cap (oversize → ok:false, error:'RESULT_TOO_LARGE').
10878
+ * result-size cap (mirror ClientToolsRegistry.MAX_RESULT_SIZE).
10879
10879
  *
10880
10880
  * An unknown action (e.g. bundle not yet received) resolves to ok:false with
10881
10881
  * the runner's `adapter_not_in_bundle` reason — same behavior as the existing
@@ -10908,7 +10908,7 @@ var ClientScriptManager = /*#__PURE__*/function () {
10908
10908
  case 3:
10909
10909
  result = _context2.v;
10910
10910
  elapsedMs = Date.now() - startedAt;
10911
- ok = result && _typeof(result) === 'object' && 'ok' in result ? !!result.ok : true; // 50KB result-size cap (mirror ClientToolsRegistry.MAX_RESULT_SIZE).
10911
+ ok = result && _typeof(result) === 'object' && 'ok' in result ? !!result.ok : true; // Result-size cap (mirror ClientToolsRegistry.MAX_RESULT_SIZE).
10912
10912
  serializedSize = 0;
10913
10913
  try {
10914
10914
  serializedSize = JSON.stringify(result).length;
@@ -11045,7 +11045,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
11045
11045
  * the registered handler and sends back the result or error.
11046
11046
  */
11047
11047
  var ClientToolsRegistry = /*#__PURE__*/function () {
11048
- // 500KB for results containing images (base64)
11048
+ // ~500KB cap on all client_tool_result payloads
11049
11049
 
11050
11050
  function ClientToolsRegistry(sendMessage) {
11051
11051
  var getConversationId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
@@ -11205,10 +11205,7 @@ var ClientToolsRegistry = /*#__PURE__*/function () {
11205
11205
  value: function sendResult(toolCallId, toolName, result, executionTimeMs) {
11206
11206
  var conversationId = this.getConversationId();
11207
11207
  var timestamp = new Date().toISOString();
11208
-
11209
- // Size validation — images (base64) get a higher limit than text-only results
11210
- var hasImage = result && _typeof(result) === 'object' && result.image && result.mimeType;
11211
- var maxSize = hasImage ? ClientToolsRegistry.MAX_IMAGE_RESULT_SIZE : ClientToolsRegistry.MAX_RESULT_SIZE;
11208
+ var maxSize = ClientToolsRegistry.MAX_RESULT_SIZE;
11212
11209
  var serializedSize = 0;
11213
11210
  try {
11214
11211
  serializedSize = JSON.stringify(result).length;
@@ -11218,7 +11215,9 @@ var ClientToolsRegistry = /*#__PURE__*/function () {
11218
11215
  if (serializedSize > maxSize) {
11219
11216
  var sizeKB = Math.round(serializedSize / 1024);
11220
11217
  var limitKB = Math.round(maxSize / 1024);
11221
- console.error("\uD83D\uDD27 ClientTools: Result for '".concat(toolName, "' exceeds size limit (").concat(sizeKB, "KB > ").concat(limitKB, "KB)"));
11218
+ var productCount = result && Array.isArray(result.products) ? result.products.length : null;
11219
+ var detail = productCount != null ? " (".concat(productCount, " products in payload \u2014 trim search.api rows or raise cap)") : '';
11220
+ console.error("\uD83D\uDD27 ClientTools: Result for '".concat(toolName, "' exceeds size limit (").concat(sizeKB, "KB > ").concat(limitKB, "KB)").concat(detail), (result === null || result === void 0 ? void 0 : result.reason) || (result === null || result === void 0 ? void 0 : result.error) || '');
11222
11221
  this.sendError(toolCallId, toolName, "Result too large: ".concat(sizeKB, "KB exceeds ").concat(limitKB, "KB limit"), 'RESULT_TOO_LARGE', executionTimeMs);
11223
11222
  return;
11224
11223
  }
@@ -11278,9 +11277,7 @@ var ClientToolsRegistry = /*#__PURE__*/function () {
11278
11277
  }
11279
11278
  }]);
11280
11279
  }();
11281
- _defineProperty(ClientToolsRegistry, "MAX_RESULT_SIZE", 50000);
11282
- // 50KB for text-only results
11283
- _defineProperty(ClientToolsRegistry, "MAX_IMAGE_RESULT_SIZE", 512000);
11280
+ _defineProperty(ClientToolsRegistry, "MAX_RESULT_SIZE", 512000);
11284
11281
  /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (ClientToolsRegistry);
11285
11282
 
11286
11283
  /***/ }),
@@ -11947,7 +11944,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
11947
11944
 
11948
11945
  // SDK build time for debugging
11949
11946
  if (true) {
11950
- helloMessage.lastBuildTime = "2026-06-13T08:36:31.824Z";
11947
+ helloMessage.lastBuildTime = "2026-06-28T10:02:02.645Z";
11951
11948
  }
11952
11949
  try {
11953
11950
  this.ws.send(JSON.stringify(helloMessage));
@@ -13564,6 +13561,7 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13564
13561
  this.onTogglePause = opts.onTogglePause || function () {};
13565
13562
  this.onToggleSpeaker = opts.onToggleSpeaker || function () {};
13566
13563
  this.onSendText = opts.onSendText || function () {};
13564
+ this.onAbout = typeof opts.onAbout === 'function' ? opts.onAbout : null;
13567
13565
  this._kbdOpen = false;
13568
13566
  this._el = null;
13569
13567
  this._build();
@@ -13651,11 +13649,8 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13651
13649
  }, {
13652
13650
  key: "_footerHtml",
13653
13651
  value: function _footerHtml() {
13654
- if (!this.footer) return '';
13655
- var _this$footer = this.footer,
13656
- brandUrl = _this$footer.brandUrl,
13657
- brandName = _this$footer.brandName;
13658
- 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>");
13652
+ var brand = this.footer ? "<span>Powered by <a href=\"".concat(this.footer.brandUrl, "\" target=\"_blank\" rel=\"noopener noreferrer\"><b>").concat(this.footer.brandName, "</b></a></span>") : '';
13653
+ return "\n <div id=\"ttp-ecom-footer\" class=\"ttp-ecom-footer\">\n ".concat(brand, "\n <button type=\"button\" class=\"ttp-ecom-info-btn\" id=\"ttp-ecom-btn-info\" title=\"About\" aria-label=\"About\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"9\"/>\n <line x1=\"12\" y1=\"11\" x2=\"12\" y2=\"16\"/>\n <circle cx=\"12\" cy=\"7.7\" r=\"1.05\" fill=\"currentColor\" stroke=\"none\"/>\n </svg>\n </button>\n </div>");
13659
13654
  }
13660
13655
  }, {
13661
13656
  key: "_wireEvents",
@@ -13684,6 +13679,12 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13684
13679
  return $('ttp-ecom-input').focus();
13685
13680
  }, 50);
13686
13681
  });
13682
+ var infoBtn = $('ttp-ecom-btn-info');
13683
+ if (infoBtn) {
13684
+ infoBtn.addEventListener('click', function () {
13685
+ if (_this.onAbout) _this.onAbout();
13686
+ });
13687
+ }
13687
13688
  $('ttp-ecom-btn-end').addEventListener('click', function () {
13688
13689
  return _this.onHangup();
13689
13690
  });
@@ -13703,11 +13704,16 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13703
13704
  }, {
13704
13705
  key: "show",
13705
13706
  value: function show() {
13707
+ var _this2 = this;
13706
13708
  var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
13707
13709
  if (!this._el) return;
13708
13710
  if (opts.reset !== false) this._reset();
13709
13711
  this._el.style.display = '';
13710
13712
  this.syncFromVoice();
13713
+ // Now that the strip has real dimensions, clamp any restored drag position.
13714
+ if (this._dragEnabled) requestAnimationFrame(function () {
13715
+ return _this2.reclampDrag();
13716
+ });
13711
13717
  }
13712
13718
  }, {
13713
13719
  key: "hide",
@@ -13839,6 +13845,177 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13839
13845
  if (!this.getVoiceUiState) return;
13840
13846
  this.syncFromVoice();
13841
13847
  }
13848
+
13849
+ /**
13850
+ * Make the strip draggable by its body. The wrapper is centered with
13851
+ * `translateX(-50%)`; a drag offset is layered on top of that. Control buttons
13852
+ * and the text input don't start a drag. Position is clamped to the viewport
13853
+ * (re-clamped on show/resize) and optionally persisted under `storageKey`.
13854
+ *
13855
+ * @param {Object} opts
13856
+ * @param {boolean} [opts.persist=true]
13857
+ * @param {string} [opts.storageKey]
13858
+ */
13859
+ }, {
13860
+ key: "enableDrag",
13861
+ value: function enableDrag() {
13862
+ var _this3 = this;
13863
+ var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
13864
+ if (!this._el || this._dragEnabled) return;
13865
+ var wrapper = this._el; // fixed, centered via translateX(-50%)
13866
+ var handle = wrapper.querySelector('#ttp-ecom-strip');
13867
+ if (!handle) return;
13868
+ this._dragEnabled = true;
13869
+ this._dragPersist = opts.persist !== false;
13870
+ this._dragKey = opts.storageKey || '';
13871
+ this._dragOffset = {
13872
+ x: 0,
13873
+ y: 0
13874
+ };
13875
+ handle.style.setProperty('cursor', 'move');
13876
+ handle.style.setProperty('touch-action', 'none');
13877
+ var EXCLUDE = 'button, a, input, textarea, select, [contenteditable]';
13878
+ var THRESHOLD = 5;
13879
+ var startX = 0,
13880
+ startY = 0,
13881
+ baseX = 0,
13882
+ baseY = 0,
13883
+ baseRect = null;
13884
+ var dragging = false,
13885
+ moved = false;
13886
+ var onMove = function onMove(e) {
13887
+ var dx = e.clientX - startX,
13888
+ dy = e.clientY - startY;
13889
+ if (!moved && Math.hypot(dx, dy) < THRESHOLD) return;
13890
+ moved = true;
13891
+ _this3._dragOffset = _this3._clampDragOffset(baseX + dx, baseY + dy, baseRect);
13892
+ _this3._applyDragOffset();
13893
+ };
13894
+ var _onUp = function onUp(e) {
13895
+ window.removeEventListener('pointermove', onMove);
13896
+ window.removeEventListener('pointerup', _onUp);
13897
+ handle.style.setProperty('cursor', 'move');
13898
+ try {
13899
+ handle.releasePointerCapture(e.pointerId);
13900
+ } catch (err) {/* not captured */}
13901
+ dragging = false;
13902
+ if (moved && _this3._dragPersist && _this3._dragKey) {
13903
+ try {
13904
+ window.localStorage.setItem(_this3._dragKey, JSON.stringify(_this3._dragOffset));
13905
+ } catch (err) {/* unavailable */}
13906
+ }
13907
+ };
13908
+ var onDown = function onDown(e) {
13909
+ if (e.button != null && e.button !== 0) return; // primary / touch / pen only
13910
+ if (dragging) return;
13911
+ if (e.target && e.target.closest && e.target.closest(EXCLUDE)) return;
13912
+ dragging = true;
13913
+ moved = false;
13914
+ startX = e.clientX;
13915
+ startY = e.clientY;
13916
+ baseX = _this3._dragOffset.x;
13917
+ baseY = _this3._dragOffset.y;
13918
+ baseRect = _this3._dragBaseRect();
13919
+ handle.style.setProperty('cursor', 'grabbing');
13920
+ try {
13921
+ handle.setPointerCapture(e.pointerId);
13922
+ } catch (err) {/* unsupported */}
13923
+ window.addEventListener('pointermove', onMove);
13924
+ window.addEventListener('pointerup', _onUp);
13925
+ };
13926
+ handle.addEventListener('pointerdown', onDown);
13927
+
13928
+ // Restore saved offset (unclamped here — the strip is hidden until show(),
13929
+ // where it gets clamped against real dimensions).
13930
+ if (this._dragPersist && this._dragKey) {
13931
+ try {
13932
+ var saved = JSON.parse(window.localStorage.getItem(this._dragKey) || 'null');
13933
+ if (saved && typeof saved.x === 'number' && typeof saved.y === 'number') {
13934
+ this._dragOffset = {
13935
+ x: saved.x,
13936
+ y: saved.y
13937
+ };
13938
+ this._applyDragOffset();
13939
+ }
13940
+ } catch (err) {/* ignore corrupt storage */}
13941
+ }
13942
+ }
13943
+
13944
+ /**
13945
+ * Base (offset-0) viewport rect used for clamping. Measures the *visible* inner
13946
+ * strip (`#ttp-ecom-strip`), not the wrapper: the wrapper is fixed at `bottom:0`
13947
+ * with bottom padding (incl. safe-area inset), so its bottom edge hugs the
13948
+ * viewport bottom and would wrongly forbid any downward drag. The drag offset is
13949
+ * applied to the wrapper, but both elements move together by the same amount, so
13950
+ * the inner strip's offset-0 rect is just its current rect minus the offset.
13951
+ */
13952
+ }, {
13953
+ key: "_dragBaseRect",
13954
+ value: function _dragBaseRect() {
13955
+ var o = this._dragOffset || {
13956
+ x: 0,
13957
+ y: 0
13958
+ };
13959
+ var inner = this._el && this._el.querySelector('#ttp-ecom-strip') || this._el;
13960
+ var r = inner.getBoundingClientRect();
13961
+ return {
13962
+ left: r.left - o.x,
13963
+ top: r.top - o.y,
13964
+ right: r.right - o.x,
13965
+ bottom: r.bottom - o.y
13966
+ };
13967
+ }
13968
+
13969
+ /**
13970
+ * Clamp a drag offset so the strip stays within the viewport. An 8px margin is
13971
+ * kept on the top/left/right edges. The bottom is allowed to drag *past* the
13972
+ * viewport edge so the strip can be tucked partly off-screen — but at least
13973
+ * `MIN_VISIBLE` (10px) of its top stays on screen so it can always be grabbed
13974
+ * and dragged back up.
13975
+ */
13976
+ }, {
13977
+ key: "_clampDragOffset",
13978
+ value: function _clampDragOffset(x, y, base) {
13979
+ if (!base) return {
13980
+ x: x,
13981
+ y: y
13982
+ };
13983
+ var margin = 8;
13984
+ var MIN_VISIBLE = 10;
13985
+ var minX = margin - base.left,
13986
+ maxX = window.innerWidth - margin - base.right;
13987
+ var minY = margin - base.top,
13988
+ maxY = window.innerHeight - MIN_VISIBLE - base.top;
13989
+ if (minX > maxX) maxX = minX;
13990
+ if (minY > maxY) maxY = minY;
13991
+ return {
13992
+ x: Math.min(Math.max(x, minX), maxX),
13993
+ y: Math.min(Math.max(y, minY), maxY)
13994
+ };
13995
+ }
13996
+
13997
+ /** Apply the drag offset on top of the centering transform. */
13998
+ }, {
13999
+ key: "_applyDragOffset",
14000
+ value: function _applyDragOffset() {
14001
+ if (!this._el) return;
14002
+ var o = this._dragOffset || {
14003
+ x: 0,
14004
+ y: 0
14005
+ };
14006
+ this._el.style.setProperty('transform', "translateX(-50%) translate(".concat(o.x, "px, ").concat(o.y, "px)"), 'important');
14007
+ }
14008
+
14009
+ /** Re-clamp the dragged strip to the current viewport (on show / resize). */
14010
+ }, {
14011
+ key: "reclampDrag",
14012
+ value: function reclampDrag() {
14013
+ if (!this._dragEnabled || !this._el) return;
14014
+ var r = this._el.getBoundingClientRect();
14015
+ if (!r.width && !r.height) return; // hidden — nothing to clamp yet
14016
+ this._dragOffset = this._clampDragOffset(this._dragOffset.x, this._dragOffset.y, this._dragBaseRect());
14017
+ this._applyDragOffset();
14018
+ }
13842
14019
  }, {
13843
14020
  key: "destroy",
13844
14021
  value: function destroy() {
@@ -13850,12 +14027,12 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13850
14027
  }, {
13851
14028
  key: "_reset",
13852
14029
  value: function _reset() {
13853
- var _this2 = this,
14030
+ var _this4 = this,
13854
14031
  _$,
13855
14032
  _$2;
13856
14033
  this._kbdOpen = false;
13857
14034
  var $ = function $(id) {
13858
- return _this2._el.querySelector('#' + id);
14035
+ return _this4._el.querySelector('#' + id);
13859
14036
  };
13860
14037
  var tx = $('ttp-ecom-transcript');
13861
14038
  if (tx) {
@@ -13898,7 +14075,7 @@ var EcommerceVoiceStrip = /*#__PURE__*/function () {
13898
14075
  }
13899
14076
  }]);
13900
14077
  }();
13901
- 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";
14078
+ 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 gap: 8px;\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-info-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 2px;\n margin: 0;\n background: none;\n border: none;\n cursor: pointer;\n line-height: 0;\n border-radius: 50%;\n color: rgba(255, 255, 255, 0.35);\n transition: color 0.2s, background 0.2s;\n}\n.ttp-ecom-info-btn:hover {\n color: rgba(255, 255, 255, 0.7);\n background: rgba(255, 255, 255, 0.08);\n}\n.ttp-ecom-info-btn svg { display: block; }\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";
13902
14079
 
13903
14080
  /***/ }),
13904
14081
 
@@ -15519,7 +15696,7 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
15519
15696
  value: (function () {
15520
15697
  var _runAdapterBody2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(action, args, entry) {
15521
15698
  var _entry$version4;
15522
- var eventName, safeArgs, productIds, _productIds$, idStr, _idStr, ctx, result, postCtx, _t3, _t4, _t5;
15699
+ var eventName, safeArgs, productIds, _productIds$, idStr, _idStr, ctx, result, _result$totalCount, bytes, kb, extra, postCtx, _t3, _t4, _t5;
15523
15700
  return _regenerator().w(function (_context3) {
15524
15701
  while (1) switch (_context3.p = _context3.n) {
15525
15702
  case 0:
@@ -15583,6 +15760,19 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
15583
15760
  error: String((_t4 === null || _t4 === void 0 ? void 0 : _t4.message) || _t4)
15584
15761
  });
15585
15762
  case 7:
15763
+ // Payload size diagnostics — log before ClientToolsRegistry rejects oversize results.
15764
+ try {
15765
+ bytes = JSON.stringify(result).length;
15766
+ kb = Math.round(bytes / 1024);
15767
+ extra = action === 'search.api' && result && _typeof(result) === 'object' ? " products=".concat(Array.isArray(result.products) ? result.products.length : '?', " totalCount=").concat((_result$totalCount = result.totalCount) !== null && _result$totalCount !== void 0 ? _result$totalCount : '?') : '';
15768
+ console.log("".concat(LOG, " ").concat(action, " done ok=").concat(adapterResultOk(result), " payload=").concat(kb, "KB").concat(extra));
15769
+ if (bytes > 480000) {
15770
+ console.warn("".concat(LOG, " ").concat(action, " payload near/over 512KB wire limit (").concat(kb, "KB) \u2014 backend may see totalCount=0"));
15771
+ }
15772
+ } catch (_unused) {/* ignore circular refs */}
15773
+
15774
+ // Inline post-hook runs only when main succeeded (mirror of after-hook
15775
+ // gating). Best-effort: errors are logged, the main result is still returned.
15586
15776
  if (!(entry.postFn && adapterResultOk(result))) {
15587
15777
  _context3.n = 11;
15588
15778
  break;
@@ -20113,8 +20303,8 @@ var VoiceSDK = _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"];
20113
20303
 
20114
20304
 
20115
20305
  // Version - injected at build time from package.json via webpack DefinePlugin
20116
- var VERSION = "2.45.6";
20117
- var BUILD_TIME = "2026-06-13T08:36:31.824Z";
20306
+ var VERSION = "2.45.12";
20307
+ var BUILD_TIME = "2026-06-28T10:02:02.645Z";
20118
20308
  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;');
20119
20309
 
20120
20310
  // Named exports
@@ -23039,208 +23229,300 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
23039
23229
  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; }
23040
23230
  /**
23041
23231
  * DOM Context Extraction
23042
- *
23043
- * Extracts structured information about interactive elements on the page
23044
- * to provide context to the LLM for visual assistance.
23232
+ *
23233
+ * Extracts the actual readable content of the page — text, headings, code
23234
+ * blocks, lists plus interactive elements (buttons, forms, links) for
23235
+ * navigation and action context.
23045
23236
  */
23046
23237
 
23047
23238
 
23048
-
23049
- // Default limits to prevent token explosion
23050
23239
  var DEFAULT_LIMITS = {
23240
+ maxContentLength: 8000,
23241
+ // chars of main text content
23242
+ maxCodeBlockLength: 600,
23243
+ // chars per code block
23051
23244
  buttons: 20,
23052
23245
  links: 30,
23053
23246
  forms: 5,
23054
23247
  fieldsPerForm: 15,
23055
- sections: 10,
23056
- headings: 15,
23057
- maxTextLength: 100,
23058
- maxSummaryLength: 150
23248
+ maxTextLength: 100 // per interactive element label
23059
23249
  };
23060
23250
 
23061
- /**
23062
- * Generate a unique, stable CSS selector for an element
23063
- * Prioritizes stable attributes over auto-generated IDs
23064
- *
23065
- * @param {HTMLElement} element - The element to generate selector for
23066
- * @returns {string} - CSS selector string
23067
- */
23068
- function generateSelector(element) {
23069
- var _element$dataset, _element$dataset2;
23070
- if (!element || !element.tagName) return null;
23071
-
23072
- // Priority 1: data-testid (designed to be stable)
23073
- if ((_element$dataset = element.dataset) !== null && _element$dataset !== void 0 && _element$dataset.testid) {
23074
- return "[data-testid=\"".concat(element.dataset.testid, "\"]");
23075
- }
23076
-
23077
- // Priority 2: data-cy (Cypress test attribute)
23078
- if ((_element$dataset2 = element.dataset) !== null && _element$dataset2 !== void 0 && _element$dataset2.cy) {
23079
- return "[data-cy=\"".concat(element.dataset.cy, "\"]");
23080
- }
23081
-
23082
- // Priority 3: aria-label (accessibility, usually stable)
23083
- var ariaLabel = element.getAttribute('aria-label');
23084
- if (ariaLabel && ariaLabel.length < 50) {
23085
- return "[aria-label=\"".concat(ariaLabel.replace(/"/g, '\\"'), "\"]");
23086
- }
23087
-
23088
- // Priority 4: Stable ID (not auto-generated)
23089
- if (element.id && !isAutoGeneratedId(element.id)) {
23090
- return "#".concat(CSS.escape(element.id));
23091
- }
23092
-
23093
- // Priority 5: name attribute (for form fields)
23094
- if (element.name) {
23095
- var tag = element.tagName.toLowerCase();
23096
- return "".concat(tag, "[name=\"").concat(element.name, "\"]");
23097
- }
23251
+ // ─── Noise exclusion ────────────────────────────────────────────────────────
23252
+
23253
+ var NOISE_TAGS = new Set(['nav', 'header', 'footer', 'aside', 'script', 'style', 'noscript', 'svg', 'canvas', 'iframe']);
23254
+ var NOISE_ROLES = new Set(['navigation', 'banner', 'complementary', 'search']);
23255
+ function isNoise(el) {
23256
+ if (NOISE_TAGS.has(el.tagName.toLowerCase())) return true;
23257
+ var role = el.getAttribute('role');
23258
+ if (role && NOISE_ROLES.has(role)) return true;
23259
+ var cls = el.className || '';
23260
+ // Common sidebar/nav class patterns
23261
+ if (/\b(sidebar|navbar|topbar|breadcrumb|cookie|toast|modal-backdrop)\b/i.test(cls)) return true;
23262
+ return false;
23263
+ }
23098
23264
 
23099
- // Priority 6: Unique text content for buttons/links
23100
- if (['BUTTON', 'A'].includes(element.tagName)) {
23101
- var _element$innerText;
23102
- var text = (_element$innerText = element.innerText) === null || _element$innerText === void 0 ? void 0 : _element$innerText.trim();
23103
- if (text && text.length < 30 && text.length > 0) {
23104
- var _tag = element.tagName.toLowerCase();
23105
- // Check if this text is unique on the page
23106
- var matches = document.querySelectorAll("".concat(_tag));
23107
- var sameTextCount = Array.from(matches).filter(function (el) {
23108
- var _el$innerText;
23109
- return ((_el$innerText = el.innerText) === null || _el$innerText === void 0 ? void 0 : _el$innerText.trim()) === text;
23110
- }).length;
23111
- if (sameTextCount === 1) {
23112
- return "".concat(_tag, ":contains(\"").concat(text.replace(/"/g, '\\"'), "\")");
23113
- }
23114
- }
23115
- }
23265
+ // ─── Visibility ──────────────────────────────────────────────────────────────
23116
23266
 
23117
- // Priority 7: Build path from stable ancestors
23118
- return buildPathSelector(element);
23267
+ function isVisible(el) {
23268
+ if (!el) return false;
23269
+ var style = window.getComputedStyle(el);
23270
+ return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0' && el.offsetWidth > 0 && el.offsetHeight > 0;
23271
+ }
23272
+ function isInViewport(el) {
23273
+ var buffer = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 500;
23274
+ var rect = el.getBoundingClientRect();
23275
+ return rect.bottom >= -buffer && rect.top <= window.innerHeight + buffer && rect.right >= 0 && rect.left <= window.innerWidth;
23119
23276
  }
23120
23277
 
23121
- /**
23122
- * Check if an ID looks auto-generated (React, Angular, Vue, etc.)
23123
- */
23278
+ // ─── Selector generation (kept for interactive elements) ─────────────────────
23279
+
23124
23280
  function isAutoGeneratedId(id) {
23125
- var autoGenPatterns = [/^:r[0-9a-z]+:$/,
23126
- // React 18+ useId
23127
- /^react-/i,
23128
- // React
23129
- /^ng-/i,
23130
- // Angular
23131
- /^vue-/i,
23132
- // Vue
23133
- /^ember[0-9]+/i,
23134
- // Ember
23135
- /^_[a-z0-9]{8,}$/i,
23136
- // Generic hash IDs
23137
- /^[a-f0-9]{8}-[a-f0-9]{4}/i,
23138
- // UUID-like
23139
- /^uid-/i,
23140
- // Generic UID
23141
- /^id[0-9]+$/i // Generic numbered IDs
23142
- ];
23143
- return autoGenPatterns.some(function (pattern) {
23144
- return pattern.test(id);
23281
+ return /^(:r[0-9a-z]+:|react-|ng-|vue-|ember[0-9]+|_[a-z0-9]{8,}|[a-f0-9]{8}-[a-f0-9]{4}|uid-|id[0-9]+$)/i.test(id);
23282
+ }
23283
+ function getMeaningfulClass(el) {
23284
+ var utilityPatterns = [/^(flex|grid|block|inline|hidden)$/, /^(m|p|w|h|min|max)-/, /^(text|font|bg|border)-/, /^(col|row)-/, /^(sm|md|lg|xl|2xl):/, /^hover:|focus:|active:/, /^[a-z]{1,3}-[0-9]+$/];
23285
+ var classes = Array.from(el.classList || []);
23286
+ var meaningful = classes.filter(function (cls) {
23287
+ return !utilityPatterns.some(function (p) {
23288
+ return p.test(cls);
23289
+ }) && cls.length > 2 && cls.length < 30;
23145
23290
  });
23291
+ return meaningful[0] || null;
23146
23292
  }
23147
-
23148
- /**
23149
- * Build a selector path from ancestors
23150
- */
23151
- function buildPathSelector(element) {
23293
+ function buildPathSelector(el) {
23152
23294
  var maxDepth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;
23153
23295
  var path = [];
23154
- var current = element;
23296
+ var cur = el;
23155
23297
  var depth = 0;
23156
- while (current && current !== document.body && depth < maxDepth) {
23157
- var tag = current.tagName.toLowerCase();
23158
-
23159
- // Try to add distinguishing info
23160
- if (current.id && !isAutoGeneratedId(current.id)) {
23161
- path.unshift("#".concat(CSS.escape(current.id)));
23162
- break; // ID is unique, stop here
23163
- }
23164
-
23165
- // Add class if it seems meaningful
23166
- var meaningfulClass = getMeaningfulClass(current);
23167
- if (meaningfulClass) {
23168
- path.unshift("".concat(tag, ".").concat(meaningfulClass));
23298
+ while (cur && cur !== document.body && depth < maxDepth) {
23299
+ var tag = cur.tagName.toLowerCase();
23300
+ if (cur.id && !isAutoGeneratedId(cur.id)) {
23301
+ path.unshift("#".concat(CSS.escape(cur.id)));
23302
+ break;
23303
+ }
23304
+ var cls = getMeaningfulClass(cur);
23305
+ if (cls) {
23306
+ path.unshift("".concat(tag, ".").concat(cls));
23169
23307
  } else {
23170
- var _current$parentElemen;
23171
- // Add nth-child if needed
23172
- var siblings = (_current$parentElemen = current.parentElement) === null || _current$parentElemen === void 0 ? void 0 : _current$parentElemen.children;
23308
+ var _cur$parentElement;
23309
+ var siblings = (_cur$parentElement = cur.parentElement) === null || _cur$parentElement === void 0 ? void 0 : _cur$parentElement.children;
23173
23310
  if (siblings && siblings.length > 1) {
23174
- var index = Array.from(siblings).indexOf(current) + 1;
23175
- path.unshift("".concat(tag, ":nth-child(").concat(index, ")"));
23311
+ var idx = Array.from(siblings).indexOf(cur) + 1;
23312
+ path.unshift("".concat(tag, ":nth-child(").concat(idx, ")"));
23176
23313
  } else {
23177
23314
  path.unshift(tag);
23178
23315
  }
23179
23316
  }
23180
- current = current.parentElement;
23317
+ cur = cur.parentElement;
23181
23318
  depth++;
23182
23319
  }
23183
23320
  return path.join(' > ');
23184
23321
  }
23322
+ function generateSelector(el) {
23323
+ var _el$dataset, _el$dataset2;
23324
+ if (!el || !el.tagName) return null;
23325
+ if ((_el$dataset = el.dataset) !== null && _el$dataset !== void 0 && _el$dataset.testid) return "[data-testid=\"".concat(el.dataset.testid, "\"]");
23326
+ if ((_el$dataset2 = el.dataset) !== null && _el$dataset2 !== void 0 && _el$dataset2.cy) return "[data-cy=\"".concat(el.dataset.cy, "\"]");
23327
+ var aria = el.getAttribute('aria-label');
23328
+ if (aria && aria.length < 50) return "[aria-label=\"".concat(aria.replace(/"/g, '\\"'), "\"]");
23329
+ if (el.id && !isAutoGeneratedId(el.id)) return "#".concat(CSS.escape(el.id));
23330
+ if (el.name) return "".concat(el.tagName.toLowerCase(), "[name=\"").concat(el.name, "\"]");
23331
+ if (['BUTTON', 'A'].includes(el.tagName)) {
23332
+ var _el$innerText;
23333
+ var text = (_el$innerText = el.innerText) === null || _el$innerText === void 0 ? void 0 : _el$innerText.trim();
23334
+ if (text && text.length < 30) {
23335
+ var tag = el.tagName.toLowerCase();
23336
+ var matches = document.querySelectorAll(tag);
23337
+ if (Array.from(matches).filter(function (e) {
23338
+ var _e$innerText;
23339
+ return ((_e$innerText = e.innerText) === null || _e$innerText === void 0 ? void 0 : _e$innerText.trim()) === text;
23340
+ }).length === 1) {
23341
+ return "".concat(tag, ":contains(\"").concat(text.replace(/"/g, '\\"'), "\")");
23342
+ }
23343
+ }
23344
+ }
23345
+ return buildPathSelector(el);
23346
+ }
23347
+
23348
+ // ─── Main content extraction ─────────────────────────────────────────────────
23185
23349
 
23186
23350
  /**
23187
- * Get a meaningful class name (not utility classes)
23351
+ * Find the best container element that holds the page's primary content.
23352
+ * Tries progressively wider selectors; falls back to document.body.
23188
23353
  */
23189
- function getMeaningfulClass(element) {
23190
- var classes = Array.from(element.classList || []);
23191
-
23192
- // Skip utility class patterns (Tailwind, Bootstrap, etc.)
23193
- var utilityPatterns = [/^(flex|grid|block|inline|hidden)$/, /^(m|p|w|h|min|max)-/,
23194
- // Spacing/sizing
23195
- /^(text|font|bg|border)-/,
23196
- // Typography/colors
23197
- /^(col|row)-/,
23198
- // Grid
23199
- /^(sm|md|lg|xl|2xl):/,
23200
- // Responsive
23201
- /^hover:|focus:|active:/,
23202
- // States
23203
- /^[a-z]{1,3}-[0-9]+$/ // Generic utilities
23204
- ];
23205
- var meaningfulClasses = classes.filter(function (cls) {
23206
- return !utilityPatterns.some(function (pattern) {
23207
- return pattern.test(cls);
23208
- }) && cls.length > 2 && cls.length < 30;
23209
- });
23210
- return meaningfulClasses[0] || null;
23354
+ function findContentRoot() {
23355
+ var candidates = ['article', '[role="article"]', 'main', '[role="main"]', '.prose', '.markdown-body', '.content', '#content', '#main-content', '#main'];
23356
+ for (var _i = 0, _candidates = candidates; _i < _candidates.length; _i++) {
23357
+ var sel = _candidates[_i];
23358
+ try {
23359
+ var el = document.querySelector(sel);
23360
+ if (el && isVisible(el)) return el;
23361
+ } catch (_unused) {}
23362
+ }
23363
+ return document.body;
23211
23364
  }
23212
23365
 
23213
23366
  /**
23214
- * Check if element is in or near viewport
23367
+ * Walk the content root and emit structured content items.
23368
+ * Skips noise elements (nav, header, aside…).
23369
+ *
23370
+ * Returns an array of items:
23371
+ * { type: 'heading', level: 'h2', text: '...' }
23372
+ * { type: 'text', text: '...' }
23373
+ * { type: 'code', lang: 'bash', text: '...' }
23374
+ * { type: 'listItem', text: '...' }
23215
23375
  */
23216
- function isInViewport(element) {
23217
- var buffer = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 500;
23218
- var rect = element.getBoundingClientRect();
23219
- return rect.bottom >= -buffer && rect.top <= window.innerHeight + buffer && rect.right >= 0 && rect.left <= window.innerWidth;
23376
+ function walkContent(root, limits) {
23377
+ var items = [];
23378
+ var charCount = 0;
23379
+ var BLOCK_TAGS = new Set(['p', 'li', 'dt', 'dd', 'td', 'th', 'blockquote', 'figcaption']);
23380
+ var HEADING_TAGS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']);
23381
+ function walk(node) {
23382
+ if (charCount >= limits.maxContentLength) return;
23383
+ if (node.nodeType !== Node.ELEMENT_NODE) return;
23384
+ var tag = node.tagName.toLowerCase();
23385
+
23386
+ // Skip noise subtrees entirely
23387
+ if (isNoise(node)) return;
23388
+ if (!isVisible(node)) return;
23389
+
23390
+ // Heading → new section marker
23391
+ if (HEADING_TAGS.has(tag)) {
23392
+ var text = (node.innerText || '').trim();
23393
+ if (text) {
23394
+ items.push({
23395
+ type: 'heading',
23396
+ level: tag,
23397
+ text: text
23398
+ });
23399
+ charCount += text.length;
23400
+ }
23401
+ return; // don't recurse — heading text already captured
23402
+ }
23403
+
23404
+ // Code block
23405
+ if (tag === 'pre') {
23406
+ var codeEl = node.querySelector('code');
23407
+ var raw = (codeEl || node).innerText || '';
23408
+ var _text = raw.trim().substring(0, limits.maxCodeBlockLength);
23409
+ if (_text) {
23410
+ var _match;
23411
+ var lang = ((_match = ((codeEl === null || codeEl === void 0 ? void 0 : codeEl.className) || '').match(/language-(\w+)/)) === null || _match === void 0 ? void 0 : _match[1]) || '';
23412
+ items.push({
23413
+ type: 'code',
23414
+ lang: lang,
23415
+ text: _text
23416
+ });
23417
+ charCount += _text.length;
23418
+ }
23419
+ return;
23420
+ }
23421
+
23422
+ // Inline code (not inside pre)
23423
+ if (tag === 'code' && node.closest('pre') === null) {
23424
+ // Let the parent block pick it up via innerText
23425
+ return;
23426
+ }
23427
+
23428
+ // Block-level text elements
23429
+ if (BLOCK_TAGS.has(tag)) {
23430
+ var _node$parentElement;
23431
+ // Skip if nested inside another block (e.g. <p> inside <blockquote> handled by blockquote)
23432
+ var parentTag = (_node$parentElement = node.parentElement) === null || _node$parentElement === void 0 ? void 0 : _node$parentElement.tagName.toLowerCase();
23433
+ if (tag !== 'blockquote' && BLOCK_TAGS.has(parentTag) && parentTag !== 'li') return;
23434
+ var _text2 = (node.innerText || '').trim();
23435
+ if (_text2) {
23436
+ var type = tag === 'li' ? 'listItem' : 'text';
23437
+ var truncated = _text2.substring(0, 400);
23438
+ items.push({
23439
+ type: type,
23440
+ text: truncated
23441
+ });
23442
+ charCount += truncated.length;
23443
+ }
23444
+ return; // innerText already captures children
23445
+ }
23446
+
23447
+ // Container — recurse into children
23448
+ var _iterator = _createForOfIteratorHelper(node.children),
23449
+ _step;
23450
+ try {
23451
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
23452
+ var child = _step.value;
23453
+ if (charCount >= limits.maxContentLength) break;
23454
+ walk(child);
23455
+ }
23456
+ } catch (err) {
23457
+ _iterator.e(err);
23458
+ } finally {
23459
+ _iterator.f();
23460
+ }
23461
+ }
23462
+ walk(root);
23463
+ return items;
23220
23464
  }
23221
23465
 
23222
23466
  /**
23223
- * Check if element is visible (not hidden)
23467
+ * Convert structured content items into a readable flat string,
23468
+ * organised by heading sections.
23224
23469
  */
23225
- function isVisible(element) {
23226
- if (!element) return false;
23227
- var style = window.getComputedStyle(element);
23228
- return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0' && element.offsetWidth > 0 && element.offsetHeight > 0;
23470
+ function itemsToText(items) {
23471
+ var text = '';
23472
+ var _iterator2 = _createForOfIteratorHelper(items),
23473
+ _step2;
23474
+ try {
23475
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
23476
+ var item = _step2.value;
23477
+ switch (item.type) {
23478
+ case 'heading':
23479
+ text += "\n".concat('#'.repeat(parseInt(item.level[1])), " ").concat(item.text, "\n");
23480
+ break;
23481
+ case 'text':
23482
+ text += "".concat(item.text, "\n");
23483
+ break;
23484
+ case 'listItem':
23485
+ text += "\u2022 ".concat(item.text, "\n");
23486
+ break;
23487
+ case 'code':
23488
+ text += "```".concat(item.lang, "\n").concat(item.text, "\n```\n");
23489
+ break;
23490
+ }
23491
+ }
23492
+ } catch (err) {
23493
+ _iterator2.e(err);
23494
+ } finally {
23495
+ _iterator2.f();
23496
+ }
23497
+ return text.trim();
23229
23498
  }
23230
23499
 
23231
23500
  /**
23232
- * Extract buttons from the page
23501
+ * Extract the main readable content of the page.
23233
23502
  */
23234
- function extractButtons() {
23235
- var limits = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_LIMITS;
23503
+ function extractMainContent(limits) {
23504
+ var root = findContentRoot();
23505
+ var items = walkContent(root, limits);
23506
+ var text = itemsToText(items);
23507
+ var truncated = text.length >= limits.maxContentLength;
23508
+ return {
23509
+ text: text,
23510
+ items: items,
23511
+ truncated: truncated,
23512
+ sourceElement: root.tagName.toLowerCase() + (root.id ? "#".concat(root.id) : '') + (getMeaningfulClass(root) ? ".".concat(getMeaningfulClass(root)) : '')
23513
+ };
23514
+ }
23515
+
23516
+ // ─── Interactive elements ─────────────────────────────────────────────────────
23517
+
23518
+ function extractButtons(limits) {
23236
23519
  var buttons = [];
23237
- var selector = 'button, [type="submit"], [type="button"], [role="button"], .btn, .button';
23238
- var elements = document.querySelectorAll(selector);
23239
- var _iterator = _createForOfIteratorHelper(elements),
23240
- _step;
23520
+ var els = document.querySelectorAll('button, [type="submit"], [type="button"], [role="button"], .btn, .button');
23521
+ var _iterator3 = _createForOfIteratorHelper(els),
23522
+ _step3;
23241
23523
  try {
23242
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
23243
- var el = _step.value;
23524
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
23525
+ var el = _step3.value;
23244
23526
  if (buttons.length >= limits.buttons) break;
23245
23527
  if (!isVisible(el)) continue;
23246
23528
  var text = (el.innerText || el.value || el.getAttribute('aria-label') || '').trim();
@@ -23254,101 +23536,76 @@ function extractButtons() {
23254
23536
  });
23255
23537
  }
23256
23538
  } catch (err) {
23257
- _iterator.e(err);
23539
+ _iterator3.e(err);
23258
23540
  } finally {
23259
- _iterator.f();
23541
+ _iterator3.f();
23260
23542
  }
23261
23543
  return buttons;
23262
23544
  }
23263
-
23264
- /**
23265
- * Extract links from the page
23266
- */
23267
- function extractLinks() {
23268
- var limits = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_LIMITS;
23545
+ function extractLinks(limits) {
23269
23546
  var links = [];
23270
- var elements = document.querySelectorAll('a[href]');
23271
- var currentOrigin = window.location.origin;
23272
- var _iterator2 = _createForOfIteratorHelper(elements),
23273
- _step2;
23547
+ var els = document.querySelectorAll('a[href]');
23548
+ var origin = window.location.origin;
23549
+ var _iterator4 = _createForOfIteratorHelper(els),
23550
+ _step4;
23274
23551
  try {
23275
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
23276
- var el = _step2.value;
23552
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
23553
+ var el = _step4.value;
23277
23554
  if (links.length >= limits.links) break;
23278
23555
  if (!isVisible(el)) continue;
23279
23556
  var text = (el.innerText || el.getAttribute('aria-label') || '').trim();
23280
23557
  if (!text || text.length < 2) continue;
23281
-
23282
- // Skip anchor links and javascript:void
23283
23558
  var href = el.href;
23284
23559
  if (!href || href.startsWith('javascript:') || href === '#') continue;
23285
- var isInternal = false;
23560
+ var internal = false;
23286
23561
  try {
23287
- isInternal = new URL(href).origin === currentOrigin;
23288
- } catch (_unused) {}
23562
+ internal = new URL(href).origin === origin;
23563
+ } catch (_unused2) {}
23289
23564
  links.push({
23290
23565
  selector: generateSelector(el),
23291
23566
  text: text.substring(0, limits.maxTextLength),
23292
23567
  href: href,
23293
- internal: isInternal,
23568
+ internal: internal,
23294
23569
  inViewport: isInViewport(el, 0)
23295
23570
  });
23296
23571
  }
23297
23572
  } catch (err) {
23298
- _iterator2.e(err);
23573
+ _iterator4.e(err);
23299
23574
  } finally {
23300
- _iterator2.f();
23575
+ _iterator4.f();
23301
23576
  }
23302
23577
  return links;
23303
23578
  }
23304
-
23305
- /**
23306
- * Extract forms from the page (with sensitive field filtering)
23307
- */
23308
- function extractForms() {
23309
- var limits = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_LIMITS;
23579
+ function extractForms(limits) {
23310
23580
  var forms = [];
23311
- var elements = document.querySelectorAll('form');
23312
23581
  var sensitiveCount = 0;
23313
- var _iterator3 = _createForOfIteratorHelper(elements),
23314
- _step3;
23582
+ var _iterator5 = _createForOfIteratorHelper(document.querySelectorAll('form')),
23583
+ _step5;
23315
23584
  try {
23316
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
23317
- var form = _step3.value;
23585
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
23586
+ var form = _step5.value;
23318
23587
  if (forms.length >= limits.forms) break;
23319
23588
  if (!isVisible(form)) continue;
23320
23589
  var fields = [];
23321
- var fieldElements = form.querySelectorAll('input, textarea, select');
23322
- var _iterator4 = _createForOfIteratorHelper(fieldElements),
23323
- _step4;
23590
+ var _iterator6 = _createForOfIteratorHelper(form.querySelectorAll('input, textarea, select')),
23591
+ _step6;
23324
23592
  try {
23325
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
23593
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
23326
23594
  var _label, _field$placeholder;
23327
- var field = _step4.value;
23595
+ var field = _step6.value;
23328
23596
  if (fields.length >= limits.fieldsPerForm) break;
23329
-
23330
- // Skip hidden fields
23331
23597
  if (field.type === 'hidden') continue;
23332
-
23333
- // IMPORTANT: Filter sensitive fields
23334
23598
  if ((0,_sensitive_filter_js__WEBPACK_IMPORTED_MODULE_0__.isSensitiveField)(field)) {
23335
23599
  sensitiveCount++;
23336
23600
  continue;
23337
23601
  }
23338
-
23339
- // Get label
23340
23602
  var label = null;
23341
23603
  if (field.id) {
23342
- var _labelEl$innerText;
23343
- var labelEl = document.querySelector("label[for=\"".concat(field.id, "\"]"));
23344
- label = labelEl === null || labelEl === void 0 || (_labelEl$innerText = labelEl.innerText) === null || _labelEl$innerText === void 0 ? void 0 : _labelEl$innerText.trim();
23345
- }
23346
- if (!label && field.getAttribute('aria-label')) {
23347
- label = field.getAttribute('aria-label');
23348
- }
23349
- if (!label && field.placeholder) {
23350
- label = field.placeholder;
23604
+ var _lEl$innerText;
23605
+ var lEl = document.querySelector("label[for=\"".concat(field.id, "\"]"));
23606
+ label = lEl === null || lEl === void 0 || (_lEl$innerText = lEl.innerText) === null || _lEl$innerText === void 0 ? void 0 : _lEl$innerText.trim();
23351
23607
  }
23608
+ label = label || field.getAttribute('aria-label') || field.placeholder || null;
23352
23609
  fields.push({
23353
23610
  selector: generateSelector(field),
23354
23611
  name: field.name || null,
@@ -23356,15 +23613,14 @@ function extractForms() {
23356
23613
  label: ((_label = label) === null || _label === void 0 ? void 0 : _label.substring(0, 50)) || null,
23357
23614
  placeholder: ((_field$placeholder = field.placeholder) === null || _field$placeholder === void 0 ? void 0 : _field$placeholder.substring(0, 50)) || null,
23358
23615
  required: field.required || false
23359
- // NEVER include value for security
23360
23616
  });
23361
23617
  }
23362
23618
  } catch (err) {
23363
- _iterator4.e(err);
23619
+ _iterator6.e(err);
23364
23620
  } finally {
23365
- _iterator4.f();
23621
+ _iterator6.f();
23366
23622
  }
23367
- if (fields.length === 0) continue;
23623
+ if (!fields.length) continue;
23368
23624
  forms.push({
23369
23625
  selector: generateSelector(form),
23370
23626
  id: form.id || null,
@@ -23374,9 +23630,9 @@ function extractForms() {
23374
23630
  });
23375
23631
  }
23376
23632
  } catch (err) {
23377
- _iterator3.e(err);
23633
+ _iterator5.e(err);
23378
23634
  } finally {
23379
- _iterator3.f();
23635
+ _iterator5.f();
23380
23636
  }
23381
23637
  return {
23382
23638
  forms: forms,
@@ -23384,143 +23640,56 @@ function extractForms() {
23384
23640
  };
23385
23641
  }
23386
23642
 
23387
- /**
23388
- * Extract sections/landmarks from the page
23389
- */
23390
- function extractSections() {
23391
- var limits = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_LIMITS;
23392
- var sections = [];
23393
- var selector = 'section, article, main, [role="main"], [role="region"], .section';
23394
- var elements = document.querySelectorAll(selector);
23395
- var _iterator5 = _createForOfIteratorHelper(elements),
23396
- _step5;
23397
- try {
23398
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
23399
- var _heading$innerText, _firstP$innerText, _el$innerText2;
23400
- var el = _step5.value;
23401
- if (sections.length >= limits.sections) break;
23402
- if (!isVisible(el)) continue;
23403
-
23404
- // Get a summary of the section content
23405
- var heading = el.querySelector('h1, h2, h3');
23406
- var headingText = (heading === null || heading === void 0 || (_heading$innerText = heading.innerText) === null || _heading$innerText === void 0 ? void 0 : _heading$innerText.trim()) || '';
23407
-
23408
- // Get first paragraph or first N characters
23409
- var firstP = el.querySelector('p');
23410
- var summary = headingText || (firstP === null || firstP === void 0 || (_firstP$innerText = firstP.innerText) === null || _firstP$innerText === void 0 ? void 0 : _firstP$innerText.trim().substring(0, limits.maxSummaryLength)) || ((_el$innerText2 = el.innerText) === null || _el$innerText2 === void 0 ? void 0 : _el$innerText2.trim().substring(0, limits.maxSummaryLength)) || '';
23411
- sections.push({
23412
- selector: generateSelector(el),
23413
- id: el.id || null,
23414
- tagName: el.tagName.toLowerCase(),
23415
- summary: summary.substring(0, limits.maxSummaryLength),
23416
- inViewport: isInViewport(el, 0)
23417
- });
23418
- }
23419
- } catch (err) {
23420
- _iterator5.e(err);
23421
- } finally {
23422
- _iterator5.f();
23423
- }
23424
- return sections;
23425
- }
23426
-
23427
- /**
23428
- * Extract headings from the page
23429
- */
23430
- function extractHeadings() {
23431
- var limits = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_LIMITS;
23432
- var headings = [];
23433
- var elements = document.querySelectorAll('h1, h2, h3');
23434
- var _iterator6 = _createForOfIteratorHelper(elements),
23435
- _step6;
23436
- try {
23437
- for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
23438
- var _el$innerText3;
23439
- var el = _step6.value;
23440
- if (headings.length >= limits.headings) break;
23441
- if (!isVisible(el)) continue;
23442
- var text = (_el$innerText3 = el.innerText) === null || _el$innerText3 === void 0 ? void 0 : _el$innerText3.trim();
23443
- if (!text) continue;
23444
- headings.push({
23445
- selector: generateSelector(el),
23446
- level: el.tagName.toLowerCase(),
23447
- text: text.substring(0, limits.maxTextLength),
23448
- inViewport: isInViewport(el, 0)
23449
- });
23450
- }
23451
- } catch (err) {
23452
- _iterator6.e(err);
23453
- } finally {
23454
- _iterator6.f();
23455
- }
23456
- return headings;
23457
- }
23643
+ // ─── Public API ───────────────────────────────────────────────────────────────
23458
23644
 
23459
23645
  /**
23460
- * Main function: Extract full DOM context
23461
- *
23462
- * @param {Object} options - Extraction options
23463
- * @param {Object} options.limits - Override default limits
23464
- * @returns {Object} - Structured DOM context
23646
+ * Main extraction function called by the read_page tool handler.
23647
+ *
23648
+ * Returns:
23649
+ * content — flat readable text of the page (most important)
23650
+ * contentItems — structured array of { type, text, level?, lang? }
23651
+ * contentTruncated — true if content was cut at maxContentLength
23652
+ * url, title, language, viewport
23653
+ * buttons, links, forms — interactive elements for navigation / actions
23654
+ * metadata
23465
23655
  */
23466
23656
  function extractDOMContext() {
23467
23657
  return _extractDOMContext.apply(this, arguments);
23468
23658
  }
23469
23659
 
23470
23660
  /**
23471
- * Extract minimal context (for re-extraction after navigation)
23661
+ * Minimal context snapshot used on reconnect / navigation events.
23472
23662
  */
23473
23663
  function _extractDOMContext() {
23474
23664
  _extractDOMContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
23475
23665
  var _document$querySelect;
23476
23666
  var options,
23477
23667
  limits,
23668
+ mainContent,
23478
23669
  buttons,
23479
23670
  links,
23480
23671
  _extractForms,
23481
23672
  forms,
23482
23673
  sensitiveCount,
23483
- sections,
23484
- headings,
23485
- totalElements,
23486
- visibleInViewport,
23487
23674
  htmlLang,
23488
- browserLang,
23489
23675
  language,
23490
23676
  _args = arguments;
23491
23677
  return _regenerator().w(function (_context) {
23492
23678
  while (1) switch (_context.n) {
23493
23679
  case 0:
23494
23680
  options = _args.length > 0 && _args[0] !== undefined ? _args[0] : {};
23495
- limits = _objectSpread(_objectSpread({}, DEFAULT_LIMITS), options.limits); // Extract all elements
23681
+ limits = _objectSpread(_objectSpread({}, DEFAULT_LIMITS), options.limits);
23682
+ mainContent = extractMainContent(limits);
23496
23683
  buttons = extractButtons(limits);
23497
23684
  links = extractLinks(limits);
23498
23685
  _extractForms = extractForms(limits), forms = _extractForms.forms, sensitiveCount = _extractForms.sensitiveCount;
23499
- sections = extractSections(limits);
23500
- headings = extractHeadings(limits); // Count totals
23501
- totalElements = buttons.length + links.length + forms.reduce(function (sum, f) {
23502
- return sum + f.fields.length;
23503
- }, 0) + sections.length + headings.length;
23504
- visibleInViewport = buttons.filter(function (b) {
23505
- return b.inViewport;
23506
- }).length + links.filter(function (l) {
23507
- return l.inViewport;
23508
- }).length + sections.filter(function (s) {
23509
- return s.inViewport;
23510
- }).length + headings.filter(function (h) {
23511
- return h.inViewport;
23512
- }).length; // Detect site language
23513
- // Priority: HTML lang attribute > browser language
23514
23686
  htmlLang = document.documentElement.lang || ((_document$querySelect = document.querySelector('html')) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.getAttribute('lang')) || null;
23515
- browserLang = navigator.language || navigator.languages && navigator.languages[0] || null;
23516
- language = htmlLang || browserLang || null;
23687
+ language = htmlLang || navigator.language || navigator.languages && navigator.languages[0] || null;
23517
23688
  return _context.a(2, {
23518
23689
  url: window.location.href,
23519
23690
  title: document.title,
23520
23691
  timestamp: Date.now(),
23521
23692
  language: language,
23522
- // Site language (from HTML lang attribute or browser)
23523
-
23524
23693
  viewport: {
23525
23694
  width: window.innerWidth,
23526
23695
  height: window.innerHeight,
@@ -23528,14 +23697,20 @@ function _extractDOMContext() {
23528
23697
  scrollY: window.scrollY,
23529
23698
  pageHeight: document.documentElement.scrollHeight
23530
23699
  },
23700
+ // ── Primary: actual page content ──────────────────────────────────────
23701
+ content: mainContent.text,
23702
+ contentItems: mainContent.items,
23703
+ contentTruncated: mainContent.truncated,
23704
+ contentSource: mainContent.sourceElement,
23705
+ // ── Secondary: interactive elements ───────────────────────────────────
23531
23706
  buttons: buttons,
23532
23707
  links: links,
23533
23708
  forms: forms,
23534
- sections: sections,
23535
- headings: headings,
23536
23709
  metadata: {
23537
- totalElements: totalElements,
23538
- visibleInViewport: visibleInViewport,
23710
+ totalContentChars: mainContent.text.length,
23711
+ contentItemCount: mainContent.items.length,
23712
+ buttonsCount: buttons.length,
23713
+ linksCount: links.length,
23539
23714
  formsCount: forms.length,
23540
23715
  sensitiveFieldsFiltered: sensitiveCount
23541
23716
  }
@@ -23551,14 +23726,12 @@ function extractMinimalContext() {
23551
23726
  function _extractMinimalContext() {
23552
23727
  _extractMinimalContext = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
23553
23728
  var _document$querySelect2;
23554
- var htmlLang, browserLang, language;
23729
+ var htmlLang, language;
23555
23730
  return _regenerator().w(function (_context2) {
23556
23731
  while (1) switch (_context2.n) {
23557
23732
  case 0:
23558
- // Detect site language
23559
23733
  htmlLang = document.documentElement.lang || ((_document$querySelect2 = document.querySelector('html')) === null || _document$querySelect2 === void 0 ? void 0 : _document$querySelect2.getAttribute('lang')) || null;
23560
- browserLang = navigator.language || navigator.languages && navigator.languages[0] || null;
23561
- language = htmlLang || browserLang || null;
23734
+ language = htmlLang || navigator.language || navigator.languages && navigator.languages[0] || null;
23562
23735
  return _context2.a(2, {
23563
23736
  url: window.location.href,
23564
23737
  title: document.title,
@@ -24039,16 +24212,19 @@ function registerVisualTools(registry) {
24039
24212
  while (1) switch (_context.n) {
24040
24213
  case 0:
24041
24214
  params = _args.length > 0 && _args[0] !== undefined ? _args[0] : {};
24042
- console.log('[TTP] 📋 Reading page structure...');
24215
+ console.log('[TTP] 📋 Reading page content...');
24043
24216
  _context.n = 1;
24044
24217
  return (0,_dom_context_js__WEBPACK_IMPORTED_MODULE_0__.extractDOMContext)(params);
24045
24218
  case 1:
24046
24219
  context = _context.v;
24047
- console.log('[TTP] ✅ Page structure extracted:', {
24048
- buttons: context.buttons.length,
24049
- links: context.links.length,
24050
- forms: context.forms.length,
24051
- headings: context.headings.length
24220
+ console.log('[TTP] ✅ Page content extracted:', {
24221
+ contentChars: context.metadata.totalContentChars,
24222
+ contentItems: context.metadata.contentItemCount,
24223
+ truncated: context.contentTruncated,
24224
+ source: context.contentSource,
24225
+ buttons: context.metadata.buttonsCount,
24226
+ links: context.metadata.linksCount,
24227
+ forms: context.metadata.formsCount
24052
24228
  });
24053
24229
  return _context.a(2, context);
24054
24230
  }
@@ -24267,12 +24443,12 @@ function _extractPageContext() {
24267
24443
  timestamp: domContext.timestamp,
24268
24444
  language: domContext.language,
24269
24445
  viewport: domContext.viewport,
24446
+ content: domContext.content,
24447
+ contentTruncated: domContext.contentTruncated,
24270
24448
  dom: {
24271
24449
  buttons: domContext.buttons,
24272
24450
  links: domContext.links,
24273
24451
  forms: domContext.forms,
24274
- sections: domContext.sections,
24275
- headings: domContext.headings,
24276
24452
  metadata: domContext.metadata
24277
24453
  }
24278
24454
  });
@@ -24420,9 +24596,17 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
24420
24596
  // Transcript sync
24421
24597
  _this.pendingSentenceText = null; // Text waiting for first audio chunk
24422
24598
  _this.pendingSynced = null; // Synced actions waiting for first audio chunk
24423
- _this.sentenceTimings = []; // [{startTime, text, synced, displayed}, ...]
24599
+ _this.sentenceTimings = []; // [{segmentId, startTime, endTime, text, synced, displayed, doneReported}, ...]
24424
24600
  _this.isCheckingTranscripts = false;
24425
24601
 
24602
+ // Audio segment correlation (backend-assigned id carried on audio_start). pendingSegmentId
24603
+ // waits for the segment's first chunk, then is promoted to currentSegmentId at scheduling.
24604
+ // currentSegmentId is stamped onto playbackStarted/playbackStopped so the backend can match
24605
+ // the "stopped" against the last burst it sent. Per-segment completion is reported via the
24606
+ // 'segmentDone' event (finished / barged_in / interrupted + playedMs).
24607
+ _this.pendingSegmentId = null;
24608
+ _this.currentSegmentId = null;
24609
+
24426
24610
  // Flag to prevent queuing new audio after stopImmediate() is called (barge-in protection)
24427
24611
  // This prevents race conditions where audio chunks arrive after stop but before sources are stopped
24428
24612
  // Cleared when markNewSentence() is called (signals new audio is starting)
@@ -24804,7 +24988,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
24804
24988
  // Keep scheduling frames as long as we have them and haven't reached the lookahead limit
24805
24989
  scheduledCount = 0;
24806
24990
  _loop = /*#__PURE__*/_regenerator().m(function _loop() {
24807
- var preparedFrame, source, currentTime, isMobile, startupBuffer, minStartTime, startTime;
24991
+ var preparedFrame, source, currentTime, isMobile, startupBuffer, minStartTime, segFrameEnd, startTime;
24808
24992
  return _regenerator().w(function (_context3) {
24809
24993
  while (1) switch (_context3.n) {
24810
24994
  case 0:
@@ -24864,22 +25048,32 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
24864
25048
  // Always use nextStartTime (which is already calculated for seamless playback)
24865
25049
  }
24866
25050
 
24867
- // ✅ MOVED OUTSIDE - Record sentence timing on first chunk of EACH sentence
24868
- if (_this4.pendingSentenceText) {
25051
+ // Audio-clock window for THIS frame: [nextStartTime, nextStartTime + duration).
25052
+ segFrameEnd = _this4.nextStartTime + preparedFrame.duration; // ✅ MOVED OUTSIDE - Record sentence timing on first chunk of EACH sentence
25053
+ if (_this4.pendingSentenceText || _this4.pendingSegmentId != null) {
25054
+ // First chunk of a new segment: open a timing entry and adopt its id as current.
24869
25055
  _this4.sentenceTimings.push({
25056
+ segmentId: _this4.pendingSegmentId,
24870
25057
  startTime: _this4.nextStartTime,
25058
+ endTime: segFrameEnd,
24871
25059
  text: _this4.pendingSentenceText,
24872
25060
  synced: _this4.pendingSynced,
24873
- displayed: false
25061
+ displayed: false,
25062
+ doneReported: false
24874
25063
  });
24875
- console.log("\uD83D\uDCDD AudioPlayer: Sentence scheduled at ".concat(_this4.nextStartTime.toFixed(3), "s: \"").concat(_this4.pendingSentenceText.substring(0, 40), "...\" (synced: ").concat(_this4.pendingSynced ? _this4.pendingSynced.length : 0, ")"));
25064
+ _this4.currentSegmentId = _this4.pendingSegmentId;
25065
+ console.log("\uD83D\uDCDD AudioPlayer: Sentence scheduled at ".concat(_this4.nextStartTime.toFixed(3), "s (segmentId: ").concat(_this4.pendingSegmentId, "): \"").concat((_this4.pendingSentenceText || '').substring(0, 40), "...\" (synced: ").concat(_this4.pendingSynced ? _this4.pendingSynced.length : 0, ")"));
24876
25066
  _this4.pendingSentenceText = null;
24877
25067
  _this4.pendingSynced = null;
25068
+ _this4.pendingSegmentId = null;
24878
25069
 
24879
25070
  // Start checker if not running
24880
25071
  if (!_this4.isCheckingTranscripts) {
24881
25072
  _this4.startTranscriptChecker();
24882
25073
  }
25074
+ } else if (_this4.sentenceTimings.length > 0) {
25075
+ // Subsequent chunk of the current segment: extend its end on the audio clock.
25076
+ _this4.sentenceTimings[_this4.sentenceTimings.length - 1].endTime = segFrameEnd;
24883
25077
  }
24884
25078
 
24885
25079
  // Schedule this chunk to start at nextStartTime
@@ -24934,8 +25128,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
24934
25128
  if (_this4.scheduledBuffers === 0 && _this4.preparedBuffer.length === 0 && _this4.pcmChunkQueue.length === 0 && !_this4._isStopped) {
24935
25129
  _this4.isPlaying = false;
24936
25130
  _this4.isSchedulingFrames = false;
24937
- console.log('🛑 AudioPlayer: Emitting playbackStopped event (all buffers finished)');
24938
- _this4.emit('playbackStopped');
25131
+ console.log('🛑 AudioPlayer: Emitting playbackStopped event (all buffers finished, segmentId: ' + _this4.currentSegmentId + ')');
25132
+ _this4.emit('playbackStopped', {
25133
+ segmentId: _this4.currentSegmentId
25134
+ });
24939
25135
  } else if (_this4.preparedBuffer.length > 0 && !_this4._isStopped) {
24940
25136
  // More frames available, schedule them immediately
24941
25137
 
@@ -24971,8 +25167,10 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
24971
25167
  };
24972
25168
  if (!_this4.isPlaying) {
24973
25169
  _this4.isPlaying = true;
24974
- console.log('🎵 AudioPlayer: Emitting playbackStarted event');
24975
- _this4.emit('playbackStarted');
25170
+ console.log('🎵 AudioPlayer: Emitting playbackStarted event (segmentId: ' + _this4.currentSegmentId + ')');
25171
+ _this4.emit('playbackStarted', {
25172
+ segmentId: _this4.currentSegmentId
25173
+ });
24976
25174
  }
24977
25175
  case 3:
24978
25176
  return _context3.a(2);
@@ -26316,14 +26514,66 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26316
26514
  // Note: scheduledBuffers was already reset to 0 above when clearing scheduledSources
26317
26515
  this.nextStartTime = 0;
26318
26516
 
26517
+ // Report per-segment completion for the interrupted burst BEFORE clearing timing state.
26518
+ // Audible segment → barged_in (ms heard); scheduled-but-unstarted → interrupted;
26519
+ // already-elapsed but unreported → finished. Plus any pending segment (audio_start arrived
26520
+ // but no chunks scheduled yet) → interrupted.
26521
+ try {
26522
+ var nowTs = this.audioContext ? this.audioContext.currentTime : 0;
26523
+ var _iterator = _createForOfIteratorHelper(this.sentenceTimings),
26524
+ _step;
26525
+ try {
26526
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
26527
+ var timing = _step.value;
26528
+ if (timing.doneReported) continue;
26529
+ timing.doneReported = true;
26530
+ var status = void 0,
26531
+ playedMs = void 0;
26532
+ if (timing.endTime != null && nowTs >= timing.endTime) {
26533
+ status = 'finished';
26534
+ playedMs = Math.round((timing.endTime - timing.startTime) * 1000);
26535
+ } else if (nowTs <= timing.startTime) {
26536
+ status = 'interrupted';
26537
+ playedMs = 0;
26538
+ } else {
26539
+ status = 'barged_in';
26540
+ playedMs = Math.round((nowTs - timing.startTime) * 1000);
26541
+ }
26542
+ this.emit('segmentDone', {
26543
+ segmentId: timing.segmentId,
26544
+ status: status,
26545
+ playedMs: playedMs
26546
+ });
26547
+ }
26548
+ } catch (err) {
26549
+ _iterator.e(err);
26550
+ } finally {
26551
+ _iterator.f();
26552
+ }
26553
+ if (this.pendingSegmentId != null) {
26554
+ this.emit('segmentDone', {
26555
+ segmentId: this.pendingSegmentId,
26556
+ status: 'interrupted',
26557
+ playedMs: 0
26558
+ });
26559
+ }
26560
+ } catch (e) {
26561
+ console.warn('⚠️ AudioPlayer: segmentDone (barge-in) reporting failed:', e && e.message);
26562
+ }
26563
+ // Capture the last segment that was playing for the coarse playbackStopped below
26564
+ // (clearTranscriptState resets currentSegmentId).
26565
+ var stoppedSegmentId = this.currentSegmentId;
26566
+
26319
26567
  // Clear transcript state
26320
26568
  this.clearTranscriptState();
26321
26569
 
26322
26570
  // Emit stopped event - CRITICAL for barge-in
26323
26571
 
26324
26572
  if (wasPlaying) {
26325
- console.log('🛑 AudioPlayer: Emitting playbackStopped event (stopImmediate called)');
26326
- this.emit('playbackStopped');
26573
+ console.log('🛑 AudioPlayer: Emitting playbackStopped event (stopImmediate called, segmentId: ' + stoppedSegmentId + ')');
26574
+ this.emit('playbackStopped', {
26575
+ segmentId: stoppedSegmentId
26576
+ });
26327
26577
  }
26328
26578
  }
26329
26579
 
@@ -26333,7 +26583,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26333
26583
  */
26334
26584
  }, {
26335
26585
  key: "markNewSentence",
26336
- value: function markNewSentence(text, synced) {
26586
+ value: function markNewSentence(text, synced, segmentId) {
26337
26587
  var _this0 = this;
26338
26588
  var wasStopped = this._isStopped;
26339
26589
  var isCurrentlyPlaying = this.isPlaying || this.scheduledSources.size > 0;
@@ -26378,10 +26628,12 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26378
26628
  console.log("\uD83D\uDCDD AudioPlayer: New sentence queued while audio playing - will start after current sentence finishes");
26379
26629
  }
26380
26630
 
26381
- // Always update pending sentence text and synced actions (for transcript display)
26631
+ // Always update pending sentence text, synced actions (for transcript display), and the
26632
+ // backend-assigned segment id (echoed back on the playback events for this segment).
26382
26633
  this.pendingSentenceText = text;
26383
26634
  this.pendingSynced = synced || null;
26384
- console.log("\uD83D\uDCDD AudioPlayer: New sentence marked: \"".concat(text.substring(0, 40), "...\" (wasStopped: ").concat(wasStopped, ", isPlaying: ").concat(isCurrentlyPlaying, ", synced: ").concat(synced ? synced.length : 0, ")"));
26635
+ this.pendingSegmentId = segmentId != null ? segmentId : null;
26636
+ console.log("\uD83D\uDCDD AudioPlayer: New sentence marked (segmentId: ".concat(this.pendingSegmentId, "): \"").concat(text.substring(0, 40), "...\" (wasStopped: ").concat(wasStopped, ", isPlaying: ").concat(isCurrentlyPlaying, ", synced: ").concat(synced ? synced.length : 0, ")"));
26385
26637
 
26386
26638
  // CRITICAL: Set timeout to detect empty sentences (audio_start but no chunks)
26387
26639
  // This prevents queue blocking if backend sends audio_start but no audio chunks follow
@@ -26393,6 +26645,18 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26393
26645
  // Check if this sentence still has no chunks after timeout
26394
26646
  if (_this0.pendingSentenceText === sentenceText && _this0.scheduledBuffers === 0 && _this0.preparedBuffer.length === 0 && _this0.pcmChunkQueue.length === 0 && !_this0._isStopped) {
26395
26647
  console.warn("\u26A0\uFE0F AudioPlayer: Empty sentence detected after 5s timeout - no chunks received for: \"".concat(sentenceText.substring(0, 40), "...\""));
26648
+ // If this empty sentence carried a segment id, report it done (nothing was heard) and
26649
+ // adopt it as current so the coarse stop below matches the backend's last-sent id.
26650
+ if (_this0.pendingSegmentId != null) {
26651
+ var emptySegId = _this0.pendingSegmentId;
26652
+ _this0.currentSegmentId = emptySegId;
26653
+ _this0.pendingSegmentId = null;
26654
+ _this0.emit('segmentDone', {
26655
+ segmentId: emptySegId,
26656
+ status: 'finished',
26657
+ playedMs: 0
26658
+ });
26659
+ }
26396
26660
  // Clear pending sentence to unblock next sentence
26397
26661
  if (_this0.pendingSentenceText === sentenceText) {
26398
26662
  _this0.pendingSentenceText = null;
@@ -26401,7 +26665,9 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26401
26665
  // Only if we're not currently playing (to avoid interrupting real playback)
26402
26666
  if (!_this0.isPlaying && _this0.scheduledSources.size === 0) {
26403
26667
  console.log('🛑 AudioPlayer: Emitting playbackStopped for empty sentence timeout');
26404
- _this0.emit('playbackStopped');
26668
+ _this0.emit('playbackStopped', {
26669
+ segmentId: _this0.currentSegmentId
26670
+ });
26405
26671
  }
26406
26672
  }
26407
26673
  _this0._emptySentenceTimeout = null;
@@ -26421,34 +26687,67 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26421
26687
  var _checkLoop = function checkLoop() {
26422
26688
  if (!_this1.isCheckingTranscripts || !_this1.audioContext) return;
26423
26689
  var currentTime = _this1.audioContext.currentTime;
26424
- var _iterator = _createForOfIteratorHelper(_this1.sentenceTimings),
26425
- _step;
26690
+ var _iterator2 = _createForOfIteratorHelper(_this1.sentenceTimings),
26691
+ _step2;
26426
26692
  try {
26427
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
26428
- var timing = _step.value;
26429
- if (!timing.displayed && currentTime >= timing.startTime) {
26430
- timing.displayed = true;
26693
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
26694
+ var _timing = _step2.value;
26695
+ if (!_timing.displayed && _timing.text && currentTime >= _timing.startTime) {
26696
+ _timing.displayed = true;
26431
26697
  var eventData = {
26432
- text: timing.text
26698
+ text: _timing.text
26433
26699
  };
26434
- if (timing.synced) {
26435
- eventData.synced = timing.synced;
26700
+ if (_timing.synced) {
26701
+ eventData.synced = _timing.synced;
26436
26702
  }
26437
- console.log("\uD83D\uDCDD AudioPlayer: Display transcript at ".concat(currentTime.toFixed(3), "s: \"").concat(timing.text.substring(0, 40), "...\" (synced: ").concat(timing.synced ? timing.synced.length : 0, ")"));
26703
+ console.log("\uD83D\uDCDD AudioPlayer: Display transcript at ".concat(currentTime.toFixed(3), "s: \"").concat(_timing.text.substring(0, 40), "...\" (synced: ").concat(_timing.synced ? _timing.synced.length : 0, ")"));
26438
26704
  _this1.emit('transcriptDisplay', eventData);
26439
26705
  }
26706
+ // Per-segment natural finish: this segment's audio window has fully elapsed.
26707
+ if (!_timing.doneReported && _timing.endTime != null && currentTime >= _timing.endTime) {
26708
+ _timing.doneReported = true;
26709
+ var _playedMs = Math.round((_timing.endTime - _timing.startTime) * 1000);
26710
+ _this1.emit('segmentDone', {
26711
+ segmentId: _timing.segmentId,
26712
+ status: 'finished',
26713
+ playedMs: _playedMs
26714
+ });
26715
+ }
26440
26716
  }
26441
26717
  } catch (err) {
26442
- _iterator.e(err);
26718
+ _iterator2.e(err);
26443
26719
  } finally {
26444
- _iterator.f();
26720
+ _iterator2.f();
26445
26721
  }
26446
26722
  if (_this1.isPlaying || _this1.scheduledBuffers > 0) {
26447
26723
  requestAnimationFrame(_checkLoop);
26448
26724
  } else {
26449
- _this1.isCheckingTranscripts = false;
26450
- console.log('📝 AudioPlayer: Transcript checker stopped');
26451
- }
26725
+ // Playback drained naturally — flush any segment whose finish tick we may have missed
26726
+ // (the last buffer's onended can flip isPlaying=false before this loop's next tick).
26727
+ var _iterator3 = _createForOfIteratorHelper(_this1.sentenceTimings),
26728
+ _step3;
26729
+ try {
26730
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
26731
+ var timing = _step3.value;
26732
+ if (!timing.doneReported) {
26733
+ timing.doneReported = true;
26734
+ var end = timing.endTime != null ? timing.endTime : timing.startTime;
26735
+ var playedMs = Math.round((end - timing.startTime) * 1000);
26736
+ _this1.emit('segmentDone', {
26737
+ segmentId: timing.segmentId,
26738
+ status: 'finished',
26739
+ playedMs: playedMs
26740
+ });
26741
+ }
26742
+ }
26743
+ } catch (err) {
26744
+ _iterator3.e(err);
26745
+ } finally {
26746
+ _iterator3.f();
26747
+ }
26748
+ _this1.isCheckingTranscripts = false;
26749
+ console.log('📝 AudioPlayer: Transcript checker stopped');
26750
+ }
26452
26751
  };
26453
26752
  requestAnimationFrame(_checkLoop);
26454
26753
  }
@@ -26461,6 +26760,8 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
26461
26760
  value: function clearTranscriptState() {
26462
26761
  this.pendingSentenceText = null;
26463
26762
  this.pendingSynced = null;
26763
+ this.pendingSegmentId = null;
26764
+ this.currentSegmentId = null;
26464
26765
  this.sentenceTimings = [];
26465
26766
  this.isCheckingTranscripts = false;
26466
26767
  console.log('📝 AudioPlayer: Transcript state cleared');
@@ -26610,13 +26911,13 @@ __webpack_require__.r(__webpack_exports__);
26610
26911
  /* harmony import */ var _utils_screenshot_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../utils/screenshot.js */ "./src/utils/screenshot.js");
26611
26912
  /* harmony import */ var _utils_visual_tools_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../utils/visual-tools.js */ "./src/utils/visual-tools.js");
26612
26913
  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); }
26914
+ 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; } } }; }
26915
+ 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; } }
26916
+ 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; }
26613
26917
  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 }; })(); }
26614
26918
  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); }
26615
26919
  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); }
26616
26920
  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); }); }; }
26617
- 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; } } }; }
26618
- 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; } }
26619
- 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; }
26620
26921
  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; }
26621
26922
  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; }
26622
26923
  function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
@@ -27183,19 +27484,23 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
27183
27484
  key: "setupAudioPlayerEvents",
27184
27485
  value: function setupAudioPlayerEvents() {
27185
27486
  var _this3 = this;
27186
- this.audioPlayer.on('playbackStarted', function () {
27487
+ this.audioPlayer.on('playbackStarted', function (info) {
27187
27488
  _this3.isPlaying = true;
27188
27489
  _this3.emit('playbackStarted');
27189
27490
 
27190
27491
  // Transcripts are displayed when they arrive (between sentences)
27191
27492
  // No need to handle transcript display here - it's handled in handleTranscript()
27192
27493
 
27193
- // Notify server - CRITICAL for barge-in detection
27494
+ // Notify server - CRITICAL for barge-in detection.
27495
+ // Carry the segmentId of the burst that started playing (echoed from audio_start) so the
27496
+ // backend can correlate playback events with the bursts it sent.
27194
27497
 
27195
27498
  if (_this3.isConnected) {
27196
- console.log('📤 VoiceSDK v2: Sending audio_started_playing message to server');
27499
+ var segmentId = info && info.segmentId != null ? info.segmentId : null;
27500
+ console.log('📤 VoiceSDK v2: Sending audio_started_playing message to server (segmentId: ' + segmentId + ')');
27197
27501
  _this3.sendMessage({
27198
- t: 'audio_started_playing'
27502
+ t: 'audio_started_playing',
27503
+ segmentId: segmentId
27199
27504
  });
27200
27505
  } else {
27201
27506
  console.warn('⚠️ VoiceSDK v2: Cannot send audio_started_playing - not connected');
@@ -27219,39 +27524,48 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
27219
27524
  _this3.emit('transcriptDisplay', fullTranscriptData);
27220
27525
  _this3.emit('transcript', fullTranscriptData);
27221
27526
  if (transcriptData.synced && Array.isArray(transcriptData.synced)) {
27222
- console.log("\uD83D\uDCDD VoiceSDK v2: Emitting ".concat(transcriptData.synced.length, " synced action(s) as message events"));
27223
- var _iterator = _createForOfIteratorHelper(transcriptData.synced),
27224
- _step;
27225
- try {
27226
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
27227
- var action = _step.value;
27228
- _this3.emit('message', action);
27229
- }
27230
- } catch (err) {
27231
- _iterator.e(err);
27232
- } finally {
27233
- _iterator.f();
27234
- }
27527
+ console.log("\uD83D\uDCDD VoiceSDK v2: Dispatching ".concat(transcriptData.synced.length, " synced action(s)"));
27528
+ void _this3._dispatchSyncedActions(transcriptData.synced);
27235
27529
  }
27236
27530
  });
27237
- this.audioPlayer.on('playbackStopped', function () {
27531
+ this.audioPlayer.on('playbackStopped', function (info) {
27238
27532
  _this3.isPlaying = false;
27239
27533
  _this3.emit('playbackStopped');
27240
27534
 
27241
27535
  // When playback stops, keep currentPlayingSentenceId to track which sentence just finished
27242
27536
  // The next transcript will arrive between sentences and will be displayed immediately
27243
27537
 
27244
- // Notify server - CRITICAL for barge-in detection
27538
+ // Notify server - CRITICAL for barge-in detection.
27539
+ // Carry the segmentId of the last burst that was playing so the backend's idle gate can
27540
+ // match this stop against the last burst it sent (deterministic "fully idle").
27245
27541
 
27246
27542
  if (_this3.isConnected) {
27247
- console.log('📤 VoiceSDK v2: Sending audio_stopped_playing message to server');
27543
+ var segmentId = info && info.segmentId != null ? info.segmentId : null;
27544
+ console.log('📤 VoiceSDK v2: Sending audio_stopped_playing message to server (segmentId: ' + segmentId + ')');
27248
27545
  _this3.sendMessage({
27249
- t: 'audio_stopped_playing'
27546
+ t: 'audio_stopped_playing',
27547
+ segmentId: segmentId
27250
27548
  });
27251
27549
  } else {
27252
27550
  console.warn('⚠️ VoiceSDK v2: Cannot send audio_stopped_playing - not connected');
27253
27551
  }
27254
27552
  });
27553
+
27554
+ // Per-segment playback completion (finished / barged_in / interrupted) with the ms of THIS
27555
+ // segment actually heard. Additive telemetry alongside the coarse started/stopped events.
27556
+
27557
+ this.audioPlayer.on('segmentDone', function (info) {
27558
+ if (!info) return;
27559
+ if (_this3.isConnected) {
27560
+ console.log('📤 VoiceSDK v2: Sending audio_segment_done (segmentId: ' + info.segmentId + ', status: ' + info.status + ', playedMs: ' + info.playedMs + ')');
27561
+ _this3.sendMessage({
27562
+ t: 'audio_segment_done',
27563
+ segmentId: info.segmentId != null ? info.segmentId : null,
27564
+ status: info.status,
27565
+ playedMs: info.playedMs
27566
+ });
27567
+ }
27568
+ });
27255
27569
  this.audioPlayer.on('playbackError', function (error) {
27256
27570
  _this3.emit('playbackError', error);
27257
27571
  _this3.emit('error', error);
@@ -27616,7 +27930,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
27616
27930
 
27617
27931
  // Include SDK build time for debugging
27618
27932
  if (true) {
27619
- helloMessage.lastBuildTime = "2026-06-13T08:36:31.824Z";
27933
+ helloMessage.lastBuildTime = "2026-06-28T10:02:02.645Z";
27620
27934
  }
27621
27935
 
27622
27936
  // Page context is intentionally NOT attached to the hello message.
@@ -27771,7 +28085,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
27771
28085
  case 'audio_start':
27772
28086
  // Backend sends audio_start with transcript text before audio chunks
27773
28087
  // Store the text in AudioPlayer for synced display when audio actually starts playing
27774
- console.log('📝 VoiceSDK v2: Received audio_start with text:', message.text);
28088
+ console.log('📝 VoiceSDK v2: Received audio_start with text:', message.text, '(segmentId:', message.segmentId, ')');
27775
28089
 
27776
28090
  // CRITICAL: Record timestamp to prevent premature stop_playing messages from cutting sentences
27777
28091
  this.lastAudioStartTime = Date.now();
@@ -27787,7 +28101,10 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
27787
28101
  if (synced) {
27788
28102
  console.log("\uD83D\uDCDD VoiceSDK v2: audio_start carries ".concat(synced.length, " synced action(s)"));
27789
28103
  }
27790
- this.audioPlayer.markNewSentence(message.text, synced);
28104
+ // Pass the backend-assigned segmentId (may be undefined for legacy backends) so the
28105
+ // player can echo it back on this segment's playback events.
28106
+ var segmentId = message.segmentId != null ? message.segmentId : null;
28107
+ this.audioPlayer.markNewSentence(message.text, synced, segmentId);
27791
28108
  }
27792
28109
  // Also emit as message for other listeners
27793
28110
  this.emit('message', message);
@@ -28316,6 +28633,104 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28316
28633
  }
28317
28634
  }
28318
28635
 
28636
+ /**
28637
+ * Dispatch widget actions synced to a TTS sentence boundary.
28638
+ * run_partner_script envelopes must go through the same handler as WS traffic
28639
+ * (show_media and other t= messages still emit as generic message events).
28640
+ * @private
28641
+ */
28642
+ }, {
28643
+ key: "_dispatchSyncedActions",
28644
+ value: (function () {
28645
+ var _dispatchSyncedActions2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(actions) {
28646
+ var _iterator, _step, raw, _t2;
28647
+ return _regenerator().w(function (_context3) {
28648
+ while (1) switch (_context3.p = _context3.n) {
28649
+ case 0:
28650
+ _iterator = _createForOfIteratorHelper(actions);
28651
+ _context3.p = 1;
28652
+ _iterator.s();
28653
+ case 2:
28654
+ if ((_step = _iterator.n()).done) {
28655
+ _context3.n = 4;
28656
+ break;
28657
+ }
28658
+ raw = _step.value;
28659
+ _context3.n = 3;
28660
+ return this._dispatchSyncedAction(raw);
28661
+ case 3:
28662
+ _context3.n = 2;
28663
+ break;
28664
+ case 4:
28665
+ _context3.n = 6;
28666
+ break;
28667
+ case 5:
28668
+ _context3.p = 5;
28669
+ _t2 = _context3.v;
28670
+ _iterator.e(_t2);
28671
+ case 6:
28672
+ _context3.p = 6;
28673
+ _iterator.f();
28674
+ return _context3.f(6);
28675
+ case 7:
28676
+ return _context3.a(2);
28677
+ }
28678
+ }, _callee3, this, [[1, 5, 6, 7]]);
28679
+ }));
28680
+ function _dispatchSyncedActions(_x) {
28681
+ return _dispatchSyncedActions2.apply(this, arguments);
28682
+ }
28683
+ return _dispatchSyncedActions;
28684
+ }())
28685
+ }, {
28686
+ key: "_dispatchSyncedAction",
28687
+ value: function () {
28688
+ var _dispatchSyncedAction2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(action) {
28689
+ var message, _t3;
28690
+ return _regenerator().w(function (_context4) {
28691
+ while (1) switch (_context4.p = _context4.n) {
28692
+ case 0:
28693
+ message = action;
28694
+ if (!(typeof message === 'string')) {
28695
+ _context4.n = 3;
28696
+ break;
28697
+ }
28698
+ _context4.p = 1;
28699
+ message = JSON.parse(message);
28700
+ _context4.n = 3;
28701
+ break;
28702
+ case 2:
28703
+ _context4.p = 2;
28704
+ _t3 = _context4.v;
28705
+ console.warn('⚠️ VoiceSDK v2: Ignoring invalid synced action string:', action);
28706
+ return _context4.a(2);
28707
+ case 3:
28708
+ if (!(!message || _typeof(message) !== 'object')) {
28709
+ _context4.n = 4;
28710
+ break;
28711
+ }
28712
+ return _context4.a(2);
28713
+ case 4:
28714
+ if (!(message.t === 'run_partner_script')) {
28715
+ _context4.n = 6;
28716
+ break;
28717
+ }
28718
+ _context4.n = 5;
28719
+ return this._handleRunPartnerScript(message);
28720
+ case 5:
28721
+ return _context4.a(2);
28722
+ case 6:
28723
+ this.emit('message', message);
28724
+ case 7:
28725
+ return _context4.a(2);
28726
+ }
28727
+ }, _callee4, this, [[1, 2]]);
28728
+ }));
28729
+ function _dispatchSyncedAction(_x2) {
28730
+ return _dispatchSyncedAction2.apply(this, arguments);
28731
+ }
28732
+ return _dispatchSyncedAction;
28733
+ }()
28319
28734
  /**
28320
28735
  * Handle a run_partner_script envelope (async partner-adapter dispatch).
28321
28736
  *
@@ -28334,30 +28749,30 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28334
28749
  }, {
28335
28750
  key: "_handleRunPartnerScript",
28336
28751
  value: (function () {
28337
- var _handleRunPartnerScript2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(message) {
28752
+ var _handleRunPartnerScript2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(message) {
28338
28753
  var _this$clientToolsRegi;
28339
- var _ref, requestId, partnerId, action, args, handler, startedAt, result, elapsedMs, ok, reason, extras, preview, _elapsedMs, _t2;
28340
- return _regenerator().w(function (_context3) {
28341
- while (1) switch (_context3.p = _context3.n) {
28754
+ var _ref, requestId, partnerId, action, args, handler, startedAt, result, elapsedMs, ok, reason, extras, preview, _elapsedMs, _t4;
28755
+ return _regenerator().w(function (_context5) {
28756
+ while (1) switch (_context5.p = _context5.n) {
28342
28757
  case 0:
28343
28758
  _ref = message || {}, requestId = _ref.requestId, partnerId = _ref.partnerId, action = _ref.action;
28344
28759
  args = message && message.args || {};
28345
28760
  if (requestId) {
28346
- _context3.n = 1;
28761
+ _context5.n = 1;
28347
28762
  break;
28348
28763
  }
28349
28764
  console.warn('⚠️ VoiceSDK v2: run_partner_script missing requestId; dropping', message);
28350
- return _context3.a(2);
28765
+ return _context5.a(2);
28351
28766
  case 1:
28352
28767
  if (!_core_ClientScriptManager_js__WEBPACK_IMPORTED_MODULE_5__.ClientScriptManager.isClientToolsScript(message)) {
28353
- _context3.n = 2;
28768
+ _context5.n = 2;
28354
28769
  break;
28355
28770
  }
28356
- return _context3.a(2, this.clientScriptManager.handleRunPartnerScript(message));
28771
+ return _context5.a(2, this.clientScriptManager.handleRunPartnerScript(message));
28357
28772
  case 2:
28358
28773
  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');
28359
28774
  if (handler) {
28360
- _context3.n = 3;
28775
+ _context5.n = 3;
28361
28776
  break;
28362
28777
  }
28363
28778
  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));
@@ -28369,18 +28784,18 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28369
28784
  ok: false,
28370
28785
  error: 'no_handler_registered'
28371
28786
  });
28372
- return _context3.a(2);
28787
+ return _context5.a(2);
28373
28788
  case 3:
28374
28789
  startedAt = Date.now();
28375
28790
  console.log("\uD83E\uDDE9 VoiceSDK v2: run_partner_script dispatch partner=".concat(partnerId, " action=").concat(action, " requestId=").concat(requestId));
28376
- _context3.p = 4;
28377
- _context3.n = 5;
28791
+ _context5.p = 4;
28792
+ _context5.n = 5;
28378
28793
  return handler({
28379
28794
  action: action,
28380
28795
  args: args
28381
28796
  });
28382
28797
  case 5:
28383
- result = _context3.v;
28798
+ result = _context5.v;
28384
28799
  elapsedMs = Date.now() - startedAt; // Adapters return { ok, ... } themselves. Honor that flag if present;
28385
28800
  // otherwise treat presence-of-result as success.
28386
28801
  ok = result && _typeof(result) === 'object' && 'ok' in result ? !!result.ok : true; // Surface the failure reason inline so debugging doesn't require pulling
@@ -28407,27 +28822,27 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28407
28822
  ok: ok,
28408
28823
  result: result
28409
28824
  });
28410
- _context3.n = 7;
28825
+ _context5.n = 7;
28411
28826
  break;
28412
28827
  case 6:
28413
- _context3.p = 6;
28414
- _t2 = _context3.v;
28828
+ _context5.p = 6;
28829
+ _t4 = _context5.v;
28415
28830
  _elapsedMs = Date.now() - startedAt;
28416
- console.error("\uD83E\uDDE9 VoiceSDK v2: run_partner_script threw partner=".concat(partnerId, " action=").concat(action, " requestId=").concat(requestId, " (").concat(_elapsedMs, "ms):"), _t2);
28831
+ console.error("\uD83E\uDDE9 VoiceSDK v2: run_partner_script threw partner=".concat(partnerId, " action=").concat(action, " requestId=").concat(requestId, " (").concat(_elapsedMs, "ms):"), _t4);
28417
28832
  this.sendMessage({
28418
28833
  t: 'run_partner_script_result',
28419
28834
  requestId: requestId,
28420
28835
  partnerId: partnerId,
28421
28836
  action: action,
28422
28837
  ok: false,
28423
- error: _t2 && _t2.message || String(_t2)
28838
+ error: _t4 && _t4.message || String(_t4)
28424
28839
  });
28425
28840
  case 7:
28426
- return _context3.a(2);
28841
+ return _context5.a(2);
28427
28842
  }
28428
- }, _callee3, this, [[4, 6]]);
28843
+ }, _callee5, this, [[4, 6]]);
28429
28844
  }));
28430
- function _handleRunPartnerScript(_x) {
28845
+ function _handleRunPartnerScript(_x3) {
28431
28846
  return _handleRunPartnerScript2.apply(this, arguments);
28432
28847
  }
28433
28848
  return _handleRunPartnerScript;
@@ -28472,7 +28887,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28472
28887
  }, {
28473
28888
  key: "injectData",
28474
28889
  value: (function () {
28475
- var _injectData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(data) {
28890
+ var _injectData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(data) {
28476
28891
  var _this7 = this;
28477
28892
  var options,
28478
28893
  trimmedData,
@@ -28483,15 +28898,15 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28483
28898
  timeout,
28484
28899
  source,
28485
28900
  message,
28486
- _args4 = arguments;
28487
- return _regenerator().w(function (_context4) {
28488
- while (1) switch (_context4.n) {
28901
+ _args6 = arguments;
28902
+ return _regenerator().w(function (_context6) {
28903
+ while (1) switch (_context6.n) {
28489
28904
  case 0:
28490
- options = _args4.length > 1 && _args4[1] !== undefined ? _args4[1] : {};
28905
+ options = _args6.length > 1 && _args6[1] !== undefined ? _args6[1] : {};
28491
28906
  // Validation: data content
28492
28907
  trimmedData = data === null || data === void 0 ? void 0 : data.trim();
28493
28908
  if (trimmedData) {
28494
- _context4.n = 1;
28909
+ _context6.n = 1;
28495
28910
  break;
28496
28911
  }
28497
28912
  error = {
@@ -28503,7 +28918,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28503
28918
  throw new Error(error.message);
28504
28919
  case 1:
28505
28920
  if (!(!this.isConnected || !this.websocket)) {
28506
- _context4.n = 2;
28921
+ _context6.n = 2;
28507
28922
  break;
28508
28923
  }
28509
28924
  _error = {
@@ -28515,7 +28930,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28515
28930
  throw new Error(_error.message);
28516
28931
  case 2:
28517
28932
  if (!(trimmedData.length > 5000)) {
28518
- _context4.n = 3;
28933
+ _context6.n = 3;
28519
28934
  break;
28520
28935
  }
28521
28936
  _error2 = {
@@ -28544,7 +28959,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28544
28959
  }
28545
28960
 
28546
28961
  // Return Promise that resolves when acknowledgment is received
28547
- return _context4.a(2, new Promise(function (_resolve, _reject) {
28962
+ return _context6.a(2, new Promise(function (_resolve, _reject) {
28548
28963
  // Set up timeout
28549
28964
  var timeoutId = setTimeout(function () {
28550
28965
  // Remove from pending map
@@ -28595,9 +29010,9 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28595
29010
  }
28596
29011
  }));
28597
29012
  }
28598
- }, _callee4, this);
29013
+ }, _callee6, this);
28599
29014
  }));
28600
- function injectData(_x2) {
29015
+ function injectData(_x4) {
28601
29016
  return _injectData.apply(this, arguments);
28602
29017
  }
28603
29018
  return injectData;
@@ -28727,20 +29142,20 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28727
29142
  }, {
28728
29143
  key: "handleBinaryMessage",
28729
29144
  value: (function () {
28730
- var _handleBinaryMessage = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(data) {
28731
- var arrayBuffer, processedAudio, playbackFormat, container, encoding, pcmData, codec, decoded, _t3;
28732
- return _regenerator().w(function (_context5) {
28733
- while (1) switch (_context5.p = _context5.n) {
29145
+ var _handleBinaryMessage = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7(data) {
29146
+ var arrayBuffer, processedAudio, playbackFormat, container, encoding, pcmData, codec, decoded, _t5;
29147
+ return _regenerator().w(function (_context7) {
29148
+ while (1) switch (_context7.p = _context7.n) {
28734
29149
  case 0:
28735
29150
  if (!(data instanceof Blob)) {
28736
- _context5.n = 2;
29151
+ _context7.n = 2;
28737
29152
  break;
28738
29153
  }
28739
- _context5.n = 1;
29154
+ _context7.n = 1;
28740
29155
  return data.arrayBuffer();
28741
29156
  case 1:
28742
- arrayBuffer = _context5.v;
28743
- _context5.n = 3;
29157
+ arrayBuffer = _context7.v;
29158
+ _context7.n = 3;
28744
29159
  break;
28745
29160
  case 2:
28746
29161
  arrayBuffer = data;
@@ -28748,21 +29163,21 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28748
29163
  // Convert format if needed (before passing to AudioPlayer)
28749
29164
  processedAudio = arrayBuffer;
28750
29165
  if (!(this.formatConverter && this.formatConverter.needsConversion())) {
28751
- _context5.n = 7;
29166
+ _context7.n = 7;
28752
29167
  break;
28753
29168
  }
28754
- _context5.p = 4;
28755
- _context5.n = 5;
29169
+ _context7.p = 4;
29170
+ _context7.n = 5;
28756
29171
  return this.formatConverter.convert(arrayBuffer);
28757
29172
  case 5:
28758
- processedAudio = _context5.v;
29173
+ processedAudio = _context7.v;
28759
29174
  console.log('✅ VoiceSDK v2: Audio converted to requested format');
28760
- _context5.n = 7;
29175
+ _context7.n = 7;
28761
29176
  break;
28762
29177
  case 6:
28763
- _context5.p = 6;
28764
- _t3 = _context5.v;
28765
- console.error('❌ VoiceSDK v2: Format conversion failed:', _t3);
29178
+ _context7.p = 6;
29179
+ _t5 = _context7.v;
29180
+ console.error('❌ VoiceSDK v2: Format conversion failed:', _t5);
28766
29181
  console.warn(' Using backend format without conversion');
28767
29182
  processedAudio = arrayBuffer; // Fall back to original
28768
29183
  case 7:
@@ -28812,11 +29227,11 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28812
29227
  this.audioPlayer.playAudio(processedAudio);
28813
29228
  }
28814
29229
  case 8:
28815
- return _context5.a(2);
29230
+ return _context7.a(2);
28816
29231
  }
28817
- }, _callee5, this, [[4, 6]]);
29232
+ }, _callee7, this, [[4, 6]]);
28818
29233
  }));
28819
- function handleBinaryMessage(_x3) {
29234
+ function handleBinaryMessage(_x5) {
28820
29235
  return _handleBinaryMessage.apply(this, arguments);
28821
29236
  }
28822
29237
  return handleBinaryMessage;
@@ -29004,22 +29419,22 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29004
29419
  }, {
29005
29420
  key: "startRecording",
29006
29421
  value: (function () {
29007
- var _startRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
29422
+ var _startRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
29008
29423
  var existingMediaStream,
29009
29424
  error,
29010
29425
  _error4,
29011
29426
  _error5,
29012
29427
  permissionError,
29013
- _args6 = arguments,
29014
- _t4,
29015
- _t5,
29016
- _t6;
29017
- return _regenerator().w(function (_context6) {
29018
- while (1) switch (_context6.p = _context6.n) {
29428
+ _args8 = arguments,
29429
+ _t6,
29430
+ _t7,
29431
+ _t8;
29432
+ return _regenerator().w(function (_context8) {
29433
+ while (1) switch (_context8.p = _context8.n) {
29019
29434
  case 0:
29020
- existingMediaStream = _args6.length > 0 && _args6[0] !== undefined ? _args6[0] : null;
29435
+ existingMediaStream = _args8.length > 0 && _args8[0] !== undefined ? _args8[0] : null;
29021
29436
  if (this.isConnected) {
29022
- _context6.n = 1;
29437
+ _context8.n = 1;
29023
29438
  break;
29024
29439
  }
29025
29440
  error = new Error('Not connected to voice server');
@@ -29027,14 +29442,14 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29027
29442
  throw error;
29028
29443
  case 1:
29029
29444
  if (!this.isRecording) {
29030
- _context6.n = 2;
29445
+ _context8.n = 2;
29031
29446
  break;
29032
29447
  }
29033
29448
  console.warn('VoiceSDK v2: Already recording');
29034
- return _context6.a(2, true);
29449
+ return _context8.a(2, true);
29035
29450
  case 2:
29036
29451
  if (!this.disclaimersPending) {
29037
- _context6.n = 3;
29452
+ _context8.n = 3;
29038
29453
  break;
29039
29454
  }
29040
29455
  _error4 = new Error('Server disclaimers must be accepted before recording. Call sendDisclaimerAck(true) first.');
@@ -29043,28 +29458,28 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29043
29458
  throw _error4;
29044
29459
  case 3:
29045
29460
  console.log('🎤 VoiceSDK v2: Starting recording...');
29046
- _context6.p = 4;
29461
+ _context8.p = 4;
29047
29462
  // CRITICAL for mobile browsers: Request microphone permission FIRST
29048
29463
  // iOS: Pass existingMediaStream to avoid second getUserMedia (can fail on iOS)
29049
29464
  console.log('🎤 VoiceSDK v2: Requesting microphone permission (mobile-friendly)...');
29050
- _context6.n = 5;
29465
+ _context8.n = 5;
29051
29466
  return this.audioRecorder.start(existingMediaStream ? {
29052
29467
  existingMediaStream: existingMediaStream
29053
29468
  } : {});
29054
29469
  case 5:
29055
29470
  if (!(!this.isConnected || !this.websocket || this.websocket.readyState !== WebSocket.OPEN)) {
29056
- _context6.n = 10;
29471
+ _context8.n = 10;
29057
29472
  break;
29058
29473
  }
29059
- _context6.p = 6;
29060
- _context6.n = 7;
29474
+ _context8.p = 6;
29475
+ _context8.n = 7;
29061
29476
  return this.audioRecorder.stop();
29062
29477
  case 7:
29063
- _context6.n = 9;
29478
+ _context8.n = 9;
29064
29479
  break;
29065
29480
  case 8:
29066
- _context6.p = 8;
29067
- _t4 = _context6.v;
29481
+ _context8.p = 8;
29482
+ _t6 = _context8.v;
29068
29483
  case 9:
29069
29484
  _error5 = new Error('Connection lost - server may have rejected the call');
29070
29485
  _error5.name = 'ServerRejected';
@@ -29073,7 +29488,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29073
29488
  // The error handler in VoiceInterface will handle it appropriately
29074
29489
  throw _error5;
29075
29490
  case 10:
29076
- _context6.n = 11;
29491
+ _context8.n = 11;
29077
29492
  return this.audioPlayer.resumeAudioContext();
29078
29493
  case 11:
29079
29494
  // After permission is granted, send start_continuous_mode message to server
@@ -29084,45 +29499,45 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29084
29499
 
29085
29500
  // Request screen wake lock to keep screen on during voice call (mobile)
29086
29501
  this._requestWakeLock();
29087
- return _context6.a(2, true);
29502
+ return _context8.a(2, true);
29088
29503
  case 12:
29089
- _context6.p = 12;
29090
- _t5 = _context6.v;
29504
+ _context8.p = 12;
29505
+ _t7 = _context8.v;
29091
29506
  if (!(this.audioRecorder && this.audioRecorder.isRecording)) {
29092
- _context6.n = 16;
29507
+ _context8.n = 16;
29093
29508
  break;
29094
29509
  }
29095
- _context6.p = 13;
29096
- _context6.n = 14;
29510
+ _context8.p = 13;
29511
+ _context8.n = 14;
29097
29512
  return this.audioRecorder.stop();
29098
29513
  case 14:
29099
- _context6.n = 16;
29514
+ _context8.n = 16;
29100
29515
  break;
29101
29516
  case 15:
29102
- _context6.p = 15;
29103
- _t6 = _context6.v;
29517
+ _context8.p = 15;
29518
+ _t8 = _context8.v;
29104
29519
  case 16:
29105
- if (!(_t5.isServerRejection || _t5.name === 'ServerRejected')) {
29106
- _context6.n = 17;
29520
+ if (!(_t7.isServerRejection || _t7.name === 'ServerRejected')) {
29521
+ _context8.n = 17;
29107
29522
  break;
29108
29523
  }
29109
- return _context6.a(2, false);
29524
+ return _context8.a(2, false);
29110
29525
  case 17:
29111
29526
  // Log and emit other errors (permission denied, etc.)
29112
- console.error('❌ VoiceSDK v2: Failed to start recording:', _t5);
29527
+ console.error('❌ VoiceSDK v2: Failed to start recording:', _t7);
29113
29528
 
29114
29529
  // Provide helpful error messages for permission issues
29115
- if (_t5.name === 'NotAllowedError' || _t5.name === 'PermissionDeniedError') {
29530
+ if (_t7.name === 'NotAllowedError' || _t7.name === 'PermissionDeniedError') {
29116
29531
  permissionError = new Error('Microphone permission denied. Please allow microphone access in your browser settings and try again.');
29117
- permissionError.name = _t5.name;
29118
- permissionError.originalError = _t5;
29532
+ permissionError.name = _t7.name;
29533
+ permissionError.originalError = _t7;
29119
29534
  this.emit('error', permissionError);
29120
29535
  } else {
29121
- this.emit('error', _t5);
29536
+ this.emit('error', _t7);
29122
29537
  }
29123
- return _context6.a(2, false);
29538
+ return _context8.a(2, false);
29124
29539
  }
29125
- }, _callee6, this, [[13, 15], [6, 8], [4, 12]]);
29540
+ }, _callee8, this, [[13, 15], [6, 8], [4, 12]]);
29126
29541
  }));
29127
29542
  function startRecording() {
29128
29543
  return _startRecording.apply(this, arguments);
@@ -29136,20 +29551,20 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29136
29551
  }, {
29137
29552
  key: "stopRecording",
29138
29553
  value: (function () {
29139
- var _stopRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7() {
29140
- var _t7;
29141
- return _regenerator().w(function (_context7) {
29142
- while (1) switch (_context7.p = _context7.n) {
29554
+ var _stopRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
29555
+ var _t9;
29556
+ return _regenerator().w(function (_context9) {
29557
+ while (1) switch (_context9.p = _context9.n) {
29143
29558
  case 0:
29144
29559
  if (this.isRecording) {
29145
- _context7.n = 1;
29560
+ _context9.n = 1;
29146
29561
  break;
29147
29562
  }
29148
29563
  console.warn('VoiceSDK v2: Not recording');
29149
- return _context7.a(2, true);
29564
+ return _context9.a(2, true);
29150
29565
  case 1:
29151
29566
  console.log('🛑 VoiceSDK v2: Stopping recording...');
29152
- _context7.p = 2;
29567
+ _context9.p = 2;
29153
29568
  // Send stop_continuous_mode message to server
29154
29569
 
29155
29570
  this.sendMessage({
@@ -29167,21 +29582,21 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29167
29582
  this._releaseWakeLock();
29168
29583
 
29169
29584
  // Stop capturing audio from microphone
29170
- _context7.n = 3;
29585
+ _context9.n = 3;
29171
29586
  return this.audioRecorder.stop();
29172
29587
  case 3:
29173
29588
  // Stop any playing audio
29174
29589
 
29175
29590
  this.stopAudioPlayback();
29176
- return _context7.a(2, true);
29591
+ return _context9.a(2, true);
29177
29592
  case 4:
29178
- _context7.p = 4;
29179
- _t7 = _context7.v;
29180
- console.error('VoiceSDK v2: Error stopping recording:', _t7);
29181
- this.emit('error', _t7);
29182
- return _context7.a(2, false);
29593
+ _context9.p = 4;
29594
+ _t9 = _context9.v;
29595
+ console.error('VoiceSDK v2: Error stopping recording:', _t9);
29596
+ this.emit('error', _t9);
29597
+ return _context9.a(2, false);
29183
29598
  }
29184
- }, _callee7, this, [[2, 4]]);
29599
+ }, _callee9, this, [[2, 4]]);
29185
29600
  }));
29186
29601
  function stopRecording() {
29187
29602
  return _stopRecording.apply(this, arguments);
@@ -29195,27 +29610,27 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29195
29610
  }, {
29196
29611
  key: "toggleRecording",
29197
29612
  value: (function () {
29198
- var _toggleRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
29199
- return _regenerator().w(function (_context8) {
29200
- while (1) switch (_context8.n) {
29613
+ var _toggleRecording = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
29614
+ return _regenerator().w(function (_context0) {
29615
+ while (1) switch (_context0.n) {
29201
29616
  case 0:
29202
29617
  if (!this.isRecording) {
29203
- _context8.n = 2;
29618
+ _context0.n = 2;
29204
29619
  break;
29205
29620
  }
29206
- _context8.n = 1;
29621
+ _context0.n = 1;
29207
29622
  return this.stopRecording();
29208
29623
  case 1:
29209
- return _context8.a(2, _context8.v);
29624
+ return _context0.a(2, _context0.v);
29210
29625
  case 2:
29211
- _context8.n = 3;
29626
+ _context0.n = 3;
29212
29627
  return this.startRecording();
29213
29628
  case 3:
29214
- return _context8.a(2, _context8.v);
29629
+ return _context0.a(2, _context0.v);
29215
29630
  case 4:
29216
- return _context8.a(2);
29631
+ return _context0.a(2);
29217
29632
  }
29218
- }, _callee8, this);
29633
+ }, _callee0, this);
29219
29634
  }));
29220
29635
  function toggleRecording() {
29221
29636
  return _toggleRecording.apply(this, arguments);
@@ -29246,33 +29661,33 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29246
29661
  }, {
29247
29662
  key: "_requestWakeLock",
29248
29663
  value: (function () {
29249
- var _requestWakeLock2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
29664
+ var _requestWakeLock2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() {
29250
29665
  var _this9 = this;
29251
- var _t8;
29252
- return _regenerator().w(function (_context9) {
29253
- while (1) switch (_context9.p = _context9.n) {
29666
+ var _t0;
29667
+ return _regenerator().w(function (_context1) {
29668
+ while (1) switch (_context1.p = _context1.n) {
29254
29669
  case 0:
29255
29670
  if ('wakeLock' in navigator) {
29256
- _context9.n = 1;
29671
+ _context1.n = 1;
29257
29672
  break;
29258
29673
  }
29259
29674
  console.log('📱 VoiceSDK v2: Wake Lock API not available in this browser');
29260
- return _context9.a(2);
29675
+ return _context1.a(2);
29261
29676
  case 1:
29262
29677
  if (!(this.wakeLock !== null || this._wakeLockRequesting)) {
29263
- _context9.n = 2;
29678
+ _context1.n = 2;
29264
29679
  break;
29265
29680
  }
29266
29681
  console.log('📱 VoiceSDK v2: Wake lock already active or being requested');
29267
- return _context9.a(2);
29682
+ return _context1.a(2);
29268
29683
  case 2:
29269
- _context9.p = 2;
29684
+ _context1.p = 2;
29270
29685
  this._wakeLockRequesting = true;
29271
29686
  // Request wake lock (screen type keeps screen on)
29272
- _context9.n = 3;
29687
+ _context1.n = 3;
29273
29688
  return navigator.wakeLock.request('screen');
29274
29689
  case 3:
29275
- this.wakeLock = _context9.v;
29690
+ this.wakeLock = _context1.v;
29276
29691
  console.log('📱 VoiceSDK v2: Screen wake lock activated - screen will stay on during call');
29277
29692
 
29278
29693
  // Handle wake lock release (e.g., user switches tabs, locks device)
@@ -29280,23 +29695,23 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29280
29695
  console.log('📱 VoiceSDK v2: Wake lock released by system');
29281
29696
  _this9.wakeLock = null;
29282
29697
  });
29283
- _context9.n = 5;
29698
+ _context1.n = 5;
29284
29699
  break;
29285
29700
  case 4:
29286
- _context9.p = 4;
29287
- _t8 = _context9.v;
29701
+ _context1.p = 4;
29702
+ _t0 = _context1.v;
29288
29703
  // Wake lock request failed (e.g., permission denied, battery saver mode)
29289
- console.warn('📱 VoiceSDK v2: Failed to request wake lock:', _t8.message);
29704
+ console.warn('📱 VoiceSDK v2: Failed to request wake lock:', _t0.message);
29290
29705
  this.wakeLock = null;
29291
29706
  // Don't throw - this is a nice-to-have feature, not critical
29292
29707
  case 5:
29293
- _context9.p = 5;
29708
+ _context1.p = 5;
29294
29709
  this._wakeLockRequesting = false;
29295
- return _context9.f(5);
29710
+ return _context1.f(5);
29296
29711
  case 6:
29297
- return _context9.a(2);
29712
+ return _context1.a(2);
29298
29713
  }
29299
- }, _callee9, this, [[2, 4, 5, 6]]);
29714
+ }, _callee1, this, [[2, 4, 5, 6]]);
29300
29715
  }));
29301
29716
  function _requestWakeLock() {
29302
29717
  return _requestWakeLock2.apply(this, arguments);
@@ -29311,34 +29726,34 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29311
29726
  }, {
29312
29727
  key: "_releaseWakeLock",
29313
29728
  value: (function () {
29314
- var _releaseWakeLock2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
29315
- var _t9;
29316
- return _regenerator().w(function (_context0) {
29317
- while (1) switch (_context0.p = _context0.n) {
29729
+ var _releaseWakeLock2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee10() {
29730
+ var _t1;
29731
+ return _regenerator().w(function (_context10) {
29732
+ while (1) switch (_context10.p = _context10.n) {
29318
29733
  case 0:
29319
29734
  if (!(this.wakeLock === null)) {
29320
- _context0.n = 1;
29735
+ _context10.n = 1;
29321
29736
  break;
29322
29737
  }
29323
- return _context0.a(2);
29738
+ return _context10.a(2);
29324
29739
  case 1:
29325
- _context0.p = 1;
29326
- _context0.n = 2;
29740
+ _context10.p = 1;
29741
+ _context10.n = 2;
29327
29742
  return this.wakeLock.release();
29328
29743
  case 2:
29329
29744
  this.wakeLock = null;
29330
29745
  console.log('📱 VoiceSDK v2: Screen wake lock released');
29331
- _context0.n = 4;
29746
+ _context10.n = 4;
29332
29747
  break;
29333
29748
  case 3:
29334
- _context0.p = 3;
29335
- _t9 = _context0.v;
29336
- console.warn('📱 VoiceSDK v2: Error releasing wake lock:', _t9.message);
29749
+ _context10.p = 3;
29750
+ _t1 = _context10.v;
29751
+ console.warn('📱 VoiceSDK v2: Error releasing wake lock:', _t1.message);
29337
29752
  this.wakeLock = null;
29338
29753
  case 4:
29339
- return _context0.a(2);
29754
+ return _context10.a(2);
29340
29755
  }
29341
- }, _callee0, this, [[1, 3]]);
29756
+ }, _callee10, this, [[1, 3]]);
29342
29757
  }));
29343
29758
  function _releaseWakeLock() {
29344
29759
  return _releaseWakeLock2.apply(this, arguments);
@@ -29362,21 +29777,21 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29362
29777
  }, {
29363
29778
  key: "handleBargeIn",
29364
29779
  value: (function () {
29365
- var _handleBargeIn = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() {
29366
- return _regenerator().w(function (_context1) {
29367
- while (1) switch (_context1.n) {
29780
+ var _handleBargeIn = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee11() {
29781
+ return _regenerator().w(function (_context11) {
29782
+ while (1) switch (_context11.n) {
29368
29783
  case 0:
29369
29784
  this.stopAudioPlayback();
29370
29785
  if (this.isRecording) {
29371
- _context1.n = 1;
29786
+ _context11.n = 1;
29372
29787
  break;
29373
29788
  }
29374
- _context1.n = 1;
29789
+ _context11.n = 1;
29375
29790
  return this.startRecording();
29376
29791
  case 1:
29377
- return _context1.a(2);
29792
+ return _context11.a(2);
29378
29793
  }
29379
- }, _callee1, this);
29794
+ }, _callee11, this);
29380
29795
  }));
29381
29796
  function handleBargeIn() {
29382
29797
  return _handleBargeIn.apply(this, arguments);
@@ -32289,10 +32704,17 @@ var TTPChatWidget = /*#__PURE__*/function () {
32289
32704
  this._flavorObj = this.config.flavor && _typeof(this.config.flavor) === 'object' ? this.config.flavor : null;
32290
32705
  if (this._flavorObj && this._flavorObj.type) {
32291
32706
  this._initFlavor(this._flavorObj.type);
32292
- this._initEcommerceVoiceStrip();
32293
- } else if (this._flavorObj && !this._flavorObj.type) {
32707
+ } else if (this._flavorObj && !this._flavorObj.type && this._flavorObj.callView !== 'minimized') {
32708
+ // A flavor object with neither a "type" nor callView:'minimized' is malformed.
32709
+ // (callView:'minimized' alone is a valid flavor-less minimized-strip config.)
32294
32710
  console.error('[TTPChatWidget] flavor object is set but missing required "type" field. Widget will run in vanilla mode.');
32295
32711
  }
32712
+ // The desktop minimized bottom strip is flavor-independent: it only needs
32713
+ // flavor.callView === 'minimized' (+ desktop width). Init it regardless of
32714
+ // whether a flavor type is present, so callView:'minimized' works standalone
32715
+ // with no partner/flavor. _initEcommerceVoiceStrip() self-guards and no-ops
32716
+ // when callView !== 'minimized' or on mobile.
32717
+ this._initEcommerceVoiceStrip();
32296
32718
  }
32297
32719
 
32298
32720
  /**
@@ -32708,7 +33130,11 @@ var TTPChatWidget = /*#__PURE__*/function () {
32708
33130
  offset: positionConfig.offset || {
32709
33131
  x: 20,
32710
33132
  y: 20
32711
- }
33133
+ },
33134
+ // When true, the launcher pill / mobile FAB can be dragged around the
33135
+ // viewport. Set position.draggablePersist:false to reset position each load.
33136
+ draggable: false,
33137
+ draggablePersist: true
32712
33138
  }, _typeof(userConfig.position) === 'object' ? userConfig.position : {}),
32713
33139
  // Button Configuration
32714
33140
  button: _objectSpread({
@@ -33344,6 +33770,125 @@ var TTPChatWidget = /*#__PURE__*/function () {
33344
33770
  var footer = (_root$querySelector = root.querySelector) === null || _root$querySelector === void 0 ? void 0 : _root$querySelector.call(root, '.ttp-footer');
33345
33771
  if (footer) footer.style.display = visible ? '' : 'none';
33346
33772
  }
33773
+
33774
+ /**
33775
+ * Resolve the most recent conversation id used by either channel.
33776
+ * Prefers a live voice/text SDK value, then falls back to persisted state.
33777
+ * @returns {string|null}
33778
+ */
33779
+ }, {
33780
+ key: "_getLastConversationId",
33781
+ value: function _getLastConversationId() {
33782
+ var _this$voiceInterface3, _this$sdk;
33783
+ var live = (_this$voiceInterface3 = this.voiceInterface) === null || _this$voiceInterface3 === void 0 || (_this$voiceInterface3 = _this$voiceInterface3.sdk) === null || _this$voiceInterface3 === void 0 || (_this$voiceInterface3 = _this$voiceInterface3.voiceSDK) === null || _this$voiceInterface3 === void 0 ? void 0 : _this$voiceInterface3.conversationId;
33784
+ if (live) return live;
33785
+ var textId = (_this$sdk = this.sdk) === null || _this$sdk === void 0 || (_this$sdk = _this$sdk.config) === null || _this$sdk === void 0 ? void 0 : _this$sdk.conversationId;
33786
+ if (textId) return textId;
33787
+ try {
33788
+ var raw = sessionStorage.getItem('voice_resume_state');
33789
+ if (raw) {
33790
+ var s = JSON.parse(raw);
33791
+ if (s && s.conversationId) return s.conversationId;
33792
+ }
33793
+ } catch (_) {/* ignore */}
33794
+ try {
33795
+ var t = localStorage.getItem('ttp_text_chat_conversation_id');
33796
+ if (t) return t;
33797
+ } catch (_) {/* ignore */}
33798
+ return null;
33799
+ }
33800
+
33801
+ /** Inline TalkToPC logo mark (speech bubble + sound waves). */
33802
+ }, {
33803
+ key: "_aboutLogoSVG",
33804
+ value: function _aboutLogoSVG() {
33805
+ return "<svg class=\"ttp-about-logo-mark\" width=\"34\" height=\"34\" viewBox=\"0 0 48 48\" fill=\"none\" aria-hidden=\"true\">\n <rect x=\"4\" y=\"6\" width=\"40\" height=\"30\" rx=\"10\" fill=\"url(#ttpAboutGrad)\"/>\n <path d=\"M16 36l-4 7 11-7z\" fill=\"url(#ttpAboutGrad)\"/>\n <g stroke=\"#ffffff\" stroke-width=\"2.6\" stroke-linecap=\"round\">\n <line x1=\"18\" y1=\"17\" x2=\"18\" y2=\"25\"/>\n <line x1=\"24\" y1=\"13\" x2=\"24\" y2=\"29\"/>\n <line x1=\"30\" y1=\"18\" x2=\"30\" y2=\"24\"/>\n </g>\n <defs>\n <linearGradient id=\"ttpAboutGrad\" x1=\"4\" y1=\"6\" x2=\"44\" y2=\"43\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#8b5cf6\"/>\n <stop offset=\"1\" stop-color=\"#6d28d9\"/>\n </linearGradient>\n </defs>\n </svg>";
33806
+ }
33807
+
33808
+ /**
33809
+ * Inject the "About" dialog styles into document.head once.
33810
+ * Global (light DOM) so the dialog renders above the desktop minimized strip,
33811
+ * which lives outside the shadow root at the maximum z-index.
33812
+ */
33813
+ }, {
33814
+ key: "_ensureAboutStyles",
33815
+ value: function _ensureAboutStyles() {
33816
+ if (document.getElementById('ttp-about-dialog-styles')) return;
33817
+ var style = document.createElement('style');
33818
+ style.id = 'ttp-about-dialog-styles';
33819
+ style.textContent = "\n .ttp-about-overlay {\n position: fixed; inset: 0; z-index: 2147483647;\n display: flex; align-items: center; justify-content: center;\n padding: 16px; box-sizing: border-box;\n background: rgba(15,15,25,0.55);\n backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);\n animation: ttpAboutFade 0.18s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n }\n @keyframes ttpAboutFade { from { opacity: 0; } to { opacity: 1; } }\n .ttp-about-card {\n position: relative; width: 100%; max-width: 320px; box-sizing: border-box;\n background: #ffffff; color: #1f2937; border-radius: 16px;\n padding: 24px 22px 22px; box-shadow: 0 24px 60px rgba(0,0,0,0.35);\n text-align: center; animation: ttpAboutPop 0.2s ease-out;\n }\n @keyframes ttpAboutPop {\n from { transform: translateY(8px) scale(0.98); opacity: 0; }\n to { transform: translateY(0) scale(1); opacity: 1; }\n }\n .ttp-about-close {\n position: absolute; top: 10px; inset-inline-end: 10px;\n background: none; border: none; cursor: pointer; color: #9ca3af;\n padding: 6px; line-height: 0; border-radius: 50%;\n transition: background 0.2s, color 0.2s;\n }\n .ttp-about-close:hover { background: #f3f4f6; color: #4b5563; }\n .ttp-about-logo {\n display: flex; align-items: center; justify-content: center;\n gap: 8px; margin-bottom: 14px;\n }\n .ttp-about-logo-mark { display: block; flex-shrink: 0; }\n .ttp-about-brand {\n font-size: 20px; font-weight: 800; letter-spacing: -0.2px; color: #6d28d9;\n }\n .ttp-about-title {\n font-size: 13px; font-weight: 600; text-transform: uppercase;\n letter-spacing: 1px; color: #9ca3af; margin-bottom: 16px;\n }\n .ttp-about-rows {\n display: flex; flex-direction: column; gap: 10px; text-align: start;\n }\n .ttp-about-row {\n display: flex; flex-direction: column; gap: 3px;\n background: #f9fafb; border: 1px solid #f0f0f4;\n border-radius: 10px; padding: 10px 12px;\n }\n .ttp-about-key {\n font-size: 11px; font-weight: 600; text-transform: uppercase;\n letter-spacing: 0.5px; color: #9ca3af;\n }\n .ttp-about-val {\n font-size: 14px; font-weight: 600; color: #1f2937; word-break: break-all;\n }\n .ttp-about-mono {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12.5px; font-weight: 500;\n }\n .ttp-about-muted { color: #9ca3af; font-weight: 500; font-style: italic; }\n ";
33820
+ document.head.appendChild(style);
33821
+ }
33822
+
33823
+ /** Open the shared "About" dialog (logo + SDK version + last conversation id). */
33824
+ }, {
33825
+ key: "_openAboutModal",
33826
+ value: function _openAboutModal() {
33827
+ var _this8 = this,
33828
+ _overlay$querySelecto;
33829
+ // Toggle: if already open, close it.
33830
+ if (this._aboutOverlay) {
33831
+ this._closeAboutModal();
33832
+ return;
33833
+ }
33834
+ this._ensureAboutStyles();
33835
+ var version = true ? "2.45.12" : 0;
33836
+ var convId = this._getLastConversationId();
33837
+ var t = function t(k, fb) {
33838
+ try {
33839
+ var _this8$voiceInterface, _this8$voiceInterface2, _this8$textInterface, _this8$textInterface$;
33840
+ return ((_this8$voiceInterface = _this8.voiceInterface) === null || _this8$voiceInterface === void 0 || (_this8$voiceInterface2 = _this8$voiceInterface.t) === null || _this8$voiceInterface2 === void 0 ? void 0 : _this8$voiceInterface2.call(_this8$voiceInterface, k)) || ((_this8$textInterface = _this8.textInterface) === null || _this8$textInterface === void 0 || (_this8$textInterface$ = _this8$textInterface.t) === null || _this8$textInterface$ === void 0 ? void 0 : _this8$textInterface$.call(_this8$textInterface, k)) || fb;
33841
+ } catch (_) {
33842
+ return fb;
33843
+ }
33844
+ };
33845
+ var title = t('aboutTitle', 'About');
33846
+ var versionLabel = t('aboutVersion', 'SDK version');
33847
+ var convLabel = t('aboutConversationId', 'Last conversation ID');
33848
+ var noneLabel = t('aboutNoConversation', 'No conversation yet');
33849
+ var overlay = document.createElement('div');
33850
+ overlay.className = 'ttp-about-overlay';
33851
+ overlay.innerHTML = "\n <div class=\"ttp-about-card\" role=\"dialog\" aria-modal=\"true\" aria-label=\"".concat(title, "\">\n <button type=\"button\" class=\"ttp-about-close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <path d=\"M10 2L2 10M2 2l8 8\"/>\n </svg>\n </button>\n <div class=\"ttp-about-logo\">").concat(this._aboutLogoSVG(), "<span class=\"ttp-about-brand\">TalkToPC</span></div>\n <div class=\"ttp-about-title\">").concat(title, "</div>\n <div class=\"ttp-about-rows\">\n <div class=\"ttp-about-row\">\n <span class=\"ttp-about-key\">").concat(versionLabel, "</span>\n <span class=\"ttp-about-val\" data-about-version></span>\n </div>\n <div class=\"ttp-about-row\">\n <span class=\"ttp-about-key\">").concat(convLabel, "</span>\n <span class=\"ttp-about-val ttp-about-mono\" data-about-conv></span>\n </div>\n </div>\n </div>");
33852
+
33853
+ // Fill dynamic values via textContent to avoid any injection.
33854
+ var verEl = overlay.querySelector('[data-about-version]');
33855
+ if (verEl) verEl.textContent = version ? "v".concat(version) : '—';
33856
+ var convEl = overlay.querySelector('[data-about-conv]');
33857
+ if (convEl) {
33858
+ convEl.textContent = convId || noneLabel;
33859
+ if (!convId) convEl.classList.add('ttp-about-muted');
33860
+ }
33861
+ var close = function close() {
33862
+ return _this8._closeAboutModal();
33863
+ };
33864
+ overlay.addEventListener('click', function (e) {
33865
+ if (e.target === overlay) close();
33866
+ });
33867
+ (_overlay$querySelecto = overlay.querySelector('.ttp-about-close')) === null || _overlay$querySelecto === void 0 || _overlay$querySelecto.addEventListener('click', function (e) {
33868
+ e.stopPropagation();
33869
+ close();
33870
+ });
33871
+ this._aboutKeyHandler = function (e) {
33872
+ if (e.key === 'Escape') close();
33873
+ };
33874
+ document.addEventListener('keydown', this._aboutKeyHandler);
33875
+ document.body.appendChild(overlay);
33876
+ this._aboutOverlay = overlay;
33877
+ }
33878
+
33879
+ /** Close the "About" dialog if open. */
33880
+ }, {
33881
+ key: "_closeAboutModal",
33882
+ value: function _closeAboutModal() {
33883
+ if (this._aboutKeyHandler) {
33884
+ document.removeEventListener('keydown', this._aboutKeyHandler);
33885
+ this._aboutKeyHandler = null;
33886
+ }
33887
+ if (this._aboutOverlay) {
33888
+ this._aboutOverlay.remove();
33889
+ this._aboutOverlay = null;
33890
+ }
33891
+ }
33347
33892
  }, {
33348
33893
  key: "generatePositionStyles",
33349
33894
  value: function generatePositionStyles() {
@@ -33475,13 +34020,14 @@ var TTPChatWidget = /*#__PURE__*/function () {
33475
34020
  var callActivePanelBg = callSurfaceGradient;
33476
34021
  var voiceIdleVisibleSelector = '#voiceInterface.active #voiceIdleState:not([style*="display: none"]):not([style*="display:none"])';
33477
34022
  var voiceActiveVisibleSelector = '#voiceInterface.active #voiceActiveState:not([style*="display: none"]):not([style*="display:none"])';
33478
- return "\n /* Inline style display overrides - ensures JS show/hide works with !important CSS */\n #text-chat-panel [style*=\"display: none\"],\n #text-chat-panel [style*=\"display:none\"],\n #text-chat-button-container [style*=\"display: none\"],\n #text-chat-button-container [style*=\"display:none\"],\n #ttpMobileLanding[style*=\"display: none\"],\n #ttpMobileLanding[style*=\"display:none\"] {\n display: none !important;\n }\n #text-chat-panel [style*=\"display: flex\"],\n #text-chat-panel [style*=\"display:flex\"],\n #text-chat-button-container [style*=\"display: flex\"],\n #text-chat-button-container [style*=\"display:flex\"] {\n display: flex !important;\n }\n #text-chat-panel [style*=\"display: block\"],\n #text-chat-panel [style*=\"display:block\"] {\n display: block !important;\n }\n\n /* MOBILE FIRST - Default styles for all devices */\n #text-chat-widget {\n position: fixed !important;\n z-index: 10000".concat(important, ";\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif").concat(important, ";\n /* Mobile defaults */\n right: 10px").concat(important, ";\n bottom: 10px").concat(important, ";\n left: auto").concat(important, ";\n top: auto").concat(important, ";\n }\n \n /* Desktop positioning (only on larger screens) */\n @media (min-width: 769px) {\n #text-chat-widget {\n ").concat(positionStyles, "\n right: ").concat(this.config.position.horizontal === 'right' ? '20px' : 'auto').concat(important, ";\n left: ").concat(this.config.position.horizontal === 'left' ? '20px' : 'auto').concat(important, ";\n bottom: ").concat(this.config.position.vertical === 'bottom' ? '20px' : 'auto').concat(important, ";\n top: ").concat(this.config.position.vertical === 'top' ? '20px' : 'auto').concat(important, ";\n }\n }\n \n /* Mobile override (force mobile positioning) */\n @media (max-width: 768px) {\n #text-chat-widget {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n transform: none !important;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-widget {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n /* \u2500\u2500 Desktop Pill Launcher \u2500\u2500 */\n #text-chat-button {\n position: relative").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 8px").concat(important, ";\n padding-block: 10px").concat(important, ";\n padding-inline-start: 6px").concat(important, ";\n padding-inline-end: 12px").concat(important, ";\n z-index: 1").concat(important, ";\n background: ").concat(pillGradient, ";\n border: 1px solid rgba(139,92,246,0.15)").concat(important, ";\n border-radius: 999px").concat(important, ";\n box-shadow: 0 8px 32px rgba(0,0,0,0.25), 0 2px 8px rgba(0,0,0,0.15)").concat(important, ";\n cursor: pointer").concat(important, ";\n transition: box-shadow 0.25s, transform 0.2s, padding 0.3s, gap 0.3s").concat(important, ";\n font-family: inherit").concat(important, ";\n color: ").concat(pillTextColor, ";\n overflow: hidden").concat(important, ";\n box-sizing: border-box").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n #text-chat-button:hover {\n transform: translateY(-1px)").concat(important, ";\n box-shadow: 0 10px 40px rgba(0,0,0,0.35), 0 4px 12px rgba(0,0,0,0.2)").concat(important, ";\n }\n #text-chat-button.open {\n gap: 0px").concat(important, ";\n padding: 9px").concat(important, ";\n width: auto").concat(important, ";\n min-width: 0").concat(important, ";\n max-width: none").concat(important, ";\n }\n\n @media (min-width: 769px) {\n #text-chat-button {\n width: 158px").concat(important, ";\n min-width: 158px").concat(important, ";\n max-width: 158px").concat(important, ";\n }\n }\n\n .ttp-pill-icon {\n width: 36px").concat(important, "; height: 36px").concat(important, "; border-radius: 50%").concat(important, ";\n background: #ffffff").concat(important, ";\n display: flex").concat(important, "; align-items: center").concat(important, "; justify-content: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n .ttp-launcher-logo {\n width: 22px").concat(important, "; height: 22px").concat(important, "; border-radius: 50%").concat(important, ";\n object-fit: cover").concat(important, ";\n }\n\n /* Default pill launcher: same animated waveform as mobile FAB / landing */\n .ttp-pill-icon--wave {\n background: rgba(255, 255, 255, 0.15)").concat(important, ";\n }\n .ttp-pill-waveform {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 2px").concat(important, ";\n height: 16px").concat(important, ";\n }\n .ttp-pill-waveform .bar {\n width: 2px").concat(important, ";\n background: #fff").concat(important, ";\n border-radius: 1px").concat(important, ";\n animation: ttpWaveAnimation 0.8s ease-in-out infinite").concat(important, ";\n }\n .ttp-pill-waveform .bar:nth-child(1) { height: 5px").concat(important, "; animation-delay: 0s").concat(important, "; }\n .ttp-pill-waveform .bar:nth-child(2) { height: 10px").concat(important, "; animation-delay: 0.1s").concat(important, "; }\n .ttp-pill-waveform .bar:nth-child(3) { height: 16px").concat(important, "; animation-delay: 0.2s").concat(important, "; }\n .ttp-pill-waveform .bar:nth-child(4) { height: 8px").concat(important, "; animation-delay: 0.3s").concat(important, "; }\n .ttp-pill-waveform .bar:nth-child(5) { height: 12px").concat(important, "; animation-delay: 0.4s").concat(important, "; }\n\n .ttp-pill-text {\n display: flex").concat(important, "; flex-direction: column").concat(important, "; gap: 2px").concat(important, ";\n max-width: 82px").concat(important, ";\n opacity: 1").concat(important, ";\n overflow: hidden").concat(important, ";\n white-space: nowrap").concat(important, ";\n transition: max-width 0.3s, opacity 0.2s").concat(important, ";\n }\n #text-chat-button.open .ttp-pill-text {\n max-width: 0").concat(important, ";\n opacity: 0").concat(important, ";\n }\n .ttp-pill-title {\n font-size: 13px").concat(important, "; font-weight: 600").concat(important, "; color: ").concat(pillTextColor, ";\n line-height: 1.25").concat(important, ";\n overflow: hidden").concat(important, ";\n text-overflow: ellipsis").concat(important, ";\n }\n .ttp-pill-status {\n display: flex").concat(important, "; align-items: center").concat(important, "; gap: 4px").concat(important, ";\n font-size: 11px").concat(important, "; color: ").concat(pillTextColor, "8c;\n line-height: 1.25").concat(important, ";\n }\n\n /* Online dot (inside pill status) */\n .ttp-launcher-dot {\n width: 7px").concat(important, "; height: 7px").concat(important, "; border-radius: 50%").concat(important, ";\n background: ").concat(pillDotColor, ";\n flex-shrink: 0").concat(important, ";\n animation: ttp-dot-pulse 2s ease-in-out infinite").concat(important, ";\n }\n @keyframes ttp-dot-pulse { 0%,100%{opacity:1} 50%{opacity:.4} }\n \n @media (max-width: 768px) {\n #text-chat-widget {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n transform: none !important;\n }\n \n #text-chat-button-container {\n width: auto !important;\n height: auto !important;\n }\n \n #text-chat-panel {\n position: fixed !important;\n right: 10px !important;\n left: auto !important;\n bottom: 92px !important; /* 56px button + 20px gap + 16px footer */\n top: auto !important;\n width: 340px !important;\n max-width: calc(100vw - 20px) !important;\n height: auto !important;\n max-height: calc(100vh - 130px) !important;\n transform: none !important;\n margin: 0 !important;\n }\n \n #text-chat-panel .widget-header {\n padding: 10px 14px").concat(important, ";\n min-height: 56px").concat(important, ";\n }\n \n #text-chat-panel .header-title {\n font-size: 15px").concat(important, ";\n }\n \n #text-chat-panel .header-icon {\n width: 40px").concat(important, ";\n height: 40px").concat(important, ";\n min-width: 40px").concat(important, ";\n min-height: 40px").concat(important, ";\n }\n \n #text-chat-input {\n font-size: 16px !important; /* Prevents iOS zoom on focus */\n padding: 12px 16px !important;\n min-height: 48px !important;\n }\n \n #text-chat-send {\n min-width: 48px !important;\n min-height: 48px !important;\n width: 48px !important;\n height: 48px !important;\n }\n \n }\n \n @media (max-width: 480px) {\n #text-chat-widget {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n \n #text-chat-button-container {\n width: auto !important;\n height: auto !important;\n }\n \n #text-chat-panel {\n right: 8px !important;\n left: auto !important;\n bottom: 86px !important; /* 54px button + 20px gap + 12px footer */\n top: auto !important;\n width: 340px !important;\n max-width: calc(100vw - 16px) !important;\n height: auto !important;\n max-height: calc(100vh - 120px) !important;\n }\n \n #text-chat-panel .widget-header {\n padding: 8px 12px").concat(important, ";\n min-height: 52px").concat(important, ";\n }\n \n #text-chat-panel .header-title {\n font-size: 14px").concat(important, ";\n }\n \n }\n \n ").concat(anim.enableHover ? "\n #text-chat-button:hover {\n transform: translateY(-1px);\n }\n " : '', "\n \n /* Prompt Animation Keyframes */\n @keyframes widget-shimmer {\n 0% { transform: translateX(-100%); }\n 100% { transform: translateX(200%); }\n }\n \n @keyframes widget-ripple {\n 0% { transform: scale(1); opacity: 0.6; }\n 100% { transform: scale(2.5); opacity: 0; }\n }\n \n @keyframes widget-float {\n 0%, 100% { transform: translateX(-50%) translateY(0); }\n 50% { transform: translateX(-50%) translateY(-8px); }\n }\n \n @keyframes widget-bounce {\n 0%, 100% { transform: translateX(-50%) translateY(0); }\n 50% { transform: translateX(-50%) translateY(-10px); }\n }\n \n @keyframes widget-pulse-ring {\n 0% { transform: scale(1); opacity: 0.4; }\n 100% { transform: scale(1.8); opacity: 0; }\n }\n \n /* Prompt Bubble Container */\n #text-chat-button-container {\n position: fixed").concat(important, ";\n ").concat(this.config.position.vertical === 'bottom' ? "bottom: ".concat(((_this$config$position = this.config.position.offset) === null || _this$config$position === void 0 ? void 0 : _this$config$position.y) || 20, "px;") : "top: ".concat(((_this$config$position2 = this.config.position.offset) === null || _this$config$position2 === void 0 ? void 0 : _this$config$position2.y) || 20, "px;"), "\n ").concat(this.config.position.horizontal === 'right' ? "right: ".concat(((_this$config$position3 = this.config.position.offset) === null || _this$config$position3 === void 0 ? void 0 : _this$config$position3.x) || 20, "px;") : "left: ".concat(((_this$config$position4 = this.config.position.offset) === null || _this$config$position4 === void 0 ? void 0 : _this$config$position4.x) || 20, "px;"), "\n width: auto").concat(important, ";\n height: auto").concat(important, ";\n z-index: 10001").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n }\n \n @media (max-width: 768px) {\n #text-chat-button-container {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-button-container {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n /* Prompt Bubble Styles */\n .prompt-bubble {\n position: absolute").concat(important, ";\n z-index: 10002").concat(important, ";\n pointer-events: none").concat(important, ";\n white-space: nowrap").concat(important, ";\n }\n \n .prompt-bubble.top {\n bottom: calc(100% + 18px)").concat(important, ";\n left: 50%").concat(important, ";\n transform: translateX(-50%)").concat(important, ";\n }\n \n .prompt-bubble.left {\n right: calc(100% + 12px)").concat(important, ";\n top: 50%").concat(important, ";\n transform: translateY(-50%)").concat(important, ";\n }\n \n .prompt-bubble.right {\n left: calc(100% + 12px)").concat(important, ";\n top: 50%").concat(important, ";\n transform: translateY(-50%)").concat(important, ";\n }\n \n /* Ensure animations preserve horizontal centering for top position */\n .prompt-bubble.top.animation-bounce {\n animation: widget-bounce 1s ease-in-out infinite").concat(important, ";\n transform: translateX(-50%)").concat(important, "; /* Keep centering */\n }\n \n .prompt-bubble.top.animation-float {\n animation: widget-float 2s ease-in-out infinite").concat(important, ";\n transform: translateX(-50%)").concat(important, "; /* Keep centering */\n }\n \n .prompt-bubble.top.animation-pulse {\n animation: pulse 2s ease-in-out infinite").concat(important, ";\n transform: translateX(-50%)").concat(important, "; /* Keep centering */\n }\n \n .prompt-bubble-content {\n position: relative").concat(important, ";\n padding: 8px 16px").concat(important, ";\n border-radius: 20px").concat(important, ";\n font-weight: 500").concat(important, ";\n font-size: 14px").concat(important, ";\n box-shadow: 0 4px 15px rgba(124, 58, 237, 0.3)").concat(important, ";\n overflow: hidden").concat(important, ";\n }\n \n .prompt-bubble-shimmer {\n position: absolute").concat(important, ";\n inset: 0").concat(important, ";\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent)").concat(important, ";\n animation: widget-shimmer 2s infinite").concat(important, ";\n }\n \n .prompt-bubble.animation-bounce {\n animation: widget-bounce 1s ease-in-out infinite").concat(important, ";\n }\n \n .prompt-bubble.animation-pulse {\n animation: pulse 2s ease-in-out infinite").concat(important, ";\n }\n \n .prompt-bubble.animation-float {\n animation: widget-float 2s ease-in-out infinite").concat(important, ";\n }\n \n /* Ensure top-positioned bubbles maintain horizontal centering during animations */\n .prompt-bubble.top.animation-bounce,\n .prompt-bubble.top.animation-float,\n .prompt-bubble.top.animation-pulse {\n /* Animation keyframes already include translateX(-50%) for centering */\n }\n \n /* Prompt Bubble Arrow */\n .prompt-bubble-arrow {\n position: absolute").concat(important, ";\n width: 0").concat(important, ";\n height: 0").concat(important, ";\n border: 8px solid transparent").concat(important, ";\n }\n \n .prompt-bubble.top .prompt-bubble-arrow {\n top: 100%").concat(important, ";\n left: 50%").concat(important, ";\n transform: translateX(-50%)").concat(important, ";\n border-top-color: var(--prompt-bubble-bg-color, #7c3aed)").concat(important, ";\n border-bottom: none").concat(important, ";\n margin-left: 0").concat(important, ";\n }\n \n .prompt-bubble.left .prompt-bubble-arrow {\n left: 100%").concat(important, ";\n top: 50%").concat(important, ";\n transform: translateY(-50%)").concat(important, ";\n border-left-color: var(--prompt-bubble-bg-color, #7c3aed)").concat(important, ";\n border-right: none").concat(important, ";\n }\n \n .prompt-bubble.right .prompt-bubble-arrow {\n right: 100%").concat(important, ";\n top: 50%").concat(important, ";\n transform: translateY(-50%)").concat(important, ";\n border-right-color: var(--prompt-bubble-bg-color, #7c3aed)").concat(important, ";\n border-left: none").concat(important, ";\n }\n \n /* Pulse Rings */\n .prompt-pulse-rings {\n position: absolute").concat(important, ";\n inset: 0").concat(important, ";\n border-radius: 50%").concat(important, ";\n pointer-events: none").concat(important, ";\n }\n \n .prompt-pulse-ring {\n position: absolute").concat(important, ";\n inset: 0").concat(important, ";\n border-radius: 50%").concat(important, ";\n animation: widget-pulse-ring 2s ease-out infinite").concat(important, ";\n }\n \n .prompt-pulse-ring:nth-child(2) {\n animation-delay: 0.5s").concat(important, ";\n }\n \n /* Mobile adjustments for prompt bubble */\n @media (max-width: 768px) {\n .prompt-bubble-content {\n font-size: 12px").concat(important, ";\n padding: 6px 12px").concat(important, ";\n }\n \n .prompt-bubble.top {\n bottom: calc(100% + 14px)").concat(important, ";\n }\n \n .prompt-bubble.left,\n .prompt-bubble.right {\n display: none").concat(important, "; /* Hide side prompts on mobile */\n }\n }\n \n /* Shared panel chrome; size/position differ \u2014 desktop card vs mobile sheet (see @media blocks earlier) */\n #text-chat-panel {\n display: none").concat(important, ";\n position: fixed").concat(important, ";\n background: ").concat(panelBg, ";\n border-radius: 20px").concat(important, ";\n border: ").concat(panel.border || '1px solid rgba(255,255,255,0.08)', ";\n flex-direction: column").concat(important, ";\n overflow: hidden").concat(important, ";\n box-shadow: 0 24px 60px rgba(0,0,0,0.6)").concat(important, ";\n opacity: 0").concat(important, ";\n pointer-events: none").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n\n /* Desktop / tablet: floating 340px card anchored to launcher */\n @media (min-width: 769px) {\n #text-chat-panel {\n bottom: calc(54px + 24px)").concat(important, "; /* launcher pill + gap */\n ").concat(this.config.position.horizontal === 'right' ? 'right: 20px;' : 'left: 20px;', "\n width: 340px").concat(important, ";\n max-width: calc(100vw - 40px)").concat(important, ";\n height: auto").concat(important, ";\n max-height: calc(100vh - 100px)").concat(important, ";\n transform-origin: bottom right").concat(important, ";\n }\n /* Text chat: fixed height so the message list scrolls instead of growing the panel per message */\n #text-chat-panel:has(.text-interface.active) {\n height: min(520px, calc(100vh - 100px))").concat(important, ";\n }\n }\n \n #text-chat-panel.open {\n display: flex").concat(important, ";\n opacity: 1").concat(important, ";\n pointer-events: auto").concat(important, ";\n }\n\n /* Shell for panel inner */\n .widget-shell { width: 100%").concat(important, "; padding: 0").concat(important, "; border-radius: 20px").concat(important, "; background: transparent").concat(important, "; overflow: hidden").concat(important, "; display: flex").concat(important, "; flex-direction: column").concat(important, "; box-sizing: border-box").concat(important, "; flex: 1").concat(important, "; min-height: 0").concat(important, "; }\n .panel-inner { width: 100%").concat(important, "; background: ").concat(panelBg, "; border-radius: 20px").concat(important, "; border: none").concat(important, "; overflow: hidden").concat(important, "; display:flex").concat(important, "; flex-direction: column").concat(important, "; padding: 0").concat(important, "; box-sizing: border-box").concat(important, "; max-width: 100%").concat(important, "; flex: 1").concat(important, "; min-height: 0").concat(important, "; }\n\n #text-chat-panel .widget-container {\n width: 100%").concat(important, "; background: ").concat(panelBg, "; overflow: visible").concat(important, "; display: flex").concat(important, "; flex-direction: column").concat(important, "; border-radius: 20px").concat(important, "; flex: 1").concat(important, "; min-height: 0").concat(important, ";\n }\n\n /* Voice idle: one gradient for voice UI + footer (Powered by), not a flat strip under buttons */\n #text-chat-panel.open:has(").concat(voiceIdleVisibleSelector, ") {\n background: linear-gradient(160deg, ").concat(idleHeroGrad1, " 0%, ").concat(idleHeroGrad2, " 100%)").concat(important, ";\n }\n #text-chat-panel .widget-container:has(").concat(voiceIdleVisibleSelector, ") {\n background: linear-gradient(160deg, ").concat(idleHeroGrad1, " 0%, ").concat(idleHeroGrad2, " 100%)").concat(important, ";\n }\n\n /* Voice active call: same hero gradient as idle + mobile minimized bar */\n #text-chat-panel.open:has(").concat(voiceActiveVisibleSelector, ") {\n background: ").concat(callActivePanelBg).concat(important, ";\n }\n #text-chat-panel .widget-container:has(").concat(voiceActiveVisibleSelector, ") {\n background: ").concat(callActivePanelBg).concat(important, ";\n }\n #text-chat-panel.open:has(").concat(voiceActiveVisibleSelector, ") .widget-shell {\n background: ").concat(callActivePanelBg).concat(important, ";\n }\n #text-chat-panel.open:has(").concat(voiceActiveVisibleSelector, ") .panel-inner {\n background: ").concat(callActivePanelBg).concat(important, ";\n }\n #text-chat-panel.open:has(").concat(voiceActiveVisibleSelector, ") .ttp-footer {\n background: ").concat(callActivePanelBg).concat(important, ";\n }\n\n ").concat(textUsesVoiceTheme ? "\n /* Text chat with voice theme: same hero surface as voice idle; inner shells transparent */\n #text-chat-panel.open:has(.text-interface.active) {\n background: ".concat(callSurfaceGradient).concat(important, ";\n }\n #text-chat-panel.open:has(.text-interface.active) .widget-shell,\n #text-chat-panel.open:has(.text-interface.active) .panel-inner,\n #text-chat-panel.open:has(.text-interface.active) .widget-container {\n background: transparent").concat(important, ";\n }\n #text-chat-panel.open:has(.text-interface.active) .ttp-footer {\n background: transparent").concat(important, ";\n border-top-color: rgba(255,255,255,0.06)").concat(important, ";\n }\n #text-chat-panel.open:has(.text-interface.active) .ttp-footer span {\n color: rgba(255,255,255,0.35)").concat(important, ";\n }\n #text-chat-panel.open:has(.text-interface.active) .ttp-footer a {\n color: ").concat(footerLinkOnDarkHero).concat(important, ";\n }\n ") : '', "\n \n /* Voice may scroll as a whole; text chat keeps a fixed shell and scrolls #messagesContainer only */\n #text-chat-panel .widget-container > .voice-interface {\n flex: 1").concat(important, ";\n min-height: 0").concat(important, ";\n overflow-y: auto").concat(important, ";\n overflow-x: visible").concat(important, ";\n }\n #text-chat-panel .widget-container > .text-interface {\n flex: 1").concat(important, ";\n min-height: 0").concat(important, ";\n overflow: hidden").concat(important, ";\n overflow-x: visible").concat(important, ";\n }\n \n /* Header is removed in new design - hero serves as header */\n #text-chat-panel .widget-header { display: none").concat(important, "; }\n\n ").concat(showVoice ? this.voiceInterface.generateCSS() : '', "\n ").concat(showText ? this.textInterface.generateCSS() : '', "\n \n /* Footer Branding */\n .ttp-footer {\n padding: 6px 18px 10px").concat(important, ";\n border-top: 1px solid ").concat(footerBorderColor, ";\n display: flex").concat(important, ";\n justify-content: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n box-sizing: border-box").concat(important, ";\n background: transparent").concat(important, ";\n }\n .ttp-footer span { font-size: 10px").concat(important, "; color: ").concat(footerTextColor, "; }\n .ttp-footer a { color: ").concat(footerLinkColor, "; text-decoration: none").concat(important, "; font-weight: 600").concat(important, "; }\n .ttp-footer a:hover { opacity: 0.85").concat(important, "; }\n .ttp-footer a b { font-weight: 700").concat(important, "; }\n \n #text-chat-send-hint {\n text-align: center").concat(important, ";\n line-height: 1.4").concat(important, ";\n }\n \n .agent-thinking {\n font-style: italic").concat(important, ";\n color: #6B7280").concat(important, ";\n }\n\n /* ========================================\n MOBILE PILL BUTTON & LANDING OVERLAY\n Hidden on desktop, shown on mobile only\n ======================================== */\n .ttp-mobile-fab {\n display: none").concat(important, ";\n }\n\n .ttp-mobile-landing {\n display: none").concat(important, ";\n }\n\n @keyframes ttpWaveAnimation {\n 0%, 100% { transform: scaleY(0.4); }\n 50% { transform: scaleY(1); }\n }\n\n @keyframes ttpPulseAnimation {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n @media (max-width: 768px) {\n /* Hide desktop pill button on mobile */\n #text-chat-button {\n display: none !important;\n }\n\n /* Re-flow the container for pill shape */\n #text-chat-button-container {\n width: auto !important;\n height: auto !important;\n min-width: 0 !important;\n min-height: 0 !important;\n max-width: none !important;\n max-height: none !important;\n }\n\n /* Show mobile pill button */\n .ttp-mobile-fab {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 10px").concat(important, ";\n padding: 10px 16px 10px 12px").concat(important, ";\n background: ").concat(pillGradient, ";\n border: none").concat(important, ";\n border-radius: 24px").concat(important, ";\n cursor: pointer").concat(important, ";\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3)").concat(important, ";\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1)").concat(important, ";\n z-index: 1").concat(important, ";\n font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif").concat(important, ";\n touch-action: manipulation").concat(important, ";\n -webkit-tap-highlight-color: transparent").concat(important, ";\n }\n\n .ttp-mobile-fab:hover {\n transform: translateY(-3px)").concat(important, ";\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4)").concat(important, ";\n }\n\n .ttp-mobile-fab__icon-wrap {\n width: 36px").concat(important, ";\n height: 36px").concat(important, ";\n border-radius: 50%").concat(important, ";\n background: rgba(255, 255, 255, 0.15)").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n\n .ttp-mobile-fab__waveform {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 2px").concat(important, ";\n height: 16px").concat(important, ";\n }\n\n .ttp-mobile-fab__waveform .bar {\n width: 2px").concat(important, ";\n background: #fff").concat(important, ";\n border-radius: 1px").concat(important, ";\n animation: ttpWaveAnimation 0.8s ease-in-out infinite").concat(important, ";\n }\n\n .ttp-mobile-fab__waveform .bar:nth-child(1) { height: 5px").concat(important, "; animation-delay: 0s").concat(important, "; }\n .ttp-mobile-fab__waveform .bar:nth-child(2) { height: 10px").concat(important, "; animation-delay: 0.1s").concat(important, "; }\n .ttp-mobile-fab__waveform .bar:nth-child(3) { height: 16px").concat(important, "; animation-delay: 0.2s").concat(important, "; }\n .ttp-mobile-fab__waveform .bar:nth-child(4) { height: 8px").concat(important, "; animation-delay: 0.3s").concat(important, "; }\n .ttp-mobile-fab__waveform .bar:nth-child(5) { height: 12px").concat(important, "; animation-delay: 0.4s").concat(important, "; }\n\n .ttp-mobile-fab__label-wrap {\n display: flex").concat(important, ";\n flex-direction: column").concat(important, ";\n align-items: flex-start").concat(important, ";\n gap: 1px").concat(important, ";\n }\n\n .ttp-mobile-fab__label {\n color: ").concat(pillTextColor, ";\n font-size: 14px").concat(important, ";\n font-weight: 600").concat(important, ";\n line-height: 1.2").concat(important, ";\n }\n\n .ttp-mobile-fab__status {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 4px").concat(important, ";\n font-size: 11px").concat(important, ";\n color: ").concat(pillTextColor, "b3;\n line-height: 1.2").concat(important, ";\n }\n\n .ttp-mobile-fab__dot {\n width: 6px").concat(important, ";\n height: 6px").concat(important, ";\n background: ").concat(pillDotColor, ";\n border-radius: 50%").concat(important, ";\n box-shadow: 0 0 6px ").concat(pillDotColor, "99;\n }\n\n /* ---- Mobile Landing Overlay ---- */\n .ttp-mobile-landing.active {\n display: block").concat(important, ";\n position: fixed").concat(important, ";\n bottom: 16px").concat(important, ";\n left: 12px").concat(important, ";\n right: 12px").concat(important, ";\n background: ").concat(pillGradient, ";\n border-radius: 20px").concat(important, ";\n overflow: hidden").concat(important, ";\n box-shadow:\n 0 10px 40px rgba(0, 0, 0, 0.3),\n 0 0 0 1px rgba(255, 255, 255, 0.1) inset").concat(important, ";\n z-index: 10002").concat(important, ";\n font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif").concat(important, ";\n }\n\n .ttp-mobile-landing::before {\n content: ''").concat(important, ";\n position: absolute").concat(important, ";\n top: 0").concat(important, ";\n left: 0").concat(important, ";\n right: 0").concat(important, ";\n height: 1px").concat(important, ";\n background: linear-gradient(90deg,\n transparent 0%,\n rgba(255, 255, 255, 0.3) 50%,\n transparent 100%);\n }\n\n .ttp-mobile-landing__header {\n padding: 14px 16px").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 12px").concat(important, ";\n }\n\n .ttp-mobile-landing__avatar {\n width: 44px").concat(important, ";\n height: 44px").concat(important, ";\n border-radius: 12px").concat(important, ";\n background: rgba(255, 255, 255, 0.15)").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n\n .ttp-mobile-landing__waveform {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 3px").concat(important, ";\n height: 20px").concat(important, ";\n }\n\n .ttp-mobile-landing__waveform .bar {\n width: 3px").concat(important, ";\n background: rgba(255, 255, 255, 0.9)").concat(important, ";\n border-radius: 2px").concat(important, ";\n animation: ttpWaveAnimation 0.8s ease-in-out infinite").concat(important, ";\n }\n\n .ttp-mobile-landing__waveform .bar:nth-child(1) { height: 6px").concat(important, "; animation-delay: 0s").concat(important, "; }\n .ttp-mobile-landing__waveform .bar:nth-child(2) { height: 12px").concat(important, "; animation-delay: 0.1s").concat(important, "; }\n .ttp-mobile-landing__waveform .bar:nth-child(3) { height: 20px").concat(important, "; animation-delay: 0.2s").concat(important, "; }\n .ttp-mobile-landing__waveform .bar:nth-child(4) { height: 10px").concat(important, "; animation-delay: 0.3s").concat(important, "; }\n .ttp-mobile-landing__waveform .bar:nth-child(5) { height: 16px").concat(important, "; animation-delay: 0.4s").concat(important, "; }\n\n .ttp-mobile-landing__info {\n flex: 1").concat(important, ";\n min-width: 0").concat(important, ";\n }\n\n .ttp-mobile-landing__name {\n color: #fff").concat(important, ";\n font-size: 15px").concat(important, ";\n font-weight: 600").concat(important, ";\n margin-bottom: 2px").concat(important, ";\n }\n\n .ttp-mobile-landing__status {\n color: rgba(255, 255, 255, 0.7)").concat(important, ";\n font-size: 12px").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 5px").concat(important, ";\n }\n\n .ttp-mobile-landing__dot {\n width: 6px").concat(important, ";\n height: 6px").concat(important, ";\n background: ").concat(header.onlineIndicatorDotColor || '#10b981', ";\n border-radius: 50%").concat(important, ";\n animation: ttpPulseAnimation 2s ease-in-out infinite").concat(important, ";\n }\n\n .ttp-mobile-landing__close {\n width: 32px").concat(important, ";\n height: 32px").concat(important, ";\n border-radius: 10px").concat(important, ";\n background: rgba(255, 255, 255, 0.1)").concat(important, ";\n border: none").concat(important, ";\n color: rgba(255, 255, 255, 0.7)").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n cursor: pointer").concat(important, ";\n transition: all 0.2s").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n\n .ttp-mobile-landing__close:hover {\n background: rgba(255, 255, 255, 0.2)").concat(important, ";\n color: #fff").concat(important, ";\n }\n\n .ttp-mobile-landing__error {\n margin: 0 14px 10px").concat(important, ";\n padding: 10px 12px").concat(important, ";\n background: #fef2f2").concat(important, ";\n border: 1px solid #fecaca").concat(important, ";\n border-radius: 10px").concat(important, ";\n display: flex").concat(important, ";\n align-items: flex-start").concat(important, ";\n gap: 8px").concat(important, ";\n color: #b91c1c").concat(important, ";\n font-size: 13px").concat(important, ";\n line-height: 1.4").concat(important, ";\n }\n .ttp-mobile-landing__error svg {\n flex-shrink: 0").concat(important, ";\n margin-top: 1px").concat(important, ";\n stroke: #dc2626").concat(important, ";\n }\n\n .ttp-mobile-landing__actions {\n padding: 0 14px 14px").concat(important, ";\n display: flex").concat(important, ";\n gap: 10px").concat(important, ";\n }\n\n .ttp-mobile-landing__btn {\n flex: 1").concat(important, ";\n padding: 14px").concat(important, ";\n border-radius: 12px").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n font-family: inherit").concat(important, ";\n font-size: 14px").concat(important, ";\n font-weight: 600").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n gap: 10px").concat(important, ";\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1)").concat(important, ";\n touch-action: manipulation").concat(important, ";\n -webkit-tap-highlight-color: transparent").concat(important, ";\n }\n\n .ttp-mobile-landing__btn svg {\n width: 20px").concat(important, ";\n height: 20px").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n\n .ttp-mobile-landing__btn--call {\n background: #fff").concat(important, ";\n color: ").concat(header.backgroundColor || '#7C3AED', ";\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15)").concat(important, ";\n }\n\n .ttp-mobile-landing__btn--call:hover {\n transform: translateY(-2px)").concat(important, ";\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2)").concat(important, ";\n }\n\n .ttp-mobile-landing__btn--text {\n background: rgba(255, 255, 255, 0.15)").concat(important, ";\n color: #fff").concat(important, ";\n border: 1px solid rgba(255, 255, 255, 0.1)").concat(important, ";\n }\n\n .ttp-mobile-landing__btn--text:hover {\n background: rgba(255, 255, 255, 0.2)").concat(important, ";\n border-color: rgba(255, 255, 255, 0.2)").concat(important, ";\n }\n\n .ttp-mobile-landing__footer {\n padding: 10px 16px").concat(important, ";\n display: flex").concat(important, ";\n justify-content: center").concat(important, ";\n border-top: 1px solid rgba(255, 255, 255, 0.08)").concat(important, ";\n }\n\n .ttp-mobile-landing__powered {\n color: rgba(255, 255, 255, 0.4)").concat(important, ";\n font-size: 11px").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 4px").concat(important, ";\n }\n\n .ttp-mobile-landing__powered svg {\n width: 10px").concat(important, ";\n height: 10px").concat(important, ";\n color: #f59e0b").concat(important, ";\n }\n\n .ttp-mobile-landing__powered a {\n color: ").concat(footerLinkColor).concat(important, ";\n text-decoration: none").concat(important, ";\n }\n\n .ttp-mobile-landing__powered a:hover {\n opacity: 0.88").concat(important, ";\n }\n }\n ");
34023
+ return "\n /* Inline style display overrides - ensures JS show/hide works with !important CSS */\n #text-chat-panel [style*=\"display: none\"],\n #text-chat-panel [style*=\"display:none\"],\n #text-chat-button-container [style*=\"display: none\"],\n #text-chat-button-container [style*=\"display:none\"],\n #ttpMobileLanding[style*=\"display: none\"],\n #ttpMobileLanding[style*=\"display:none\"] {\n display: none !important;\n }\n #text-chat-panel [style*=\"display: flex\"],\n #text-chat-panel [style*=\"display:flex\"],\n #text-chat-button-container [style*=\"display: flex\"],\n #text-chat-button-container [style*=\"display:flex\"] {\n display: flex !important;\n }\n #text-chat-panel [style*=\"display: block\"],\n #text-chat-panel [style*=\"display:block\"] {\n display: block !important;\n }\n\n /* MOBILE FIRST - Default styles for all devices */\n #text-chat-widget {\n position: fixed !important;\n z-index: 10000".concat(important, ";\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif").concat(important, ";\n /* Mobile defaults */\n right: 10px").concat(important, ";\n bottom: 10px").concat(important, ";\n left: auto").concat(important, ";\n top: auto").concat(important, ";\n }\n \n /* Desktop positioning (only on larger screens) */\n @media (min-width: 769px) {\n #text-chat-widget {\n ").concat(positionStyles, "\n right: ").concat(this.config.position.horizontal === 'right' ? '20px' : 'auto').concat(important, ";\n left: ").concat(this.config.position.horizontal === 'left' ? '20px' : 'auto').concat(important, ";\n bottom: ").concat(this.config.position.vertical === 'bottom' ? '20px' : 'auto').concat(important, ";\n top: ").concat(this.config.position.vertical === 'top' ? '20px' : 'auto').concat(important, ";\n }\n }\n \n /* Mobile override (force mobile positioning) */\n @media (max-width: 768px) {\n #text-chat-widget {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n transform: none !important;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-widget {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n /* \u2500\u2500 Desktop Pill Launcher \u2500\u2500 */\n #text-chat-button {\n position: relative").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 8px").concat(important, ";\n padding-block: 10px").concat(important, ";\n padding-inline-start: 6px").concat(important, ";\n padding-inline-end: 12px").concat(important, ";\n z-index: 1").concat(important, ";\n background: ").concat(pillGradient, ";\n border: 1px solid rgba(139,92,246,0.15)").concat(important, ";\n border-radius: 999px").concat(important, ";\n box-shadow: 0 8px 32px rgba(0,0,0,0.25), 0 2px 8px rgba(0,0,0,0.15)").concat(important, ";\n cursor: pointer").concat(important, ";\n transition: box-shadow 0.25s, transform 0.2s, padding 0.3s, gap 0.3s").concat(important, ";\n font-family: inherit").concat(important, ";\n color: ").concat(pillTextColor, ";\n overflow: hidden").concat(important, ";\n box-sizing: border-box").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n #text-chat-button:hover {\n transform: translateY(-1px)").concat(important, ";\n box-shadow: 0 10px 40px rgba(0,0,0,0.35), 0 4px 12px rgba(0,0,0,0.2)").concat(important, ";\n }\n #text-chat-button.open {\n gap: 0px").concat(important, ";\n padding: 9px").concat(important, ";\n width: auto").concat(important, ";\n min-width: 0").concat(important, ";\n max-width: none").concat(important, ";\n }\n\n @media (min-width: 769px) {\n #text-chat-button {\n width: 158px").concat(important, ";\n min-width: 158px").concat(important, ";\n max-width: 158px").concat(important, ";\n }\n }\n\n .ttp-pill-icon {\n width: 36px").concat(important, "; height: 36px").concat(important, "; border-radius: 50%").concat(important, ";\n background: #ffffff").concat(important, ";\n display: flex").concat(important, "; align-items: center").concat(important, "; justify-content: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n .ttp-launcher-logo {\n width: 22px").concat(important, "; height: 22px").concat(important, "; border-radius: 50%").concat(important, ";\n object-fit: cover").concat(important, ";\n }\n\n /* Default pill launcher: same animated waveform as mobile FAB / landing */\n .ttp-pill-icon--wave {\n background: rgba(255, 255, 255, 0.15)").concat(important, ";\n }\n .ttp-pill-waveform {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 2px").concat(important, ";\n height: 16px").concat(important, ";\n }\n .ttp-pill-waveform .bar {\n width: 2px").concat(important, ";\n background: #fff").concat(important, ";\n border-radius: 1px").concat(important, ";\n animation: ttpWaveAnimation 0.8s ease-in-out infinite").concat(important, ";\n }\n .ttp-pill-waveform .bar:nth-child(1) { height: 5px").concat(important, "; animation-delay: 0s").concat(important, "; }\n .ttp-pill-waveform .bar:nth-child(2) { height: 10px").concat(important, "; animation-delay: 0.1s").concat(important, "; }\n .ttp-pill-waveform .bar:nth-child(3) { height: 16px").concat(important, "; animation-delay: 0.2s").concat(important, "; }\n .ttp-pill-waveform .bar:nth-child(4) { height: 8px").concat(important, "; animation-delay: 0.3s").concat(important, "; }\n .ttp-pill-waveform .bar:nth-child(5) { height: 12px").concat(important, "; animation-delay: 0.4s").concat(important, "; }\n\n .ttp-pill-text {\n display: flex").concat(important, "; flex-direction: column").concat(important, "; gap: 2px").concat(important, ";\n max-width: 82px").concat(important, ";\n opacity: 1").concat(important, ";\n overflow: hidden").concat(important, ";\n white-space: nowrap").concat(important, ";\n transition: max-width 0.3s, opacity 0.2s").concat(important, ";\n }\n #text-chat-button.open .ttp-pill-text {\n max-width: 0").concat(important, ";\n opacity: 0").concat(important, ";\n }\n .ttp-pill-title {\n font-size: 13px").concat(important, "; font-weight: 600").concat(important, "; color: ").concat(pillTextColor, ";\n line-height: 1.25").concat(important, ";\n overflow: hidden").concat(important, ";\n text-overflow: ellipsis").concat(important, ";\n }\n .ttp-pill-status {\n display: flex").concat(important, "; align-items: center").concat(important, "; gap: 4px").concat(important, ";\n font-size: 11px").concat(important, "; color: ").concat(pillTextColor, "8c;\n line-height: 1.25").concat(important, ";\n }\n\n /* Online dot (inside pill status) */\n .ttp-launcher-dot {\n width: 7px").concat(important, "; height: 7px").concat(important, "; border-radius: 50%").concat(important, ";\n background: ").concat(pillDotColor, ";\n flex-shrink: 0").concat(important, ";\n animation: ttp-dot-pulse 2s ease-in-out infinite").concat(important, ";\n }\n @keyframes ttp-dot-pulse { 0%,100%{opacity:1} 50%{opacity:.4} }\n \n @media (max-width: 768px) {\n #text-chat-widget {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n transform: none !important;\n }\n \n #text-chat-button-container {\n width: auto !important;\n height: auto !important;\n }\n \n #text-chat-panel {\n position: fixed !important;\n right: 10px !important;\n left: auto !important;\n bottom: 92px !important; /* 56px button + 20px gap + 16px footer */\n top: auto !important;\n width: 340px !important;\n max-width: calc(100vw - 20px) !important;\n height: auto !important;\n max-height: calc(100vh - 130px) !important;\n transform: none !important;\n margin: 0 !important;\n }\n \n #text-chat-panel .widget-header {\n padding: 10px 14px").concat(important, ";\n min-height: 56px").concat(important, ";\n }\n \n #text-chat-panel .header-title {\n font-size: 15px").concat(important, ";\n }\n \n #text-chat-panel .header-icon {\n width: 40px").concat(important, ";\n height: 40px").concat(important, ";\n min-width: 40px").concat(important, ";\n min-height: 40px").concat(important, ";\n }\n \n #text-chat-input {\n font-size: 16px !important; /* Prevents iOS zoom on focus */\n padding: 12px 16px !important;\n min-height: 48px !important;\n }\n \n #text-chat-send {\n min-width: 48px !important;\n min-height: 48px !important;\n width: 48px !important;\n height: 48px !important;\n }\n \n }\n \n @media (max-width: 480px) {\n #text-chat-widget {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n \n #text-chat-button-container {\n width: auto !important;\n height: auto !important;\n }\n \n #text-chat-panel {\n right: 8px !important;\n left: auto !important;\n bottom: 86px !important; /* 54px button + 20px gap + 12px footer */\n top: auto !important;\n width: 340px !important;\n max-width: calc(100vw - 16px) !important;\n height: auto !important;\n max-height: calc(100vh - 120px) !important;\n }\n \n #text-chat-panel .widget-header {\n padding: 8px 12px").concat(important, ";\n min-height: 52px").concat(important, ";\n }\n \n #text-chat-panel .header-title {\n font-size: 14px").concat(important, ";\n }\n \n }\n \n ").concat(anim.enableHover ? "\n #text-chat-button:hover {\n transform: translateY(-1px);\n }\n " : '', "\n \n /* Prompt Animation Keyframes */\n @keyframes widget-shimmer {\n 0% { transform: translateX(-100%); }\n 100% { transform: translateX(200%); }\n }\n \n @keyframes widget-ripple {\n 0% { transform: scale(1); opacity: 0.6; }\n 100% { transform: scale(2.5); opacity: 0; }\n }\n \n @keyframes widget-float {\n 0%, 100% { transform: translateX(-50%) translateY(0); }\n 50% { transform: translateX(-50%) translateY(-8px); }\n }\n \n @keyframes widget-bounce {\n 0%, 100% { transform: translateX(-50%) translateY(0); }\n 50% { transform: translateX(-50%) translateY(-10px); }\n }\n \n @keyframes widget-pulse-ring {\n 0% { transform: scale(1); opacity: 0.4; }\n 100% { transform: scale(1.8); opacity: 0; }\n }\n \n /* Prompt Bubble Container */\n #text-chat-button-container {\n position: fixed").concat(important, ";\n ").concat(this.config.position.vertical === 'bottom' ? "bottom: ".concat(((_this$config$position = this.config.position.offset) === null || _this$config$position === void 0 ? void 0 : _this$config$position.y) || 20, "px;") : "top: ".concat(((_this$config$position2 = this.config.position.offset) === null || _this$config$position2 === void 0 ? void 0 : _this$config$position2.y) || 20, "px;"), "\n ").concat(this.config.position.horizontal === 'right' ? "right: ".concat(((_this$config$position3 = this.config.position.offset) === null || _this$config$position3 === void 0 ? void 0 : _this$config$position3.x) || 20, "px;") : "left: ".concat(((_this$config$position4 = this.config.position.offset) === null || _this$config$position4 === void 0 ? void 0 : _this$config$position4.x) || 20, "px;"), "\n width: auto").concat(important, ";\n height: auto").concat(important, ";\n z-index: 10001").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n }\n \n @media (max-width: 768px) {\n #text-chat-button-container {\n right: 10px !important;\n bottom: 10px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n @media (max-width: 480px) {\n #text-chat-button-container {\n right: 8px !important;\n bottom: 8px !important;\n left: auto !important;\n top: auto !important;\n }\n }\n \n /* Prompt Bubble Styles */\n .prompt-bubble {\n position: absolute").concat(important, ";\n z-index: 10002").concat(important, ";\n pointer-events: none").concat(important, ";\n white-space: nowrap").concat(important, ";\n }\n \n .prompt-bubble.top {\n bottom: calc(100% + 18px)").concat(important, ";\n left: 50%").concat(important, ";\n transform: translateX(-50%)").concat(important, ";\n }\n \n .prompt-bubble.left {\n right: calc(100% + 12px)").concat(important, ";\n top: 50%").concat(important, ";\n transform: translateY(-50%)").concat(important, ";\n }\n \n .prompt-bubble.right {\n left: calc(100% + 12px)").concat(important, ";\n top: 50%").concat(important, ";\n transform: translateY(-50%)").concat(important, ";\n }\n \n /* Ensure animations preserve horizontal centering for top position */\n .prompt-bubble.top.animation-bounce {\n animation: widget-bounce 1s ease-in-out infinite").concat(important, ";\n transform: translateX(-50%)").concat(important, "; /* Keep centering */\n }\n \n .prompt-bubble.top.animation-float {\n animation: widget-float 2s ease-in-out infinite").concat(important, ";\n transform: translateX(-50%)").concat(important, "; /* Keep centering */\n }\n \n .prompt-bubble.top.animation-pulse {\n animation: pulse 2s ease-in-out infinite").concat(important, ";\n transform: translateX(-50%)").concat(important, "; /* Keep centering */\n }\n \n .prompt-bubble-content {\n position: relative").concat(important, ";\n padding: 8px 16px").concat(important, ";\n border-radius: 20px").concat(important, ";\n font-weight: 500").concat(important, ";\n font-size: 14px").concat(important, ";\n box-shadow: 0 4px 15px rgba(124, 58, 237, 0.3)").concat(important, ";\n overflow: hidden").concat(important, ";\n }\n \n .prompt-bubble-shimmer {\n position: absolute").concat(important, ";\n inset: 0").concat(important, ";\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent)").concat(important, ";\n animation: widget-shimmer 2s infinite").concat(important, ";\n }\n \n .prompt-bubble.animation-bounce {\n animation: widget-bounce 1s ease-in-out infinite").concat(important, ";\n }\n \n .prompt-bubble.animation-pulse {\n animation: pulse 2s ease-in-out infinite").concat(important, ";\n }\n \n .prompt-bubble.animation-float {\n animation: widget-float 2s ease-in-out infinite").concat(important, ";\n }\n \n /* Ensure top-positioned bubbles maintain horizontal centering during animations */\n .prompt-bubble.top.animation-bounce,\n .prompt-bubble.top.animation-float,\n .prompt-bubble.top.animation-pulse {\n /* Animation keyframes already include translateX(-50%) for centering */\n }\n \n /* Prompt Bubble Arrow */\n .prompt-bubble-arrow {\n position: absolute").concat(important, ";\n width: 0").concat(important, ";\n height: 0").concat(important, ";\n border: 8px solid transparent").concat(important, ";\n }\n \n .prompt-bubble.top .prompt-bubble-arrow {\n top: 100%").concat(important, ";\n left: 50%").concat(important, ";\n transform: translateX(-50%)").concat(important, ";\n border-top-color: var(--prompt-bubble-bg-color, #7c3aed)").concat(important, ";\n border-bottom: none").concat(important, ";\n margin-left: 0").concat(important, ";\n }\n \n .prompt-bubble.left .prompt-bubble-arrow {\n left: 100%").concat(important, ";\n top: 50%").concat(important, ";\n transform: translateY(-50%)").concat(important, ";\n border-left-color: var(--prompt-bubble-bg-color, #7c3aed)").concat(important, ";\n border-right: none").concat(important, ";\n }\n \n .prompt-bubble.right .prompt-bubble-arrow {\n right: 100%").concat(important, ";\n top: 50%").concat(important, ";\n transform: translateY(-50%)").concat(important, ";\n border-right-color: var(--prompt-bubble-bg-color, #7c3aed)").concat(important, ";\n border-left: none").concat(important, ";\n }\n \n /* Pulse Rings */\n .prompt-pulse-rings {\n position: absolute").concat(important, ";\n inset: 0").concat(important, ";\n border-radius: 50%").concat(important, ";\n pointer-events: none").concat(important, ";\n }\n \n .prompt-pulse-ring {\n position: absolute").concat(important, ";\n inset: 0").concat(important, ";\n border-radius: 50%").concat(important, ";\n animation: widget-pulse-ring 2s ease-out infinite").concat(important, ";\n }\n \n .prompt-pulse-ring:nth-child(2) {\n animation-delay: 0.5s").concat(important, ";\n }\n \n /* Mobile adjustments for prompt bubble */\n @media (max-width: 768px) {\n .prompt-bubble-content {\n font-size: 12px").concat(important, ";\n padding: 6px 12px").concat(important, ";\n }\n \n .prompt-bubble.top {\n bottom: calc(100% + 14px)").concat(important, ";\n }\n \n .prompt-bubble.left,\n .prompt-bubble.right {\n display: none").concat(important, "; /* Hide side prompts on mobile */\n }\n }\n \n /* Shared panel chrome; size/position differ \u2014 desktop card vs mobile sheet (see @media blocks earlier) */\n #text-chat-panel {\n display: none").concat(important, ";\n position: fixed").concat(important, ";\n background: ").concat(panelBg, ";\n border-radius: 20px").concat(important, ";\n border: ").concat(panel.border || '1px solid rgba(255,255,255,0.08)', ";\n flex-direction: column").concat(important, ";\n overflow: hidden").concat(important, ";\n box-shadow: 0 24px 60px rgba(0,0,0,0.6)").concat(important, ";\n opacity: 0").concat(important, ";\n pointer-events: none").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n\n /* Desktop / tablet: floating 340px card anchored to launcher */\n @media (min-width: 769px) {\n #text-chat-panel {\n bottom: calc(54px + 24px)").concat(important, "; /* launcher pill + gap */\n ").concat(this.config.position.horizontal === 'right' ? 'right: 20px;' : 'left: 20px;', "\n width: 340px").concat(important, ";\n max-width: calc(100vw - 40px)").concat(important, ";\n height: auto").concat(important, ";\n max-height: calc(100vh - 100px)").concat(important, ";\n transform-origin: bottom right").concat(important, ";\n }\n /* Text chat: fixed height so the message list scrolls instead of growing the panel per message */\n #text-chat-panel:has(.text-interface.active) {\n height: min(520px, calc(100vh - 100px))").concat(important, ";\n }\n }\n \n #text-chat-panel.open {\n display: flex").concat(important, ";\n opacity: 1").concat(important, ";\n pointer-events: auto").concat(important, ";\n }\n\n /* Shell for panel inner */\n .widget-shell { width: 100%").concat(important, "; padding: 0").concat(important, "; border-radius: 20px").concat(important, "; background: transparent").concat(important, "; overflow: hidden").concat(important, "; display: flex").concat(important, "; flex-direction: column").concat(important, "; box-sizing: border-box").concat(important, "; flex: 1").concat(important, "; min-height: 0").concat(important, "; }\n .panel-inner { width: 100%").concat(important, "; background: ").concat(panelBg, "; border-radius: 20px").concat(important, "; border: none").concat(important, "; overflow: hidden").concat(important, "; display:flex").concat(important, "; flex-direction: column").concat(important, "; padding: 0").concat(important, "; box-sizing: border-box").concat(important, "; max-width: 100%").concat(important, "; flex: 1").concat(important, "; min-height: 0").concat(important, "; }\n\n #text-chat-panel .widget-container {\n width: 100%").concat(important, "; background: ").concat(panelBg, "; overflow: visible").concat(important, "; display: flex").concat(important, "; flex-direction: column").concat(important, "; border-radius: 20px").concat(important, "; flex: 1").concat(important, "; min-height: 0").concat(important, ";\n }\n\n /* Voice idle: one gradient for voice UI + footer (Powered by), not a flat strip under buttons */\n #text-chat-panel.open:has(").concat(voiceIdleVisibleSelector, ") {\n background: linear-gradient(160deg, ").concat(idleHeroGrad1, " 0%, ").concat(idleHeroGrad2, " 100%)").concat(important, ";\n }\n #text-chat-panel .widget-container:has(").concat(voiceIdleVisibleSelector, ") {\n background: linear-gradient(160deg, ").concat(idleHeroGrad1, " 0%, ").concat(idleHeroGrad2, " 100%)").concat(important, ";\n }\n\n /* Voice active call: same hero gradient as idle + mobile minimized bar */\n #text-chat-panel.open:has(").concat(voiceActiveVisibleSelector, ") {\n background: ").concat(callActivePanelBg).concat(important, ";\n }\n #text-chat-panel .widget-container:has(").concat(voiceActiveVisibleSelector, ") {\n background: ").concat(callActivePanelBg).concat(important, ";\n }\n #text-chat-panel.open:has(").concat(voiceActiveVisibleSelector, ") .widget-shell {\n background: ").concat(callActivePanelBg).concat(important, ";\n }\n #text-chat-panel.open:has(").concat(voiceActiveVisibleSelector, ") .panel-inner {\n background: ").concat(callActivePanelBg).concat(important, ";\n }\n #text-chat-panel.open:has(").concat(voiceActiveVisibleSelector, ") .ttp-footer {\n background: ").concat(callActivePanelBg).concat(important, ";\n }\n\n ").concat(textUsesVoiceTheme ? "\n /* Text chat with voice theme: same hero surface as voice idle; inner shells transparent */\n #text-chat-panel.open:has(.text-interface.active) {\n background: ".concat(callSurfaceGradient).concat(important, ";\n }\n #text-chat-panel.open:has(.text-interface.active) .widget-shell,\n #text-chat-panel.open:has(.text-interface.active) .panel-inner,\n #text-chat-panel.open:has(.text-interface.active) .widget-container {\n background: transparent").concat(important, ";\n }\n #text-chat-panel.open:has(.text-interface.active) .ttp-footer {\n background: transparent").concat(important, ";\n border-top-color: rgba(255,255,255,0.06)").concat(important, ";\n }\n #text-chat-panel.open:has(.text-interface.active) .ttp-footer span {\n color: rgba(255,255,255,0.35)").concat(important, ";\n }\n #text-chat-panel.open:has(.text-interface.active) .ttp-footer a {\n color: ").concat(footerLinkOnDarkHero).concat(important, ";\n }\n ") : '', "\n \n /* Voice may scroll as a whole; text chat keeps a fixed shell and scrolls #messagesContainer only */\n #text-chat-panel .widget-container > .voice-interface {\n flex: 1").concat(important, ";\n min-height: 0").concat(important, ";\n overflow-y: auto").concat(important, ";\n overflow-x: visible").concat(important, ";\n }\n #text-chat-panel .widget-container > .text-interface {\n flex: 1").concat(important, ";\n min-height: 0").concat(important, ";\n overflow: hidden").concat(important, ";\n overflow-x: visible").concat(important, ";\n }\n \n /* Header is removed in new design - hero serves as header */\n #text-chat-panel .widget-header { display: none").concat(important, "; }\n\n ").concat(showVoice ? this.voiceInterface.generateCSS() : '', "\n ").concat(showText ? this.textInterface.generateCSS() : '', "\n \n /* Footer Branding */\n .ttp-footer {\n padding: 6px 18px 10px").concat(important, ";\n border-top: 1px solid ").concat(footerBorderColor, ";\n display: flex").concat(important, ";\n justify-content: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n box-sizing: border-box").concat(important, ";\n background: transparent").concat(important, ";\n }\n .ttp-footer span { font-size: 10px").concat(important, "; color: ").concat(footerTextColor, "; }\n .ttp-footer a { color: ").concat(footerLinkColor, "; text-decoration: none").concat(important, "; font-weight: 600").concat(important, "; }\n .ttp-footer a:hover { opacity: 0.85").concat(important, "; }\n .ttp-footer a b { font-weight: 700").concat(important, "; }\n \n #text-chat-send-hint {\n text-align: center").concat(important, ";\n line-height: 1.4").concat(important, ";\n }\n \n .agent-thinking {\n font-style: italic").concat(important, ";\n color: #6B7280").concat(important, ";\n }\n\n /* ========================================\n MOBILE PILL BUTTON & LANDING OVERLAY\n Hidden on desktop, shown on mobile only\n ======================================== */\n .ttp-mobile-fab {\n display: none").concat(important, ";\n }\n\n .ttp-mobile-landing {\n display: none").concat(important, ";\n }\n\n @keyframes ttpWaveAnimation {\n 0%, 100% { transform: scaleY(0.4); }\n 50% { transform: scaleY(1); }\n }\n\n @keyframes ttpPulseAnimation {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n @media (max-width: 768px) {\n /* Hide desktop pill button on mobile */\n #text-chat-button {\n display: none !important;\n }\n\n /* Re-flow the container for pill shape */\n #text-chat-button-container {\n width: auto !important;\n height: auto !important;\n min-width: 0 !important;\n min-height: 0 !important;\n max-width: none !important;\n max-height: none !important;\n }\n\n /* Show mobile pill button */\n .ttp-mobile-fab {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 10px").concat(important, ";\n padding: 10px 16px 10px 12px").concat(important, ";\n background: ").concat(pillGradient, ";\n border: none").concat(important, ";\n border-radius: 24px").concat(important, ";\n cursor: pointer").concat(important, ";\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3)").concat(important, ";\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1)").concat(important, ";\n z-index: 1").concat(important, ";\n font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif").concat(important, ";\n touch-action: manipulation").concat(important, ";\n -webkit-tap-highlight-color: transparent").concat(important, ";\n }\n\n .ttp-mobile-fab:hover {\n transform: translateY(-3px)").concat(important, ";\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4)").concat(important, ";\n }\n\n .ttp-mobile-fab__icon-wrap {\n width: 36px").concat(important, ";\n height: 36px").concat(important, ";\n border-radius: 50%").concat(important, ";\n background: rgba(255, 255, 255, 0.15)").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n\n .ttp-mobile-fab__waveform {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 2px").concat(important, ";\n height: 16px").concat(important, ";\n }\n\n .ttp-mobile-fab__waveform .bar {\n width: 2px").concat(important, ";\n background: #fff").concat(important, ";\n border-radius: 1px").concat(important, ";\n animation: ttpWaveAnimation 0.8s ease-in-out infinite").concat(important, ";\n }\n\n .ttp-mobile-fab__waveform .bar:nth-child(1) { height: 5px").concat(important, "; animation-delay: 0s").concat(important, "; }\n .ttp-mobile-fab__waveform .bar:nth-child(2) { height: 10px").concat(important, "; animation-delay: 0.1s").concat(important, "; }\n .ttp-mobile-fab__waveform .bar:nth-child(3) { height: 16px").concat(important, "; animation-delay: 0.2s").concat(important, "; }\n .ttp-mobile-fab__waveform .bar:nth-child(4) { height: 8px").concat(important, "; animation-delay: 0.3s").concat(important, "; }\n .ttp-mobile-fab__waveform .bar:nth-child(5) { height: 12px").concat(important, "; animation-delay: 0.4s").concat(important, "; }\n\n .ttp-mobile-fab__label-wrap {\n display: flex").concat(important, ";\n flex-direction: column").concat(important, ";\n align-items: flex-start").concat(important, ";\n gap: 1px").concat(important, ";\n }\n\n .ttp-mobile-fab__label {\n color: ").concat(pillTextColor, ";\n font-size: 14px").concat(important, ";\n font-weight: 600").concat(important, ";\n line-height: 1.2").concat(important, ";\n }\n\n .ttp-mobile-fab__status {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 4px").concat(important, ";\n font-size: 11px").concat(important, ";\n color: ").concat(pillTextColor, "b3;\n line-height: 1.2").concat(important, ";\n }\n\n .ttp-mobile-fab__dot {\n width: 6px").concat(important, ";\n height: 6px").concat(important, ";\n background: ").concat(pillDotColor, ";\n border-radius: 50%").concat(important, ";\n box-shadow: 0 0 6px ").concat(pillDotColor, "99;\n }\n\n /* ---- Mobile Landing Overlay ---- */\n .ttp-mobile-landing.active {\n display: block").concat(important, ";\n position: fixed").concat(important, ";\n bottom: 16px").concat(important, ";\n left: 12px").concat(important, ";\n right: 12px").concat(important, ";\n background: ").concat(pillGradient, ";\n border-radius: 20px").concat(important, ";\n overflow: hidden").concat(important, ";\n box-shadow:\n 0 10px 40px rgba(0, 0, 0, 0.3),\n 0 0 0 1px rgba(255, 255, 255, 0.1) inset").concat(important, ";\n z-index: 10002").concat(important, ";\n font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif").concat(important, ";\n }\n\n .ttp-mobile-landing::before {\n content: ''").concat(important, ";\n position: absolute").concat(important, ";\n top: 0").concat(important, ";\n left: 0").concat(important, ";\n right: 0").concat(important, ";\n height: 1px").concat(important, ";\n background: linear-gradient(90deg,\n transparent 0%,\n rgba(255, 255, 255, 0.3) 50%,\n transparent 100%);\n }\n\n .ttp-mobile-landing__header {\n padding: 14px 16px").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 12px").concat(important, ";\n }\n\n .ttp-mobile-landing__avatar {\n width: 44px").concat(important, ";\n height: 44px").concat(important, ";\n border-radius: 12px").concat(important, ";\n background: rgba(255, 255, 255, 0.15)").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n\n .ttp-mobile-landing__waveform {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 3px").concat(important, ";\n height: 20px").concat(important, ";\n }\n\n .ttp-mobile-landing__waveform .bar {\n width: 3px").concat(important, ";\n background: rgba(255, 255, 255, 0.9)").concat(important, ";\n border-radius: 2px").concat(important, ";\n animation: ttpWaveAnimation 0.8s ease-in-out infinite").concat(important, ";\n }\n\n .ttp-mobile-landing__waveform .bar:nth-child(1) { height: 6px").concat(important, "; animation-delay: 0s").concat(important, "; }\n .ttp-mobile-landing__waveform .bar:nth-child(2) { height: 12px").concat(important, "; animation-delay: 0.1s").concat(important, "; }\n .ttp-mobile-landing__waveform .bar:nth-child(3) { height: 20px").concat(important, "; animation-delay: 0.2s").concat(important, "; }\n .ttp-mobile-landing__waveform .bar:nth-child(4) { height: 10px").concat(important, "; animation-delay: 0.3s").concat(important, "; }\n .ttp-mobile-landing__waveform .bar:nth-child(5) { height: 16px").concat(important, "; animation-delay: 0.4s").concat(important, "; }\n\n .ttp-mobile-landing__info {\n flex: 1").concat(important, ";\n min-width: 0").concat(important, ";\n }\n\n .ttp-mobile-landing__name {\n color: #fff").concat(important, ";\n font-size: 15px").concat(important, ";\n font-weight: 600").concat(important, ";\n margin-bottom: 2px").concat(important, ";\n }\n\n .ttp-mobile-landing__status {\n color: rgba(255, 255, 255, 0.7)").concat(important, ";\n font-size: 12px").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 5px").concat(important, ";\n }\n\n .ttp-mobile-landing__dot {\n width: 6px").concat(important, ";\n height: 6px").concat(important, ";\n background: ").concat(header.onlineIndicatorDotColor || '#10b981', ";\n border-radius: 50%").concat(important, ";\n animation: ttpPulseAnimation 2s ease-in-out infinite").concat(important, ";\n }\n\n .ttp-mobile-landing__close {\n width: 32px").concat(important, ";\n height: 32px").concat(important, ";\n border-radius: 10px").concat(important, ";\n background: rgba(255, 255, 255, 0.1)").concat(important, ";\n border: none").concat(important, ";\n color: rgba(255, 255, 255, 0.7)").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n cursor: pointer").concat(important, ";\n transition: all 0.2s").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n\n .ttp-mobile-landing__close:hover {\n background: rgba(255, 255, 255, 0.2)").concat(important, ";\n color: #fff").concat(important, ";\n }\n\n .ttp-mobile-landing__error {\n margin: 0 14px 10px").concat(important, ";\n padding: 10px 12px").concat(important, ";\n background: #fef2f2").concat(important, ";\n border: 1px solid #fecaca").concat(important, ";\n border-radius: 10px").concat(important, ";\n display: flex").concat(important, ";\n align-items: flex-start").concat(important, ";\n gap: 8px").concat(important, ";\n color: #b91c1c").concat(important, ";\n font-size: 13px").concat(important, ";\n line-height: 1.4").concat(important, ";\n }\n .ttp-mobile-landing__error svg {\n flex-shrink: 0").concat(important, ";\n margin-top: 1px").concat(important, ";\n stroke: #dc2626").concat(important, ";\n }\n\n .ttp-mobile-landing__actions {\n padding: 0 14px 14px").concat(important, ";\n display: flex").concat(important, ";\n gap: 10px").concat(important, ";\n }\n\n .ttp-mobile-landing__btn {\n flex: 1").concat(important, ";\n padding: 14px").concat(important, ";\n border-radius: 12px").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n font-family: inherit").concat(important, ";\n font-size: 14px").concat(important, ";\n font-weight: 600").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n gap: 10px").concat(important, ";\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1)").concat(important, ";\n touch-action: manipulation").concat(important, ";\n -webkit-tap-highlight-color: transparent").concat(important, ";\n }\n\n .ttp-mobile-landing__btn svg {\n width: 20px").concat(important, ";\n height: 20px").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n\n .ttp-mobile-landing__btn--call {\n background: #fff").concat(important, ";\n color: ").concat(header.backgroundColor || '#7C3AED', ";\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15)").concat(important, ";\n }\n\n .ttp-mobile-landing__btn--call:hover {\n transform: translateY(-2px)").concat(important, ";\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2)").concat(important, ";\n }\n\n .ttp-mobile-landing__btn--text {\n background: rgba(255, 255, 255, 0.15)").concat(important, ";\n color: #fff").concat(important, ";\n border: 1px solid rgba(255, 255, 255, 0.1)").concat(important, ";\n }\n\n .ttp-mobile-landing__btn--text:hover {\n background: rgba(255, 255, 255, 0.2)").concat(important, ";\n border-color: rgba(255, 255, 255, 0.2)").concat(important, ";\n }\n\n .ttp-mobile-landing__footer {\n padding: 10px 16px").concat(important, ";\n display: flex").concat(important, ";\n justify-content: center").concat(important, ";\n border-top: 1px solid rgba(255, 255, 255, 0.08)").concat(important, ";\n }\n\n .ttp-mobile-landing__powered {\n color: rgba(255, 255, 255, 0.4)").concat(important, ";\n font-size: 11px").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 4px").concat(important, ";\n }\n\n .ttp-mobile-landing__powered svg {\n width: 10px").concat(important, ";\n height: 10px").concat(important, ";\n color: #f59e0b").concat(important, ";\n }\n\n .ttp-mobile-landing__powered a {\n color: ").concat(footerLinkColor).concat(important, ";\n text-decoration: none").concat(important, ";\n }\n\n .ttp-mobile-landing__powered a:hover {\n opacity: 0.88").concat(important, ";\n }\n }\n\n /* ========================================\n \"About\" info (\"i\") button + dialog\n ======================================== */\n .ttp-header-actions {\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 4px").concat(important, ";\n }\n .ttp-info-btn {\n background: none").concat(important, ";\n border: none").concat(important, ";\n cursor: pointer").concat(important, ";\n color: currentColor").concat(important, ";\n opacity: 0.7").concat(important, ";\n padding: 4px").concat(important, ";\n margin: 0").concat(important, ";\n display: inline-flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n border-radius: 50%").concat(important, ";\n line-height: 0").concat(important, ";\n transition: opacity 0.2s, background 0.2s").concat(important, ";\n flex-shrink: 0").concat(important, ";\n }\n .ttp-info-btn:hover {\n opacity: 1").concat(important, ";\n background: rgba(127,127,127,0.18)").concat(important, ";\n }\n .ttp-info-btn svg { display: block").concat(important, "; }\n /* Floating variant for views without a header row (mobile idle) */\n .ttp-info-btn--floating {\n position: absolute").concat(important, ";\n top: 10px").concat(important, ";\n inset-inline-end: 10px").concat(important, ";\n z-index: 5").concat(important, ";\n }\n .ttp-info-btn--onbadge {\n width: 22px").concat(important, ";\n height: 22px").concat(important, ";\n padding: 3px").concat(important, ";\n }\n .ttp-info-btn--onbadge svg { width: 13px").concat(important, "; height: 13px").concat(important, "; }\n ");
33479
34024
  }
33480
34025
  }, {
33481
34026
  key: "setupWidgetEvents",
33482
34027
  value: function setupWidgetEvents() {
33483
- var _this8 = this,
33484
- _this$config$behavior8;
34028
+ var _this9 = this,
34029
+ _this$config$behavior8,
34030
+ _this$config$position5;
33485
34031
  if (!this.shadowRoot) return;
33486
34032
 
33487
34033
  // Setup prompt animation visibility and interactions
@@ -33492,16 +34038,18 @@ var TTPChatWidget = /*#__PURE__*/function () {
33492
34038
  if (launcherBtn) {
33493
34039
  launcherBtn.onclick = function (e) {
33494
34040
  e.stopPropagation();
33495
- if (_this8.config.promptAnimation.hideAfterClick) {
33496
- _this8.hidePrompt();
34041
+ // A drag just ended — don't treat the trailing click as "open panel".
34042
+ if (_this9._suppressLauncherClick) return;
34043
+ if (_this9.config.promptAnimation.hideAfterClick) {
34044
+ _this9.hidePrompt();
33497
34045
  }
33498
- _this8.togglePanel();
34046
+ _this9.togglePanel();
33499
34047
  };
33500
34048
  }
33501
34049
  var closeBtn = this.shadowRoot.getElementById('closeBtn');
33502
34050
  if (closeBtn) {
33503
34051
  closeBtn.onclick = function () {
33504
- _this8._doTogglePanel();
34052
+ _this9._doTogglePanel();
33505
34053
  };
33506
34054
  }
33507
34055
 
@@ -33509,31 +34057,41 @@ var TTPChatWidget = /*#__PURE__*/function () {
33509
34057
  var closeIdle = this.shadowRoot.getElementById('ttpCloseIdle');
33510
34058
  if (closeIdle) {
33511
34059
  closeIdle.onclick = function () {
33512
- return _this8._doTogglePanel();
34060
+ return _this9._doTogglePanel();
33513
34061
  };
33514
34062
  }
33515
34063
  var closeActive = this.shadowRoot.getElementById('ttpCloseActive');
33516
34064
  if (closeActive) {
33517
34065
  closeActive.onclick = function () {
33518
- return _this8._doTogglePanel();
34066
+ return _this9._doTogglePanel();
33519
34067
  };
33520
34068
  }
33521
34069
 
34070
+ // "About" info buttons (present in every view: idle / active / compact /
34071
+ // mobile idle / mobile active / text top-bar). One shared dialog.
34072
+ this.shadowRoot.querySelectorAll('.ttp-info-btn').forEach(function (btn) {
34073
+ btn.onclick = function (e) {
34074
+ e.stopPropagation();
34075
+ e.preventDefault();
34076
+ _this9._openAboutModal();
34077
+ };
34078
+ });
34079
+
33522
34080
  // "Send a Message" button in idle view → switch to text interface
33523
34081
  var startTextBtn = this.shadowRoot.getElementById('startTextBtn');
33524
34082
  if (startTextBtn) {
33525
34083
  startTextBtn.onclick = function () {
33526
- return _this8.showText();
34084
+ return _this9.showText();
33527
34085
  };
33528
34086
  }
33529
34087
  var textUnifiedBackBtn = this.shadowRoot.getElementById('textUnifiedBackBtn');
33530
34088
  if (textUnifiedBackBtn) {
33531
34089
  textUnifiedBackBtn.onclick = function () {
33532
- var _this8$config$behavio;
33533
- var widgetMode = ((_this8$config$behavio = _this8.config.behavior) === null || _this8$config$behavio === void 0 ? void 0 : _this8$config$behavio.mode) || 'unified';
34090
+ var _this9$config$behavio;
34091
+ var widgetMode = ((_this9$config$behavio = _this9.config.behavior) === null || _this9$config$behavio === void 0 ? void 0 : _this9$config$behavio.mode) || 'unified';
33534
34092
  if (widgetMode !== 'unified') return;
33535
- _this8.showLanding();
33536
- _this8._openMobileCallTextLanding();
34093
+ _this9.showLanding();
34094
+ _this9._openMobileCallTextLanding();
33537
34095
  };
33538
34096
  }
33539
34097
 
@@ -33563,30 +34121,30 @@ var TTPChatWidget = /*#__PURE__*/function () {
33563
34121
  // Setup back button handler
33564
34122
  if (backBtn) {
33565
34123
  backBtn.onclick = function () {
33566
- var _this8$voiceInterface, _this8$voiceInterface3;
34124
+ var _this9$voiceInterface, _this9$voiceInterface3;
33567
34125
  // Check if we're showing a domain error — clear it and return to voice hero
33568
- if ((_this8$voiceInterface = _this8.voiceInterface) !== null && _this8$voiceInterface !== void 0 && _this8$voiceInterface.isShowingDomainError) {
33569
- var _this8$voiceInterface2;
34126
+ if ((_this9$voiceInterface = _this9.voiceInterface) !== null && _this9$voiceInterface !== void 0 && _this9$voiceInterface.isShowingDomainError) {
34127
+ var _this9$voiceInterface2;
33570
34128
  console.log('🔙 Back button clicked - clearing domain error and returning to voice hero');
33571
34129
  // Clear the domain error flag
33572
- _this8.voiceInterface.isShowingDomainError = false;
34130
+ _this9.voiceInterface.isShowingDomainError = false;
33573
34131
  // Reset the connecting state (this will restore normal UI)
33574
- if ((_this8$voiceInterface2 = _this8.voiceInterface) !== null && _this8$voiceInterface2 !== void 0 && _this8$voiceInterface2.resetConnectingState) {
33575
- _this8.voiceInterface.resetConnectingState();
34132
+ if ((_this9$voiceInterface2 = _this9.voiceInterface) !== null && _this9$voiceInterface2 !== void 0 && _this9$voiceInterface2.resetConnectingState) {
34133
+ _this9.voiceInterface.resetConnectingState();
33576
34134
  }
33577
- _this8.showLanding();
33578
- _this8._openMobileCallTextLanding();
34135
+ _this9.showLanding();
34136
+ _this9._openMobileCallTextLanding();
33579
34137
  return;
33580
34138
  }
33581
- if ((_this8$voiceInterface3 = _this8.voiceInterface) !== null && _this8$voiceInterface3 !== void 0 && _this8$voiceInterface3.isActive) {
34139
+ if ((_this9$voiceInterface3 = _this9.voiceInterface) !== null && _this9$voiceInterface3 !== void 0 && _this9$voiceInterface3.isActive) {
33582
34140
  // If call is active, show message inside widget instead of modal
33583
- _this8.showBackButtonWarning();
34141
+ _this9.showBackButtonWarning();
33584
34142
  } else if (window.innerWidth <= 768) {
33585
- _this8.showLanding();
33586
- _this8._openMobileCallTextLanding();
34143
+ _this9.showLanding();
34144
+ _this9._openMobileCallTextLanding();
33587
34145
  } else {
33588
34146
  // Desktop: return to voice hero (unified home)
33589
- _this8.showLanding();
34147
+ _this9.showLanding();
33590
34148
  }
33591
34149
  };
33592
34150
  }
@@ -33629,7 +34187,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
33629
34187
  // Setup header button handlers
33630
34188
  if (newChatBtn) {
33631
34189
  newChatBtn.onclick = function () {
33632
- return _this8.textInterface.startNewChat();
34190
+ return _this9.textInterface.startNewChat();
33633
34191
  };
33634
34192
  // Hide new chat on unified home (voice hero) initially
33635
34193
  if (isUnified && this.currentView !== 'text') {
@@ -33640,11 +34198,328 @@ var TTPChatWidget = /*#__PURE__*/function () {
33640
34198
  // Mobile floating button & landing overlay
33641
34199
  this.setupMobileWidgetEvents();
33642
34200
 
34201
+ // Optional: make the launcher pill / mobile FAB + the open panel draggable
34202
+ // (they move together as one unit). The minimized strip is wired separately
34203
+ // in _initEcommerceVoiceStrip() since it lives outside the shadow DOM.
34204
+ if ((_this$config$position5 = this.config.position) !== null && _this$config$position5 !== void 0 && _this$config$position5.draggable) {
34205
+ this.setupDraggableWidget();
34206
+ }
34207
+
33643
34208
  // Keyboard navigation
33644
34209
  if (this.config.accessibility.keyboardNavigation) {
33645
34210
  this.setupKeyboardNavigation();
33646
34211
  }
33647
34212
  }
34213
+
34214
+ /**
34215
+ * Page-scoped namespace for persisted drag positions (origin+path) so the same
34216
+ * widget on different routes keeps independent spots.
34217
+ */
34218
+ }, {
34219
+ key: "_dragScope",
34220
+ value: function _dragScope() {
34221
+ var scope = '';
34222
+ try {
34223
+ scope = window.location.origin + window.location.pathname;
34224
+ } catch (e) {/* sandboxed */}
34225
+ return scope;
34226
+ }
34227
+
34228
+ /** localStorage key for a dragged surface ('unit' = pill+panel, 'strip'). */
34229
+ }, {
34230
+ key: "_dragStorageKey",
34231
+ value: function _dragStorageKey(kind) {
34232
+ return 'ttp-widget-' + kind + '-pos::' + this._dragScope();
34233
+ }
34234
+
34235
+ /**
34236
+ * The launcher pill and the chat panel move together as one unit. They share a
34237
+ * single translate offset (`this._unitOffset`) so dragging either one (the pill,
34238
+ * the mobile FAB, or a panel header) moves both — they never split apart.
34239
+ */
34240
+ }, {
34241
+ key: "_unitEls",
34242
+ value: function _unitEls() {
34243
+ var root = this.shadowRoot;
34244
+ if (!root) return [];
34245
+ return [root.getElementById('text-chat-button-container'), root.getElementById('text-chat-panel')].filter(Boolean);
34246
+ }
34247
+
34248
+ /** Apply the shared unit offset as a translate transform to both elements. */
34249
+ }, {
34250
+ key: "_applyUnitOffset",
34251
+ value: function _applyUnitOffset() {
34252
+ var o = this._unitOffset || (this._unitOffset = {
34253
+ x: 0,
34254
+ y: 0
34255
+ });
34256
+ var value = "translate(".concat(o.x, "px, ").concat(o.y, "px)");
34257
+ this._unitEls().forEach(function (el) {
34258
+ return el.style.setProperty('transform', value, 'important');
34259
+ });
34260
+ }
34261
+
34262
+ /**
34263
+ * Base (offset-0) viewport rects of the currently-visible unit elements. The
34264
+ * applied transform equals `this._unitOffset`, so subtracting it recovers the
34265
+ * anchored position. Hidden elements (display:none → 0×0) are skipped.
34266
+ */
34267
+ }, {
34268
+ key: "_unitBaseRects",
34269
+ value: function _unitBaseRects() {
34270
+ var o = this._unitOffset || {
34271
+ x: 0,
34272
+ y: 0
34273
+ };
34274
+ var rects = [];
34275
+ this._unitEls().forEach(function (el) {
34276
+ var r = el.getBoundingClientRect();
34277
+ if (!r.width && !r.height) return; // hidden
34278
+ rects.push({
34279
+ left: r.left - o.x,
34280
+ top: r.top - o.y,
34281
+ right: r.right - o.x,
34282
+ bottom: r.bottom - o.y
34283
+ });
34284
+ });
34285
+ return rects;
34286
+ }
34287
+
34288
+ /**
34289
+ * Clamp a desired unit offset so every visible unit element stays within the
34290
+ * viewport (8px margin). Constraints from all visible elements are intersected.
34291
+ */
34292
+ }, {
34293
+ key: "_clampUnitOffset",
34294
+ value: function _clampUnitOffset(x, y, bases) {
34295
+ var margin = 8;
34296
+ var vw = window.innerWidth,
34297
+ vh = window.innerHeight;
34298
+ var minX = -Infinity,
34299
+ maxX = Infinity,
34300
+ minY = -Infinity,
34301
+ maxY = Infinity;
34302
+ bases.forEach(function (b) {
34303
+ minX = Math.max(minX, margin - b.left);
34304
+ maxX = Math.min(maxX, vw - margin - b.right);
34305
+ minY = Math.max(minY, margin - b.top);
34306
+ maxY = Math.min(maxY, vh - margin - b.bottom);
34307
+ });
34308
+ // Element taller/wider than the viewport → keep its top/left edge visible.
34309
+ if (minX > maxX) maxX = minX;
34310
+ if (minY > maxY) maxY = minY;
34311
+ if (!bases.length) return {
34312
+ x: x,
34313
+ y: y
34314
+ };
34315
+ return {
34316
+ x: Math.min(Math.max(x, minX), maxX),
34317
+ y: Math.min(Math.max(y, minY), maxY)
34318
+ };
34319
+ }
34320
+
34321
+ /** Re-clamp the unit to the current viewport (e.g. on resize or panel open). */
34322
+ }, {
34323
+ key: "_reclampUnit",
34324
+ value: function _reclampUnit() {
34325
+ if (!this._unitOffset) return;
34326
+ var bases = this._unitBaseRects();
34327
+ if (!bases.length) return;
34328
+ this._unitOffset = this._clampUnitOffset(this._unitOffset.x, this._unitOffset.y, bases);
34329
+ this._applyUnitOffset();
34330
+ }
34331
+
34332
+ /** Restore a saved unit offset (deferred so elements can be measured at offset 0). */
34333
+ }, {
34334
+ key: "_restoreUnitOffset",
34335
+ value: function _restoreUnitOffset(key) {
34336
+ var _this0 = this;
34337
+ var saved;
34338
+ try {
34339
+ saved = JSON.parse(window.localStorage.getItem(key) || 'null');
34340
+ } catch (e) {
34341
+ return;
34342
+ }
34343
+ if (!saved || typeof saved.x !== 'number' || typeof saved.y !== 'number') return;
34344
+ requestAnimationFrame(function () {
34345
+ _this0._unitOffset = {
34346
+ x: 0,
34347
+ y: 0
34348
+ }; // ensure DOM matches before measuring
34349
+ var bases = _this0._unitBaseRects();
34350
+ _this0._unitOffset = _this0._clampUnitOffset(saved.x, saved.y, bases);
34351
+ _this0._applyUnitOffset();
34352
+ });
34353
+ }
34354
+
34355
+ /**
34356
+ * Wire a single handle to drag the whole pill+panel unit (shared offset).
34357
+ * @param {HTMLElement} handle
34358
+ * @param {Object} opts
34359
+ * @param {boolean} opts.persist
34360
+ * @param {string} opts.key localStorage key
34361
+ * @param {string} [opts.restCursor] idle cursor (default 'grab')
34362
+ * @param {string} [opts.excludeSelector] don't start a drag from these (e.g. buttons)
34363
+ * @param {Function} [opts.onMoved] called once after a real drag ends
34364
+ */
34365
+ }, {
34366
+ key: "_attachUnitDrag",
34367
+ value: function _attachUnitDrag(handle, opts) {
34368
+ var _this1 = this;
34369
+ var persist = opts.persist,
34370
+ key = opts.key,
34371
+ excludeSelector = opts.excludeSelector,
34372
+ onMoved = opts.onMoved;
34373
+ var restCursor = opts.restCursor || 'grab';
34374
+ handle.style.setProperty('touch-action', 'none');
34375
+ handle.style.setProperty('cursor', restCursor);
34376
+ var DRAG_THRESHOLD = 5; // px before a press becomes a drag
34377
+ var startX = 0,
34378
+ startY = 0,
34379
+ baseX = 0,
34380
+ baseY = 0,
34381
+ bases = [];
34382
+ var dragging = false,
34383
+ moved = false;
34384
+ var onMove = function onMove(e) {
34385
+ var dx = e.clientX - startX;
34386
+ var dy = e.clientY - startY;
34387
+ if (!moved && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;
34388
+ moved = true;
34389
+ _this1._unitOffset = _this1._clampUnitOffset(baseX + dx, baseY + dy, bases);
34390
+ _this1._applyUnitOffset();
34391
+ };
34392
+ var _onUp = function onUp(e) {
34393
+ window.removeEventListener('pointermove', onMove);
34394
+ window.removeEventListener('pointerup', _onUp);
34395
+ handle.style.setProperty('cursor', restCursor);
34396
+ try {
34397
+ handle.releasePointerCapture(e.pointerId);
34398
+ } catch (err) {/* not captured */}
34399
+ dragging = false;
34400
+ if (moved) {
34401
+ if (typeof onMoved === 'function') onMoved();
34402
+ if (persist && key) {
34403
+ try {
34404
+ window.localStorage.setItem(key, JSON.stringify(_this1._unitOffset));
34405
+ } catch (err) {/* unavailable */}
34406
+ }
34407
+ }
34408
+ };
34409
+ var onDown = function onDown(e) {
34410
+ if (e.button != null && e.button !== 0) return; // primary / touch / pen only
34411
+ if (dragging) return;
34412
+ if (excludeSelector && e.target && e.target.closest && e.target.closest(excludeSelector)) return;
34413
+ dragging = true;
34414
+ moved = false;
34415
+ var o = _this1._unitOffset || (_this1._unitOffset = {
34416
+ x: 0,
34417
+ y: 0
34418
+ });
34419
+ startX = e.clientX;
34420
+ startY = e.clientY;
34421
+ baseX = o.x;
34422
+ baseY = o.y;
34423
+ bases = _this1._unitBaseRects();
34424
+ handle.style.setProperty('cursor', 'grabbing');
34425
+ try {
34426
+ handle.setPointerCapture(e.pointerId);
34427
+ } catch (err) {/* unsupported */}
34428
+ window.addEventListener('pointermove', onMove);
34429
+ window.addEventListener('pointerup', _onUp);
34430
+ };
34431
+ handle.addEventListener('pointerdown', onDown);
34432
+ }
34433
+
34434
+ /**
34435
+ * Make the launcher pill / mobile FAB AND the open chat panel draggable as one
34436
+ * unit — dragging any of them moves both, so they never split apart. Pill/FAB
34437
+ * drags suppress the trailing click (so the panel doesn't toggle); panel header
34438
+ * bars (.ttp-header / .ttp-call-topbar / .text-interface-top-bar) are drag
34439
+ * handles too, with their interactive controls (e.g. close) excluded.
34440
+ */
34441
+ }, {
34442
+ key: "setupDraggableWidget",
34443
+ value: function setupDraggableWidget() {
34444
+ var _this$config$position6,
34445
+ _this10 = this;
34446
+ var root = this.shadowRoot;
34447
+ if (!root) return;
34448
+ var container = root.getElementById('text-chat-button-container');
34449
+ var panel = root.getElementById('text-chat-panel');
34450
+ if (!container && !panel) return;
34451
+ this._unitOffset = this._unitOffset || {
34452
+ x: 0,
34453
+ y: 0
34454
+ };
34455
+ var persist = ((_this$config$position6 = this.config.position) === null || _this$config$position6 === void 0 ? void 0 : _this$config$position6.draggablePersist) !== false;
34456
+ var key = this._dragStorageKey('unit');
34457
+ if (persist) this._restoreUnitOffset(key);
34458
+
34459
+ // Launcher handles: pill + mobile FAB (suppress the click after a drag).
34460
+ [root.getElementById('text-chat-button'), root.querySelector('.ttp-mobile-fab')].filter(Boolean).forEach(function (h) {
34461
+ return _this10._attachUnitDrag(h, {
34462
+ persist: persist,
34463
+ key: key,
34464
+ restCursor: 'grab',
34465
+ onMoved: function onMoved() {
34466
+ _this10._suppressLauncherClick = true;
34467
+ setTimeout(function () {
34468
+ _this10._suppressLauncherClick = false;
34469
+ }, 0);
34470
+ }
34471
+ });
34472
+ });
34473
+
34474
+ // Panel handles: header bars across voice idle / active call / text views.
34475
+ if (panel) {
34476
+ ['.ttp-header', '.ttp-call-topbar', '.text-interface-top-bar'].map(function (sel) {
34477
+ return panel.querySelector(sel);
34478
+ }).filter(Boolean).forEach(function (h) {
34479
+ return _this10._attachUnitDrag(h, {
34480
+ persist: persist,
34481
+ key: key,
34482
+ restCursor: 'move',
34483
+ excludeSelector: 'button, a, input, textarea, select, [contenteditable]'
34484
+ });
34485
+ });
34486
+ }
34487
+
34488
+ // Re-apply any in-memory offset to freshly-rendered elements (re-render).
34489
+ this._applyUnitOffset();
34490
+ this._bindDragResize();
34491
+ }
34492
+
34493
+ /**
34494
+ * Make the desktop minimized voice strip (EcommerceVoiceStrip) draggable. The
34495
+ * strip lives outside the shadow DOM and owns its own drag logic; we just hand
34496
+ * it a persistence key (so it remembers its spot independently of the unit).
34497
+ */
34498
+ }, {
34499
+ key: "setupDraggableStrip",
34500
+ value: function setupDraggableStrip() {
34501
+ var _this$config$position7;
34502
+ var strip = this._ecommerceVoiceStrip;
34503
+ if (!strip || typeof strip.enableDrag !== 'function') return;
34504
+ strip.enableDrag({
34505
+ persist: ((_this$config$position7 = this.config.position) === null || _this$config$position7 === void 0 ? void 0 : _this$config$position7.draggablePersist) !== false,
34506
+ storageKey: this._dragStorageKey('strip')
34507
+ });
34508
+ }
34509
+
34510
+ /** Bind a single resize listener that re-clamps every dragged surface. */
34511
+ }, {
34512
+ key: "_bindDragResize",
34513
+ value: function _bindDragResize() {
34514
+ var _this11 = this;
34515
+ if (this._dragResizeBound) return;
34516
+ this._dragResizeBound = true;
34517
+ window.addEventListener('resize', function () {
34518
+ var _this11$_ecommerceVoi, _this11$_ecommerceVoi2;
34519
+ _this11._reclampUnit();
34520
+ (_this11$_ecommerceVoi = _this11._ecommerceVoiceStrip) === null || _this11$_ecommerceVoi === void 0 || (_this11$_ecommerceVoi2 = _this11$_ecommerceVoi.reclampDrag) === null || _this11$_ecommerceVoi2 === void 0 || _this11$_ecommerceVoi2.call(_this11$_ecommerceVoi);
34521
+ });
34522
+ }
33648
34523
  }, {
33649
34524
  key: "startNewChat",
33650
34525
  value: function startNewChat() {
@@ -33691,9 +34566,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
33691
34566
  }, {
33692
34567
  key: "_isDomainErrorVisible",
33693
34568
  value: function _isDomainErrorVisible() {
33694
- var _this$voiceInterface3, _this$voiceInterface4, _idleState$dataset;
33695
- if ((_this$voiceInterface3 = this.voiceInterface) !== null && _this$voiceInterface3 !== void 0 && _this$voiceInterface3.isShowingDomainError) return true;
33696
- var idleState = (_this$voiceInterface4 = this.voiceInterface) === null || _this$voiceInterface4 === void 0 || (_this$voiceInterface4 = _this$voiceInterface4.shadowRoot) === null || _this$voiceInterface4 === void 0 ? void 0 : _this$voiceInterface4.getElementById('voiceIdleState');
34569
+ var _this$voiceInterface4, _this$voiceInterface5, _idleState$dataset;
34570
+ if ((_this$voiceInterface4 = this.voiceInterface) !== null && _this$voiceInterface4 !== void 0 && _this$voiceInterface4.isShowingDomainError) return true;
34571
+ var idleState = (_this$voiceInterface5 = this.voiceInterface) === null || _this$voiceInterface5 === void 0 || (_this$voiceInterface5 = _this$voiceInterface5.shadowRoot) === null || _this$voiceInterface5 === void 0 ? void 0 : _this$voiceInterface5.getElementById('voiceIdleState');
33697
34572
  return idleState && ((_idleState$dataset = idleState.dataset) === null || _idleState$dataset === void 0 ? void 0 : _idleState$dataset.domainError) === 'true';
33698
34573
  }
33699
34574
  }, {
@@ -33721,7 +34596,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
33721
34596
  }, {
33722
34597
  key: "_openMobileCallTextLanding",
33723
34598
  value: function _openMobileCallTextLanding() {
33724
- var _this9 = this;
34599
+ var _this12 = this;
33725
34600
  if (!this.shadowRoot || window.innerWidth > 768) return;
33726
34601
  var mobileFab = this.shadowRoot.querySelector('.ttp-mobile-fab');
33727
34602
  var mobileLanding = this.shadowRoot.getElementById('ttpMobileLanding');
@@ -33732,30 +34607,30 @@ var TTPChatWidget = /*#__PURE__*/function () {
33732
34607
  // "Back" button: composedPath includes the panel but not #ttpMobileLanding, so it treated
33733
34608
  // the click as "outside" and closed the landing immediately (user only saw the FAB again).
33734
34609
  var open = function open() {
33735
- if (!_this9.shadowRoot || window.innerWidth > 768) return;
33736
- var panel = _this9.shadowRoot.getElementById('text-chat-panel');
34610
+ if (!_this12.shadowRoot || window.innerWidth > 768) return;
34611
+ var panel = _this12.shadowRoot.getElementById('text-chat-panel');
33737
34612
  if (panel) {
33738
34613
  panel.classList.remove('open');
33739
- _this9.isOpen = false;
34614
+ _this12.isOpen = false;
33740
34615
  }
33741
- if (_this9.config.promptAnimation.hideAfterClick) {
33742
- _this9.hidePrompt();
34616
+ if (_this12.config.promptAnimation.hideAfterClick) {
34617
+ _this12.hidePrompt();
33743
34618
  }
33744
- var errorBanner = _this9.shadowRoot.getElementById('ttpMobileLandingError');
34619
+ var errorBanner = _this12.shadowRoot.getElementById('ttpMobileLandingError');
33745
34620
  if (errorBanner) errorBanner.style.display = 'none';
33746
- var fab = _this9.shadowRoot.querySelector('.ttp-mobile-fab');
33747
- var landing = _this9.shadowRoot.getElementById('ttpMobileLanding');
34621
+ var fab = _this12.shadowRoot.querySelector('.ttp-mobile-fab');
34622
+ var landing = _this12.shadowRoot.getElementById('ttpMobileLanding');
33748
34623
  if (!fab || !landing) return;
33749
34624
  fab.style.display = 'none';
33750
34625
  landing.classList.add('active');
33751
- _this9.isMobileLandingOpen = true;
34626
+ _this12.isMobileLandingOpen = true;
33752
34627
  };
33753
34628
  queueMicrotask(open);
33754
34629
  }
33755
34630
  }, {
33756
34631
  key: "setupMobileWidgetEvents",
33757
34632
  value: function setupMobileWidgetEvents() {
33758
- var _this0 = this;
34633
+ var _this13 = this;
33759
34634
  if (!this.shadowRoot) return;
33760
34635
  var mobileFab = this.shadowRoot.querySelector('.ttp-mobile-fab');
33761
34636
  var mobileLanding = this.shadowRoot.getElementById('ttpMobileLanding');
@@ -33763,10 +34638,12 @@ var TTPChatWidget = /*#__PURE__*/function () {
33763
34638
  var closeMobileLanding = function closeMobileLanding() {
33764
34639
  mobileLanding.classList.remove('active');
33765
34640
  mobileFab.style.display = '';
33766
- _this0.isMobileLandingOpen = false;
34641
+ _this13.isMobileLandingOpen = false;
33767
34642
  };
33768
34643
  mobileFab.onclick = function () {
33769
- return _this0._openMobileCallTextLanding();
34644
+ // A drag just ended — don't open the landing on the trailing click.
34645
+ if (_this13._suppressLauncherClick) return;
34646
+ _this13._openMobileCallTextLanding();
33770
34647
  };
33771
34648
  var mobileClose = mobileLanding.querySelector('.ttp-mobile-landing__close');
33772
34649
  if (mobileClose) {
@@ -33776,18 +34653,18 @@ var TTPChatWidget = /*#__PURE__*/function () {
33776
34653
  if (mobileCallBtn) {
33777
34654
  mobileCallBtn.onclick = function () {
33778
34655
  closeMobileLanding();
33779
- var panel = _this0.shadowRoot.getElementById('text-chat-panel');
34656
+ var panel = _this13.shadowRoot.getElementById('text-chat-panel');
33780
34657
  if (panel) {
33781
34658
  panel.style.removeProperty('display');
33782
34659
  panel.classList.add('open');
33783
- _this0.isOpen = true;
34660
+ _this13.isOpen = true;
33784
34661
  }
33785
34662
  mobileFab.style.display = 'none';
33786
- _this0.showVoice();
34663
+ _this13.showVoice();
33787
34664
  // Landing already chose "Voice call" — start the call (mic permission / pre-prompt) instead of
33788
34665
  // leaving users on a second "Start Call" tap that feels broken.
33789
34666
  queueMicrotask(function () {
33790
- _this0.startVoiceCall().catch(function (err) {
34667
+ _this13.startVoiceCall().catch(function (err) {
33791
34668
  console.error('[TTPChatWidget] Mobile landing voice start failed:', err);
33792
34669
  });
33793
34670
  });
@@ -33797,20 +34674,20 @@ var TTPChatWidget = /*#__PURE__*/function () {
33797
34674
  if (mobileChatBtn) {
33798
34675
  mobileChatBtn.onclick = function () {
33799
34676
  closeMobileLanding();
33800
- var panel = _this0.shadowRoot.getElementById('text-chat-panel');
34677
+ var panel = _this13.shadowRoot.getElementById('text-chat-panel');
33801
34678
  if (panel) {
33802
34679
  panel.style.removeProperty('display');
33803
34680
  panel.classList.add('open');
33804
- _this0.isOpen = true;
34681
+ _this13.isOpen = true;
33805
34682
  }
33806
34683
  mobileFab.style.display = 'none';
33807
- _this0.showText();
34684
+ _this13.showText();
33808
34685
  };
33809
34686
  }
33810
34687
 
33811
34688
  // Close mobile landing on outside click
33812
34689
  document.addEventListener('click', function (e) {
33813
- if (!_this0.isMobileLandingOpen) return;
34690
+ if (!_this13.isMobileLandingOpen) return;
33814
34691
  var path = e.composedPath();
33815
34692
  if (!path.includes(mobileLanding) && !path.includes(mobileFab)) {
33816
34693
  closeMobileLanding();
@@ -33819,7 +34696,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
33819
34696
 
33820
34697
  // Close mobile landing on Escape
33821
34698
  document.addEventListener('keydown', function (e) {
33822
- if (e.key === 'Escape' && _this0.isMobileLandingOpen) {
34699
+ if (e.key === 'Escape' && _this13.isMobileLandingOpen) {
33823
34700
  closeMobileLanding();
33824
34701
  }
33825
34702
  });
@@ -33835,19 +34712,19 @@ var TTPChatWidget = /*#__PURE__*/function () {
33835
34712
  _prevIsMobile = nowMobile;
33836
34713
  if (nowMobile) {
33837
34714
  // Switched desktop → mobile: close desktop panel so only mobile FAB shows
33838
- if (_this0.isOpen) {
33839
- var _this0$shadowRoot, _this0$shadowRoot2;
33840
- var panel = (_this0$shadowRoot = _this0.shadowRoot) === null || _this0$shadowRoot === void 0 ? void 0 : _this0$shadowRoot.getElementById('text-chat-panel');
34715
+ if (_this13.isOpen) {
34716
+ var _this13$shadowRoot, _this13$shadowRoot2;
34717
+ var panel = (_this13$shadowRoot = _this13.shadowRoot) === null || _this13$shadowRoot === void 0 ? void 0 : _this13$shadowRoot.getElementById('text-chat-panel');
33841
34718
  if (panel) {
33842
34719
  panel.classList.remove('open');
33843
- _this0.isOpen = false;
34720
+ _this13.isOpen = false;
33844
34721
  }
33845
- var pill = (_this0$shadowRoot2 = _this0.shadowRoot) === null || _this0$shadowRoot2 === void 0 ? void 0 : _this0$shadowRoot2.getElementById('text-chat-button');
34722
+ var pill = (_this13$shadowRoot2 = _this13.shadowRoot) === null || _this13$shadowRoot2 === void 0 ? void 0 : _this13$shadowRoot2.getElementById('text-chat-button');
33846
34723
  if (pill) pill.classList.remove('open');
33847
34724
  }
33848
34725
  } else {
33849
34726
  // Switched mobile → desktop: close mobile landing/FAB so only desktop pill shows
33850
- if (_this0.isMobileLandingOpen) {
34727
+ if (_this13.isMobileLandingOpen) {
33851
34728
  closeMobileLanding();
33852
34729
  }
33853
34730
  // Ensure mobile FAB is reset to CSS-controlled state (media query hides it on desktop)
@@ -33859,10 +34736,10 @@ var TTPChatWidget = /*#__PURE__*/function () {
33859
34736
  }, {
33860
34737
  key: "setupKeyboardNavigation",
33861
34738
  value: function setupKeyboardNavigation() {
33862
- var _this1 = this;
34739
+ var _this14 = this;
33863
34740
  document.addEventListener('keydown', function (e) {
33864
- if (e.key === 'Escape' && _this1.isOpen) {
33865
- _this1.togglePanel();
34741
+ if (e.key === 'Escape' && _this14.isOpen) {
34742
+ _this14.togglePanel();
33866
34743
  }
33867
34744
  });
33868
34745
  }
@@ -33873,7 +34750,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
33873
34750
  }, {
33874
34751
  key: "setupPromptAnimation",
33875
34752
  value: function setupPromptAnimation() {
33876
- var _this10 = this;
34753
+ var _this15 = this;
33877
34754
  if (!this.shadowRoot) return;
33878
34755
  var promptConfig = this.config.promptAnimation || {};
33879
34756
  // Default to disabled if not specified (enabled === true means explicitly enabled)
@@ -33903,7 +34780,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
33903
34780
  // Auto-hide after configured seconds (only if widget is closed)
33904
34781
  if (!this.isOpen && promptConfig.hideAfterSeconds !== null && promptConfig.hideAfterSeconds > 0) {
33905
34782
  this.promptAutoHideTimer = setTimeout(function () {
33906
- _this10.hidePrompt();
34783
+ _this15.hidePrompt();
33907
34784
  }, promptConfig.hideAfterSeconds * 1000);
33908
34785
  }
33909
34786
 
@@ -33915,7 +34792,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
33915
34792
  // Wrap the callback to also hide pulse rings
33916
34793
  this.config.onConversationStart = function () {
33917
34794
  // Hide pulse rings when call starts
33918
- var pulseRings = _this10.shadowRoot.getElementById('prompt-pulse-rings');
34795
+ var pulseRings = _this15.shadowRoot.getElementById('prompt-pulse-rings');
33919
34796
  if (pulseRings) pulseRings.style.display = 'none';
33920
34797
 
33921
34798
  // Call original callback if it exists
@@ -33927,7 +34804,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
33927
34804
  // Also listen to recordingStarted event from SDK if available
33928
34805
  if (this.voiceInterface.sdk.voiceSDK) {
33929
34806
  this.voiceInterface.sdk.voiceSDK.on('recordingStarted', function () {
33930
- var pulseRings = _this10.shadowRoot.getElementById('prompt-pulse-rings');
34807
+ var pulseRings = _this15.shadowRoot.getElementById('prompt-pulse-rings');
33931
34808
  if (pulseRings) pulseRings.style.display = 'none';
33932
34809
  });
33933
34810
  }
@@ -33975,7 +34852,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
33975
34852
  }, {
33976
34853
  key: "showPrompt",
33977
34854
  value: function showPrompt() {
33978
- var _this11 = this;
34855
+ var _this16 = this;
33979
34856
  if (!this.shadowRoot) return;
33980
34857
  var promptConfig = this.config.promptAnimation || {};
33981
34858
  // Default to enabled if not specified
@@ -33995,8 +34872,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
33995
34872
  // Function to actually show the elements
33996
34873
  var doShow = function doShow() {
33997
34874
  // Show prompt bubble (check both shadow root and document for compatibility)
33998
- var promptBubble = _this11.shadowRoot.getElementById('prompt-bubble');
33999
- if (!promptBubble && _this11.shadowRoot !== document) {
34875
+ var promptBubble = _this16.shadowRoot.getElementById('prompt-bubble');
34876
+ if (!promptBubble && _this16.shadowRoot !== document) {
34000
34877
  promptBubble = document.getElementById('prompt-bubble');
34001
34878
  }
34002
34879
  if (promptBubble) {
@@ -34010,8 +34887,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
34010
34887
 
34011
34888
  // Show pulse rings if enabled
34012
34889
  if (promptConfig.showPulseRings !== false) {
34013
- var _pulseRings = _this11.shadowRoot.getElementById('prompt-pulse-rings');
34014
- if (!_pulseRings && _this11.shadowRoot !== document) {
34890
+ var _pulseRings = _this16.shadowRoot.getElementById('prompt-pulse-rings');
34891
+ if (!_pulseRings && _this16.shadowRoot !== document) {
34015
34892
  _pulseRings = document.getElementById('prompt-pulse-rings');
34016
34893
  }
34017
34894
  if (_pulseRings) {
@@ -34048,7 +34925,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34048
34925
  }
34049
34926
  // Start new timer
34050
34927
  this.promptAutoHideTimer = setTimeout(function () {
34051
- _this11.hidePrompt();
34928
+ _this16.hidePrompt();
34052
34929
  }, promptConfig.hideAfterSeconds * 1000);
34053
34930
  }
34054
34931
  }
@@ -34064,14 +34941,23 @@ var TTPChatWidget = /*#__PURE__*/function () {
34064
34941
  }, {
34065
34942
  key: "_doTogglePanel",
34066
34943
  value: function _doTogglePanel() {
34067
- var _this12 = this;
34944
+ var _this17 = this;
34068
34945
  if (!this.shadowRoot) return;
34069
34946
  this.isOpen = !this.isOpen;
34070
34947
  var panel = this.shadowRoot.getElementById('text-chat-panel');
34071
34948
  if (panel) {
34072
34949
  panel.classList.toggle('open');
34073
34950
  if (this.isOpen) {
34951
+ var _this$config$position8;
34074
34952
  panel.style.removeProperty('display');
34953
+ // The panel joins the dragged unit when it opens; re-clamp now that it
34954
+ // has real dimensions (it was hidden/zero-size while closed).
34955
+ if ((_this$config$position8 = this.config.position) !== null && _this$config$position8 !== void 0 && _this$config$position8.draggable) {
34956
+ this._applyUnitOffset();
34957
+ requestAnimationFrame(function () {
34958
+ return _this17._reclampUnit();
34959
+ });
34960
+ }
34075
34961
  }
34076
34962
  }
34077
34963
 
@@ -34086,7 +34972,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34086
34972
  // Panel is opening - hide prompt
34087
34973
  this.hidePrompt();
34088
34974
  setTimeout(function () {
34089
- var input = _this12.shadowRoot.getElementById('messageInput');
34975
+ var input = _this17.shadowRoot.getElementById('messageInput');
34090
34976
  if (input) input.focus();
34091
34977
  }, 100);
34092
34978
  } else {
@@ -34098,13 +34984,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
34098
34984
  // Panel is closing - show prompt (if enabled)
34099
34985
  this.showPrompt();
34100
34986
  setTimeout(function () {
34101
- var _this12$config$prompt;
34102
- var promptBubble = _this12.shadowRoot.getElementById('prompt-bubble');
34987
+ var _this17$config$prompt;
34988
+ var promptBubble = _this17.shadowRoot.getElementById('prompt-bubble');
34103
34989
  if (promptBubble && promptBubble.style.display === 'none') {
34104
34990
  promptBubble.style.display = 'block';
34105
34991
  }
34106
- var pulseRings = _this12.shadowRoot.getElementById('prompt-pulse-rings');
34107
- if (pulseRings && ((_this12$config$prompt = _this12.config.promptAnimation) === null || _this12$config$prompt === void 0 ? void 0 : _this12$config$prompt.showPulseRings) !== false) {
34992
+ var pulseRings = _this17.shadowRoot.getElementById('prompt-pulse-rings');
34993
+ if (pulseRings && ((_this17$config$prompt = _this17.config.promptAnimation) === null || _this17$config$prompt === void 0 ? void 0 : _this17$config$prompt.showPulseRings) !== false) {
34108
34994
  if (pulseRings.style.display === 'none') {
34109
34995
  pulseRings.style.display = 'block';
34110
34996
  }
@@ -34296,8 +35182,8 @@ var TTPChatWidget = /*#__PURE__*/function () {
34296
35182
  }, {
34297
35183
  key: "showBackButtonWarning",
34298
35184
  value: function showBackButtonWarning() {
34299
- var _this$voiceInterface5, _this$voiceInterface$;
34300
- if (!((_this$voiceInterface5 = this.voiceInterface) !== null && _this$voiceInterface5 !== void 0 && _this$voiceInterface5.isActive)) return;
35185
+ var _this$voiceInterface6, _this$voiceInterface$;
35186
+ if (!((_this$voiceInterface6 = this.voiceInterface) !== null && _this$voiceInterface6 !== void 0 && _this$voiceInterface6.isActive)) return;
34301
35187
 
34302
35188
  // Show warning message in the voice interface
34303
35189
  if ((_this$voiceInterface$ = this.voiceInterface.desktop) !== null && _this$voiceInterface$ !== void 0 && _this$voiceInterface$.showBackButtonWarning) {
@@ -34378,7 +35264,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34378
35264
  _this$config$voice0,
34379
35265
  _this$config$voice1,
34380
35266
  _this$config$voice10,
34381
- _this13 = this;
35267
+ _this18 = this;
34382
35268
  // Deep merge nested objects
34383
35269
  var mergedConfig = _objectSpread({}, this.config);
34384
35270
 
@@ -34596,20 +35482,20 @@ var TTPChatWidget = /*#__PURE__*/function () {
34596
35482
  outputBitDepth: ((_this$config$voice10 = this.config.voice) === null || _this$config$voice10 === void 0 ? void 0 : _this$config$voice10.outputBitDepth) || 16,
34597
35483
  desktopMinimizedEcommerceStripUi: desktopMinimizedEcommerceStripUi,
34598
35484
  onDesktopMinimizedStripCallStart: desktopMinimizedEcommerceStripUi ? function () {
34599
- var _this13$_ecommerceVoi;
34600
- (_this13$_ecommerceVoi = _this13._ecommerceVoiceStrip) === null || _this13$_ecommerceVoi === void 0 || _this13$_ecommerceVoi.show();
34601
- if (_this13.isOpen) _this13.minimize();
34602
- _this13._setPanelFooterVisible(false);
34603
- _this13._setEcommerceStripLauncherVisible(false);
35485
+ var _this18$_ecommerceVoi;
35486
+ (_this18$_ecommerceVoi = _this18._ecommerceVoiceStrip) === null || _this18$_ecommerceVoi === void 0 || _this18$_ecommerceVoi.show();
35487
+ if (_this18.isOpen) _this18.minimize();
35488
+ _this18._setPanelFooterVisible(false);
35489
+ _this18._setEcommerceStripLauncherVisible(false);
34604
35490
  } : undefined,
34605
35491
  onDesktopMinimizedStripLauncherRestore: desktopMinimizedEcommerceStripUi ? function () {
34606
- return _this13._restoreDesktopEcommerceStripChrome();
35492
+ return _this18._restoreDesktopEcommerceStripChrome();
34607
35493
  } : undefined,
34608
35494
  // Pass event callbacks to voice interface (forward only; panel is not auto-minimized on call start)
34609
35495
  onConversationStart: function onConversationStart() {
34610
- if (typeof _this13.config.onConversationStart === 'function') {
34611
- var _this13$config;
34612
- (_this13$config = _this13.config).onConversationStart.apply(_this13$config, arguments);
35496
+ if (typeof _this18.config.onConversationStart === 'function') {
35497
+ var _this18$config;
35498
+ (_this18$config = _this18.config).onConversationStart.apply(_this18$config, arguments);
34613
35499
  }
34614
35500
  },
34615
35501
  onConversationEnd: this.config.onConversationEnd,
@@ -34618,48 +35504,48 @@ var TTPChatWidget = /*#__PURE__*/function () {
34618
35504
  onAudioStoppedPlaying: this.config.onAudioStoppedPlaying,
34619
35505
  onSubtitleDisplay: this.config.onSubtitleDisplay,
34620
35506
  onCallEnd: function onCallEnd() {
34621
- var _this13$config$behavi;
34622
- if (_this13._isDomainErrorVisible()) return;
34623
- var widgetMode = ((_this13$config$behavi = _this13.config.behavior) === null || _this13$config$behavi === void 0 ? void 0 : _this13$config$behavi.mode) || 'unified';
35507
+ var _this18$config$behavi;
35508
+ if (_this18._isDomainErrorVisible()) return;
35509
+ var widgetMode = ((_this18$config$behavi = _this18.config.behavior) === null || _this18$config$behavi === void 0 ? void 0 : _this18$config$behavi.mode) || 'unified';
34624
35510
  if (widgetMode === 'unified') {
34625
35511
  if (window.innerWidth <= 768) {
34626
- var _this13$shadowRoot, _this13$shadowRoot2;
34627
- var panel = (_this13$shadowRoot = _this13.shadowRoot) === null || _this13$shadowRoot === void 0 ? void 0 : _this13$shadowRoot.getElementById('text-chat-panel');
35512
+ var _this18$shadowRoot, _this18$shadowRoot2;
35513
+ var panel = (_this18$shadowRoot = _this18.shadowRoot) === null || _this18$shadowRoot === void 0 ? void 0 : _this18$shadowRoot.getElementById('text-chat-panel');
34628
35514
  if (panel) {
34629
35515
  panel.classList.remove('open');
34630
- _this13.isOpen = false;
35516
+ _this18.isOpen = false;
34631
35517
  }
34632
- var fab = (_this13$shadowRoot2 = _this13.shadowRoot) === null || _this13$shadowRoot2 === void 0 ? void 0 : _this13$shadowRoot2.querySelector('.ttp-mobile-fab');
35518
+ var fab = (_this18$shadowRoot2 = _this18.shadowRoot) === null || _this18$shadowRoot2 === void 0 ? void 0 : _this18$shadowRoot2.querySelector('.ttp-mobile-fab');
34633
35519
  if (fab) fab.style.display = '';
34634
35520
  }
34635
- _this13.showLanding();
35521
+ _this18.showLanding();
34636
35522
  }
34637
35523
  },
34638
35524
  onShowVoice: function onShowVoice() {
34639
- _this13.showVoice();
35525
+ _this18.showVoice();
34640
35526
  },
34641
35527
  onShowLanding: function onShowLanding() {
34642
- _this13.showLanding();
35528
+ _this18.showLanding();
34643
35529
  },
34644
35530
  onDisclaimerDeclined: function onDisclaimerDeclined() {
34645
- var _this13$config$behavi2;
34646
- if (_this13._isDomainErrorVisible()) return;
34647
- var widgetMode = ((_this13$config$behavi2 = _this13.config.behavior) === null || _this13$config$behavi2 === void 0 ? void 0 : _this13$config$behavi2.mode) || 'unified';
35531
+ var _this18$config$behavi2;
35532
+ if (_this18._isDomainErrorVisible()) return;
35533
+ var widgetMode = ((_this18$config$behavi2 = _this18.config.behavior) === null || _this18$config$behavi2 === void 0 ? void 0 : _this18$config$behavi2.mode) || 'unified';
34648
35534
  if (widgetMode === 'voice-only') {
34649
- _this13.showVoice();
35535
+ _this18.showVoice();
34650
35536
  return;
34651
35537
  }
34652
35538
  if (widgetMode !== 'unified') return;
34653
35539
  if (window.innerWidth <= 768) {
34654
- var _this13$shadowRoot3;
34655
- var panel = (_this13$shadowRoot3 = _this13.shadowRoot) === null || _this13$shadowRoot3 === void 0 ? void 0 : _this13$shadowRoot3.getElementById('text-chat-panel');
35540
+ var _this18$shadowRoot3;
35541
+ var panel = (_this18$shadowRoot3 = _this18.shadowRoot) === null || _this18$shadowRoot3 === void 0 ? void 0 : _this18$shadowRoot3.getElementById('text-chat-panel');
34656
35542
  if (panel) {
34657
35543
  panel.classList.remove('open');
34658
- _this13.isOpen = false;
35544
+ _this18.isOpen = false;
34659
35545
  }
34660
- _this13._openMobileCallTextLanding();
35546
+ _this18._openMobileCallTextLanding();
34661
35547
  } else {
34662
- _this13.showLanding();
35548
+ _this18.showLanding();
34663
35549
  }
34664
35550
  },
34665
35551
  direction: this.getEffectiveTextDirection()
@@ -34706,8 +35592,9 @@ var TTPChatWidget = /*#__PURE__*/function () {
34706
35592
  }, {
34707
35593
  key: "_initEcommerceVoiceStrip",
34708
35594
  value: function _initEcommerceVoiceStrip() {
34709
- var _this14 = this,
34710
- _this$voiceInterface6;
35595
+ var _this19 = this,
35596
+ _this$config$position9,
35597
+ _this$voiceInterface7;
34711
35598
  var flavor = this.config.flavor && _typeof(this.config.flavor) === 'object' ? this.config.flavor : null;
34712
35599
  if (window.innerWidth <= 768) return;
34713
35600
  if (!flavor || flavor.callView !== 'minimized') return;
@@ -34728,31 +35615,34 @@ var TTPChatWidget = /*#__PURE__*/function () {
34728
35615
  endCallButtonColor: v.endCallButtonColor || '#ef4444'
34729
35616
  },
34730
35617
  getVoiceUiState: function getVoiceUiState() {
34731
- var _this14$voiceInterfac, _this14$voiceInterfac2, _this14$voiceInterfac3;
35618
+ var _this19$voiceInterfac, _this19$voiceInterfac2, _this19$voiceInterfac3;
34732
35619
  return {
34733
- isMicMuted: !!((_this14$voiceInterfac = _this14.voiceInterface) !== null && _this14$voiceInterfac !== void 0 && _this14$voiceInterfac.isMicMuted),
34734
- isPaused: !!((_this14$voiceInterfac2 = _this14.voiceInterface) !== null && _this14$voiceInterfac2 !== void 0 && _this14$voiceInterfac2.isPaused),
34735
- isSpeakerMuted: !!((_this14$voiceInterfac3 = _this14.voiceInterface) !== null && _this14$voiceInterfac3 !== void 0 && _this14$voiceInterfac3.isSpeakerMuted)
35620
+ isMicMuted: !!((_this19$voiceInterfac = _this19.voiceInterface) !== null && _this19$voiceInterfac !== void 0 && _this19$voiceInterfac.isMicMuted),
35621
+ isPaused: !!((_this19$voiceInterfac2 = _this19.voiceInterface) !== null && _this19$voiceInterfac2 !== void 0 && _this19$voiceInterfac2.isPaused),
35622
+ isSpeakerMuted: !!((_this19$voiceInterfac3 = _this19.voiceInterface) !== null && _this19$voiceInterfac3 !== void 0 && _this19$voiceInterfac3.isSpeakerMuted)
34736
35623
  };
34737
35624
  },
35625
+ onAbout: function onAbout() {
35626
+ return _this19._openAboutModal();
35627
+ },
34738
35628
  onHangup: function onHangup() {
34739
- if (_this14.voiceInterface) _this14.voiceInterface.endVoiceCall();
35629
+ if (_this19.voiceInterface) _this19.voiceInterface.endVoiceCall();
34740
35630
  },
34741
35631
  onToggleMute: function onToggleMute() {
34742
- if (_this14.voiceInterface) _this14.voiceInterface.toggleMute();
35632
+ if (_this19.voiceInterface) _this19.voiceInterface.toggleMute();
34743
35633
  strip.syncFromVoice();
34744
35634
  },
34745
35635
  onTogglePause: function onTogglePause() {
34746
- if (_this14.voiceInterface) _this14.voiceInterface.togglePause();
35636
+ if (_this19.voiceInterface) _this19.voiceInterface.togglePause();
34747
35637
  strip.syncFromVoice();
34748
35638
  },
34749
35639
  onToggleSpeaker: function onToggleSpeaker() {
34750
- if (_this14.voiceInterface) _this14.voiceInterface.toggleSpeaker();
35640
+ if (_this19.voiceInterface) _this19.voiceInterface.toggleSpeaker();
34751
35641
  strip.syncFromVoice();
34752
35642
  },
34753
35643
  onSendText: function onSendText(text) {
34754
- var _this14$voiceInterfac4;
34755
- 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;
35644
+ var _this19$voiceInterfac4;
35645
+ var voiceSDK = (_this19$voiceInterfac4 = _this19.voiceInterface) === null || _this19$voiceInterfac4 === void 0 || (_this19$voiceInterfac4 = _this19$voiceInterfac4.sdk) === null || _this19$voiceInterfac4 === void 0 ? void 0 : _this19$voiceInterfac4.voiceSDK;
34756
35646
  if (voiceSDK && typeof voiceSDK.sendMessage === 'function') {
34757
35647
  voiceSDK.sendMessage({
34758
35648
  t: 'text_inject',
@@ -34763,8 +35653,13 @@ var TTPChatWidget = /*#__PURE__*/function () {
34763
35653
  });
34764
35654
  this._ecommerceVoiceStrip = strip;
34765
35655
 
35656
+ // Make the strip draggable too when position.draggable is enabled.
35657
+ if ((_this$config$position9 = this.config.position) !== null && _this$config$position9 !== void 0 && _this$config$position9.draggable) {
35658
+ this.setupDraggableStrip();
35659
+ }
35660
+
34766
35661
  // Show strip when conversation starts
34767
- var agentSDK = (_this$voiceInterface6 = this.voiceInterface) === null || _this$voiceInterface6 === void 0 ? void 0 : _this$voiceInterface6.sdk;
35662
+ var agentSDK = (_this$voiceInterface7 = this.voiceInterface) === null || _this$voiceInterface7 === void 0 ? void 0 : _this$voiceInterface7.sdk;
34768
35663
  if (agentSDK) {
34769
35664
  var origConvStart = agentSDK.onConversationStart;
34770
35665
  agentSDK.onConversationStart = function () {
@@ -34802,7 +35697,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34802
35697
  var _this$voiceInterface$2;
34803
35698
  var origOnCallEnd = this.voiceInterface.config.onCallEnd;
34804
35699
  this.voiceInterface.config.onCallEnd = function () {
34805
- _this14._restoreDesktopEcommerceStripChrome();
35700
+ _this19._restoreDesktopEcommerceStripChrome();
34806
35701
  if (typeof origOnCallEnd === 'function') origOnCallEnd();
34807
35702
  };
34808
35703
 
@@ -34926,7 +35821,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34926
35821
  key: "injectData",
34927
35822
  value: (function () {
34928
35823
  var _injectData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(data) {
34929
- var _this$voiceInterface7;
35824
+ var _this$voiceInterface8;
34930
35825
  var options,
34931
35826
  voiceSDK,
34932
35827
  ack,
@@ -34951,7 +35846,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34951
35846
  throw new Error('Voice call is not active');
34952
35847
  case 2:
34953
35848
  // Check if SDK is available
34954
- voiceSDK = (_this$voiceInterface7 = this.voiceInterface) === null || _this$voiceInterface7 === void 0 ? void 0 : _this$voiceInterface7.getVoiceSDK();
35849
+ voiceSDK = (_this$voiceInterface8 = this.voiceInterface) === null || _this$voiceInterface8 === void 0 ? void 0 : _this$voiceInterface8.getVoiceSDK();
34955
35850
  if (voiceSDK) {
34956
35851
  _context6.n = 3;
34957
35852
  break;
@@ -35216,7 +36111,7 @@ var TextInterface = /*#__PURE__*/function () {
35216
36111
  var inputPlaceholder = this.config.inputPlaceholder || ((_this$config$panel = this.config.panel) === null || _this$config$panel === void 0 ? void 0 : _this$config$panel.inputPlaceholder) || this.t('typeMessage') || 'Type your message...';
35217
36112
  var unifiedMode = ((_this$config$behavior = this.config.behavior) === null || _this$config$behavior === void 0 ? void 0 : _this$config$behavior.mode) === 'unified';
35218
36113
  var backArrow = this.config.direction === 'rtl' ? '→' : '←';
35219
- var backBar = unifiedMode ? "\n <div class=\"text-interface-top-bar\">\n <button type=\"button\" class=\"text-interface-back-btn\" id=\"textUnifiedBackBtn\" aria-label=\"".concat(this.t('back'), "\">\n <span class=\"text-interface-back-icon\" aria-hidden=\"true\">").concat(backArrow, "</span>\n <span class=\"text-interface-back-label\">").concat(this.t('back'), "</span>\n </button>\n </div>") : '';
36114
+ var backBar = unifiedMode ? "\n <div class=\"text-interface-top-bar\">\n <button type=\"button\" class=\"text-interface-back-btn\" id=\"textUnifiedBackBtn\" aria-label=\"".concat(this.t('back'), "\">\n <span class=\"text-interface-back-icon\" aria-hidden=\"true\">").concat(backArrow, "</span>\n <span class=\"text-interface-back-label\">").concat(this.t('back'), "</span>\n </button>\n <button type=\"button\" class=\"ttp-info-btn\" aria-label=\"").concat(this.t('aboutTitle') || 'About', "\" title=\"").concat(this.t('aboutTitle') || 'About', "\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"9\"/>\n <line x1=\"12\" y1=\"11\" x2=\"12\" y2=\"16\"/>\n <circle cx=\"12\" cy=\"7.7\" r=\"1.05\" fill=\"currentColor\" stroke=\"none\"/>\n </svg>\n </button>\n </div>") : '';
35220
36115
  return "<div class=\"text-interface\" id=\"textInterface\">\n ".concat(backBar, "\n <div class=\"messages-container\" id=\"messagesContainer\">\n <div class=\"empty-state\">\n <div class=\"empty-state-icon\">\uD83D\uDCAC</div>\n <div class=\"empty-state-title\">").concat(this.t('hello'), "</div>\n <div class=\"empty-state-text\">").concat(this.t('sendMessage'), "</div>\n </div>\n </div>\n <div class=\"input-container\">\n <div class=\"input-wrapper\" style=\"flex:1;\">\n <textarea class=\"message-input\" id=\"messageInput\" placeholder=\"").concat(inputPlaceholder, "\" rows=\"1\" dir=\"").concat(this.config.direction || 'ltr', "\"></textarea>\n </div>\n <button class=\"send-button\" id=\"sendButton\" aria-label=\"").concat(this.t('sendMessageAria'), "\">").concat(this.config.sendButtonText || '➤', "</button>\n ").concat((_this$config$sendButt = this.config.sendButtonHint) !== null && _this$config$sendButt !== void 0 && _this$config$sendButt.text || (_this$config$panel2 = this.config.panel) !== null && _this$config$panel2 !== void 0 && (_this$config$panel2 = _this$config$panel2.sendButtonHint) !== null && _this$config$panel2 !== void 0 && _this$config$panel2.text ? "\n <div class=\"send-button-hint\" style=\"color: ".concat(((_this$config$sendButt2 = this.config.sendButtonHint) === null || _this$config$sendButt2 === void 0 ? void 0 : _this$config$sendButt2.color) || ((_this$config$panel3 = this.config.panel) === null || _this$config$panel3 === void 0 || (_this$config$panel3 = _this$config$panel3.sendButtonHint) === null || _this$config$panel3 === void 0 ? void 0 : _this$config$panel3.color) || '#6B7280', "; font-size: ").concat(((_this$config$sendButt3 = this.config.sendButtonHint) === null || _this$config$sendButt3 === void 0 ? void 0 : _this$config$sendButt3.fontSize) || ((_this$config$panel4 = this.config.panel) === null || _this$config$panel4 === void 0 || (_this$config$panel4 = _this$config$panel4.sendButtonHint) === null || _this$config$panel4 === void 0 ? void 0 : _this$config$panel4.fontSize) || '14px', "; text-align: center; margin-top: 4px;\">\n ").concat(((_this$config$sendButt4 = this.config.sendButtonHint) === null || _this$config$sendButt4 === void 0 ? void 0 : _this$config$sendButt4.text) || ((_this$config$panel5 = this.config.panel) === null || _this$config$panel5 === void 0 || (_this$config$panel5 = _this$config$panel5.sendButtonHint) === null || _this$config$panel5 === void 0 ? void 0 : _this$config$panel5.text), "\n </div>\n ") : '', "\n </div>\n </div>");
35221
36116
  }
35222
36117
 
@@ -35348,7 +36243,7 @@ var TextInterface = /*#__PURE__*/function () {
35348
36243
 
35349
36244
  // Add !important to display rules when not using Shadow DOM (to override theme CSS)
35350
36245
  var important = this.config.useShadowDOM === false ? ' !important' : '';
35351
- return "\n .text-interface-top-bar {\n flex-shrink: 0".concat(important, ";\n padding: 10px 12px 8px").concat(important, ";\n border-bottom: 1px solid ").concat(topBarBorder).concat(important, ";\n background: ").concat(topBarBg).concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n }\n .text-interface-back-btn {\n display: inline-flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 6px").concat(important, ";\n padding: 6px 10px").concat(important, ";\n margin: 0").concat(important, ";\n border: none").concat(important, ";\n border-radius: 8px").concat(important, ";\n background: transparent").concat(important, ";\n color: ").concat(backBtnColor).concat(important, ";\n font-size: 16px").concat(important, ";\n font-weight: 600").concat(important, ";\n cursor: pointer").concat(important, ";\n font-family: inherit").concat(important, ";\n }\n .text-interface-back-btn:hover {\n background: ").concat(backBtnHoverBg).concat(important, ";\n color: ").concat(backBtnHoverColor).concat(important, ";\n }\n .text-interface-back-icon {\n font-size: 18px").concat(important, ";\n line-height: 1").concat(important, ";\n }\n .text-interface-back-label {\n line-height: 1.2").concat(important, ";\n }\n /* Messages container using new classes */\n #messagesContainer { \n flex: 1").concat(important, "; \n overflow-y: auto").concat(important, "; \n overflow-x: hidden").concat(important, "; \n padding: 20px").concat(important, "; \n background: ").concat(messagesAreaBg).concat(important, "; \n display: flex").concat(important, "; \n flex-direction: column").concat(important, "; \n gap: 16px").concat(important, "; \n min-height: 0").concat(important, "; \n }\n .empty-state { \n flex: 1").concat(important, "; \n display: flex").concat(important, "; \n flex-direction: column").concat(important, "; \n align-items: center").concat(important, "; \n justify-content: center").concat(important, "; \n gap: 12px").concat(important, "; \n color: ").concat(emptyMuted).concat(important, "; \n text-align: center").concat(important, "; \n padding: 20px").concat(important, "; \n }\n .empty-state-icon { font-size: 52px").concat(important, "; opacity: 0.3").concat(important, "; }\n .empty-state-title { font-size: 22px").concat(important, "; font-weight: 700").concat(important, "; color: ").concat(emptyTitle).concat(important, "; }\n .empty-state-text { font-size: 15px").concat(important, "; max-width: 300px").concat(important, "; line-height: 1.45").concat(important, "; }\n\n .text-interface { \n display: none").concat(important, "; \n flex: 1").concat(important, "; \n flex-direction: column").concat(important, "; \n min-height: 0").concat(important, "; \n overflow: hidden").concat(important, "; \n }\n .text-interface.active { display: flex").concat(important, "; }\n \n .message { \n display: flex").concat(important, "; \n gap: 8px").concat(important, "; \n padding: 4px 0").concat(important, "; \n max-width: 100%").concat(important, "; \n align-items: center").concat(important, "; \n }\n .message.edge-left { flex-direction: row").concat(important, "; }\n .message.edge-right { flex-direction: row-reverse").concat(important, "; }\n .message-bubble { \n padding: 14px 16px").concat(important, "; \n border-radius: ").concat(messages.borderRadius, "px").concat(important, "; \n max-width: 80%").concat(important, "; \n font-size: ").concat(messages.fontSize).concat(important, "; \n line-height: 1.45").concat(important, ";\n word-wrap: break-word").concat(important, "; \n text-align: ").concat(this.config.direction === 'rtl' ? 'right' : 'left').concat(important, "; \n direction: ").concat(this.config.direction || 'ltr', ";\n }\n .message.user .message-bubble { \n background: ").concat(userBubbleBg).concat(important, "; \n color: ").concat(userBubbleTextColor).concat(important, "; \n }\n .message.agent .message-bubble { \n background: ").concat(agentBubbleBg).concat(important, "; \n color: ").concat(agentBubbleTextColor).concat(important, "; \n border: ").concat(agentBubbleBorder).concat(important, "; \n }\n\n .message.user { \n align-self: ").concat(this.config.direction === 'rtl' ? 'flex-start' : 'flex-end').concat(important, "; \n }\n .message.agent { \n align-self: ").concat(this.config.direction === 'rtl' ? 'flex-end' : 'flex-start').concat(important, "; \n }\n .message .message-bubble { \n text-align: ").concat(this.config.direction === 'rtl' ? 'right' : 'left', " !important; \n }\n ").concat(this.config.direction === 'rtl' ? "\n .message-bubble {\n text-align: right !important;\n }\n " : '', "\n .message-avatar { \n width: ").concat(avatarSize).concat(important, "; \n height: ").concat(avatarSize).concat(important, "; \n min-width: ").concat(avatarSize).concat(important, "; \n border-radius: 50%").concat(important, "; \n display: flex").concat(important, "; \n align-items: center").concat(important, "; \n justify-content: center").concat(important, "; \n flex-shrink: 0").concat(important, "; \n color: inherit").concat(important, "; \n font-size: ").concat(useVoiceTheme ? '14' : '20', "px").concat(important, "; \n line-height: 1").concat(important, "; \n background: transparent").concat(important, "; \n border: none").concat(important, "; \n box-sizing: border-box").concat(important, "; \n }\n .message-avatar.user { background: ").concat(avatarUserBg).concat(important, "; color: ").concat(avatarUserColor).concat(important, "; }\n .message-avatar.agent { background: ").concat(avatarAgentBg).concat(important, "; }\n \n .message.system {\n background: ").concat(messages.systemBackgroundColor, ";\n align-self: flex-start").concat(important, ";\n }\n .message.error {\n background: ").concat(messages.errorBackgroundColor, ";\n align-self: flex-start").concat(important, ";\n }\n \n .input-container {\n display: flex").concat(important, ";\n gap: 8px").concat(important, ";\n padding: 12px 16px").concat(important, ";\n background: ").concat(inputContainerBg).concat(important, ";\n border-top: 1px solid ").concat(inputContainerBorderTop).concat(important, ";\n align-items: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n flex-direction: ").concat(this.config.direction === 'rtl' ? 'row-reverse' : 'row').concat(important, ";\n }\n \n .input-wrapper {\n position: relative").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n }\n \n .message-input {\n width: 100%").concat(important, ";\n min-height: ").concat(TEXT_INPUT_MIN_HEIGHT_PX, "px").concat(important, ";\n max-height: ").concat(TEXT_INPUT_MAX_HEIGHT_PX, "px").concat(important, ";\n padding: ").concat(inputPadding, ";\n border: 1px solid ").concat(inputBorderColor, ";\n border-radius: ").concat(inputBorderRadius, "px;\n font-size: ").concat(inputFontSize, ";\n font-family: inherit").concat(important, ";\n line-height: 1.4").concat(important, ";\n resize: none").concat(important, ";\n overflow-y: auto").concat(important, ";\n background: ").concat(inputBackgroundColor, ";\n color: ").concat(inputTextColor, ";\n vertical-align: top").concat(important, ";\n margin: 0").concat(important, ";\n display: block").concat(important, ";\n white-space: pre-wrap").concat(important, ";\n word-wrap: break-word").concat(important, ";\n text-align: ").concat(this.config.direction === 'rtl' ? 'right' : 'left').concat(important, ";\n direction: ").concat(this.config.direction || 'ltr', ";\n -webkit-appearance: none").concat(important, ";\n appearance: none").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n \n .message-input:focus {\n outline: none").concat(important, ";\n border-color: ").concat(inputFocusColor, ";\n background: ").concat(inputFocusBg).concat(important, ";\n box-shadow: ").concat(inputFocusBoxShadow, ";\n }\n \n .message-input::placeholder {\n color: ").concat(placeholderColor).concat(important, ";\n text-align: ").concat(this.config.direction === 'rtl' ? 'right' : 'left').concat(important, ";\n }\n \n .send-button {\n width: 44px").concat(important, ";\n height: 44px").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n background: ").concat(sendButtonColor, ";\n color: ").concat(sendButtonTextColor, ";\n font-size: ").concat(this.config.sendButtonFontSize || ((_this$config$panel15 = this.config.panel) === null || _this$config$panel15 === void 0 ? void 0 : _this$config$panel15.sendButtonFontSize) || '20px', ";\n font-weight: ").concat(this.config.sendButtonFontWeight || ((_this$config$panel16 = this.config.panel) === null || _this$config$panel16 === void 0 ? void 0 : _this$config$panel16.sendButtonFontWeight) || '500', ";\n cursor: pointer").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n transition: all 0.2s ease").concat(important, ";\n box-shadow: 0 4px 12px rgba(").concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", 0.32)").concat(important, ";\n }\n \n .send-button:hover:not(:disabled) {\n background: ").concat(sendButtonHoverColor, ";\n transform: scale(1.05)").concat(important, ";\n box-shadow: 0 6px 16px rgba(").concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", 0.42)").concat(important, ";\n }\n \n .send-button-hint {\n width: 100%").concat(important, ";\n text-align: center").concat(important, ";\n margin-top: 4px").concat(important, ";\n }\n \n .send-button:disabled {\n opacity: 0.5").concat(important, ";\n cursor: not-allowed").concat(important, ";\n }\n \n .typing-indicator {\n display: inline-flex").concat(important, ";\n gap: 4px").concat(important, ";\n align-items: center").concat(important, ";\n }\n \n .typing-dot {\n width: 6px").concat(important, ";\n height: 6px").concat(important, ";\n border-radius: 50%").concat(important, ";\n background: rgba(").concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", ").concat(typingDotAlpha, ")").concat(important, ";\n animation: typingDot 1.4s ease-in-out infinite").concat(important, ";\n }\n \n .typing-dot:nth-child(2) { animation-delay: 0.2s").concat(important, "; }\n .typing-dot:nth-child(3) { animation-delay: 0.4s").concat(important, "; }\n \n @keyframes typingDot {\n 0%, 60%, 100% { transform: translateY(0); opacity: 0.7; }\n 30% { transform: translateY(-8px); opacity: 1; }\n }\n \n .error-message {\n padding: 12px").concat(important, ";\n background: ").concat(errorBubbleBg, ";\n border-radius: ").concat(messages.borderRadius, "px;\n color: ").concat(errorBubbleColor).concat(important, ";\n border: ").concat(errorBubbleBorder).concat(important, ";\n font-size: ").concat(messages.fontSize).concat(important, ";\n margin: 8px 0").concat(important, ";\n }\n \n ").concat(useVoiceTheme ? "\n #textInterface.active .input-container .send-button-hint {\n color: rgba(255,255,255,0.72)".concat(important, ";\n }\n ") : '', "\n \n @media (max-width: 768px) {\n #messagesContainer {\n padding: 12px").concat(important, ";\n gap: 12px").concat(important, ";\n }\n \n .message-bubble {\n max-width: 85%").concat(important, ";\n font-size: ").concat(messages.fontSize).concat(important, ";\n padding: 12px 14px").concat(important, ";\n }\n \n .text-input-container {\n padding: 10px").concat(important, ";\n gap: 8px").concat(important, ";\n }\n \n #text-chat-input {\n font-size: 16px !important; /* Prevents iOS zoom on focus */\n padding: 10px 14px").concat(important, ";\n min-height: 44px").concat(important, ";\n }\n \n #text-chat-send {\n min-width: 56px").concat(important, ";\n min-height: 44px").concat(important, ";\n width: 56px").concat(important, ";\n height: 44px").concat(important, ";\n }\n \n .empty-state-icon {\n font-size: 44px").concat(important, ";\n }\n \n .empty-state-title {\n font-size: 20px").concat(important, ";\n }\n \n .empty-state-text {\n font-size: 14px").concat(important, ";\n }\n }\n \n @media (max-width: 480px) {\n #messagesContainer {\n padding: 10px").concat(important, ";\n gap: 10px").concat(important, ";\n }\n \n .message-bubble {\n max-width: 90%").concat(important, ";\n font-size: ").concat(messages.fontSize).concat(important, ";\n padding: 10px 12px").concat(important, ";\n }\n \n .text-input-container {\n padding: 8px").concat(important, ";\n }\n \n #text-chat-input {\n font-size: 16px !important;\n padding: 8px 12px").concat(important, ";\n }\n }\n ");
36246
+ return "\n .text-interface-top-bar {\n flex-shrink: 0".concat(important, ";\n padding: 10px 12px 8px").concat(important, ";\n border-bottom: 1px solid ").concat(topBarBorder).concat(important, ";\n background: ").concat(topBarBg).concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: space-between").concat(important, ";\n }\n .text-interface-top-bar .ttp-info-btn {\n color: ").concat(backBtnColor).concat(important, ";\n }\n .text-interface-top-bar .ttp-info-btn:hover {\n background: ").concat(backBtnHoverBg).concat(important, ";\n color: ").concat(backBtnHoverColor).concat(important, ";\n }\n .text-interface-back-btn {\n display: inline-flex").concat(important, ";\n align-items: center").concat(important, ";\n gap: 6px").concat(important, ";\n padding: 6px 10px").concat(important, ";\n margin: 0").concat(important, ";\n border: none").concat(important, ";\n border-radius: 8px").concat(important, ";\n background: transparent").concat(important, ";\n color: ").concat(backBtnColor).concat(important, ";\n font-size: 16px").concat(important, ";\n font-weight: 600").concat(important, ";\n cursor: pointer").concat(important, ";\n font-family: inherit").concat(important, ";\n }\n .text-interface-back-btn:hover {\n background: ").concat(backBtnHoverBg).concat(important, ";\n color: ").concat(backBtnHoverColor).concat(important, ";\n }\n .text-interface-back-icon {\n font-size: 18px").concat(important, ";\n line-height: 1").concat(important, ";\n }\n .text-interface-back-label {\n line-height: 1.2").concat(important, ";\n }\n /* Messages container using new classes */\n #messagesContainer { \n flex: 1").concat(important, "; \n overflow-y: auto").concat(important, "; \n overflow-x: hidden").concat(important, "; \n padding: 20px").concat(important, "; \n background: ").concat(messagesAreaBg).concat(important, "; \n display: flex").concat(important, "; \n flex-direction: column").concat(important, "; \n gap: 16px").concat(important, "; \n min-height: 0").concat(important, "; \n }\n .empty-state { \n flex: 1").concat(important, "; \n display: flex").concat(important, "; \n flex-direction: column").concat(important, "; \n align-items: center").concat(important, "; \n justify-content: center").concat(important, "; \n gap: 12px").concat(important, "; \n color: ").concat(emptyMuted).concat(important, "; \n text-align: center").concat(important, "; \n padding: 20px").concat(important, "; \n }\n .empty-state-icon { font-size: 52px").concat(important, "; opacity: 0.3").concat(important, "; }\n .empty-state-title { font-size: 22px").concat(important, "; font-weight: 700").concat(important, "; color: ").concat(emptyTitle).concat(important, "; }\n .empty-state-text { font-size: 15px").concat(important, "; max-width: 300px").concat(important, "; line-height: 1.45").concat(important, "; }\n\n .text-interface { \n display: none").concat(important, "; \n flex: 1").concat(important, "; \n flex-direction: column").concat(important, "; \n min-height: 0").concat(important, "; \n overflow: hidden").concat(important, "; \n }\n .text-interface.active { display: flex").concat(important, "; }\n \n .message { \n display: flex").concat(important, "; \n gap: 8px").concat(important, "; \n padding: 4px 0").concat(important, "; \n max-width: 100%").concat(important, "; \n align-items: center").concat(important, "; \n }\n .message.edge-left { flex-direction: row").concat(important, "; }\n .message.edge-right { flex-direction: row-reverse").concat(important, "; }\n .message-bubble { \n padding: 14px 16px").concat(important, "; \n border-radius: ").concat(messages.borderRadius, "px").concat(important, "; \n max-width: 80%").concat(important, "; \n font-size: ").concat(messages.fontSize).concat(important, "; \n line-height: 1.45").concat(important, ";\n word-wrap: break-word").concat(important, "; \n text-align: ").concat(this.config.direction === 'rtl' ? 'right' : 'left').concat(important, "; \n direction: ").concat(this.config.direction || 'ltr', ";\n }\n .message.user .message-bubble { \n background: ").concat(userBubbleBg).concat(important, "; \n color: ").concat(userBubbleTextColor).concat(important, "; \n }\n .message.agent .message-bubble { \n background: ").concat(agentBubbleBg).concat(important, "; \n color: ").concat(agentBubbleTextColor).concat(important, "; \n border: ").concat(agentBubbleBorder).concat(important, "; \n }\n\n .message.user { \n align-self: ").concat(this.config.direction === 'rtl' ? 'flex-start' : 'flex-end').concat(important, "; \n }\n .message.agent { \n align-self: ").concat(this.config.direction === 'rtl' ? 'flex-end' : 'flex-start').concat(important, "; \n }\n .message .message-bubble { \n text-align: ").concat(this.config.direction === 'rtl' ? 'right' : 'left', " !important; \n }\n ").concat(this.config.direction === 'rtl' ? "\n .message-bubble {\n text-align: right !important;\n }\n " : '', "\n .message-avatar { \n width: ").concat(avatarSize).concat(important, "; \n height: ").concat(avatarSize).concat(important, "; \n min-width: ").concat(avatarSize).concat(important, "; \n border-radius: 50%").concat(important, "; \n display: flex").concat(important, "; \n align-items: center").concat(important, "; \n justify-content: center").concat(important, "; \n flex-shrink: 0").concat(important, "; \n color: inherit").concat(important, "; \n font-size: ").concat(useVoiceTheme ? '14' : '20', "px").concat(important, "; \n line-height: 1").concat(important, "; \n background: transparent").concat(important, "; \n border: none").concat(important, "; \n box-sizing: border-box").concat(important, "; \n }\n .message-avatar.user { background: ").concat(avatarUserBg).concat(important, "; color: ").concat(avatarUserColor).concat(important, "; }\n .message-avatar.agent { background: ").concat(avatarAgentBg).concat(important, "; }\n \n .message.system {\n background: ").concat(messages.systemBackgroundColor, ";\n align-self: flex-start").concat(important, ";\n }\n .message.error {\n background: ").concat(messages.errorBackgroundColor, ";\n align-self: flex-start").concat(important, ";\n }\n \n .input-container {\n display: flex").concat(important, ";\n gap: 8px").concat(important, ";\n padding: 12px 16px").concat(important, ";\n background: ").concat(inputContainerBg).concat(important, ";\n border-top: 1px solid ").concat(inputContainerBorderTop).concat(important, ";\n align-items: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n flex-direction: ").concat(this.config.direction === 'rtl' ? 'row-reverse' : 'row').concat(important, ";\n }\n \n .input-wrapper {\n position: relative").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n }\n \n .message-input {\n width: 100%").concat(important, ";\n min-height: ").concat(TEXT_INPUT_MIN_HEIGHT_PX, "px").concat(important, ";\n max-height: ").concat(TEXT_INPUT_MAX_HEIGHT_PX, "px").concat(important, ";\n padding: ").concat(inputPadding, ";\n border: 1px solid ").concat(inputBorderColor, ";\n border-radius: ").concat(inputBorderRadius, "px;\n font-size: ").concat(inputFontSize, ";\n font-family: inherit").concat(important, ";\n line-height: 1.4").concat(important, ";\n resize: none").concat(important, ";\n overflow-y: auto").concat(important, ";\n background: ").concat(inputBackgroundColor, ";\n color: ").concat(inputTextColor, ";\n vertical-align: top").concat(important, ";\n margin: 0").concat(important, ";\n display: block").concat(important, ";\n white-space: pre-wrap").concat(important, ";\n word-wrap: break-word").concat(important, ";\n text-align: ").concat(this.config.direction === 'rtl' ? 'right' : 'left').concat(important, ";\n direction: ").concat(this.config.direction || 'ltr', ";\n -webkit-appearance: none").concat(important, ";\n appearance: none").concat(important, ";\n box-sizing: border-box").concat(important, ";\n }\n \n .message-input:focus {\n outline: none").concat(important, ";\n border-color: ").concat(inputFocusColor, ";\n background: ").concat(inputFocusBg).concat(important, ";\n box-shadow: ").concat(inputFocusBoxShadow, ";\n }\n \n .message-input::placeholder {\n color: ").concat(placeholderColor).concat(important, ";\n text-align: ").concat(this.config.direction === 'rtl' ? 'right' : 'left').concat(important, ";\n }\n \n .send-button {\n width: 44px").concat(important, ";\n height: 44px").concat(important, ";\n border-radius: 50%").concat(important, ";\n border: none").concat(important, ";\n background: ").concat(sendButtonColor, ";\n color: ").concat(sendButtonTextColor, ";\n font-size: ").concat(this.config.sendButtonFontSize || ((_this$config$panel15 = this.config.panel) === null || _this$config$panel15 === void 0 ? void 0 : _this$config$panel15.sendButtonFontSize) || '20px', ";\n font-weight: ").concat(this.config.sendButtonFontWeight || ((_this$config$panel16 = this.config.panel) === null || _this$config$panel16 === void 0 ? void 0 : _this$config$panel16.sendButtonFontWeight) || '500', ";\n cursor: pointer").concat(important, ";\n display: flex").concat(important, ";\n align-items: center").concat(important, ";\n justify-content: center").concat(important, ";\n flex-shrink: 0").concat(important, ";\n transition: all 0.2s ease").concat(important, ";\n box-shadow: 0 4px 12px rgba(").concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", 0.32)").concat(important, ";\n }\n \n .send-button:hover:not(:disabled) {\n background: ").concat(sendButtonHoverColor, ";\n transform: scale(1.05)").concat(important, ";\n box-shadow: 0 6px 16px rgba(").concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", 0.42)").concat(important, ";\n }\n \n .send-button-hint {\n width: 100%").concat(important, ";\n text-align: center").concat(important, ";\n margin-top: 4px").concat(important, ";\n }\n \n .send-button:disabled {\n opacity: 0.5").concat(important, ";\n cursor: not-allowed").concat(important, ";\n }\n \n .typing-indicator {\n display: inline-flex").concat(important, ";\n gap: 4px").concat(important, ";\n align-items: center").concat(important, ";\n }\n \n .typing-dot {\n width: 6px").concat(important, ";\n height: 6px").concat(important, ";\n border-radius: 50%").concat(important, ";\n background: rgba(").concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", ").concat(typingDotAlpha, ")").concat(important, ";\n animation: typingDot 1.4s ease-in-out infinite").concat(important, ";\n }\n \n .typing-dot:nth-child(2) { animation-delay: 0.2s").concat(important, "; }\n .typing-dot:nth-child(3) { animation-delay: 0.4s").concat(important, "; }\n \n @keyframes typingDot {\n 0%, 60%, 100% { transform: translateY(0); opacity: 0.7; }\n 30% { transform: translateY(-8px); opacity: 1; }\n }\n \n .error-message {\n padding: 12px").concat(important, ";\n background: ").concat(errorBubbleBg, ";\n border-radius: ").concat(messages.borderRadius, "px;\n color: ").concat(errorBubbleColor).concat(important, ";\n border: ").concat(errorBubbleBorder).concat(important, ";\n font-size: ").concat(messages.fontSize).concat(important, ";\n margin: 8px 0").concat(important, ";\n }\n \n ").concat(useVoiceTheme ? "\n #textInterface.active .input-container .send-button-hint {\n color: rgba(255,255,255,0.72)".concat(important, ";\n }\n ") : '', "\n \n @media (max-width: 768px) {\n #messagesContainer {\n padding: 12px").concat(important, ";\n gap: 12px").concat(important, ";\n }\n \n .message-bubble {\n max-width: 85%").concat(important, ";\n font-size: ").concat(messages.fontSize).concat(important, ";\n padding: 12px 14px").concat(important, ";\n }\n \n .text-input-container {\n padding: 10px").concat(important, ";\n gap: 8px").concat(important, ";\n }\n \n #text-chat-input {\n font-size: 16px !important; /* Prevents iOS zoom on focus */\n padding: 10px 14px").concat(important, ";\n min-height: 44px").concat(important, ";\n }\n \n #text-chat-send {\n min-width: 56px").concat(important, ";\n min-height: 44px").concat(important, ";\n width: 56px").concat(important, ";\n height: 44px").concat(important, ";\n }\n \n .empty-state-icon {\n font-size: 44px").concat(important, ";\n }\n \n .empty-state-title {\n font-size: 20px").concat(important, ";\n }\n \n .empty-state-text {\n font-size: 14px").concat(important, ";\n }\n }\n \n @media (max-width: 480px) {\n #messagesContainer {\n padding: 10px").concat(important, ";\n gap: 10px").concat(important, ";\n }\n \n .message-bubble {\n max-width: 90%").concat(important, ";\n font-size: ").concat(messages.fontSize).concat(important, ";\n padding: 10px 12px").concat(important, ";\n }\n \n .text-input-container {\n padding: 8px").concat(important, ";\n }\n \n #text-chat-input {\n font-size: 16px !important;\n padding: 8px 12px").concat(important, ";\n }\n }\n ");
35352
36247
  }
35353
36248
 
35354
36249
  /**
@@ -37426,6 +38321,7 @@ var VoiceInterface = /*#__PURE__*/function () {
37426
38321
  clearInterval(this.callTimerInterval);
37427
38322
  this.callTimerInterval = null;
37428
38323
  }
38324
+ this.callStartTime = null;
37429
38325
 
37430
38326
  // Hide active state, show idle state first
37431
38327
  activeState = this.shadowRoot.getElementById('voiceActiveState');
@@ -37594,6 +38490,10 @@ var VoiceInterface = /*#__PURE__*/function () {
37594
38490
  clearInterval(this.callTimerInterval);
37595
38491
  this.callTimerInterval = null;
37596
38492
  }
38493
+ // Reset the start time so the next call's timer block (gated on !callStartTime)
38494
+ // actually restarts the interval. Without this, the timer only ever runs for the
38495
+ // first call and stays stuck at 00:00 on every subsequent call.
38496
+ this.callStartTime = null;
37597
38497
 
37598
38498
  // Clean up text inject timeouts and handlers (desktop)
37599
38499
  if (this._textInjectTimeout) {
@@ -40341,6 +41241,21 @@ var Templates = /*#__PURE__*/function () {
40341
41241
  return this.voiceInterface.getTooltip(key);
40342
41242
  }
40343
41243
 
41244
+ /**
41245
+ * Shared "About" info ("i") button markup. Opens the About dialog
41246
+ * (SDK version + last conversation id) wired by TTPChatWidget.
41247
+ * @param {string} [extraClass] - extra class(es) for positioning per view
41248
+ * @returns {string}
41249
+ */
41250
+ }, {
41251
+ key: "infoButton",
41252
+ value: function infoButton() {
41253
+ var extraClass = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
41254
+ var cls = extraClass ? "ttp-info-btn ".concat(extraClass) : 'ttp-info-btn';
41255
+ var label = this.t('aboutTitle') || 'About';
41256
+ return "<button type=\"button\" class=\"".concat(cls, "\" aria-label=\"").concat(escapeHtmlAttr(label), "\" title=\"").concat(escapeHtmlAttr(label), "\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"9\"/>\n <line x1=\"12\" y1=\"11\" x2=\"12\" y2=\"16\"/>\n <circle cx=\"12\" cy=\"7.7\" r=\"1.05\" fill=\"currentColor\" stroke=\"none\"/>\n </svg>\n </button>");
41257
+ }
41258
+
40344
41259
  /**
40345
41260
  * Generate HTML for voice interface.
40346
41261
  * Automatically detects mobile vs desktop based on voiceInterface.isMobile
@@ -40371,7 +41286,7 @@ var Templates = /*#__PURE__*/function () {
40371
41286
  var transcriptPlaceholder = this.t('transcriptWillAppear') || '';
40372
41287
  var transcriptDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForTranscriptSnippet)(transcriptPlaceholder, this.config);
40373
41288
  var voiceInputDir = (0,_rtlText_js__WEBPACK_IMPORTED_MODULE_0__.dirForAssistantStreamingText)(this.config);
40374
- 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>");
41289
+ 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 <div class=\"ttp-header-actions\">\n ").concat(this.infoButton(), "\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>\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 <div class=\"ttp-header-actions\">\n ").concat(this.infoButton(), "\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 </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 ").concat(this.infoButton('ttp-info-btn--onctrl'), "\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>");
40375
41290
  }
40376
41291
 
40377
41292
  /**
@@ -40388,7 +41303,7 @@ var Templates = /*#__PURE__*/function () {
40388
41303
  var headerAvatarUrl = resolveIdleHeaderAvatarUrl(this.config);
40389
41304
  var mobileAvatarInner = headerAvatarUrl ? "<img src=\"".concat(escapeHtmlAttr(headerAvatarUrl), "\" alt=\"\" class=\"voice-avatar-img\" />") : '🤖';
40390
41305
  var mobileAvatarClass = headerAvatarUrl ? 'voice-avatar voice-avatar--image' : 'voice-avatar';
40391
- 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) {
41306
+ 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 ").concat(this.infoButton('ttp-info-btn--floating'), "\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 ").concat(this.infoButton('ttp-info-btn--onbadge'), "\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) {
40392
41307
  return "<div class=\"mobile-waveform-bar\" data-index=\"".concat(i, "\"></div>");
40393
41308
  }).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>");
40394
41309
  }
@@ -41233,7 +42148,11 @@ __webpack_require__.r(__webpack_exports__);
41233
42148
  "typeMessage": "Type your message...",
41234
42149
  "sendMessageAria": "Send message",
41235
42150
  "domainNotValidated": "Domain not validated",
41236
- "domainErrorMessage": "This domain is not whitelisted. Please contact the administrator to whitelist your website URL."
42151
+ "domainErrorMessage": "This domain is not whitelisted. Please contact the administrator to whitelist your website URL.",
42152
+ "aboutTitle": "About",
42153
+ "aboutVersion": "SDK version",
42154
+ "aboutConversationId": "Last conversation ID",
42155
+ "aboutNoConversation": "No conversation yet"
41237
42156
  },
41238
42157
  "he": {
41239
42158
  "landingTitle": "איך תרצה לתקשר?",
@@ -41266,7 +42185,11 @@ __webpack_require__.r(__webpack_exports__);
41266
42185
  "typeMessage": "הקלד הודעה...",
41267
42186
  "sendMessageAria": "שלח הודעה",
41268
42187
  "domainNotValidated": "דומיין לא מאומת",
41269
- "domainErrorMessage": "דומיין זה לא ברשימת המורשים. אנא פנה למנהל המערכת כדי להוסיף את כתובת האתר שלך לרשימה."
42188
+ "domainErrorMessage": "דומיין זה לא ברשימת המורשים. אנא פנה למנהל המערכת כדי להוסיף את כתובת האתר שלך לרשימה.",
42189
+ "aboutTitle": "אודות",
42190
+ "aboutVersion": "גרסת SDK",
42191
+ "aboutConversationId": "מזהה שיחה אחרון",
42192
+ "aboutNoConversation": "אין שיחה עדיין"
41270
42193
  },
41271
42194
  "ar": {
41272
42195
  "landingTitle": "كيف تريد التواصل؟",
@@ -41299,7 +42222,11 @@ __webpack_require__.r(__webpack_exports__);
41299
42222
  "typeMessage": "اكتب رسالة...",
41300
42223
  "sendMessageAria": "إرسال رسالة",
41301
42224
  "domainNotValidated": "النطاق غير معتمد",
41302
- "domainErrorMessage": "هذا النطاق غير مسموح به. يرجى الاتصال بالمسؤول لإضافة عنوان موقع الويب الخاص بك إلى القائمة."
42225
+ "domainErrorMessage": "هذا النطاق غير مسموح به. يرجى الاتصال بالمسؤول لإضافة عنوان موقع الويب الخاص بك إلى القائمة.",
42226
+ "aboutTitle": "حول",
42227
+ "aboutVersion": "إصدار SDK",
42228
+ "aboutConversationId": "معرّف آخر محادثة",
42229
+ "aboutNoConversation": "لا توجد محادثة بعد"
41303
42230
  },
41304
42231
  "ru": {
41305
42232
  "landingTitle": "Как вы хотите общаться?",
@@ -41332,7 +42259,11 @@ __webpack_require__.r(__webpack_exports__);
41332
42259
  "typeMessage": "Введите сообщение...",
41333
42260
  "sendMessageAria": "Отправить сообщение",
41334
42261
  "domainNotValidated": "Домен не проверен",
41335
- "domainErrorMessage": "Этот домен не в белом списке. Пожалуйста, свяжитесь с администратором, чтобы добавить URL вашего веб-сайта в список."
42262
+ "domainErrorMessage": "Этот домен не в белом списке. Пожалуйста, свяжитесь с администратором, чтобы добавить URL вашего веб-сайта в список.",
42263
+ "aboutTitle": "О приложении",
42264
+ "aboutVersion": "Версия SDK",
42265
+ "aboutConversationId": "ID последнего разговора",
42266
+ "aboutNoConversation": "Разговоров пока нет"
41336
42267
  },
41337
42268
  "es": {
41338
42269
  "landingTitle": "¿Cómo te gustaría comunicarte?",
@@ -41365,7 +42296,11 @@ __webpack_require__.r(__webpack_exports__);
41365
42296
  "typeMessage": "Escribe un mensaje...",
41366
42297
  "sendMessageAria": "Enviar mensaje",
41367
42298
  "domainNotValidated": "Dominio no validado",
41368
- "domainErrorMessage": "Este dominio no está en la lista blanca. Por favor, contacte al administrador para agregar la URL de su sitio web a la lista."
42299
+ "domainErrorMessage": "Este dominio no está en la lista blanca. Por favor, contacte al administrador para agregar la URL de su sitio web a la lista.",
42300
+ "aboutTitle": "Acerca de",
42301
+ "aboutVersion": "Versión del SDK",
42302
+ "aboutConversationId": "ID de última conversación",
42303
+ "aboutNoConversation": "Aún no hay conversación"
41369
42304
  },
41370
42305
  "fr": {
41371
42306
  "landingTitle": "Comment souhaitez-vous communiquer?",
@@ -41398,7 +42333,11 @@ __webpack_require__.r(__webpack_exports__);
41398
42333
  "typeMessage": "Tapez votre message...",
41399
42334
  "sendMessageAria": "Envoyer un message",
41400
42335
  "domainNotValidated": "Domaine non validé",
41401
- "domainErrorMessage": "Ce domaine n'est pas sur la liste blanche. Veuillez contacter l'administrateur pour ajouter l'URL de votre site Web à la liste."
42336
+ "domainErrorMessage": "Ce domaine n'est pas sur la liste blanche. Veuillez contacter l'administrateur pour ajouter l'URL de votre site Web à la liste.",
42337
+ "aboutTitle": "À propos",
42338
+ "aboutVersion": "Version du SDK",
42339
+ "aboutConversationId": "ID de la dernière conversation",
42340
+ "aboutNoConversation": "Aucune conversation pour l'instant"
41402
42341
  },
41403
42342
  "de": {
41404
42343
  "landingTitle": "Wie möchten Sie kommunizieren?",
@@ -41431,7 +42370,11 @@ __webpack_require__.r(__webpack_exports__);
41431
42370
  "typeMessage": "Geben Sie eine Nachricht ein...",
41432
42371
  "sendMessageAria": "Nachricht senden",
41433
42372
  "domainNotValidated": "Domain nicht validiert",
41434
- "domainErrorMessage": "Diese Domain steht nicht auf der Whitelist. Bitte kontaktieren Sie den Administrator, um die URL Ihrer Website zur Liste hinzuzufügen."
42373
+ "domainErrorMessage": "Diese Domain steht nicht auf der Whitelist. Bitte kontaktieren Sie den Administrator, um die URL Ihrer Website zur Liste hinzuzufügen.",
42374
+ "aboutTitle": "Über",
42375
+ "aboutVersion": "SDK-Version",
42376
+ "aboutConversationId": "Letzte Konversations-ID",
42377
+ "aboutNoConversation": "Noch keine Konversation"
41435
42378
  }
41436
42379
  });
41437
42380