@flowengage/react-chatbot 1.1.9 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1945,109 +1945,108 @@ function ln(e, t) {
1945
1945
  let n = cn(e).slice(-Math.min(t, 28)), r = n.filter((e) => e.length > 2 && !sn.has(e));
1946
1946
  return r.length >= 2 ? r : n.filter((e) => e.length > 2).slice(-12);
1947
1947
  }
1948
- function un(e, t) {
1948
+ function un(e, t, { strict: n = !1 } = {}) {
1949
1949
  if (!e || !t?.length) return null;
1950
- let n = e.querySelectorAll("h1, h2, h3, h4, h5, h6, p, li, blockquote, [data-plan], [class*='pricing'] h2, [class*='pricing'] h3, section h2, section h3"), r = null, i = 0;
1951
- for (let e of n) {
1950
+ let r = e.querySelectorAll("h1, h2, h3, h4, h5, h6, p, li, blockquote, [data-plan], [class*='pricing'] h2, [class*='pricing'] h3, section h2, section h3"), i = null, a = 0;
1951
+ for (let e of r) {
1952
1952
  if (Qt(e)) continue;
1953
1953
  try {
1954
1954
  if (e.closest("aside, nav, [role='navigation'], [role='banner'], footer")) continue;
1955
1955
  } catch {}
1956
1956
  let n = e.textContent.replace(/\s+/g, " ").toLowerCase();
1957
1957
  if (n.length < 3 || n.length > 6e3) continue;
1958
- let a = 0;
1959
- for (let e of t) n.includes(e) && a++;
1960
- let o = a / t.length;
1958
+ let r = 0;
1959
+ for (let e of t) n.includes(e) && r++;
1960
+ let o = r / t.length;
1961
1961
  if (o < .2) continue;
1962
1962
  let s = o, c = e.tagName;
1963
- (c === "H2" || c === "H3" || c === "H4") && (s *= 1.35), (c === "P" || c === "BLOCKQUOTE") && (s *= 1.15), c?.startsWith("H") && n.length < 80 && (s *= 1.2), n.length > 400 && (s *= .65), s > i && (i = s, r = e);
1963
+ (c === "H2" || c === "H3" || c === "H4") && (s *= 1.35), (c === "P" || c === "BLOCKQUOTE") && (s *= 1.15), c?.startsWith("H") && n.length < 80 && (s *= 1.2), n.length > 400 && (s *= .65), s > a && (a = s, i = e);
1964
1964
  }
1965
- let a = t.length <= 1 ? .55 : .35;
1966
- return i >= a ? r : null;
1965
+ let o = n ? t.length <= 1 ? .62 : .5 : t.length <= 1 ? .55 : .35;
1966
+ return a >= o ? i : null;
1967
1967
  }
1968
1968
  function dn(e) {
1969
1969
  return /^H[1-6]$/i.test(e?.tagName || "");
1970
1970
  }
1971
- function fn(e, t) {
1971
+ function fn(e, t, n = !1) {
1972
1972
  if (!e || !t) return null;
1973
- let n = t.replace(/\s+/g, " ").trim();
1974
- if (n.length < 14) return null;
1975
- let r = n.toLowerCase(), i = ln(n, 24);
1976
- if (i.length < 3 && (i = r.replace(/[^\w\s'-]/g, " ").split(/\s+/).filter((e) => e.length > 2).slice(-18)), i.length < 3) return null;
1977
- let a = e.querySelectorAll("p, blockquote"), o = null, s = 0;
1978
- for (let e of a) {
1973
+ let r = t.replace(/\s+/g, " ").trim();
1974
+ if (r.length < 14) return null;
1975
+ let i = n ? .34 : .24, a = r.toLowerCase(), o = ln(r, 24);
1976
+ if (o.length < 3 && (o = a.replace(/[^\w\s'-]/g, " ").split(/\s+/).filter((e) => e.length > 2).slice(-18)), o.length < 3) return null;
1977
+ let s = e.querySelectorAll("p, blockquote"), c = null, l = 0;
1978
+ for (let e of s) {
1979
1979
  if (Qt(e)) continue;
1980
1980
  let t = e.textContent.replace(/\s+/g, " ").toLowerCase();
1981
1981
  if (t.length < 24) continue;
1982
1982
  let n = 0;
1983
- for (let e of i) t.includes(e) && n++;
1984
- let a = n / i.length, c = 0;
1985
- for (let e = Math.min(90, r.length); e >= 22; e -= 6) {
1986
- let n = r.slice(-e).trim();
1983
+ for (let e of o) t.includes(e) && n++;
1984
+ let r = n / o.length, s = 0;
1985
+ for (let e = Math.min(90, a.length); e >= 22; e -= 6) {
1986
+ let n = a.slice(-e).trim();
1987
1987
  if (n.length >= 22 && t.includes(n)) {
1988
- c = .28;
1988
+ s = .28;
1989
1989
  break;
1990
1990
  }
1991
1991
  }
1992
- let l = a + c;
1993
- l > s && a >= .24 && (s = l, o = e);
1992
+ let u = r + s;
1993
+ u > l && r >= i && (l = u, c = e);
1994
1994
  }
1995
- return o;
1995
+ return c;
1996
1996
  }
1997
- function pn(e) {
1997
+ function pn(e, t = {}) {
1998
1998
  if (typeof document > "u" || !e || !String(e).trim()) return null;
1999
- let t = String(e).trim(), n = t.length > 450 ? t.slice(-450).trim() : t, r = on(), i = ln(t, 26);
2000
- if (t.length >= 42) {
2001
- let e = fn(r, n);
1999
+ let n = t.strict === !0, r = String(e).trim(), i = r.length > 450 ? r.slice(-450).trim() : r, a = on(), o = ln(r, 26);
2000
+ if (r.length >= (n ? 55 : 42)) {
2001
+ let e = fn(a, i, n);
2002
2002
  if (e) return e;
2003
2003
  }
2004
- if (i.length >= 2 || i.length === 1 && i[0].length >= 5) {
2005
- let e = un(r, i);
2004
+ if ((!n || o.length >= 3 || o.length === 2 && o.every((e) => e.length >= 5)) && (o.length >= 2 || o.length === 1 && o[0].length >= 5)) {
2005
+ let e = un(a, o, { strict: n });
2006
2006
  if (e) return e;
2007
2007
  }
2008
- let a = n.split(/(?<=[.!?])\s+/).filter((e) => e.trim().length > 0), o = a[a.length - 1] || n, s = o.toLowerCase().replace(/[^\w\s'-]/g, " ").split(/\s+/).filter((e) => e.length > 2), c = [];
2009
- o.length >= 10 && (c.push(o.slice(-220).trim()), o.length > 220 && c.push(o.slice(0, 120).trim()));
2010
- for (let e = 4; e <= Math.min(10, s.length); e++) c.push(s.slice(-e).join(" "));
2011
- for (let e = Math.min(14, s.length); e >= 11; e--) c.push(s.slice(-e).join(" "));
2012
- for (let e = Math.min(8, s.length); e >= 3; e--) c.push(s.slice(-e).join(" "));
2013
- let l = /* @__PURE__ */ new Set();
2014
- for (let e of c) {
2015
- let n = e.trim();
2016
- if (n.length < 6 || l.has(n)) continue;
2017
- l.add(n);
2018
- let i = an(null, n);
2019
- if (i && !Qt(i)) {
2020
- if (dn(i) && t.length > 140) {
2021
- let e = (r.querySelector?.("[itemprop='articleBody'], .article-body, .post-content, .entry-content") || r).querySelectorAll?.("p, blockquote") || [];
2022
- for (let t of e) if (!Qt(t) && t.textContent.toLowerCase().includes(n.toLowerCase().slice(0, Math.min(48, n.length)))) return t;
2008
+ let s = i.split(/(?<=[.!?])\s+/).filter((e) => e.trim().length > 0), c = s[s.length - 1] || i, l = c.toLowerCase().replace(/[^\w\s'-]/g, " ").split(/\s+/).filter((e) => e.length > 2), u = [];
2009
+ c.length >= 10 && (u.push(c.slice(-220).trim()), c.length > 220 && u.push(c.slice(0, 120).trim()));
2010
+ for (let e = 4; e <= Math.min(10, l.length); e++) u.push(l.slice(-e).join(" "));
2011
+ for (let e = Math.min(14, l.length); e >= 11; e--) u.push(l.slice(-e).join(" "));
2012
+ for (let e = Math.min(8, l.length); e >= 3; e--) u.push(l.slice(-e).join(" "));
2013
+ let d = n ? 14 : 6, f = /* @__PURE__ */ new Set();
2014
+ for (let e of u) {
2015
+ let t = e.trim();
2016
+ if (t.length < d || f.has(t)) continue;
2017
+ f.add(t);
2018
+ let n = an(null, t);
2019
+ if (n && !Qt(n)) {
2020
+ if (dn(n) && r.length > 140) {
2021
+ let e = (a.querySelector?.("[itemprop='articleBody'], .article-body, .post-content, .entry-content") || a).querySelectorAll?.("p, blockquote") || [];
2022
+ for (let n of e) if (!Qt(n) && n.textContent.toLowerCase().includes(t.toLowerCase().slice(0, Math.min(48, t.length)))) return n;
2023
2023
  }
2024
- return i;
2024
+ return n;
2025
2025
  }
2026
2026
  }
2027
- let u = i.length >= 3 ? i : s.slice(-22);
2028
- if (u.length === 0) return null;
2029
- let d = null, f = -1, p = r.querySelectorAll("h1, h2, h3, h4, h5, h6, p, li, blockquote, article");
2030
- for (let e of p) {
2027
+ let p = o.length >= 3 ? o : l.slice(-22);
2028
+ if (p.length === 0) return null;
2029
+ let m = null, h = -1, g = a.querySelectorAll("h1, h2, h3, h4, h5, h6, p, li, blockquote, article");
2030
+ for (let e of g) {
2031
2031
  if (Qt(e)) continue;
2032
2032
  try {
2033
2033
  if (e.closest("aside, nav, [role='navigation'], footer")) continue;
2034
2034
  } catch {}
2035
- let n = e.textContent.toLowerCase();
2036
- if (n.length < 12) continue;
2037
- let r = 0;
2038
- for (let e of u) e.length > 2 && n.includes(e) && r++;
2039
- let i = r / u.length, a = e.tagName;
2040
- (a === "P" || a === "BLOCKQUOTE" || a === "LI") && (i *= 1.22), dn(e) && (t.length > 110 ? i *= .48 : i *= .92), a === "ARTICLE" && t.length > 80 && (i *= .55), i > f && i >= .28 && (f = i, d = e);
2035
+ let t = e.textContent.toLowerCase();
2036
+ if (t.length < 12) continue;
2037
+ let i = 0;
2038
+ for (let e of p) e.length > 2 && t.includes(e) && i++;
2039
+ let a = i / p.length, o = e.tagName;
2040
+ (o === "P" || o === "BLOCKQUOTE" || o === "LI") && (a *= 1.22), dn(e) && (r.length > 110 ? a *= .48 : a *= .92), o === "ARTICLE" && r.length > 80 && (a *= .55), a > h && a >= (n ? .42 : .28) && (h = a, m = e);
2041
2041
  }
2042
- return d;
2042
+ return m;
2043
2043
  }
2044
- function mn(e, { speaking: t = !1 } = {}) {
2044
+ function mn(e, { speaking: t = !1, scroll: n = !0 } = {}) {
2045
2045
  if (!e || typeof document > "u") return;
2046
2046
  rn();
2047
- let n = document.querySelectorAll(".fe-highlight-section");
2048
- for (let t of n) t !== e && (t.classList.remove("fe-highlight-section", "fe-highlight-speaking"), t.classList.add("fe-highlight-exiting"), setTimeout(() => t.classList.remove("fe-highlight-exiting"), 300));
2049
- e.classList.remove("fe-highlight-exiting"), e.classList.add("fe-highlight-section"), t ? e.classList.add("fe-highlight-speaking") : e.classList.remove("fe-highlight-speaking");
2050
- try {
2047
+ let r = document.querySelectorAll(".fe-highlight-section");
2048
+ for (let t of r) t !== e && (t.classList.remove("fe-highlight-section", "fe-highlight-speaking"), t.classList.add("fe-highlight-exiting"), setTimeout(() => t.classList.remove("fe-highlight-exiting"), 300));
2049
+ if (e.classList.remove("fe-highlight-exiting"), e.classList.add("fe-highlight-section"), t ? e.classList.add("fe-highlight-speaking") : e.classList.remove("fe-highlight-speaking"), n) try {
2051
2050
  e.scrollIntoView({
2052
2051
  behavior: "smooth",
2053
2052
  block: "center"
@@ -23020,19 +23019,19 @@ async function f_(e, t, n) {
23020
23019
  return _g(e), !0;
23021
23020
  }
23022
23021
  function p_(e) {
23023
- let t = e.siteId || null, n = (e.apiBaseUrl || "").replace(/\/$/, ""), r = !!e.preferLiveSpeechHighlight, i = typeof e.getLastUserVoiceText == "function" ? e.getLastUserVoiceText : null, a = [], o = null, s = {
23022
+ let t = e.siteId || null, n = (e.apiBaseUrl || "").replace(/\/$/, ""), r = e.voicePageHighlightMode || "tool", i = typeof e.getLastUserVoiceText == "function" ? e.getLastUserVoiceText : null, a = [], o = null, s = {
23024
23023
  navigate_to_page: async (n) => {
23025
23024
  $("navigate_to_page ▶ raw parameters:", n);
23026
- let r = r_(n), i = l_(r), a = r.section_text ?? r.sectionText ?? r.section;
23025
+ let i = r_(n), a = l_(i), o = i.section_text ?? i.sectionText ?? i.section;
23027
23026
  if ($("navigate_to_page · coerced:", {
23028
- url: i,
23029
- sectionText: a
23030
- }), !i) {
23027
+ url: a,
23028
+ sectionText: o
23029
+ }), !a) {
23031
23030
  let e = "Missing url: pass a string url, path like /pricing, or JSON with a url field.";
23032
23031
  return $("navigate_to_page ✖", e), e;
23033
23032
  }
23034
- let o = typeof window < "u" ? gn(i) : i;
23035
- if (fg(`Navigating to ${String(o).replace(/^https?:\/\/[^/]+/, "") || "/"}…`), mg(), !await vn(o, {
23033
+ let s = typeof window < "u" ? gn(a) : a;
23034
+ if (fg(`Navigating to ${String(s).replace(/^https?:\/\/[^/]+/, "") || "/"}…`), mg(), !await vn(s, {
23036
23035
  siteId: t,
23037
23036
  apiBaseUrl: e.apiBaseUrl
23038
23037
  })) {
@@ -23041,10 +23040,13 @@ function p_(e) {
23041
23040
  return $("navigate_to_page ✖ blocked", e), e;
23042
23041
  }
23043
23042
  try {
23044
- if ($("navigate_to_page → shadowNavigate", o), await bn(o, { sectionText: a || void 0 }), await new Promise((e) => setTimeout(e, 600)), hg(), pg(300), a && typeof a == "string") {
23045
- let e = an(null, a);
23046
- e && mn(e), $("navigate_to_page · highlight", {
23047
- sectionText: a,
23043
+ if ($("navigate_to_page → shadowNavigate", s), await bn(s, { sectionText: o || void 0 }), await new Promise((e) => setTimeout(e, 600)), hg(), pg(300), o && typeof o == "string" && r !== "off") {
23044
+ let e = an(null, o);
23045
+ e && mn(e, {
23046
+ speaking: !0,
23047
+ scroll: !0
23048
+ }), $("navigate_to_page · highlight", {
23049
+ sectionText: o,
23048
23050
  found: !!e
23049
23051
  });
23050
23052
  }
@@ -23056,7 +23058,7 @@ function p_(e) {
23056
23058
  e = ` This page has a form with fields: [${r}]` + (i ? ` and buttons: [${i}].` : ".") + " Call list_page_forms then start filling.";
23057
23059
  }
23058
23060
  } catch {}
23059
- let t = `OK_NAVIGATION ${o}${e}`;
23061
+ let t = `OK_NAVIGATION ${s}${e}`;
23060
23062
  return $("navigate_to_page ✓", t), t;
23061
23063
  } catch (e) {
23062
23064
  hg(), pg(200);
@@ -23065,9 +23067,13 @@ function p_(e) {
23065
23067
  }
23066
23068
  },
23067
23069
  highlight_on_page: async (e) => {
23068
- if ($("highlight_on_page ▶", e), r) return $("highlight_on_page · skipped (live speech highlight owns the page)"), "OK_HIGHLIGHT The page already follows your speech automatically; do not call highlight_on_page again for this turn.";
23070
+ if ($("highlight_on_page ▶", e), r === "off") return $("highlight_on_page · skipped (voicePageHighlightMode=off)"), "OK_HIGHLIGHT page highlighting is disabled.";
23071
+ if (r === "transcript") return $("highlight_on_page · skipped (transcript mode owns highlights)"), "OK_HIGHLIGHT Page highlight follows speech in this mode; skip redundant highlight_on_page calls.";
23069
23072
  let t = r_(e), n = t?.section_id || t?.sectionId, i = t?.section_text || t?.sectionText, a = an(n || null, i || null);
23070
- return a ? (mn(a, { speaking: !0 }), $("highlight_on_page ✓ (speaking)"), "OK_HIGHLIGHT section is now highlighted and scrolled into view.") : ($("highlight_on_page ✖ no element"), "Could not find that section on the page.");
23073
+ return a ? (mn(a, {
23074
+ speaking: !0,
23075
+ scroll: !0
23076
+ }), $("highlight_on_page ✓ (speaking)"), "OK_HIGHLIGHT section is now highlighted and scrolled into view.") : ($("highlight_on_page ✖ no element"), "Could not find that section on the page.");
23071
23077
  },
23072
23078
  search_site_content: async (e) => {
23073
23079
  $("search_site_content ▶", e);
@@ -23101,7 +23107,7 @@ function p_(e) {
23101
23107
  },
23102
23108
  read_page_content: async (e) => {
23103
23109
  $("read_page_content ▶", e);
23104
- let t = r_(e), n = t?.section_text || t?.sectionText, r = 3e3;
23110
+ let t = r_(e), n = t?.section_text || t?.sectionText, i = 3e3;
23105
23111
  try {
23106
23112
  if (n) {
23107
23113
  let e = an(null, n);
@@ -23110,13 +23116,16 @@ function p_(e) {
23110
23116
  for (let n = Math.min(t.length, 5); n >= 2 && !e; n--) e = an(null, t.slice(0, n).join(" "));
23111
23117
  }
23112
23118
  if (!e) return `Could not find a section matching "${n}" on this page. Try navigate_to_page or click_page_button with the article title, then read_page_content again.`;
23113
- mn(e, { speaking: !0 });
23114
- let t = Eg(e, r);
23119
+ r !== "off" && mn(e, {
23120
+ speaking: !0,
23121
+ scroll: !0
23122
+ });
23123
+ let t = Eg(e, i);
23115
23124
  return t ? ($("read_page_content ✓ section:", t.length, "chars"), `OK_READ: ${t}`) : "Found the section but it appears to be empty.";
23116
23125
  }
23117
23126
  let e = Og(Dg());
23118
23127
  if (!e) return "Could not determine the main content area of this page.";
23119
- let t = Eg(e, r);
23128
+ let t = Eg(e, i);
23120
23129
  return t ? ($("read_page_content ✓ page:", t.length, "chars"), `OK_READ: ${t}`) : "The page appears to have no readable text content.";
23121
23130
  } catch (e) {
23122
23131
  return $("read_page_content ✖", e?.message), `Error reading page content: ${e?.message || "unknown error"}`;
@@ -23297,136 +23306,145 @@ function y_(e, t) {
23297
23306
  let n = (t || "").trim();
23298
23307
  return n ? !!(/\b(let'?s|let us)\s+(end|stop|finish|wrap up|close)\s+(the\s+)?(voice\s+)?(session|call|chat)\b/i.test(n) || /\bend\s+(this\s+)?(the\s+)?(voice\s+)?(session|call|chat)\b/i.test(n) || /\b(hang\s+up|disconnect)\b/i.test(n) || /\b(that'?s all|we'?re done)\b/i.test(n) || e === "bot" && (/\b(I'?ll|I will|Let me|I can|I'?m going to)\s+(end|close)\s+(the\s+)?(voice\s+)?(session|call)\b/i.test(n) || /\b(ending|closing)\s+(the\s+)?(voice\s+)?session\b|\bvoice\s+session\s+(is\s+)?(now\s+)?(ended|over|closing)\b/i.test(n) || /\b(session|call)\s+(is\s+)?(now\s+)?(ended|over)\b/i.test(n) || /\bwe\s+can\s+end\s+(the\s+)?(session|call)?\s*(here|now)\b/i.test(n) || /\b(wrapping\s+up|signing\s+off)\b/i.test(n)) || /^(goodbye|bye|that'?s all|we'?re done|hang up|disconnect)\b/i.test(n)) : !1;
23299
23308
  }
23300
- function b_({ agentId: e, chatId: t, siteId: r, apiBaseUrl: a, onEnd: c, historyRef: d, hasSyncedRef: f, onMessageSync: p, onStartSync: m }) {
23301
- let [h, g] = s("idle"), [ee, te] = s(null), [_, ne] = s(!1), re = o(null), ie = o(null), ae = o(""), oe = o(async () => {}), se = o(!1), v = o(""), ce = o(null), le = o(null), ue = o(null), y = o(null), b = o(null), de = n(() => {
23302
- ue.current &&= (clearTimeout(ue.current), null), y.current &&= (clearInterval(y.current), null), v.current = "", ce.current = null, le.current = null, b.current != null && (cancelAnimationFrame(b.current), b.current = null);
23309
+ function b_({ agentId: e, chatId: t, siteId: r, apiBaseUrl: a, onEnd: c, historyRef: d, hasSyncedRef: f, onMessageSync: p, onStartSync: m, voicePageHighlightMode: h = "tool" }) {
23310
+ let [g, ee] = s("idle"), [te, _] = s(null), [ne, re] = s(!1), ie = o(null), ae = o(null), oe = o(""), se = o(async () => {}), v = o(!1), ce = o(""), le = o(null), ue = o(null), y = o(null), b = o(null), de = o(null), fe = o(!1), pe = n(() => {
23311
+ y.current &&= (clearTimeout(y.current), null), b.current &&= (clearInterval(b.current), null), ce.current = "", le.current = null, ue.current = null, fe.current = !1, de.current != null && (cancelAnimationFrame(de.current), de.current = null);
23303
23312
  try {
23304
23313
  hn();
23305
23314
  } catch {}
23306
- }, []), fe = n(async () => {
23307
- de();
23315
+ }, []), me = n(async () => {
23316
+ pe();
23308
23317
  try {
23309
- await re.current?.endSession?.();
23318
+ await ie.current?.endSession?.();
23310
23319
  } catch {}
23311
- re.current = null, ne(!1), g("idle"), se.current = !1, typeof p == "function" && await p(d.current), typeof c == "function" && c();
23320
+ ie.current = null, re(!1), ee("idle"), v.current = !1, typeof p == "function" && await p(d.current), typeof c == "function" && c();
23312
23321
  }, [
23313
- de,
23322
+ pe,
23314
23323
  d,
23315
23324
  p,
23316
23325
  c
23317
23326
  ]);
23318
- oe.current = fe, i(() => {
23327
+ se.current = me, i(() => {
23319
23328
  let t = !1, n = null;
23320
23329
  return (async () => {
23321
23330
  try {
23322
23331
  if (await navigator.mediaDevices.getUserMedia({ audio: !0 }), t) return;
23323
- d.current = [], ae.current = "", se.current = !1;
23324
- let i = m_(p_({
23332
+ d.current = [], oe.current = "", v.current = !1, fe.current = !1;
23333
+ let i = h === "transcript", o = m_(p_({
23325
23334
  siteId: r || null,
23326
23335
  apiBaseUrl: a || null,
23327
- getLastUserVoiceText: () => ae.current,
23328
- preferLiveSpeechHighlight: !0,
23336
+ getLastUserVoiceText: () => oe.current,
23337
+ voicePageHighlightMode: h,
23329
23338
  requestEndSession: () => {
23330
23339
  queueMicrotask(() => {
23331
- se.current || (se.current = !0, oe.current?.());
23340
+ v.current || (v.current = !0, se.current?.());
23332
23341
  });
23333
23342
  }
23334
- })), o = () => {
23343
+ })), s = () => {
23335
23344
  try {
23336
- let e = v.current, t = ce.current;
23345
+ if (!i || !fe.current) return;
23346
+ let e = ce.current, t = le.current;
23337
23347
  if (!e?.trim() || t == null) return;
23338
- let n = le.current, r = Date.now(), i;
23348
+ let n = ue.current, r = Date.now(), a;
23339
23349
  if (n != null) {
23340
23350
  let t = Math.max(0, (r - n) / 1e3);
23341
- i = Math.min(e.length, Math.floor(t * __));
23351
+ a = Math.min(e.length, Math.floor(t * __));
23342
23352
  } else {
23343
23353
  let n = Math.max(0, (r - t) / 1e3);
23344
- if (n > 2.1 && e.length > 36) i = Math.min(e.length, Math.floor(Math.max(0, n - .4) * __));
23354
+ if (n > 2.1 && e.length > 36) a = Math.min(e.length, Math.floor(Math.max(0, n - .4) * __));
23345
23355
  else {
23346
23356
  let t = Math.min(96, Math.floor(n * v_));
23347
- i = Math.min(e.length, t);
23357
+ a = Math.min(e.length, t);
23348
23358
  }
23349
23359
  }
23350
- let a = e.slice(0, i);
23351
- if (a.trim().length < 8) return;
23360
+ let o = e.slice(0, a);
23361
+ if (o.trim().length < 6) return;
23352
23362
  rn();
23353
- let o = pn(a);
23354
- o && mn(o, { speaking: !0 });
23363
+ let s = pn(o, { strict: !0 });
23364
+ s && mn(s, {
23365
+ speaking: !0,
23366
+ scroll: !1
23367
+ });
23355
23368
  } catch (e) {
23356
23369
  console.warn("[VoiceInterface] speech highlight failed", e?.message);
23357
23370
  }
23358
- }, s = () => {
23359
- b.current ??= requestAnimationFrame(() => {
23360
- b.current = null, o();
23361
- });
23362
23371
  }, c = () => {
23363
- ue.current && clearTimeout(ue.current), ue.current = setTimeout(() => {
23364
- ue.current = null, o();
23365
- }, 120);
23372
+ de.current ??= requestAnimationFrame(() => {
23373
+ de.current = null, s();
23374
+ });
23366
23375
  }, l = () => {
23367
- y.current ||= setInterval(() => {
23368
- o();
23376
+ y.current && clearTimeout(y.current), y.current = setTimeout(() => {
23377
+ y.current = null, s();
23378
+ }, 120);
23379
+ }, u = () => {
23380
+ b.current ||= setInterval(() => {
23381
+ s();
23369
23382
  }, 160);
23370
- }, u = await Bh.startSession({
23383
+ }, f = await Bh.startSession({
23371
23384
  agentId: e,
23372
- clientTools: i,
23385
+ clientTools: o,
23373
23386
  onUnhandledClientToolCall: (e) => {
23374
23387
  console.error("[VoiceInterface] ElevenLabs requested a client tool that is not registered in the widget:", e?.tool_name, e?.parameters, "Add a matching handler in voiceClientTools (snake_case and camelCase are both supported).");
23375
23388
  },
23376
23389
  onModeChange: ({ mode: e }) => {
23377
- t || g(e === "speaking" ? "speaking" : "listening");
23390
+ t || ee(e === "speaking" ? "speaking" : "listening");
23378
23391
  },
23379
23392
  onAudio: () => {
23380
- t || (le.current ??= Date.now(), l(), s());
23393
+ t || !i || (ue.current ??= Date.now(), u(), c());
23381
23394
  },
23382
23395
  onAgentChatResponsePart: (e) => {
23383
23396
  if (t) return;
23384
- let n = e?.text_response_part ?? e, r = n?.type, i = n?.text ?? "";
23385
- r === "start" ? (ue.current &&= (clearTimeout(ue.current), null), y.current &&= (clearInterval(y.current), null), b.current != null && (cancelAnimationFrame(b.current), b.current = null), le.current = null, v.current = i, ce.current = Date.now(), l()) : r === "delta" ? (v.current += i, ce.current ??= Date.now(), l()) : r === "stop" ? (v.current += i, l()) : (v.current += i, ce.current == null && i && (ce.current = Date.now()), l()), v.current.trim().length >= 10 && (c(), s());
23397
+ let n = e?.text_response_part ?? e, r = n?.type, a = n?.text ?? "";
23398
+ r === "start" ? (y.current &&= (clearTimeout(y.current), null), b.current &&= (clearInterval(b.current), null), de.current != null && (cancelAnimationFrame(de.current), de.current = null), ue.current = null, ce.current = a, le.current = Date.now(), i && u()) : r === "delta" ? (ce.current += a, le.current ??= Date.now(), i && u()) : r === "stop" ? (ce.current += a, i && u()) : (ce.current += a, le.current == null && a && (le.current = Date.now()), i && u()), i && ce.current.trim().length >= 10 && (l(), c());
23386
23399
  },
23387
23400
  onInterruption: () => {
23388
- de();
23401
+ pe();
23389
23402
  },
23390
23403
  onMessage: (e) => {
23391
23404
  if (t) return;
23392
23405
  let n = e.role === "user" ? "user" : "bot", r = e.message || e.text || e.content || "";
23393
23406
  if (!r) return;
23394
- let i = {
23407
+ let a = {
23395
23408
  sender: n,
23396
23409
  message: r,
23397
23410
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
23398
23411
  };
23399
- n === "user" && (ae.current = r, de()), d.current = [...d.current, i], y_(n, r) && queueMicrotask(() => {
23400
- se.current || (se.current = !0, oe.current?.());
23412
+ if (n === "user") oe.current = r, r.trim().length >= 1 && (fe.current = !0);
23413
+ else if (n === "bot" && r.trim() && i && fe.current) {
23414
+ let e = r.trim();
23415
+ e.length > (ce.current?.length || 0) && (ce.current = e, le.current ??= Date.now(), u(), e.length >= 10 && (l(), c()));
23416
+ }
23417
+ d.current = [...d.current, a], y_(n, r) && queueMicrotask(() => {
23418
+ v.current || (v.current = !0, se.current?.());
23401
23419
  });
23402
23420
  },
23403
23421
  onError: (e) => {
23404
- t || (console.error("[VoiceInterface] ElevenLabs error", e), de(), g("error"), te("Voice session encountered an error."));
23422
+ t || (console.error("[VoiceInterface] ElevenLabs error", e), pe(), ee("error"), _("Voice session encountered an error."));
23405
23423
  },
23406
23424
  onDisconnect: () => {
23407
- t || (console.log("[VoiceInterface] session disconnected"), de(), ne(!1), g("idle"));
23425
+ t || (console.log("[VoiceInterface] session disconnected"), pe(), re(!1), ee("idle"));
23408
23426
  }
23409
23427
  });
23410
23428
  if (t) {
23411
- u.endSession().catch(() => {});
23429
+ f.endSession().catch(() => {});
23412
23430
  return;
23413
23431
  }
23414
- n = u, re.current = u, ne(!0), g("listening");
23432
+ n = f, ie.current = f, re(!0), ee("listening");
23415
23433
  try {
23416
23434
  let e = typeof window < "u" ? window.location.href : "", t = "";
23417
23435
  try {
23418
- t = await i.list_page_forms();
23436
+ t = await o.list_page_forms();
23419
23437
  } catch {
23420
23438
  t = "[]";
23421
23439
  }
23422
- u.sendContextualUpdate(`Current page URL: ${e}. Site ID: ${r || "unknown"}. Page fields/buttons JSON: ${t}. Navigation: when the user wants to go to any page (contact, pricing, about, services, etc.), call search_site_content then navigate_to_page immediately — never ask permission. After navigating to a page with a form, call list_page_forms and proactively start helping the user fill it. Contact / lead forms: use only the main contact form (Full Name, Email, Message). Ignore footer or secondary marketing URL fields unless the user explicitly asks to use them. Fill in order: name → email → message. Message must be a short visitor note (what they want from the team), not a transcript of the conversation. Forms: use fill_form_fields then click_page_button (e.g. Send Message) when appropriate. Fill each field right after the user gives that value — do not click submit until required fields are filled. Voice: if the user wants to end the voice session, call end_voice_session or confirm and end.`);
23440
+ f.sendContextualUpdate(`Current page URL: ${e}. Site ID: ${r || "unknown"}. Page fields/buttons JSON: ${t}. Navigation: when the user wants to go to any page (contact, pricing, about, services, etc.), call search_site_content then navigate_to_page immediately — never ask permission. After navigating to a page with a form, call list_page_forms and proactively start helping the user fill it. Contact / lead forms: use only the main contact form (Full Name, Email, Message). Ignore footer or secondary marketing URL fields unless the user explicitly asks to use them. Fill in order: name → email → message. Message must be a short visitor note (what they want from the team), not a transcript of the conversation. Forms: use fill_form_fields then click_page_button (e.g. Send Message) when appropriate. Fill each field right after the user gives that value — do not click submit until required fields are filled. On-page reading: highlight the correct section by calling highlight_on_page({ section_text: "..." }) with visible heading/plan text, or read_page_content({ section_text: "..." }) when reading a specific section — the UI follows tool targets, not loose speech. Voice: if the user wants to end the voice session, call end_voice_session or confirm and end.`);
23423
23441
  } catch {}
23424
23442
  typeof m == "function" && m();
23425
23443
  } catch (e) {
23426
- t || (console.error("[VoiceInterface] startSession failed", e), g("error"), te("Could not start voice session. Please try again."));
23444
+ t || (console.error("[VoiceInterface] startSession failed", e), ee("error"), _("Could not start voice session. Please try again."));
23427
23445
  }
23428
23446
  })(), () => {
23429
- t = !0, de(), n ? n.endSession().catch(() => {}) : re.current && re.current.endSession().catch(() => {});
23447
+ t = !0, pe(), n ? n.endSession().catch(() => {}) : ie.current && ie.current.endSession().catch(() => {});
23430
23448
  };
23431
23449
  }, [
23432
23450
  e,
@@ -23434,11 +23452,12 @@ function b_({ agentId: e, chatId: t, siteId: r, apiBaseUrl: a, onEnd: c, history
23434
23452
  a,
23435
23453
  d,
23436
23454
  m,
23437
- de
23455
+ pe,
23456
+ h
23438
23457
  ]);
23439
- let pe = h === "listening" || h === "speaking", me = g_[h] || g_.idle, he = h_[h] || h_.idle;
23458
+ let he = g === "listening" || g === "speaking", ge = g_[g] || g_.idle, _e = h_[g] || h_.idle;
23440
23459
  return /* @__PURE__ */ u("div", {
23441
- ref: ie,
23460
+ ref: ae,
23442
23461
  style: {
23443
23462
  display: "flex",
23444
23463
  flexDirection: "column",
@@ -23460,19 +23479,19 @@ function b_({ agentId: e, chatId: t, siteId: r, apiBaseUrl: a, onEnd: c, history
23460
23479
  justifyContent: "center"
23461
23480
  },
23462
23481
  children: [
23463
- pe && /* @__PURE__ */ l("div", { style: {
23482
+ he && /* @__PURE__ */ l("div", { style: {
23464
23483
  position: "absolute",
23465
23484
  inset: "-16px",
23466
23485
  borderRadius: "50%",
23467
- border: `2px solid ${me}`,
23486
+ border: `2px solid ${ge}`,
23468
23487
  animation: "fe-orb-pulse 1.4s ease-in-out infinite",
23469
23488
  opacity: .6
23470
23489
  } }),
23471
- pe && /* @__PURE__ */ l("div", { style: {
23490
+ he && /* @__PURE__ */ l("div", { style: {
23472
23491
  position: "absolute",
23473
23492
  inset: "-8px",
23474
23493
  borderRadius: "50%",
23475
- border: `2px solid ${me}`,
23494
+ border: `2px solid ${ge}`,
23476
23495
  animation: "fe-orb-pulse 1.4s ease-in-out 0.35s infinite",
23477
23496
  opacity: .4
23478
23497
  } }),
@@ -23481,18 +23500,18 @@ function b_({ agentId: e, chatId: t, siteId: r, apiBaseUrl: a, onEnd: c, history
23481
23500
  width: "140px",
23482
23501
  height: "140px",
23483
23502
  borderRadius: "50%",
23484
- background: he,
23485
- border: `3px solid ${me}`,
23503
+ background: _e,
23504
+ border: `3px solid ${ge}`,
23486
23505
  display: "flex",
23487
23506
  alignItems: "center",
23488
23507
  justifyContent: "center",
23489
23508
  transition: "all 0.35s ease",
23490
- transform: h === "speaking" ? "scale(1.08)" : "scale(1)",
23491
- boxShadow: `0 0 40px ${he}, 0 8px 32px rgba(0,0,0,0.08)`
23509
+ transform: g === "speaking" ? "scale(1.08)" : "scale(1)",
23510
+ boxShadow: `0 0 40px ${_e}, 0 8px 32px rgba(0,0,0,0.08)`
23492
23511
  },
23493
23512
  children: /* @__PURE__ */ l(An, {
23494
23513
  size: 44,
23495
- color: me
23514
+ color: ge
23496
23515
  })
23497
23516
  })
23498
23517
  ]
@@ -23501,19 +23520,19 @@ function b_({ agentId: e, chatId: t, siteId: r, apiBaseUrl: a, onEnd: c, history
23501
23520
  style: {
23502
23521
  fontSize: "15px",
23503
23522
  fontWeight: 600,
23504
- color: h === "error" ? "#ef4444" : "#374151",
23523
+ color: g === "error" ? "#ef4444" : "#374151",
23505
23524
  letterSpacing: "0.01em",
23506
23525
  textAlign: "center"
23507
23526
  },
23508
23527
  children: [
23509
- h === "idle" && "Starting voice session…",
23510
- h === "listening" && "Listening…",
23511
- h === "speaking" && "AI is speaking…",
23512
- h === "error" && (ee || "Error")
23528
+ g === "idle" && "Starting voice session…",
23529
+ g === "listening" && "Listening…",
23530
+ g === "speaking" && "AI is speaking…",
23531
+ g === "error" && (te || "Error")
23513
23532
  ]
23514
23533
  }),
23515
23534
  /* @__PURE__ */ u("button", {
23516
- onClick: fe,
23535
+ onClick: me,
23517
23536
  style: {
23518
23537
  display: "flex",
23519
23538
  alignItems: "center",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowengage/react-chatbot",
3
- "version": "1.1.9",
3
+ "version": "1.2.1",
4
4
  "description": "Embeddable AI chat widget for React — powered by FlowEngage. Drop it in with a single siteId.",
5
5
  "keywords": [
6
6
  "chatbot",