ttp-agent-sdk 2.46.3 → 2.47.2

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.
@@ -9644,6 +9644,7 @@ var AudioPlayer = /*#__PURE__*/function (_EventEmitter) {
9644
9644
  "use strict";
9645
9645
  __webpack_require__.r(__webpack_exports__);
9646
9646
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9647
+ /* harmony export */ buildAudioConstraints: () => (/* binding */ buildAudioConstraints),
9647
9648
  /* harmony export */ "default": () => (/* binding */ AudioRecorder)
9648
9649
  /* harmony export */ });
9649
9650
  /* harmony import */ var _EventEmitter_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./EventEmitter.js */ "./src/core/EventEmitter.js");
@@ -11993,7 +11994,7 @@ var TextChatSDK = /*#__PURE__*/function (_EventEmitter) {
11993
11994
 
11994
11995
  // SDK build time for debugging
11995
11996
  if (true) {
11996
- helloMessage.lastBuildTime = "2026-07-13T09:55:44.731Z";
11997
+ helloMessage.lastBuildTime = "2026-07-30T20:53:20.013Z";
11997
11998
  }
11998
11999
  try {
11999
12000
  this.ws.send(JSON.stringify(helloMessage));
@@ -15310,7 +15311,8 @@ var MobileProductCarousel = /*#__PURE__*/function () {
15310
15311
  __webpack_require__.r(__webpack_exports__);
15311
15312
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
15312
15313
  /* harmony export */ PartnerScriptRunner: () => (/* binding */ PartnerScriptRunner),
15313
- /* harmony export */ buildRunPartnerScriptTool: () => (/* binding */ buildRunPartnerScriptTool)
15314
+ /* harmony export */ buildRunPartnerScriptTool: () => (/* binding */ buildRunPartnerScriptTool),
15315
+ /* harmony export */ compileAdapter: () => (/* binding */ compileAdapter)
15314
15316
  /* harmony export */ });
15315
15317
  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; }
15316
15318
  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; }
@@ -15476,19 +15478,62 @@ var AsyncFunction = Object.getPrototypeOf(/*#__PURE__*/_asyncToGenerator(/*#__PU
15476
15478
  // back to compiling the code as a plain body. AsyncFunction (not
15477
15479
  // `new Function`) enforces an async execution context — no accidental sync
15478
15480
  // adapter bodies.
15481
+ //
15482
+ // Detection runs against the code with leading comments removed. Adapters are
15483
+ // routinely authored with a header comment block above the function, and a
15484
+ // header-prefixed function expression that falls through to body compilation
15485
+ // is a *silent* no-op: `// h\nasync (ctx) => {...}` is a syntactically valid
15486
+ // statement body that merely constructs the arrow and discards it, so the
15487
+ // adapter compiles, "succeeds", and returns undefined without ever running.
15479
15488
  var FN_EXPR_RE = /^\s*(async[\s(])|^\s*function\b|^\s*\(|^\s*[A-Za-z_$][\w$]*\s*=>/;
15489
+
15490
+ // Drop leading whitespace and `//` / `/* */` comments. Detection-only — the
15491
+ // original source is always what gets compiled, so comments (and the line
15492
+ // numbers they occupy in stack traces) are preserved.
15493
+ function stripLeadingComments(code) {
15494
+ var i = 0;
15495
+ for (;;) {
15496
+ while (i < code.length && /\s/.test(code[i])) i++;
15497
+ if (code.startsWith('//', i)) {
15498
+ var nl = code.indexOf('\n', i);
15499
+ if (nl === -1) return '';
15500
+ i = nl + 1;
15501
+ } else if (code.startsWith('/*', i)) {
15502
+ var end = code.indexOf('*/', i + 2);
15503
+ if (end === -1) return '';
15504
+ i = end + 2;
15505
+ } else {
15506
+ return code.slice(i);
15507
+ }
15508
+ }
15509
+ }
15510
+
15511
+ /**
15512
+ * Exported so the offline adapter verifier (`scripts/verify-adapter.mjs`) can
15513
+ * gate authored scripts through the exact compiler the SDK uses at runtime,
15514
+ * rather than a copy that can drift out of sync with it.
15515
+ * @returns {{fn: Function, style: 'expression'|'body'}}
15516
+ */
15480
15517
  function compileAdapter(codeJs) {
15481
- if (FN_EXPR_RE.test(codeJs)) {
15518
+ if (FN_EXPR_RE.test(stripLeadingComments(codeJs))) {
15482
15519
  try {
15520
+ // Newline before `)` so a trailing `//` comment can't swallow it.
15483
15521
  // eslint-disable-next-line no-new-func
15484
- return new AsyncFunction('ctx', "\"use strict\"; return (".concat(codeJs, ").call(null, ctx);"));
15522
+ var fn = new AsyncFunction('ctx', "\"use strict\"; return (".concat(codeJs, "\n).call(null, ctx);"));
15523
+ return {
15524
+ fn: fn,
15525
+ style: 'expression'
15526
+ };
15485
15527
  } catch (e) {
15486
15528
  // Looked like an expression but doesn't parse as one (e.g. an
15487
15529
  // IIFE-opening statement body) — fall through to body compilation.
15488
15530
  }
15489
15531
  }
15490
15532
  // eslint-disable-next-line no-new-func
15491
- return new AsyncFunction('ctx', "\"use strict\"; ".concat(codeJs));
15533
+ return {
15534
+ fn: new AsyncFunction('ctx', "\"use strict\"; ".concat(codeJs)),
15535
+ style: 'body'
15536
+ };
15492
15537
  }
15493
15538
 
15494
15539
  // Cheap 32-bit string hash, rendered as 12 hex chars. NOT cryptographic —
@@ -15607,7 +15652,7 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
15607
15652
  var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
15608
15653
  _classCallCheck(this, PartnerScriptRunner);
15609
15654
  this.genericCtx = !!(opts && opts.genericCtx);
15610
- /** @type {Map<string, {fn?: Function, version?: number, codeSha256?: string, meta?: object, category?: string|null, beforeActions?: string[], afterActions?: string[], poisoned?: boolean, error?: string}>} */
15655
+ /** @type {Map<string, {fn?: Function, style?: 'expression'|'body', version?: number, codeSha256?: string, meta?: object, category?: string|null, beforeActions?: string[], afterActions?: string[], poisoned?: boolean, error?: string}>} */
15611
15656
  this.adapters = new Map();
15612
15657
  this.partnerId = null;
15613
15658
  this.platform = null;
@@ -15658,14 +15703,17 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
15658
15703
  }
15659
15704
  try {
15660
15705
  var _entry$version2, _entry$category;
15661
- var fn = compileAdapter(entry.code_js);
15706
+ var _compileAdapter = compileAdapter(entry.code_js),
15707
+ fn = _compileAdapter.fn,
15708
+ style = _compileAdapter.style;
15662
15709
  // Inline pre/post hooks (optional). Compiled with the same
15663
15710
  // AsyncFunction strict-mode wrapper as the main body so a syntax
15664
15711
  // error in a hook poisons only this entry (caught below).
15665
- var preFn = typeof entry.pre_code_js === 'string' ? compileAdapter(entry.pre_code_js) : null;
15666
- var postFn = typeof entry.post_code_js === 'string' ? compileAdapter(entry.post_code_js) : null;
15712
+ var preFn = typeof entry.pre_code_js === 'string' ? compileAdapter(entry.pre_code_js).fn : null;
15713
+ var postFn = typeof entry.post_code_js === 'string' ? compileAdapter(entry.post_code_js).fn : null;
15667
15714
  this.adapters.set(action, {
15668
15715
  fn: fn,
15716
+ style: style,
15669
15717
  preFn: preFn,
15670
15718
  postFn: postFn,
15671
15719
  version: (_entry$version2 = entry.version) !== null && _entry$version2 !== void 0 ? _entry$version2 : null,
@@ -15932,7 +15980,7 @@ var PartnerScriptRunner = /*#__PURE__*/function () {
15932
15980
  }
15933
15981
  }
15934
15982
  ctx = this._buildCtx(args, action, entry);
15935
- console.log("".concat(LOG, " exec ").concat(action, " v").concat((_entry$version4 = entry.version) !== null && _entry$version4 !== void 0 ? _entry$version4 : '?', " ") + "sha=".concat((entry.codeSha256 || '').slice(0, 12)));
15983
+ console.log("".concat(LOG, " exec ").concat(action, " v").concat((_entry$version4 = entry.version) !== null && _entry$version4 !== void 0 ? _entry$version4 : '?', " ") + "sha=".concat((entry.codeSha256 || '').slice(0, 12), " as=").concat(entry.style || '?'));
15936
15984
 
15937
15985
  // Inline pre-hook (best-effort): an error is logged and main still runs.
15938
15986
  if (!entry.preFn) {
@@ -21205,8 +21253,8 @@ var VoiceSDK = _v2_VoiceSDK_js__WEBPACK_IMPORTED_MODULE_0__["default"];
21205
21253
 
21206
21254
 
21207
21255
  // Version - injected at build time from package.json via webpack DefinePlugin
21208
- var VERSION = "2.46.3";
21209
- var BUILD_TIME = "2026-07-13T09:55:44.731Z";
21256
+ var VERSION = "2.47.2";
21257
+ var BUILD_TIME = "2026-07-30T20:53:20.013Z";
21210
21258
  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;');
21211
21259
 
21212
21260
  // Named exports
@@ -28536,6 +28584,8 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28536
28584
  // The backend analyzes the audio stream and detects when user interrupts
28537
28585
  // Frontend only responds to backend 'barge_in' messages (handled in handleMessage())
28538
28586
 
28587
+ // Report the capture track's real settings (AEC ground truth) to the backend
28588
+ _this4._sendClientAudioInfo();
28539
28589
  _this4.emit('recordingStarted');
28540
28590
  });
28541
28591
  this.audioRecorder.on('recordingStopped', function () {
@@ -28809,6 +28859,83 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28809
28859
  fullLocale: fullLocale
28810
28860
  };
28811
28861
  }
28862
+
28863
+ /**
28864
+ * Client environment snapshot sent in hello (`client` field). Static facts known
28865
+ * before the mic opens — device/browser identification for backend logs + Langfuse
28866
+ * trace metadata. Audio-track ground truth (AEC etc.) is sent separately via
28867
+ * client_audio_info once recording starts, because track settings don't exist yet.
28868
+ */
28869
+ }, {
28870
+ key: "_buildClientEnv",
28871
+ value: function _buildClientEnv() {
28872
+ try {
28873
+ var _uaData$mobile, _window$screen, _window$screen2;
28874
+ var ua = navigator.userAgent || '';
28875
+ var uaData = navigator.userAgentData;
28876
+ var conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
28877
+ var isAndroid = /Android/i.test(ua);
28878
+ var isIos = /iPhone|iPad|iPod/i.test(ua) || ua.includes('Mac') && 'ontouchend' in document;
28879
+ // Webview heuristic: Android WebView marks itself with "; wv)" or "Version/x.x Chrome";
28880
+ // iOS in-app webviews lack "Safari" in the UA (real Safari always has it).
28881
+ var webview = isAndroid && (/\bwv\b/.test(ua) || /Version\/[\d.]+.*Chrome/.test(ua)) || isIos && !/Safari/i.test(ua) || false;
28882
+ var env = {
28883
+ sdkVersion: true ? "2.47.2" : 0,
28884
+ ua: ua,
28885
+ platform: (uaData === null || uaData === void 0 ? void 0 : uaData.platform) || navigator.platform || '',
28886
+ mobile: (_uaData$mobile = uaData === null || uaData === void 0 ? void 0 : uaData.mobile) !== null && _uaData$mobile !== void 0 ? _uaData$mobile : isAndroid || isIos,
28887
+ os: isIos ? 'ios' : isAndroid ? 'android' : 'other',
28888
+ webview: webview,
28889
+ screen: "".concat(((_window$screen = window.screen) === null || _window$screen === void 0 ? void 0 : _window$screen.width) || 0, "x").concat(((_window$screen2 = window.screen) === null || _window$screen2 === void 0 ? void 0 : _window$screen2.height) || 0, "@").concat(window.devicePixelRatio || 1),
28890
+ cores: navigator.hardwareConcurrency || null,
28891
+ memoryGb: navigator.deviceMemory || null,
28892
+ connection: (conn === null || conn === void 0 ? void 0 : conn.effectiveType) || null
28893
+ };
28894
+ return env;
28895
+ } catch (e) {
28896
+ console.warn('⚠️ VoiceSDK v2: Failed to build client env:', e);
28897
+ return {
28898
+ sdkVersion: true ? "2.47.2" : 0
28899
+ };
28900
+ }
28901
+ }
28902
+
28903
+ /**
28904
+ * One-shot telemetry sent right after recording starts (t:'client_audio_info').
28905
+ * Reports the capture track's ACTUAL settings from getSettings() — echoCancellation /
28906
+ * noiseSuppression / autoGainControl as the browser really applied them (not what we
28907
+ * requested), which is the ground truth for diagnosing speakerphone echo barge-in —
28908
+ * plus mic label, AudioContext rates, and whether the minimal-constraints fallback fired.
28909
+ */
28910
+ }, {
28911
+ key: "_sendClientAudioInfo",
28912
+ value: function _sendClientAudioInfo() {
28913
+ try {
28914
+ var _this$audioRecorder, _this$audioRecorder$g, _s$echoCancellation, _s$noiseSuppression, _s$autoGainControl, _s$voiceIsolation, _s$sampleRate, _s$channelCount, _this$audioRecorder2, _this$audioPlayer, _this$audioRecorder3;
28915
+ var track = (_this$audioRecorder = this.audioRecorder) === null || _this$audioRecorder === void 0 || (_this$audioRecorder = _this$audioRecorder.mediaStream) === null || _this$audioRecorder === void 0 || (_this$audioRecorder$g = _this$audioRecorder.getAudioTracks) === null || _this$audioRecorder$g === void 0 ? void 0 : _this$audioRecorder$g.call(_this$audioRecorder)[0];
28916
+ if (!track) return;
28917
+ var s = track.getSettings ? track.getSettings() : {};
28918
+ var audio = {
28919
+ aec: (_s$echoCancellation = s.echoCancellation) !== null && _s$echoCancellation !== void 0 ? _s$echoCancellation : null,
28920
+ ns: (_s$noiseSuppression = s.noiseSuppression) !== null && _s$noiseSuppression !== void 0 ? _s$noiseSuppression : null,
28921
+ agc: (_s$autoGainControl = s.autoGainControl) !== null && _s$autoGainControl !== void 0 ? _s$autoGainControl : null,
28922
+ voiceIsolation: (_s$voiceIsolation = s.voiceIsolation) !== null && _s$voiceIsolation !== void 0 ? _s$voiceIsolation : null,
28923
+ trackRate: (_s$sampleRate = s.sampleRate) !== null && _s$sampleRate !== void 0 ? _s$sampleRate : null,
28924
+ channels: (_s$channelCount = s.channelCount) !== null && _s$channelCount !== void 0 ? _s$channelCount : null,
28925
+ mic: track.label || null,
28926
+ captureCtxRate: ((_this$audioRecorder2 = this.audioRecorder) === null || _this$audioRecorder2 === void 0 || (_this$audioRecorder2 = _this$audioRecorder2.audioContext) === null || _this$audioRecorder2 === void 0 ? void 0 : _this$audioRecorder2.sampleRate) || null,
28927
+ playbackCtxRate: ((_this$audioPlayer = this.audioPlayer) === null || _this$audioPlayer === void 0 || (_this$audioPlayer = _this$audioPlayer.audioContext) === null || _this$audioPlayer === void 0 ? void 0 : _this$audioPlayer.sampleRate) || null,
28928
+ constraintsFallback: ((_this$audioRecorder3 = this.audioRecorder) === null || _this$audioRecorder3 === void 0 || (_this$audioRecorder3 = _this$audioRecorder3.mediaStream) === null || _this$audioRecorder3 === void 0 ? void 0 : _this$audioRecorder3._ttpConstraintsFallback) === true
28929
+ };
28930
+ this.sendMessage({
28931
+ t: 'client_audio_info',
28932
+ audio: audio
28933
+ });
28934
+ console.log('🎛️ VoiceSDK v2: Sent client_audio_info:', audio);
28935
+ } catch (e) {
28936
+ console.warn('⚠️ VoiceSDK v2: Failed to send client_audio_info:', e);
28937
+ }
28938
+ }
28812
28939
  }, {
28813
28940
  key: "sendHelloMessage",
28814
28941
  value: function () {
@@ -28909,9 +29036,12 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
28909
29036
 
28910
29037
  // Include SDK build time for debugging
28911
29038
  if (true) {
28912
- helloMessage.lastBuildTime = "2026-07-13T09:55:44.731Z";
29039
+ helloMessage.lastBuildTime = "2026-07-30T20:53:20.013Z";
28913
29040
  }
28914
29041
 
29042
+ // Client environment (device/browser/webview) for backend logs + Langfuse metadata
29043
+ helloMessage.client = this._buildClientEnv();
29044
+
28915
29045
  // Page context is intentionally NOT attached to the hello message.
28916
29046
  // The backend caches the system prompt across turns (Anthropic prompt
28917
29047
  // caching), so anything that varies per-page would invalidate the
@@ -29048,15 +29178,21 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
29048
29178
  this.stopAudioPlayback();
29049
29179
  break;
29050
29180
  case 'stop_playing':
29051
- // CRITICAL: Ignore stop_playing messages that arrive too soon after audio_start
29052
- // Backend sometimes sends stop_playing immediately after audio_start, which cuts sentences prematurely
29053
- // Only honor stop_playing if it's been at least 200ms since the last audio_start
29054
- var timeSinceAudioStart = Date.now() - this.lastAudioStartTime;
29055
- var MIN_STOP_PLAYING_DELAY_MS = 200; // 200ms grace period after audio_start
29056
-
29057
- if (timeSinceAudioStart < MIN_STOP_PLAYING_DELAY_MS) {
29058
- console.warn("\u26A0\uFE0F VoiceSDK v2: Ignoring premature stop_playing (".concat(timeSinceAudioStart, "ms after audio_start, minimum ").concat(MIN_STOP_PLAYING_DELAY_MS, "ms required)"));
29059
- break;
29181
+ // An id-bearing stop is a deliberate barge-in flush and is honored UNCONDITIONALLY:
29182
+ // the WebSocket delivers messages in order, so every segment queued locally was sent
29183
+ // BEFORE this stop and is covered by it. The old 200ms-after-audio_start heuristic
29184
+ // silently swallowed barge-in during long multi-sentence replies (an audio_start
29185
+ // arrives every few hundred ms, so the quiet window never opened) — it is kept ONLY
29186
+ // for legacy backends whose stop carries no segmentId.
29187
+ if (message.segmentId == null) {
29188
+ var timeSinceAudioStart = Date.now() - this.lastAudioStartTime;
29189
+ var MIN_STOP_PLAYING_DELAY_MS = 200; // legacy grace period after audio_start
29190
+ if (timeSinceAudioStart < MIN_STOP_PLAYING_DELAY_MS) {
29191
+ console.warn("\u26A0\uFE0F VoiceSDK v2: Ignoring premature legacy stop_playing (".concat(timeSinceAudioStart, "ms after audio_start, minimum ").concat(MIN_STOP_PLAYING_DELAY_MS, "ms required)"));
29192
+ break;
29193
+ }
29194
+ } else {
29195
+ console.log("\uD83D\uDED1 VoiceSDK v2: Barge-in stop_playing (through segmentId ".concat(message.segmentId, ") - flushing playback"));
29060
29196
  }
29061
29197
  this.emit('stopPlaying', message);
29062
29198
  this.stopAudioPlayback();
@@ -30384,7 +30520,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30384
30520
  }, {
30385
30521
  key: "pauseCall",
30386
30522
  value: function pauseCall() {
30387
- var _this$audioRecorder;
30523
+ var _this$audioRecorder4;
30388
30524
  if (this.isPaused) return;
30389
30525
  this.sendMessage({
30390
30526
  t: 'pause_call'
@@ -30394,7 +30530,7 @@ var VoiceSDK_v2 = /*#__PURE__*/function (_EventEmitter) {
30394
30530
  this.audioRecorder.stop();
30395
30531
  }
30396
30532
  // Flush AudioWorklet ring buffer
30397
- if ((_this$audioRecorder = this.audioRecorder) !== null && _this$audioRecorder !== void 0 && _this$audioRecorder.audioWorkletNode) {
30533
+ if ((_this$audioRecorder4 = this.audioRecorder) !== null && _this$audioRecorder4 !== void 0 && _this$audioRecorder4.audioWorkletNode) {
30398
30534
  this.audioRecorder.audioWorkletNode.port.postMessage({
30399
30535
  type: 'flush'
30400
30536
  });
@@ -34945,7 +35081,7 @@ var TTPChatWidget = /*#__PURE__*/function () {
34945
35081
  return;
34946
35082
  }
34947
35083
  this._ensureAboutStyles();
34948
- var version = true ? "2.46.3" : 0;
35084
+ var version = true ? "2.47.2" : 0;
34949
35085
  var convId = this._getLastConversationId();
34950
35086
  var t = function t(k, fb) {
34951
35087
  try {
@@ -37316,6 +37452,7 @@ __webpack_require__.r(__webpack_exports__);
37316
37452
  /* harmony export */ TextInterface: () => (/* binding */ TextInterface)
37317
37453
  /* harmony export */ });
37318
37454
  /* harmony import */ var _galleryHandler_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./galleryHandler.js */ "./src/widget/galleryHandler.js");
37455
+ /* harmony import */ var _markdown_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./markdown.js */ "./src/widget/markdown.js");
37319
37456
  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); }
37320
37457
  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 _n2 = 0, F = function F() {}; return { s: F, n: function n() { return _n2 >= r.length ? { done: !0 } : { done: !1, value: r[_n2++] }; }, 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; } } }; }
37321
37458
  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; } }
@@ -37335,6 +37472,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
37335
37472
  */
37336
37473
 
37337
37474
 
37475
+
37338
37476
  function panelSolidBackground(panel) {
37339
37477
  var bg = panel === null || panel === void 0 ? void 0 : panel.backgroundColor;
37340
37478
  if (typeof bg !== 'string' || !bg.startsWith('#')) return '#FFFFFF';
@@ -37376,6 +37514,9 @@ function rgbTupleFromChromeColor(color, fallbackTuple) {
37376
37514
  var TEXT_INPUT_MIN_HEIGHT_PX = 36;
37377
37515
  var TEXT_INPUT_MAX_HEIGHT_PX = 132;
37378
37516
 
37517
+ /** How often a streaming agent bubble is re-rendered from its markdown buffer (ms). */
37518
+ var STREAM_RENDER_INTERVAL_MS = 40;
37519
+
37379
37520
  /** Resolves voice primary / gradient strings to #rrggbb for CSS hex+alpha suffixes. */
37380
37521
  function firstHexFromVoiceColor(c, fallback) {
37381
37522
  if (c == null || typeof c !== 'string') return fallback;
@@ -37560,9 +37701,21 @@ var TextInterface = /*#__PURE__*/function () {
37560
37701
  }
37561
37702
  var accentRgb = rgbTupleFromChromeColor(sendButtonColor);
37562
37703
 
37704
+ // Markdown tokens for the agent bubble. Derived from the two themes so the
37705
+ // rendered structure (code, tables, rules) reads correctly on both.
37706
+ var mdOnDark = useVoiceTheme || !panelLight;
37707
+ var mdSubtleBg = mdOnDark ? 'rgba(255,255,255,0.10)' : 'rgba(15,23,42,0.045)';
37708
+ var mdSubtlerBg = mdOnDark ? 'rgba(255,255,255,0.05)' : 'rgba(15,23,42,0.022)';
37709
+ var mdBorder = mdOnDark ? 'rgba(255,255,255,0.20)' : 'rgba(15,23,42,0.10)';
37710
+ var mdRule = mdOnDark ? 'rgba(255,255,255,0.16)' : 'rgba(15,23,42,0.08)';
37711
+ var mdMuted = mdOnDark ? 'rgba(255,255,255,0.62)' : 'rgba(15,23,42,0.55)';
37712
+ var mdLink = mdOnDark ? '#c7d2fe' : sendButtonColor;
37713
+ var mdAccent = mdOnDark ? 'rgba(255,255,255,0.45)' : "rgba(".concat(accentRgb[0], ", ").concat(accentRgb[1], ", ").concat(accentRgb[2], ", 0.55)");
37714
+ var mdMono = "ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace";
37715
+
37563
37716
  // Add !important to display rules when not using Shadow DOM (to override theme CSS)
37564
37717
  var important = this.config.useShadowDOM === false ? ' !important' : '';
37565
- 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 ");
37718
+ 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 max-width: 88%").concat(important, ";\n }\n\n /* ---------------------------------------------------------------\n Rendered markdown inside the agent bubble.\n Everything is scoped to .message-bubble so host-page styles can't\n reach in and the user bubble (plain text) is untouched.\n Logical properties (padding-inline-start, border-inline-start) keep\n the layout correct in RTL locales such as Hebrew.\n ---------------------------------------------------------------- */\n .message-bubble.md, .message-bubble .md-seg { line-height: 1.55").concat(important, "; }\n .message-bubble .md-seg { display: block").concat(important, "; }\n\n .message-bubble p {\n margin: 0 0 10px").concat(important, ";\n padding: 0").concat(important, ";\n }\n /* Deliberately gated on .md / .md-seg rather than a bare\n \".message-bubble > *\": the VOICE live-transcript row reuses the\n .message-bubble class in this same shadow root, and its children\n (.live-badge, .ttp-cursor) are not ours to restyle. */\n .message-bubble.md > *:last-child,\n .message-bubble .md-seg:last-child > *:last-child { margin-bottom: 0").concat(important, "; }\n .message-bubble.md > *:first-child,\n .message-bubble .md-seg:first-child > *:first-child { margin-top: 0").concat(important, "; }\n\n .message-bubble .md-h {\n margin: 16px 0 7px").concat(important, ";\n padding: 0").concat(important, ";\n font-weight: 700").concat(important, ";\n line-height: 1.3").concat(important, ";\n color: inherit").concat(important, ";\n }\n .message-bubble .md-h1 { font-size: 1.18em").concat(important, "; }\n .message-bubble .md-h2 { font-size: 1.09em").concat(important, "; }\n .message-bubble .md-h3 { font-size: 1.02em").concat(important, "; }\n .message-bubble .md-h4,\n .message-bubble .md-h5,\n .message-bubble .md-h6 {\n font-size: 0.95em").concat(important, ";\n letter-spacing: 0.02em").concat(important, ";\n color: ").concat(mdMuted).concat(important, ";\n }\n\n /* Logical properties only \u2014 a physical padding-left/border-left after\n these would resolve to the same property and silently win in LTR. */\n .message-bubble ul, .message-bubble ol {\n margin: 8px 0 11px").concat(important, ";\n padding-inline-start: 1.45em").concat(important, ";\n list-style-position: outside").concat(important, ";\n }\n .message-bubble ul { list-style-type: disc").concat(important, "; }\n .message-bubble ol { list-style-type: decimal").concat(important, "; }\n .message-bubble ul ul { list-style-type: circle").concat(important, "; }\n .message-bubble li {\n margin: 0 0 5px").concat(important, ";\n padding: 0").concat(important, ";\n line-height: 1.5").concat(important, ";\n }\n .message-bubble li:last-child { margin-bottom: 0").concat(important, "; }\n .message-bubble li::marker { color: ").concat(mdAccent).concat(important, "; }\n .message-bubble li > ul, .message-bubble li > ol { margin: 5px 0 2px").concat(important, "; }\n .message-bubble li > p { margin: 0 0 5px").concat(important, "; }\n\n .message-bubble strong, .message-bubble b { font-weight: 700").concat(important, "; }\n .message-bubble em, .message-bubble i { font-style: italic").concat(important, "; }\n .message-bubble del { text-decoration: line-through").concat(important, "; opacity: 0.7").concat(important, "; }\n\n .message-bubble a {\n color: ").concat(mdLink).concat(important, ";\n text-decoration: underline").concat(important, ";\n text-underline-offset: 2px").concat(important, ";\n word-break: break-word").concat(important, ";\n }\n .message-bubble a:hover { opacity: 0.82").concat(important, "; }\n\n .message-bubble code {\n font-family: ").concat(mdMono).concat(important, ";\n font-size: 0.88em").concat(important, ";\n background: ").concat(mdSubtleBg).concat(important, ";\n border: 1px solid ").concat(mdBorder).concat(important, ";\n border-radius: 5px").concat(important, ";\n padding: 1px 5px").concat(important, ";\n white-space: break-spaces").concat(important, ";\n word-break: break-word").concat(important, ";\n direction: ltr").concat(important, ";\n unicode-bidi: embed").concat(important, ";\n }\n .message-bubble pre {\n margin: 10px 0 11px").concat(important, ";\n padding: 10px 12px").concat(important, ";\n background: ").concat(mdSubtleBg).concat(important, ";\n border: 1px solid ").concat(mdBorder).concat(important, ";\n border-radius: 10px").concat(important, ";\n overflow-x: auto").concat(important, ";\n direction: ltr").concat(important, ";\n text-align: left").concat(important, ";\n }\n .message-bubble pre code {\n background: none").concat(important, ";\n border: none").concat(important, ";\n border-radius: 0").concat(important, ";\n padding: 0").concat(important, ";\n font-size: 0.85em").concat(important, ";\n line-height: 1.5").concat(important, ";\n white-space: pre").concat(important, ";\n word-break: normal").concat(important, ";\n }\n\n .message-bubble blockquote {\n margin: 10px 0").concat(important, ";\n padding-block: 2px").concat(important, ";\n padding-inline-start: 12px").concat(important, ";\n padding-inline-end: 0").concat(important, ";\n border-inline-start: 3px solid ").concat(mdAccent).concat(important, ";\n color: ").concat(mdMuted).concat(important, ";\n }\n .message-bubble blockquote > *:last-child { margin-bottom: 0").concat(important, "; }\n\n .message-bubble hr {\n border: none").concat(important, ";\n border-top: 1px solid ").concat(mdRule).concat(important, ";\n height: 0").concat(important, ";\n margin: 13px 0").concat(important, ";\n }\n\n .message-bubble .md-table-wrap {\n margin: 10px 0 11px").concat(important, ";\n overflow-x: auto").concat(important, ";\n border: 1px solid ").concat(mdBorder).concat(important, ";\n border-radius: 9px").concat(important, ";\n }\n .message-bubble table {\n border-collapse: collapse").concat(important, ";\n width: 100%").concat(important, ";\n font-size: 0.93em").concat(important, ";\n }\n .message-bubble th, .message-bubble td {\n padding: 7px 11px").concat(important, ";\n text-align: start").concat(important, ";\n border-bottom: 1px solid ").concat(mdBorder).concat(important, ";\n vertical-align: top").concat(important, ";\n }\n .message-bubble thead th {\n background: ").concat(mdSubtleBg).concat(important, ";\n font-weight: 700").concat(important, ";\n }\n .message-bubble tbody tr:nth-child(even) { background: ").concat(mdSubtlerBg).concat(important, "; }\n .message-bubble tbody tr:last-child td { border-bottom: none").concat(important, "; }\n\n .message-bubble .md-img {\n display: block").concat(important, ";\n max-width: 100%").concat(important, ";\n height: auto").concat(important, ";\n border-radius: 8px").concat(important, ";\n margin: 8px 0").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 /* structured answers can be tall \u2014 keep the avatar next to the first line */\n align-items: flex-start").concat(important, ";\n }\n .message.agent .message-avatar { margin-top: 7px").concat(important, "; }\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 ");
37566
37719
  }
37567
37720
 
37568
37721
  /**
@@ -37815,7 +37968,13 @@ var TextInterface = /*#__PURE__*/function () {
37815
37968
  avatar.textContent = avatarIcon;
37816
37969
  var bubble = document.createElement('div');
37817
37970
  bubble.className = 'message-bubble';
37818
- bubble.textContent = text;
37971
+ if (type === 'agent') {
37972
+ // Agent copy is markdown — render it so lists/bold/tables look designed.
37973
+ bubble.classList.add('md');
37974
+ (0,_markdown_js__WEBPACK_IMPORTED_MODULE_1__.renderMarkdownInto)(bubble, text);
37975
+ } else {
37976
+ bubble.textContent = text;
37977
+ }
37819
37978
 
37820
37979
  // Order is controlled by edgeClass via flex-direction
37821
37980
  message.appendChild(avatar);
@@ -37853,9 +38012,87 @@ var TextInterface = /*#__PURE__*/function () {
37853
38012
  this.streamingEl = bubble;
37854
38013
  this.hasStartedStreaming = false;
37855
38014
  this._pendingMedia = []; // Buffer for images that arrive before next text chunk
38015
+ this._streamBuffer = ''; // Raw markdown of the segment currently being written
38016
+ this._streamSegmentEl = null; // The .md-seg element that buffer renders into
37856
38017
  messages.scrollTop = messages.scrollHeight;
37857
38018
  }
37858
38019
 
38020
+ /** Drop the typing dots the first time real content arrives. */
38021
+ }, {
38022
+ key: "_clearTypingIndicator",
38023
+ value: function _clearTypingIndicator() {
38024
+ if (this.hasStartedStreaming) return;
38025
+ var dots = this.streamingEl && this.streamingEl.querySelector('.typing-indicator');
38026
+ if (dots) dots.remove();
38027
+ this.hasStartedStreaming = true;
38028
+ }
38029
+
38030
+ /**
38031
+ * The bubble is a sequence of text segments and image galleries. Text is kept
38032
+ * as raw markdown per segment and re-rendered in place as tokens arrive, so a
38033
+ * list or table only "snaps" into shape once its syntax is complete. A gallery
38034
+ * closes the current segment, and the text after it starts a fresh one.
38035
+ */
38036
+ }, {
38037
+ key: "_ensureStreamSegment",
38038
+ value: function _ensureStreamSegment() {
38039
+ if (this._streamSegmentEl || !this.streamingEl) return this._streamSegmentEl;
38040
+ var seg = document.createElement('div');
38041
+ seg.className = 'md-seg md';
38042
+ this.streamingEl.appendChild(seg);
38043
+ this._streamSegmentEl = seg;
38044
+ return seg;
38045
+ }
38046
+ }, {
38047
+ key: "_renderStreamSegment",
38048
+ value: function _renderStreamSegment() {
38049
+ this._cancelStreamRender();
38050
+ if (!this._streamBuffer) return;
38051
+ var seg = this._ensureStreamSegment();
38052
+ if (seg) (0,_markdown_js__WEBPACK_IMPORTED_MODULE_1__.renderMarkdownInto)(seg, this._streamBuffer);
38053
+ }
38054
+
38055
+ /**
38056
+ * Coalesce re-renders — tokens arrive faster than a person can read.
38057
+ * Deliberately a timer and not requestAnimationFrame: rAF is suspended in
38058
+ * background tabs, so a user who switches away mid-answer would come back to
38059
+ * a bubble that never grew. setTimeout is only throttled, not stopped.
38060
+ */
38061
+ }, {
38062
+ key: "_scheduleStreamRender",
38063
+ value: function _scheduleStreamRender() {
38064
+ var _this3 = this;
38065
+ if (this._streamTimer) return;
38066
+ this._streamTimer = setTimeout(function () {
38067
+ _this3._streamTimer = null;
38068
+ if (!_this3.streamingEl) return;
38069
+ _this3._renderStreamSegment();
38070
+ _this3._scrollToBottom();
38071
+ }, STREAM_RENDER_INTERVAL_MS);
38072
+ }
38073
+ }, {
38074
+ key: "_cancelStreamRender",
38075
+ value: function _cancelStreamRender() {
38076
+ if (!this._streamTimer) return;
38077
+ clearTimeout(this._streamTimer);
38078
+ this._streamTimer = null;
38079
+ }
38080
+
38081
+ /** Final render of the open segment, then start a new one for later text. */
38082
+ }, {
38083
+ key: "_closeStreamSegment",
38084
+ value: function _closeStreamSegment() {
38085
+ this._renderStreamSegment();
38086
+ this._streamBuffer = '';
38087
+ this._streamSegmentEl = null;
38088
+ }
38089
+ }, {
38090
+ key: "_scrollToBottom",
38091
+ value: function _scrollToBottom() {
38092
+ var messages = this.shadowRoot.getElementById('messagesContainer');
38093
+ if (messages) messages.scrollTop = messages.scrollHeight;
38094
+ }
38095
+
37859
38096
  /**
37860
38097
  * Append chunk to streaming response.
37861
38098
  * Does NOT flush buffered media here — media is flushed only when the
@@ -37865,24 +38102,10 @@ var TextInterface = /*#__PURE__*/function () {
37865
38102
  }, {
37866
38103
  key: "appendStreamingChunk",
37867
38104
  value: function appendStreamingChunk(chunk) {
37868
- if (!this.streamingEl) return;
37869
- if (!this.hasStartedStreaming) {
37870
- // remove typing indicator on first content
37871
- this.streamingEl.textContent = '';
37872
- this.hasStartedStreaming = true;
37873
- }
37874
-
37875
- // Append text — use textNode if we have inline media DOM elements
37876
- var hasInlineMedia = this.streamingEl.querySelector('.inline-media-gallery');
37877
- if (hasInlineMedia) {
37878
- this.streamingEl.appendChild(document.createTextNode(chunk));
37879
- } else {
37880
- this.streamingEl.textContent += chunk;
37881
- }
37882
- var messages = this.shadowRoot.getElementById('messagesContainer');
37883
- if (messages) {
37884
- messages.scrollTop = messages.scrollHeight;
37885
- }
38105
+ if (!this.streamingEl || typeof chunk !== 'string' || chunk === '') return;
38106
+ this._clearTypingIndicator();
38107
+ this._streamBuffer += chunk;
38108
+ this._scheduleStreamRender();
37886
38109
  }
37887
38110
 
37888
38111
  /**
@@ -37903,10 +38126,7 @@ var TextInterface = /*#__PURE__*/function () {
37903
38126
  key: "appendStreamingMedia",
37904
38127
  value: function appendStreamingMedia(images, title) {
37905
38128
  if (!this.streamingEl) return;
37906
- if (!this.hasStartedStreaming) {
37907
- this.streamingEl.textContent = '';
37908
- this.hasStartedStreaming = true;
37909
- }
38129
+ this._clearTypingIndicator();
37910
38130
 
37911
38131
  // Flush previous media — the text describing those images is complete
37912
38132
  this._flushPendingMedia();
@@ -37927,6 +38147,10 @@ var TextInterface = /*#__PURE__*/function () {
37927
38147
  value: function _flushPendingMedia() {
37928
38148
  if (!this._pendingMedia || this._pendingMedia.length === 0) return;
37929
38149
  if (!this.streamingEl) return;
38150
+
38151
+ // Commit the text written so far before the galleries land under it, so the
38152
+ // text that follows them renders as its own segment instead of being merged.
38153
+ this._closeStreamSegment();
37930
38154
  var _iterator = _createForOfIteratorHelper(this._pendingMedia),
37931
38155
  _step;
37932
38156
  try {
@@ -37950,7 +38174,7 @@ var TextInterface = /*#__PURE__*/function () {
37950
38174
  }, {
37951
38175
  key: "_renderInlineMedia",
37952
38176
  value: function _renderInlineMedia(images, title) {
37953
- var _this3 = this;
38177
+ var _this4 = this;
37954
38178
  var gallery = document.createElement('div');
37955
38179
  gallery.className = 'inline-media-gallery';
37956
38180
  gallery.style.cssText = 'display:flex;gap:6px;flex-wrap:wrap;margin:8px 0;';
@@ -37969,10 +38193,10 @@ var TextInterface = /*#__PURE__*/function () {
37969
38193
 
37970
38194
  // Open fullscreen gallery viewer on click (same as voice gallery)
37971
38195
  imgEl.addEventListener('click', function () {
37972
- if (!_this3._inlineGalleryHandler) {
37973
- _this3._inlineGalleryHandler = (0,_galleryHandler_js__WEBPACK_IMPORTED_MODULE_0__.createGalleryHandler)(_this3._widget);
38196
+ if (!_this4._inlineGalleryHandler) {
38197
+ _this4._inlineGalleryHandler = (0,_galleryHandler_js__WEBPACK_IMPORTED_MODULE_0__.createGalleryHandler)(_this4._widget);
37974
38198
  }
37975
- _this3._inlineGalleryHandler.handleShowMedia({
38199
+ _this4._inlineGalleryHandler.handleShowMedia({
37976
38200
  images: images,
37977
38201
  title: title
37978
38202
  });
@@ -38008,17 +38232,22 @@ var TextInterface = /*#__PURE__*/function () {
38008
38232
  key: "finalizeStreaming",
38009
38233
  value: function finalizeStreaming(fullText) {
38010
38234
  if (this.streamingEl) {
38011
- // Flush any remaining buffered media
38012
- this._flushPendingMedia();
38235
+ this._cancelStreamRender();
38236
+ // An empty reply must not leave the typing dots spinning forever.
38237
+ this._clearTypingIndicator();
38238
+ var hasGalleries = !!this.streamingEl.querySelector('.inline-media-gallery') || this._pendingMedia && this._pendingMedia.length > 0;
38013
38239
 
38014
- // If we have inline media (child elements beyond text), don't overwrite with textContent
38015
- // as that would destroy the gallery DOM elements.
38016
- var hasInlineMedia = this.streamingEl.querySelector('.inline-media-gallery');
38017
- if (!hasInlineMedia) {
38018
- this.streamingEl.textContent = fullText || this.streamingEl.textContent;
38240
+ // Without interleaved media the `done` payload is the authoritative text,
38241
+ // so re-render from it. With media, the bubble is a mix of segments and
38242
+ // galleries that fullText can't describe — keep what streaming produced.
38243
+ if (!hasGalleries && typeof fullText === 'string' && fullText) {
38244
+ this._streamBuffer = fullText;
38019
38245
  }
38020
- // If hasInlineMedia, keep the DOM as-is — it already has text nodes + gallery elements
38246
+ this._renderStreamSegment();
38021
38247
 
38248
+ // Flush trailing galleries (also commits the segment above them)
38249
+ this._flushPendingMedia();
38250
+ this._closeStreamSegment();
38022
38251
  var container = this.shadowRoot.getElementById('agent-streaming');
38023
38252
  if (container) container.id = '';
38024
38253
  this.streamingEl = null;
@@ -38033,10 +38262,13 @@ var TextInterface = /*#__PURE__*/function () {
38033
38262
  }, {
38034
38263
  key: "stopStreamingState",
38035
38264
  value: function stopStreamingState() {
38265
+ this._cancelStreamRender();
38036
38266
  var existing = this.shadowRoot.getElementById('agent-streaming');
38037
38267
  if (existing) existing.remove();
38038
38268
  this.streamingEl = null;
38039
38269
  this.hasStartedStreaming = false;
38270
+ this._streamBuffer = '';
38271
+ this._streamSegmentEl = null;
38040
38272
  }
38041
38273
 
38042
38274
  /**
@@ -38908,7 +39140,7 @@ var VoiceInterface = /*#__PURE__*/function () {
38908
39140
  value: function () {
38909
39141
  var _proceedWithVoiceCall = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
38910
39142
  var _this3 = this;
38911
- var isResumeCall, panel, header, toggleText, originalSection, compactSection, idleState, _idleState, activeState, stripOnly, voiceInterface, connected, serverRejected, originalOnError, originalOnDisconnected, attempts, _vs, vs, hasDisclaimers, sendDisclaimerAckAfterMobileMicReady, _vs$lastDisclaimerPay, disclaimerTexts, _this$config$inputFor, _this$config$connecti, micStream, primeSr, floatingButton, mobileFab, delayConfig, streamToUse, _this$sdk$voiceSDK, error, existingBar, _existingBar, _idleState2, _floatingButton, updateTimer, isDisclaimerDeclined, deviceInfo, _t, _t2, _t3, _t4, _t5;
39143
+ var isResumeCall, panel, header, toggleText, originalSection, compactSection, idleState, _idleState, activeState, stripOnly, voiceInterface, connected, serverRejected, originalOnError, originalOnDisconnected, attempts, _vs, vs, hasDisclaimers, sendDisclaimerAckAfterMobileMicReady, _vs$lastDisclaimerPay, disclaimerTexts, _this$config$inputFor, _this$config$connecti, primeSr, audioConstraints, micStream, floatingButton, mobileFab, delayConfig, streamToUse, _this$sdk$voiceSDK, error, existingBar, _existingBar, _idleState2, _floatingButton, updateTimer, isDisclaimerDeclined, deviceInfo, _t, _t2, _t3, _t4, _t5, _t6;
38912
39144
  return _regenerator().w(function (_context4) {
38913
39145
  while (1) switch (_context4.p = _context4.n) {
38914
39146
  case 0:
@@ -39150,21 +39382,44 @@ var VoiceInterface = /*#__PURE__*/function () {
39150
39382
 
39151
39383
  // On mobile: get getUserMedia stream once, pass to startListening to avoid double-call
39152
39384
  if (!this.isMobile) {
39153
- _context4.n = 20;
39385
+ _context4.n = 24;
39154
39386
  break;
39155
39387
  }
39156
39388
  _context4.p = 15;
39157
- _context4.n = 16;
39389
+ // This preliminary stream is REUSED as the capture stream (iOS single-stream fix),
39390
+ // so it must carry the same AEC constraints as the desktop path — bare { audio: true }
39391
+ // leaves echoCancellation/noiseSuppression/autoGainControl (and iOS voiceIsolation) to
39392
+ // browser defaults, which can let speakerphone TTS echo cross VAD and trigger barge-in.
39393
+ primeSr = ((_this$config$inputFor = this.config.inputFormat) === null || _this$config$inputFor === void 0 ? void 0 : _this$config$inputFor.sampleRate) || this.config.sampleRate || 16000;
39394
+ audioConstraints = (0,_core_AudioRecorder_js__WEBPACK_IMPORTED_MODULE_1__.buildAudioConstraints)(primeSr, this.config.audioConstraints || {});
39395
+ _context4.p = 16;
39396
+ _context4.n = 17;
39397
+ return navigator.mediaDevices.getUserMedia({
39398
+ audio: audioConstraints
39399
+ });
39400
+ case 17:
39401
+ micStream = _context4.v;
39402
+ _context4.n = 20;
39403
+ break;
39404
+ case 18:
39405
+ _context4.p = 18;
39406
+ _t2 = _context4.v;
39407
+ // Some devices/webviews reject specific constraints (OverconstrainedError etc.).
39408
+ // Fall back to minimal so permission still succeeds, matching prior behavior.
39409
+ console.warn('⚠️ Mobile getUserMedia with AEC constraints failed, retrying minimal:', (_t2 === null || _t2 === void 0 ? void 0 : _t2.name) || _t2);
39410
+ _context4.n = 19;
39158
39411
  return navigator.mediaDevices.getUserMedia({
39159
39412
  audio: true
39160
39413
  });
39161
- case 16:
39414
+ case 19:
39162
39415
  micStream = _context4.v;
39416
+ // Flagged so client_audio_info telemetry reports the AEC constraints were NOT applied
39417
+ micStream._ttpConstraintsFallback = true;
39418
+ case 20:
39163
39419
  this._preliminaryInputStream = micStream;
39164
- primeSr = ((_this$config$inputFor = this.config.inputFormat) === null || _this$config$inputFor === void 0 ? void 0 : _this$config$inputFor.sampleRate) || this.config.sampleRate || 16000;
39165
- _context4.n = 17;
39420
+ _context4.n = 21;
39166
39421
  return _core_AudioRecorder_js__WEBPACK_IMPORTED_MODULE_1__["default"].primeSharedContext(primeSr);
39167
- case 17:
39422
+ case 21:
39168
39423
  // Hide floating button now that permission is granted
39169
39424
  floatingButton = this.shadowRoot.getElementById('text-chat-button') || document.getElementById('text-chat-button');
39170
39425
  if (floatingButton) floatingButton.style.display = 'none';
@@ -39177,51 +39432,51 @@ var VoiceInterface = /*#__PURE__*/function () {
39177
39432
  android: 3000,
39178
39433
  ios: 0
39179
39434
  };
39180
- _context4.n = 18;
39435
+ _context4.n = 22;
39181
39436
  return (0,_shared_applyDelay_js__WEBPACK_IMPORTED_MODULE_3__.applyDelay)(delayConfig);
39182
- case 18:
39437
+ case 22:
39183
39438
  if (sendDisclaimerAckAfterMobileMicReady) {
39184
39439
  vs.sendDisclaimerAck(true);
39185
39440
  sendDisclaimerAckAfterMobileMicReady = false;
39186
39441
  }
39187
- _context4.n = 20;
39442
+ _context4.n = 24;
39188
39443
  break;
39189
- case 19:
39190
- _context4.p = 19;
39191
- _t2 = _context4.v;
39192
- console.error('❌ Microphone permission denied:', _t2);
39444
+ case 23:
39445
+ _context4.p = 23;
39446
+ _t3 = _context4.v;
39447
+ console.error('❌ Microphone permission denied:', _t3);
39193
39448
  this._stopPreliminaryInputStream();
39194
- throw _t2;
39195
- case 20:
39196
- _context4.p = 20;
39449
+ throw _t3;
39450
+ case 24:
39451
+ _context4.p = 24;
39197
39452
  streamToUse = this._preliminaryInputStream || null;
39198
39453
  if (streamToUse) this._preliminaryInputStream = null;
39199
- _context4.n = 21;
39454
+ _context4.n = 25;
39200
39455
  return this.sdk.startListening(streamToUse);
39201
- case 21:
39456
+ case 25:
39202
39457
  if (!(!this.sdk.isConnected || !this.sdk.voiceSDK || !this.sdk.voiceSDK.isConnected || !this.sdk.voiceSDK.websocket || this.sdk.voiceSDK.websocket.readyState !== WebSocket.OPEN)) {
39203
- _context4.n = 26;
39458
+ _context4.n = 30;
39204
39459
  break;
39205
39460
  }
39206
39461
  if (!((_this$sdk$voiceSDK = this.sdk.voiceSDK) !== null && _this$sdk$voiceSDK !== void 0 && _this$sdk$voiceSDK.isRecording)) {
39207
- _context4.n = 25;
39462
+ _context4.n = 29;
39208
39463
  break;
39209
39464
  }
39210
- _context4.p = 22;
39211
- _context4.n = 23;
39465
+ _context4.p = 26;
39466
+ _context4.n = 27;
39212
39467
  return this.sdk.voiceSDK.stopRecording();
39213
- case 23:
39214
- _context4.n = 25;
39468
+ case 27:
39469
+ _context4.n = 29;
39215
39470
  break;
39216
- case 24:
39217
- _context4.p = 24;
39218
- _t3 = _context4.v;
39219
- case 25:
39471
+ case 28:
39472
+ _context4.p = 28;
39473
+ _t4 = _context4.v;
39474
+ case 29:
39220
39475
  error = new Error('Connection lost - server may have rejected the call');
39221
39476
  error.name = 'ServerRejected';
39222
39477
  error.isServerRejection = true;
39223
39478
  throw error;
39224
- case 26:
39479
+ case 30:
39225
39480
  console.log('🎤 Started listening - permission granted');
39226
39481
  this._stopPreliminaryInputStream();
39227
39482
  this.isActive = true;
@@ -39244,19 +39499,19 @@ var VoiceInterface = /*#__PURE__*/function () {
39244
39499
  this.startDesktopWaveformAnimation();
39245
39500
  this.desktop.startLiveWaveformInterval();
39246
39501
  }
39247
- _context4.n = 29;
39502
+ _context4.n = 33;
39248
39503
  break;
39249
- case 27:
39250
- _context4.p = 27;
39251
- _t4 = _context4.v;
39252
- if (!(_t4.isServerRejection || _t4.name === 'ServerRejected')) {
39253
- _context4.n = 28;
39504
+ case 31:
39505
+ _context4.p = 31;
39506
+ _t5 = _context4.v;
39507
+ if (!(_t5.isServerRejection || _t5.name === 'ServerRejected')) {
39508
+ _context4.n = 32;
39254
39509
  break;
39255
39510
  }
39256
39511
  this.resetConnectingState();
39257
- throw _t4;
39258
- case 28:
39259
- console.error('❌ Failed to start listening:', _t4);
39512
+ throw _t5;
39513
+ case 32:
39514
+ console.error('❌ Failed to start listening:', _t5);
39260
39515
  this.resetConnectingState();
39261
39516
  if (this.isMobile) {
39262
39517
  _existingBar = document.getElementById('mobile-voice-call-bar-container');
@@ -39271,8 +39526,8 @@ var VoiceInterface = /*#__PURE__*/function () {
39271
39526
  this.config.onDesktopMinimizedStripLauncherRestore();
39272
39527
  } catch (_) {}
39273
39528
  }
39274
- throw _t4;
39275
- case 29:
39529
+ throw _t5;
39530
+ case 33:
39276
39531
  // Start timer (desktop only - mobile bar owns its own timer)
39277
39532
  if (!this.isMobile && !this.callStartTime) {
39278
39533
  this.callStartTime = Date.now();
@@ -39291,14 +39546,14 @@ var VoiceInterface = /*#__PURE__*/function () {
39291
39546
  }, 100);
39292
39547
  }
39293
39548
  console.log('✅ Voice call started successfully');
39294
- _context4.n = 36;
39549
+ _context4.n = 40;
39295
39550
  break;
39296
- case 30:
39297
- _context4.p = 30;
39298
- _t5 = _context4.v;
39299
- isDisclaimerDeclined = _t5 && _t5.message === 'DISCLAIMER_DECLINED'; // Handle server rejection gracefully (don't log as error)
39300
- if (!(_t5.isServerRejection || _t5.name === 'ServerRejected') && !isDisclaimerDeclined) {
39301
- console.error('❌ Error starting voice call:', _t5);
39551
+ case 34:
39552
+ _context4.p = 34;
39553
+ _t6 = _context4.v;
39554
+ isDisclaimerDeclined = _t6 && _t6.message === 'DISCLAIMER_DECLINED'; // Handle server rejection gracefully (don't log as error)
39555
+ if (!(_t6.isServerRejection || _t6.name === 'ServerRejected') && !isDisclaimerDeclined) {
39556
+ console.error('❌ Error starting voice call:', _t6);
39302
39557
  }
39303
39558
  this._stopPreliminaryInputStream();
39304
39559
 
@@ -39330,7 +39585,7 @@ var VoiceInterface = /*#__PURE__*/function () {
39330
39585
  // User declined server disclaimer: full idle reset (not just resetConnectingState —
39331
39586
  // connect path shows voiceActiveState on desktop; without hiding it, call UI stays visible).
39332
39587
  if (!isDisclaimerDeclined) {
39333
- _context4.n = 31;
39588
+ _context4.n = 35;
39334
39589
  break;
39335
39590
  }
39336
39591
  this.resetUIState();
@@ -39340,12 +39595,12 @@ var VoiceInterface = /*#__PURE__*/function () {
39340
39595
  this.config.onCallEnd();
39341
39596
  }
39342
39597
  return _context4.a(2);
39343
- case 31:
39598
+ case 35:
39344
39599
  this.resetConnectingState();
39345
39600
 
39346
39601
  // Handle specific error types with appropriate modals
39347
- if (!(_t5.name === 'NotAllowedError' || _t5.name === 'PermissionDeniedError')) {
39348
- _context4.n = 32;
39602
+ if (!(_t6.name === 'NotAllowedError' || _t6.name === 'PermissionDeniedError')) {
39603
+ _context4.n = 36;
39349
39604
  break;
39350
39605
  }
39351
39606
  // Permission denied - show blocked modal
@@ -39357,37 +39612,37 @@ var VoiceInterface = /*#__PURE__*/function () {
39357
39612
  // User clicked refresh
39358
39613
  window.location.reload();
39359
39614
  });
39360
- _context4.n = 36;
39615
+ _context4.n = 40;
39361
39616
  break;
39362
- case 32:
39363
- if (!(_t5.name === 'NotFoundError' || _t5.name === 'DevicesNotFoundError')) {
39364
- _context4.n = 33;
39617
+ case 36:
39618
+ if (!(_t6.name === 'NotFoundError' || _t6.name === 'DevicesNotFoundError')) {
39619
+ _context4.n = 37;
39365
39620
  break;
39366
39621
  }
39367
39622
  // No microphone found - show no mic modal
39368
39623
  (0,_shared_MicPermissionModals_js__WEBPACK_IMPORTED_MODULE_4__.showNoMicrophoneModal)(function () {
39369
39624
  _this3.resetUIState();
39370
39625
  });
39371
- _context4.n = 36;
39626
+ _context4.n = 40;
39372
39627
  break;
39373
- case 33:
39374
- if (!(_t5 && (_t5.message === 'DOMAIN_NOT_WHITELISTED' || _t5.message && _t5.message.includes('Domain not whitelisted')))) {
39375
- _context4.n = 35;
39628
+ case 37:
39629
+ if (!(_t6 && (_t6.message === 'DOMAIN_NOT_WHITELISTED' || _t6.message && _t6.message.includes('Domain not whitelisted')))) {
39630
+ _context4.n = 39;
39376
39631
  break;
39377
39632
  }
39378
- _context4.n = 34;
39633
+ _context4.n = 38;
39379
39634
  return this.endCallOnServerRejection();
39380
- case 34:
39381
- _context4.n = 36;
39635
+ case 38:
39636
+ _context4.n = 40;
39382
39637
  break;
39383
- case 35:
39638
+ case 39:
39384
39639
  // Other errors - show in transcript
39385
- this.showError(_t5.message || _t5);
39640
+ this.showError(_t6.message || _t6);
39386
39641
  this.resetUIState();
39387
- case 36:
39642
+ case 40:
39388
39643
  return _context4.a(2);
39389
39644
  }
39390
- }, _callee4, this, [[22, 24], [20, 27], [15, 19], [2, 4], [0, 30]]);
39645
+ }, _callee4, this, [[26, 28], [24, 31], [16, 18], [15, 23], [2, 4], [0, 34]]);
39391
39646
  }));
39392
39647
  function proceedWithVoiceCall() {
39393
39648
  return _proceedWithVoiceCall.apply(this, arguments);
@@ -39451,7 +39706,7 @@ var VoiceInterface = /*#__PURE__*/function () {
39451
39706
  key: "endCallOnServerRejection",
39452
39707
  value: (function () {
39453
39708
  var _endCallOnServerRejection = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
39454
- var voiceInterfaceEl, landingScreen, _this$sdk$voiceSDK2, _this$sdk$voiceSDK3, audioRecorder, isRecording, _this$sdk$voiceSDK4, activeState, idleState, floatingButton, fallbackButton, mobileFabEnd, errorMsg, _t6, _t7, _t8, _t9, _t0;
39709
+ var voiceInterfaceEl, landingScreen, _this$sdk$voiceSDK2, _this$sdk$voiceSDK3, audioRecorder, isRecording, _this$sdk$voiceSDK4, activeState, idleState, floatingButton, fallbackButton, mobileFabEnd, errorMsg, _t7, _t8, _t9, _t0, _t1;
39455
39710
  return _regenerator().w(function (_context5) {
39456
39711
  while (1) switch (_context5.p = _context5.n) {
39457
39712
  case 0:
@@ -39513,8 +39768,8 @@ var VoiceInterface = /*#__PURE__*/function () {
39513
39768
  break;
39514
39769
  case 4:
39515
39770
  _context5.p = 4;
39516
- _t6 = _context5.v;
39517
- console.warn('Error sending stop message to AudioWorklet:', _t6);
39771
+ _t7 = _context5.v;
39772
+ console.warn('Error sending stop message to AudioWorklet:', _t7);
39518
39773
  case 5:
39519
39774
  if (!(this.sdk.voiceSDK && typeof this.sdk.voiceSDK.stopRecording === 'function')) {
39520
39775
  _context5.n = 10;
@@ -39529,8 +39784,8 @@ var VoiceInterface = /*#__PURE__*/function () {
39529
39784
  break;
39530
39785
  case 8:
39531
39786
  _context5.p = 8;
39532
- _t7 = _context5.v;
39533
- console.warn('Error calling stopRecording:', _t7);
39787
+ _t8 = _context5.v;
39788
+ console.warn('Error calling stopRecording:', _t8);
39534
39789
  case 9:
39535
39790
  _context5.n = 14;
39536
39791
  break;
@@ -39550,8 +39805,8 @@ var VoiceInterface = /*#__PURE__*/function () {
39550
39805
  break;
39551
39806
  case 13:
39552
39807
  _context5.p = 13;
39553
- _t8 = _context5.v;
39554
- console.warn('Error calling stopListening:', _t8);
39808
+ _t9 = _context5.v;
39809
+ console.warn('Error calling stopListening:', _t9);
39555
39810
  case 14:
39556
39811
  if (!(audioRecorder && typeof audioRecorder.stop === 'function')) {
39557
39812
  _context5.n = 18;
@@ -39566,8 +39821,8 @@ var VoiceInterface = /*#__PURE__*/function () {
39566
39821
  break;
39567
39822
  case 17:
39568
39823
  _context5.p = 17;
39569
- _t9 = _context5.v;
39570
- console.warn('Error calling AudioRecorder.stop():', _t9);
39824
+ _t0 = _context5.v;
39825
+ console.warn('Error calling AudioRecorder.stop():', _t0);
39571
39826
  case 18:
39572
39827
  console.log('✅ Audio capture/VAD stopped');
39573
39828
  case 19:
@@ -39599,8 +39854,8 @@ var VoiceInterface = /*#__PURE__*/function () {
39599
39854
  break;
39600
39855
  case 20:
39601
39856
  _context5.p = 20;
39602
- _t0 = _context5.v;
39603
- console.warn('Error stopping listening on server rejection:', _t0);
39857
+ _t1 = _context5.v;
39858
+ console.warn('Error stopping listening on server rejection:', _t1);
39604
39859
  // Force stop media streams even if stopListening fails
39605
39860
  try {
39606
39861
  if ((_this$sdk$voiceSDK4 = this.sdk.voiceSDK) !== null && _this$sdk$voiceSDK4 !== void 0 && (_this$sdk$voiceSDK4 = _this$sdk$voiceSDK4.audioRecorder) !== null && _this$sdk$voiceSDK4 !== void 0 && _this$sdk$voiceSDK4.mediaStream) {
@@ -41037,6 +41292,829 @@ function createGalleryHandler(widget) {
41037
41292
 
41038
41293
  /***/ }),
41039
41294
 
41295
+ /***/ "./src/widget/markdown.js":
41296
+ /*!********************************!*\
41297
+ !*** ./src/widget/markdown.js ***!
41298
+ \********************************/
41299
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
41300
+
41301
+ "use strict";
41302
+ __webpack_require__.r(__webpack_exports__);
41303
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
41304
+ /* harmony export */ parseInline: () => (/* binding */ parseInline),
41305
+ /* harmony export */ parseMarkdown: () => (/* binding */ parseMarkdown),
41306
+ /* harmony export */ renderMarkdownInto: () => (/* binding */ renderMarkdownInto),
41307
+ /* harmony export */ safeHref: () => (/* binding */ safeHref)
41308
+ /* harmony export */ });
41309
+ 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; } } }; }
41310
+ 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; } }
41311
+ 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; }
41312
+ /**
41313
+ * markdown.js — tiny, dependency-free Markdown renderer for chat bubbles.
41314
+ *
41315
+ * Split in two halves on purpose:
41316
+ * parseMarkdown(text) -> block AST — pure JS, unit-tested in scripts/markdown.test.mjs
41317
+ * renderMarkdownInto(el, text) — walks the AST and builds real DOM nodes
41318
+ *
41319
+ * XSS: content is only ever placed with document.createTextNode / element
41320
+ * properties. Nothing is written through innerHTML, and link hrefs go through
41321
+ * a scheme allowlist (safeHref), so agent text can never inject markup.
41322
+ *
41323
+ * Streaming: the parser is total — an unterminated code fence, a half-written
41324
+ * bold run or a truncated table never throw, they just degrade to text. That
41325
+ * lets the caller re-render the whole accumulated buffer on every token.
41326
+ */
41327
+
41328
+ /* ------------------------------------------------------------------ */
41329
+ /* Block parsing */
41330
+ /* ------------------------------------------------------------------ */
41331
+
41332
+ var FENCE_RE = /^(\s*)(`{3,}|~{3,})\s*([^\s`]*)\s*$/;
41333
+ var HEADING_RE = /^(\s{0,3})(#{1,6})\s+(.*)$/;
41334
+ var HR_RE = /^\s{0,3}([-*_])[ \t]*(?:\1[ \t]*){2,}$/;
41335
+ var QUOTE_RE = /^\s{0,3}>\s?(.*)$/;
41336
+ var LIST_RE = /^(\s*)(?:([-*+•])|(\d{1,9})[.)])([ \t]+)(.*)$/;
41337
+ var TABLE_SEP_RE = /^\s*\|?[\s:|-]*-[\s:|-]*\|?\s*$/;
41338
+ function isBlank(line) {
41339
+ return !line || /^\s*$/.test(line);
41340
+ }
41341
+ function indentOf(line) {
41342
+ var m = /^[ \t]*/.exec(line)[0];
41343
+ // treat a tab as two columns — LLMs mix tabs and spaces freely
41344
+ return m.replace(/\t/g, ' ').length;
41345
+ }
41346
+ function matchListItem(line) {
41347
+ var m = LIST_RE.exec(line);
41348
+ if (!m) return null;
41349
+ var indent = indentOf(line);
41350
+ var markerLen = m[2] ? 1 : m[3].length + 1;
41351
+ return {
41352
+ indent: indent,
41353
+ ordered: !!m[3],
41354
+ start: m[3] ? parseInt(m[3], 10) : null,
41355
+ contentIndent: indent + markerLen + m[4].replace(/\t/g, ' ').length,
41356
+ text: m[5]
41357
+ };
41358
+ }
41359
+
41360
+ /** True when the line opens a block that must interrupt an open paragraph. */
41361
+ function startsBlock(line) {
41362
+ return FENCE_RE.test(line) || HEADING_RE.test(line) || HR_RE.test(line) || QUOTE_RE.test(line) || matchListItem(line) !== null;
41363
+ }
41364
+ function dedent(line, columns) {
41365
+ var seen = 0;
41366
+ var i = 0;
41367
+ while (i < line.length && seen < columns) {
41368
+ var c = line[i];
41369
+ if (c === ' ') seen += 1;else if (c === '\t') seen += 2;else break;
41370
+ i++;
41371
+ }
41372
+ return line.slice(i);
41373
+ }
41374
+ function splitTableRow(line) {
41375
+ var s = line.trim();
41376
+ if (s.startsWith('|')) s = s.slice(1);
41377
+ if (s.endsWith('|') && !s.endsWith('\\|')) s = s.slice(0, -1);
41378
+ var cells = [];
41379
+ var cur = '';
41380
+ for (var i = 0; i < s.length; i++) {
41381
+ if (s[i] === '\\' && s[i + 1] === '|') {
41382
+ cur += '|';
41383
+ i++;
41384
+ } else if (s[i] === '|') {
41385
+ cells.push(cur.trim());
41386
+ cur = '';
41387
+ } else {
41388
+ cur += s[i];
41389
+ }
41390
+ }
41391
+ cells.push(cur.trim());
41392
+ return cells;
41393
+ }
41394
+ function parseAlignment(sepLine) {
41395
+ return splitTableRow(sepLine).map(function (c) {
41396
+ var left = c.startsWith(':');
41397
+ var right = c.endsWith(':');
41398
+ if (left && right) return 'center';
41399
+ if (right) return 'right';
41400
+ if (left) return 'left';
41401
+ return null;
41402
+ });
41403
+ }
41404
+ function parseFence(lines, i) {
41405
+ var m = FENCE_RE.exec(lines[i]);
41406
+ var marker = m[2][0];
41407
+ var minLen = m[2].length;
41408
+ var stripIndent = m[1].length;
41409
+ var body = [];
41410
+ var j = i + 1;
41411
+ for (; j < lines.length; j++) {
41412
+ var close = FENCE_RE.exec(lines[j]);
41413
+ if (close && close[2][0] === marker && close[2].length >= minLen && !close[3]) {
41414
+ j++;
41415
+ break;
41416
+ }
41417
+ body.push(dedent(lines[j], stripIndent));
41418
+ }
41419
+ // an unterminated fence (mid-stream) still yields a code block
41420
+ return {
41421
+ node: {
41422
+ type: 'code',
41423
+ lang: m[3] || '',
41424
+ text: body.join('\n')
41425
+ },
41426
+ next: j
41427
+ };
41428
+ }
41429
+ function parseQuote(lines, i) {
41430
+ var body = [];
41431
+ var j = i;
41432
+ for (; j < lines.length; j++) {
41433
+ var m = QUOTE_RE.exec(lines[j]);
41434
+ if (m) {
41435
+ body.push(m[1]);
41436
+ continue;
41437
+ }
41438
+ // lazy continuation: plain text directly under a quote line
41439
+ if (!isBlank(lines[j]) && !startsBlock(lines[j]) && body.length && !isBlank(body[body.length - 1])) {
41440
+ body.push(lines[j].trim());
41441
+ continue;
41442
+ }
41443
+ break;
41444
+ }
41445
+ return {
41446
+ node: {
41447
+ type: 'quote',
41448
+ blocks: parseBlocks(body)
41449
+ },
41450
+ next: j
41451
+ };
41452
+ }
41453
+ function parseTable(lines, i) {
41454
+ var head = splitTableRow(lines[i]);
41455
+ var align = parseAlignment(lines[i + 1]);
41456
+ var rows = [];
41457
+ var j = i + 2;
41458
+ for (; j < lines.length; j++) {
41459
+ if (isBlank(lines[j]) || lines[j].indexOf('|') === -1) break;
41460
+ var cells = splitTableRow(lines[j]);
41461
+ while (cells.length < head.length) cells.push('');
41462
+ rows.push(cells.slice(0, head.length).map(parseInline));
41463
+ }
41464
+ return {
41465
+ node: {
41466
+ type: 'table',
41467
+ align: align,
41468
+ head: head.map(parseInline),
41469
+ rows: rows
41470
+ },
41471
+ next: j
41472
+ };
41473
+ }
41474
+ function parseList(lines, i) {
41475
+ var first = matchListItem(lines[i]);
41476
+ var baseIndent = first.indent;
41477
+ var ordered = first.ordered;
41478
+ var items = [];
41479
+ var cur = null;
41480
+ var j = i;
41481
+ while (j < lines.length) {
41482
+ var line = lines[j];
41483
+ if (isBlank(line)) {
41484
+ var k = j + 1;
41485
+ while (k < lines.length && isBlank(lines[k])) k++;
41486
+ if (k >= lines.length) break;
41487
+ var nextItem = matchListItem(lines[k]);
41488
+ var belongs = nextItem && nextItem.indent >= baseIndent && nextItem.ordered === ordered || indentOf(lines[k]) > baseIndent;
41489
+ if (!belongs) break;
41490
+ if (cur) cur.lines.push('');
41491
+ j = k;
41492
+ continue;
41493
+ }
41494
+ var item = matchListItem(line);
41495
+ if (item && item.indent <= baseIndent) {
41496
+ // a sibling item — or a different list starting at the same level
41497
+ if (item.indent < baseIndent || item.ordered !== ordered) break;
41498
+ cur = {
41499
+ lines: [item.text],
41500
+ contentIndent: item.contentIndent
41501
+ };
41502
+ items.push(cur);
41503
+ j++;
41504
+ continue;
41505
+ }
41506
+ if (!cur) break;
41507
+
41508
+ // nested item or indented continuation
41509
+ if (indentOf(line) > baseIndent) {
41510
+ cur.lines.push(dedent(line, cur.contentIndent));
41511
+ j++;
41512
+ continue;
41513
+ }
41514
+
41515
+ // lazy continuation of the current item's paragraph
41516
+ if (startsBlock(line)) break;
41517
+ cur.lines.push(line.trim());
41518
+ j++;
41519
+ }
41520
+ return {
41521
+ node: {
41522
+ type: ordered ? 'ol' : 'ul',
41523
+ start: ordered && first.start !== 1 ? first.start : null,
41524
+ items: items.map(function (it) {
41525
+ return {
41526
+ blocks: parseBlocks(it.lines)
41527
+ };
41528
+ })
41529
+ },
41530
+ next: j
41531
+ };
41532
+ }
41533
+ function parseParagraph(lines, i) {
41534
+ var body = [];
41535
+ var j = i;
41536
+ for (; j < lines.length; j++) {
41537
+ if (isBlank(lines[j])) break;
41538
+ if (j > i && startsBlock(lines[j])) break;
41539
+ if (j > i && lines[j].indexOf('|') !== -1 && j + 1 < lines.length && TABLE_SEP_RE.test(lines[j + 1])) {
41540
+ break;
41541
+ }
41542
+ body.push(lines[j].trim());
41543
+ }
41544
+ return {
41545
+ node: {
41546
+ type: 'p',
41547
+ inline: parseInline(body.join('\n'))
41548
+ },
41549
+ next: j
41550
+ };
41551
+ }
41552
+
41553
+ /**
41554
+ * Parse markdown source into a block AST.
41555
+ * @param {string} src
41556
+ * @returns {Array<object>} block nodes
41557
+ */
41558
+ function parseMarkdown(src) {
41559
+ if (typeof src !== 'string' || src === '') return [];
41560
+ return parseBlocks(src.replace(/\r\n?/g, '\n').split('\n'));
41561
+ }
41562
+ function parseBlocks(lines) {
41563
+ var blocks = [];
41564
+ var i = 0;
41565
+ while (i < lines.length) {
41566
+ var line = lines[i];
41567
+ if (isBlank(line)) {
41568
+ i++;
41569
+ continue;
41570
+ }
41571
+ if (FENCE_RE.test(line)) {
41572
+ var r = parseFence(lines, i);
41573
+ blocks.push(r.node);
41574
+ i = r.next;
41575
+ continue;
41576
+ }
41577
+ if (HR_RE.test(line)) {
41578
+ blocks.push({
41579
+ type: 'hr'
41580
+ });
41581
+ i++;
41582
+ continue;
41583
+ }
41584
+ var heading = HEADING_RE.exec(line);
41585
+ if (heading) {
41586
+ var text = heading[3].replace(/\s+#+\s*$/, '');
41587
+ blocks.push({
41588
+ type: 'h',
41589
+ level: heading[2].length,
41590
+ inline: parseInline(text)
41591
+ });
41592
+ i++;
41593
+ continue;
41594
+ }
41595
+ if (QUOTE_RE.test(line)) {
41596
+ var _r = parseQuote(lines, i);
41597
+ blocks.push(_r.node);
41598
+ i = _r.next;
41599
+ continue;
41600
+ }
41601
+ if (matchListItem(line)) {
41602
+ var _r2 = parseList(lines, i);
41603
+ // defensive: parseList always consumes at least one line
41604
+ if (_r2.next <= i) {
41605
+ blocks.push({
41606
+ type: 'p',
41607
+ inline: parseInline(line.trim())
41608
+ });
41609
+ i++;
41610
+ continue;
41611
+ }
41612
+ blocks.push(_r2.node);
41613
+ i = _r2.next;
41614
+ continue;
41615
+ }
41616
+ if (line.indexOf('|') !== -1 && i + 1 < lines.length && TABLE_SEP_RE.test(lines[i + 1])) {
41617
+ var _r3 = parseTable(lines, i);
41618
+ blocks.push(_r3.node);
41619
+ i = _r3.next;
41620
+ continue;
41621
+ }
41622
+ var p = parseParagraph(lines, i);
41623
+ if (p.next <= i) {
41624
+ i++;
41625
+ continue;
41626
+ }
41627
+ blocks.push(p.node);
41628
+ i = p.next;
41629
+ }
41630
+ return blocks;
41631
+ }
41632
+
41633
+ /* ------------------------------------------------------------------ */
41634
+ /* Inline parsing */
41635
+ /* ------------------------------------------------------------------ */
41636
+
41637
+ var ESCAPABLE = '\\`*_~[]()#+-.!>|';
41638
+ function matchLink(text, start) {
41639
+ var depth = 0;
41640
+ var j = start;
41641
+ for (; j < text.length; j++) {
41642
+ var ch = text[j];
41643
+ if (ch === '\\') {
41644
+ j++;
41645
+ continue;
41646
+ }
41647
+ if (ch === '\n') return null;
41648
+ if (ch === '[') depth++;else if (ch === ']') {
41649
+ depth--;
41650
+ if (depth === 0) break;
41651
+ }
41652
+ }
41653
+ if (j >= text.length || text[j] !== ']' || text[j + 1] !== '(') return null;
41654
+ var label = text.slice(start + 1, j);
41655
+ var k = j + 2;
41656
+ var pdepth = 1;
41657
+ var dest = '';
41658
+ for (; k < text.length; k++) {
41659
+ var _ch = text[k];
41660
+ if (_ch === '\\') {
41661
+ dest += text[k + 1] || '';
41662
+ k++;
41663
+ continue;
41664
+ }
41665
+ if (_ch === '\n') return null;
41666
+ if (_ch === '(') pdepth++;else if (_ch === ')') {
41667
+ pdepth--;
41668
+ if (pdepth === 0) break;
41669
+ }
41670
+ dest += _ch;
41671
+ }
41672
+ if (k >= text.length || pdepth !== 0) return null;
41673
+ dest = dest.trim();
41674
+ // strip an optional title: (/url "Title")
41675
+ var sp = dest.search(/\s/);
41676
+ var href = sp === -1 ? dest : dest.slice(0, sp);
41677
+ return {
41678
+ label: label,
41679
+ href: href,
41680
+ end: k + 1
41681
+ };
41682
+ }
41683
+
41684
+ /** Length of the run of identical delimiter chars starting at `i`. */
41685
+ function runLength(text, i) {
41686
+ var c = text[i];
41687
+ var n = 0;
41688
+ while (i + n < text.length && text[i + n] === c) n++;
41689
+ return n;
41690
+ }
41691
+ function matchEmphasis(text, start) {
41692
+ var c = text[start];
41693
+ var openRun = runLength(text, start);
41694
+ if (c === '~') {
41695
+ // only ~~strike~~
41696
+ if (openRun < 2) return null;
41697
+ return matchDelimited(text, start, '~~', 'del');
41698
+ }
41699
+
41700
+ // ***both*** — strong wrapping the inner single-delimiter run
41701
+ if (openRun >= 3) {
41702
+ var triple = c.repeat(3);
41703
+ var close = text.indexOf(triple, start + openRun);
41704
+ if (close === -1) return null;
41705
+ return {
41706
+ tag: 'strong',
41707
+ content: c + text.slice(start + 3, close) + c,
41708
+ end: close + 3
41709
+ };
41710
+ }
41711
+ if (c === '_') {
41712
+ var before = start > 0 ? text[start - 1] : '';
41713
+ if (before && /\w/.test(before)) return null; // snake_case
41714
+ }
41715
+ return openRun === 2 ? matchDelimited(text, start, c + c, 'strong') : matchDelimited(text, start, c, 'em');
41716
+ }
41717
+ function matchDelimited(text, start, delim, tag) {
41718
+ var c = delim[0];
41719
+ var single = delim.length === 1;
41720
+ var after = text[start + delim.length];
41721
+ if (after === undefined || /\s/.test(after)) return null;
41722
+ var j = start + delim.length;
41723
+ while (j < text.length) {
41724
+ var k = text.indexOf(delim, j);
41725
+ if (k === -1) return null;
41726
+ if (k === start + delim.length) return null; // empty content
41727
+
41728
+ if (/\s/.test(text[k - 1])) {
41729
+ j = k + delim.length;
41730
+ continue;
41731
+ }
41732
+
41733
+ // a single-char delimiter must not latch onto part of a longer run
41734
+ // ("*a **b** c*" — the closer is the final *, not the one in "**")
41735
+ if (single && (text[k - 1] === c || text[k + 1] === c)) {
41736
+ var end = k;
41737
+ while (end < text.length && text[end] === c) end++;
41738
+ j = end;
41739
+ continue;
41740
+ }
41741
+ if (c === '_') {
41742
+ var next = text[k + delim.length];
41743
+ if (next && /\w/.test(next)) {
41744
+ j = k + delim.length;
41745
+ continue;
41746
+ }
41747
+ }
41748
+ return {
41749
+ tag: tag,
41750
+ content: text.slice(start + delim.length, k),
41751
+ end: k + delim.length
41752
+ };
41753
+ }
41754
+ return null;
41755
+ }
41756
+
41757
+ /**
41758
+ * Parse a run of inline markdown into inline nodes.
41759
+ * @param {string} text
41760
+ * @returns {Array<object>}
41761
+ */
41762
+ function parseInline(text) {
41763
+ var out = [];
41764
+ var buf = '';
41765
+ var flush = function flush() {
41766
+ if (buf) {
41767
+ out.push({
41768
+ type: 'text',
41769
+ value: buf
41770
+ });
41771
+ buf = '';
41772
+ }
41773
+ };
41774
+ var i = 0;
41775
+ while (i < text.length) {
41776
+ var c = text[i];
41777
+ if (c === '\\' && i + 1 < text.length && ESCAPABLE.indexOf(text[i + 1]) !== -1) {
41778
+ buf += text[i + 1];
41779
+ i += 2;
41780
+ continue;
41781
+ }
41782
+ if (c === '\n') {
41783
+ flush();
41784
+ out.push({
41785
+ type: 'br'
41786
+ });
41787
+ i++;
41788
+ continue;
41789
+ }
41790
+ if (c === '`') {
41791
+ var m = /^(`+)([\s\S]*?)\1(?!`)/.exec(text.slice(i));
41792
+ if (m && m[2].trim()) {
41793
+ flush();
41794
+ out.push({
41795
+ type: 'code',
41796
+ value: m[2].replace(/^ | $/g, '')
41797
+ });
41798
+ i += m[0].length;
41799
+ continue;
41800
+ }
41801
+ }
41802
+ if (c === '!' && text[i + 1] === '[') {
41803
+ var img = matchLink(text, i + 1);
41804
+ if (img) {
41805
+ flush();
41806
+ out.push({
41807
+ type: 'image',
41808
+ src: img.href,
41809
+ alt: img.label
41810
+ });
41811
+ i = img.end;
41812
+ continue;
41813
+ }
41814
+ }
41815
+ if (c === '[') {
41816
+ var link = matchLink(text, i);
41817
+ if (link) {
41818
+ flush();
41819
+ out.push({
41820
+ type: 'link',
41821
+ href: link.href,
41822
+ children: parseInline(link.label)
41823
+ });
41824
+ i = link.end;
41825
+ continue;
41826
+ }
41827
+ }
41828
+ if (c === '*' || c === '_' || c === '~') {
41829
+ var em = matchEmphasis(text, i);
41830
+ if (em) {
41831
+ flush();
41832
+ out.push({
41833
+ type: em.tag,
41834
+ children: parseInline(em.content)
41835
+ });
41836
+ i = em.end;
41837
+ continue;
41838
+ }
41839
+ }
41840
+ if ((c === 'h' || c === 'w') && /^(https?:\/\/|www\.)/.test(text.slice(i))) {
41841
+ var _m = /^(?:https?:\/\/|www\.)[^\s<>"'`\]]+/.exec(text.slice(i));
41842
+ if (_m) {
41843
+ var raw = _m[0].replace(/[.,;:!?)\]]+$/, '');
41844
+ flush();
41845
+ out.push({
41846
+ type: 'link',
41847
+ href: raw.startsWith('www.') ? "https://".concat(raw) : raw,
41848
+ children: [{
41849
+ type: 'text',
41850
+ value: raw
41851
+ }]
41852
+ });
41853
+ i += raw.length;
41854
+ continue;
41855
+ }
41856
+ }
41857
+ buf += c;
41858
+ i++;
41859
+ }
41860
+ flush();
41861
+ return out;
41862
+ }
41863
+
41864
+ /* ------------------------------------------------------------------ */
41865
+ /* URL safety */
41866
+ /* ------------------------------------------------------------------ */
41867
+
41868
+ var SAFE_SCHEME = /^(https?|mailto|tel):/i;
41869
+
41870
+ /**
41871
+ * Returns the href if it is safe to put on an anchor, otherwise null.
41872
+ * Blocks javascript:, data:, vbscript: and anything else exotic.
41873
+ */
41874
+ function safeHref(href) {
41875
+ if (typeof href !== 'string') return null;
41876
+ var s = href.trim();
41877
+ if (!s) return null;
41878
+ // control characters are used to smuggle "java\nscript:"
41879
+ var clean = s.replace(/[-\s]/g, '');
41880
+ if (/^(\/|#|\.\/|\.\.\/)/.test(clean)) return s; // relative
41881
+ if (SAFE_SCHEME.test(clean)) return s;
41882
+ if (clean.indexOf(':') === -1) return s; // bare host / path
41883
+ return null;
41884
+ }
41885
+
41886
+ /* ------------------------------------------------------------------ */
41887
+ /* DOM rendering */
41888
+ /* ------------------------------------------------------------------ */
41889
+
41890
+ function renderInlineNodes(nodes, parent, doc) {
41891
+ var _iterator = _createForOfIteratorHelper(nodes),
41892
+ _step;
41893
+ try {
41894
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
41895
+ var node = _step.value;
41896
+ switch (node.type) {
41897
+ case 'text':
41898
+ parent.appendChild(doc.createTextNode(node.value));
41899
+ break;
41900
+ case 'br':
41901
+ parent.appendChild(doc.createElement('br'));
41902
+ break;
41903
+ case 'code':
41904
+ {
41905
+ var el = doc.createElement('code');
41906
+ el.textContent = node.value;
41907
+ parent.appendChild(el);
41908
+ break;
41909
+ }
41910
+ case 'strong':
41911
+ case 'em':
41912
+ case 'del':
41913
+ {
41914
+ var _el = doc.createElement(node.type);
41915
+ renderInlineNodes(node.children, _el, doc);
41916
+ parent.appendChild(_el);
41917
+ break;
41918
+ }
41919
+ case 'link':
41920
+ {
41921
+ var href = safeHref(node.href);
41922
+ if (!href) {
41923
+ renderInlineNodes(node.children, parent, doc);
41924
+ break;
41925
+ }
41926
+ var _el2 = doc.createElement('a');
41927
+ _el2.setAttribute('href', href);
41928
+ _el2.setAttribute('target', '_blank');
41929
+ _el2.setAttribute('rel', 'noopener noreferrer nofollow');
41930
+ renderInlineNodes(node.children, _el2, doc);
41931
+ parent.appendChild(_el2);
41932
+ break;
41933
+ }
41934
+ case 'image':
41935
+ {
41936
+ var src = safeHref(node.src);
41937
+ if (!src) {
41938
+ parent.appendChild(doc.createTextNode(node.alt || ''));
41939
+ break;
41940
+ }
41941
+ var _el3 = doc.createElement('img');
41942
+ _el3.setAttribute('src', src);
41943
+ _el3.setAttribute('alt', node.alt || '');
41944
+ _el3.setAttribute('loading', 'lazy');
41945
+ _el3.className = 'md-img';
41946
+ parent.appendChild(_el3);
41947
+ break;
41948
+ }
41949
+ default:
41950
+ break;
41951
+ }
41952
+ }
41953
+ } catch (err) {
41954
+ _iterator.e(err);
41955
+ } finally {
41956
+ _iterator.f();
41957
+ }
41958
+ }
41959
+ function renderBlockNodes(blocks, parent, doc) {
41960
+ var _iterator2 = _createForOfIteratorHelper(blocks),
41961
+ _step2;
41962
+ try {
41963
+ var _loop = function _loop() {
41964
+ var block = _step2.value;
41965
+ switch (block.type) {
41966
+ case 'p':
41967
+ {
41968
+ var el = doc.createElement('p');
41969
+ renderInlineNodes(block.inline, el, doc);
41970
+ parent.appendChild(el);
41971
+ break;
41972
+ }
41973
+ case 'h':
41974
+ {
41975
+ var _el4 = doc.createElement("h".concat(Math.min(block.level + 2, 6)));
41976
+ _el4.className = "md-h md-h".concat(block.level);
41977
+ renderInlineNodes(block.inline, _el4, doc);
41978
+ parent.appendChild(_el4);
41979
+ break;
41980
+ }
41981
+ case 'hr':
41982
+ parent.appendChild(doc.createElement('hr'));
41983
+ break;
41984
+ case 'code':
41985
+ {
41986
+ var pre = doc.createElement('pre');
41987
+ var code = doc.createElement('code');
41988
+ if (block.lang) code.className = "language-".concat(block.lang.replace(/[^\w+-]/g, ''));
41989
+ code.textContent = block.text;
41990
+ pre.appendChild(code);
41991
+ parent.appendChild(pre);
41992
+ break;
41993
+ }
41994
+ case 'quote':
41995
+ {
41996
+ var _el5 = doc.createElement('blockquote');
41997
+ renderBlockNodes(block.blocks, _el5, doc);
41998
+ parent.appendChild(_el5);
41999
+ break;
42000
+ }
42001
+ case 'ul':
42002
+ case 'ol':
42003
+ {
42004
+ var list = doc.createElement(block.type);
42005
+ if (block.type === 'ol' && block.start != null) list.setAttribute('start', String(block.start));
42006
+ var _iterator3 = _createForOfIteratorHelper(block.items),
42007
+ _step3;
42008
+ try {
42009
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
42010
+ var item = _step3.value;
42011
+ var li = doc.createElement('li');
42012
+ // tight item: a lone paragraph renders straight into the <li>
42013
+ if (item.blocks.length === 1 && item.blocks[0].type === 'p') {
42014
+ renderInlineNodes(item.blocks[0].inline, li, doc);
42015
+ } else {
42016
+ renderBlockNodes(item.blocks, li, doc);
42017
+ }
42018
+ list.appendChild(li);
42019
+ }
42020
+ } catch (err) {
42021
+ _iterator3.e(err);
42022
+ } finally {
42023
+ _iterator3.f();
42024
+ }
42025
+ parent.appendChild(list);
42026
+ break;
42027
+ }
42028
+ case 'table':
42029
+ {
42030
+ var wrap = doc.createElement('div');
42031
+ wrap.className = 'md-table-wrap';
42032
+ var table = doc.createElement('table');
42033
+ var thead = doc.createElement('thead');
42034
+ var hrow = doc.createElement('tr');
42035
+ block.head.forEach(function (cell, idx) {
42036
+ var th = doc.createElement('th');
42037
+ if (block.align[idx]) th.style.textAlign = block.align[idx];
42038
+ renderInlineNodes(cell, th, doc);
42039
+ hrow.appendChild(th);
42040
+ });
42041
+ thead.appendChild(hrow);
42042
+ table.appendChild(thead);
42043
+ var tbody = doc.createElement('tbody');
42044
+ var _iterator4 = _createForOfIteratorHelper(block.rows),
42045
+ _step4;
42046
+ try {
42047
+ var _loop2 = function _loop2() {
42048
+ var row = _step4.value;
42049
+ var tr = doc.createElement('tr');
42050
+ row.forEach(function (cell, idx) {
42051
+ var td = doc.createElement('td');
42052
+ if (block.align[idx]) td.style.textAlign = block.align[idx];
42053
+ renderInlineNodes(cell, td, doc);
42054
+ tr.appendChild(td);
42055
+ });
42056
+ tbody.appendChild(tr);
42057
+ };
42058
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
42059
+ _loop2();
42060
+ }
42061
+ } catch (err) {
42062
+ _iterator4.e(err);
42063
+ } finally {
42064
+ _iterator4.f();
42065
+ }
42066
+ table.appendChild(tbody);
42067
+ wrap.appendChild(table);
42068
+ parent.appendChild(wrap);
42069
+ break;
42070
+ }
42071
+ default:
42072
+ break;
42073
+ }
42074
+ };
42075
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
42076
+ _loop();
42077
+ }
42078
+ } catch (err) {
42079
+ _iterator2.e(err);
42080
+ } finally {
42081
+ _iterator2.f();
42082
+ }
42083
+ }
42084
+
42085
+ /**
42086
+ * Replace the contents of `el` with the rendered markdown of `text`.
42087
+ * Safe to call repeatedly with a growing buffer (streaming).
42088
+ *
42089
+ * @param {Element} el target element (emptied first)
42090
+ * @param {string} text markdown source
42091
+ */
42092
+ function renderMarkdownInto(el, text) {
42093
+ if (!el) return;
42094
+ var doc = el.ownerDocument || document;
42095
+ while (el.firstChild) el.removeChild(el.firstChild);
42096
+ if (typeof text !== 'string' || text === '') return;
42097
+ var blocks;
42098
+ try {
42099
+ blocks = parseMarkdown(text);
42100
+ } catch (err) {
42101
+ // never let a parser bug swallow the agent's answer
42102
+ el.textContent = text;
42103
+ return;
42104
+ }
42105
+ var frag = doc.createDocumentFragment();
42106
+ renderBlockNodes(blocks, frag, doc);
42107
+
42108
+ // A response with no renderable block (e.g. only whitespace) falls back to text.
42109
+ if (!frag.firstChild) {
42110
+ el.textContent = text;
42111
+ return;
42112
+ }
42113
+ el.appendChild(frag);
42114
+ }
42115
+
42116
+ /***/ }),
42117
+
41040
42118
  /***/ "./src/widget/voice/desktop.js":
41041
42119
  /*!*************************************!*\
41042
42120
  !*** ./src/widget/voice/desktop.js ***!