@flowengage/react-chatbot 8.0.122 → 8.0.123
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.
|
@@ -427,7 +427,7 @@ Error generating stack: `+e.message+`
|
|
|
427
427
|
`).trim()}function v_(e){let t=e.siteId||null,n=(e.apiBaseUrl||``).replace(/\/$/,``),r=typeof e.getLastUserVoiceText==`function`?e.getLastUserVoiceText:null,i=typeof e.beaconSyncFullVoiceHistory==`function`?e.beaconSyncFullVoiceHistory:null,a=[],o={"fwh-navigate-mvp":async n=>{if($(`fwh-navigate-mvp ▶ raw parameters:`,n),e?.isWalkthroughActive?.())return $(`fwh-navigate-mvp ✖ blocked — walkthrough in progress`),`NAVIGATION_BLOCKED_DURING_WALKTHROUGH. A walkthrough of the current page is in progress, so navigation is not allowed. Do NOT tell the visitor you navigated or offer to. Describe the requested section using the CURRENT page's own content (fwh-read-content-mvp), then stop.`;let a=p_(n),o=m_(a),s=a.section_text??a.sectionText??a.section,c=Array.isArray(a.expected_landmarks)?a.expected_landmarks:Array.isArray(a.expectedLandmarks)?a.expectedLandmarks:[];if($(`fwh-navigate-mvp · coerced:`,{url:o,sectionText:s,expectedLandmarks:c}),!o){let e=`Missing url: pass a string url, path like /pricing, or JSON with a url field.`;return $(`fwh-navigate-mvp ✖`,e),e}if(typeof window<`u`)try{let e=new URL(o,window.location.href);if(e.hostname===window.location.hostname&&e.origin!==window.location.origin)o=e.pathname+e.search+e.hash,$(`fwh-navigate-mvp · cross-protocol same-host URL normalized →`,o);else if(e.origin!==window.location.origin&&/^https?:$/i.test(e.protocol)){let t=e.hostname;return $(`fwh-navigate-mvp ✖ external URL blocked:`,e.href),`EXTERNAL_LINK_BLOCKED: The URL "${o}" points to an external site (${t}) that opens in a new tab. I cannot open new tabs or navigate to external sites. Tell the visitor exactly this: "That link opens an external app — I'm not able to open it for you, but you can click the link directly in the navigation header."`}}catch{}if(typeof window<`u`){try{let e=new URL(o,window.location.href);if(e.pathname===window.location.pathname&&e.search===window.location.search)return $(`fwh-navigate-mvp ✖ already on page, skipping`),`OK_ALREADY_ON_PAGE. The visitor is already on this page. Do not navigate. Proceed directly with highlights, answering the question, or reading page content.`}catch{}let e=window.__fe_user_nav_during_speech;if(e&&Date.now()-e.ts<3e4)try{if(new URL(o,window.location.href).pathname===e.fromPath)return $(`flowengage_navigate ✖ blocked — would navigate back to page visitor just left`,e),`NAV_BLOCKED: The visitor just left "${e.fromPageName}" and is now on "${e.toPageName}". Do NOT navigate them back. If they said "continue", resume verbally from where you left off — no navigation needed. If they want to return, they will navigate themselves.`}catch{}}let{allowed:l,correctedUrl:u}=await Sr(o,{siteId:t,apiBaseUrl:e.apiBaseUrl});if(!l){let e=`PAGE_NOT_IN_SITE: "${o}" is NOT a real page on this site — do NOT guess or invent URL paths. Do NOT tell the visitor you are taking them there. Instead, find the REAL page: look at your Popular Pages / Site Pages list for the closest actual match, or call fwh-list-nav-links-mvp (to read the site's real menu links) or fwh-search-mvp (to find the right page). If a close match exists (e.g. the visitor said "support" and the site has "Technical Support"), tell them the ACTUAL page name and ask "we don't have a page called that, but we do have [real page] — would you like me to take you there?" Then navigate only to that verified URL.`;return $(`fwh-navigate-mvp ✖ blocked`,e),e}u&&u!==o&&($(`fwh-navigate-mvp · corrected url:`,{requested:o,correctedUrl:u}),o=u);try{Eh(`Navigating to ${o}...`,{spinner:!0}),Wh(),In();try{i?.()}catch{}try{let e=r?r():``;if(e&&t){let n=Qg([{sender:`user`,message:e}]);n&&Ee(t,n)}}catch{}Ae();let e=o;try{e=new URL(o,typeof window<`u`?window.location.href:``).href}catch{}let n=`this`;try{let e=new URL(o,window.location.href).pathname.split(`/`).filter(Boolean).pop()||``;n=e?e.replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()):`Home`}catch{}t&&typeof window<`u`&&Le(t,window.location.href,e,n),Tr(e);try{window.__fe_agent_nav={path:new URL(e,window.location.href).pathname,ts:Date.now()}}catch{}$(`fwh-navigate-mvp → real navigation`,o);let a=typeof window<`u`&&!window.Shopify&&!!nr();if(typeof window<`u`&&window.__fe_triggerVoiceNavHooks&&window.__fe_triggerVoiceNavHooks(),a&&qn(12e3),Nr(o,{voiceActive:!0}).catch(()=>{}),a)try{let e=new URL(o,window.location.origin).pathname,t=window.location.pathname,n=Date.now()+2e3;for(;Date.now()<n&&!(typeof window<`u`&&window.location.pathname===e);)await new Promise(e=>setTimeout(e,80));let r=typeof window<`u`?window.location.pathname:e,i=typeof window<`u`?window.location.href:o;(r.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()),$(`fwh-navigate-mvp → SPA nav complete. target:`,e,`actual:`,r);try{typeof window<`u`&&window.scrollTo(0,0)}catch{}let a=[],s=null,c=``,l=Date.now()+12e3;for(;Date.now()<l&&(await new Promise(e=>setTimeout(e,300)),{headings:a,pageTitleHeading:s,introText:c}=g_(),!(a.length>=1)););h_(),$(`fwh-navigate-mvp → page headings collected:`,a,`titleHeading:`,s);let u=a.length?a.join(` | `):`none`,d=a.length?`Use ONLY these exact heading strings for fwh-highlight-mvp — never invent or abbreviate.`:`No headings collected yet — call fwh-get-current-page-mvp immediately (no speech) to get the live headings before speaking or highlighting.`,f=s?` "${s}" is the PAGE TITLE, not a content section — do not fwh-highlight-mvp it separately; if doing a walkthrough, just fold this intro into your opening line: "${c||`(no intro text found)`}"`:c?` No separate page-title heading detected, but this intro text precedes the first section — mention it briefly before covering sections: "${c}"`:``,p=``;try{if(a.length>0){let e=a.slice(0,3),t=[];for(let n of e){let e=_r(null,n);if(e){let r=__(e).substring(0,800);r&&t.push(`[Section: ${n}] ${r}`)}}t.length>0&&(p=` CONTENT FOR FIRST FEW SECTIONS: ${t.join(` | `)}. Use this content to answer immediately if the visitor asks about one of these sections.`)}}catch{}if(Or())return Er(),$(`fwh-navigate-mvp ✖ landed on a not-found/404 page:`,i),`NAVIGATION_PAGE_NOT_FOUND. The page at ${i} does not exist — it shows a "not found"/404 error, not real content. Do NOT offer a page overview, do NOT describe or highlight any sections, do NOT claim the navigation succeeded. Tell the visitor EXACTLY: "It looks like that page doesn't exist — I couldn't find it. Would you like me to take you somewhere else?" Then STOP and wait for their reply.`;let m=Date.now()+4e3,h=typeof window<`u`?window.location.pathname:r;for(;Date.now()<m&&!t_(h,e)&&t_(h,t);)await new Promise(e=>setTimeout(e,120)),h=window.location.pathname;$(`fwh-navigate-mvp route settle:`,{startPath:t,targetPath:e,settledPath:h});let g=typeof window<`u`?window.location.href:i,_=(h.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase())||`Home`,v=t_(h,e),y=!t_(h,t),b=v||y;return $(`fwh-navigate-mvp settled:`,h,`target:`,e,`match:`,b),b?(Er(),`OK_NAVIGATE_SPA. Visitor is now on: ${g} (page: "${_}") | Section headings: ${u}.${f} ${d}${p} Now confirm the arrival in ONE short sentence phrased for what the visitor actually asked for — mention the page by name and what it lets them do (e.g. for a contact request: "I've taken you to the Contact Us page — filling in this form sends your query straight to our team."). Do NOT use a fixed template. Then, in the same breath, offer either a specific section or a full overview. Then STOP — wait for their reply.`):(Er(),`NAVIGATION_DID_NOT_HAPPEN. The visitor is STILL on ${g} (page: "${_}") — the navigation did not go through. Do NOT say "I have navigated you". Do NOT claim success. Tell the visitor EXACTLY: "Sorry, I couldn't open that page just now. Would you like me to try again, or is there something else I can help with?" Then STOP and wait for their reply.`)}catch{}return`OK_NAVIGATE_RELOADING. The page is reloading now and this session is ending. Say NOTHING further — do not confirm the navigation, do not describe the page. The arrival greeting is spoken automatically on the new page; you resume from the visitor's next reply.`}catch(e){let t=`Navigation error: ${e?.message||String(e)}`;return $(`fwh-navigate-mvp ✖`,t),Eh(`Navigation failed`,{spinner:!1,icon:`⚠️`,duration:1800}),t}finally{Gh(),Dh(900)}},"fwh-highlight-mvp":async e=>{$(`fwh-highlight-mvp ▶`,e);let t=p_(e),n=t?.section_id||t?.sectionId,r=t?.section_text||t?.sectionText;if(qn(3e3),typeof window<`u`&&window.__fe_user_nav_during_speech){let e=window.__fe_user_nav_during_speech,t=window.location.pathname;if(Date.now()-e.ts>=3e4)window.__fe_user_nav_during_speech=null;else return $(`flowengage_highlight ✖ blocked — user navigated during speech`,e),`NAV_CHANGED: The visitor navigated from "${e.fromPageName}" to "${e.toPageName}" (current path: ${t}) while you were speaking. STOP the current walkthrough immediately. DO NOT call fwh-highlight-mvp again. Say out loud: "I see you've navigated to the ${e.toPageName} page — do you want me to continue what I was explaining, or shall I guide you through this page?" Wait for the visitor's answer before doing anything else.`}h_();let i=(e=>e&&e.replace(/^\d+\.\s*/,``).replace(/^["']|["']$/g,``).trim())(r),a=_r(n||null,i||null);if(!a&&i){let e=Date.now()+600;for(;!a&&Date.now()<e;)await new Promise(e=>setTimeout(e,50)),h_(),a=_r(null,i)}if(!a){$(`fwh-highlight-mvp ✖ no element for:`,i||n);let{headings:e}=g_(),t=e.length?`Page headings: ${e.map(e=>`"${e}"`).join(` | `)}. Use one of these EXACT strings for section_text — no numbers, no quotes around them.`:`No headings found — skip highlight and continue speaking.`;return`HIGHLIGHT_NOT_FOUND: "${i||n}" on the current page. ${t}`}return Fn(a),$(`fwh-highlight-mvp ✓`,i||n),`OK_HIGHLIGHT: "${i||n}". Your NEXT response must be speech only (no tool call) describing THIS section — "${i||n}" — using its own content. Do not ask whether to continue.`},"fwh-read-content-mvp":async e=>{$(`fwh-read-content-mvp ▶`,e);let t=p_(e),n=t?.section_text||t?.sectionText||``;try{let e=``;if(n){let t=_r(null,n);if(!t)return`Could not find section "${n}" on the page.`;e=__(t)}else{let t=(document.querySelector(`main`)||document.querySelector(`article`)||document.querySelector(`[role="main"]`)||document.querySelector(`#content`)||document.querySelector(`.content`)||document.body).cloneNode(!0);t.querySelectorAll(`nav, footer, header, aside, script, style, noscript, .nav, .footer, .header, .sidebar, #flowengage-root, .flowengage-shell, .flowengage-widget-root`).forEach(e=>e.remove()),e=t.textContent.replace(/\s+/g,` `).trim()}return e.length>8e3&&(e=e.substring(0,8e3)+`... (truncated)`),$(`fwh-read-content-mvp ✓`,`${e.length} chars`),e||`No readable content found on this page.`}catch(e){return $(`fwh-read-content-mvp ✖`,e.message),`Could not read page content: ${e.message}`}},"fwh-page-content-mvp":async e=>{$(`fwh-page-content-mvp ▶`,e);let r=p_(e),i=r?.url||r?.page_url||``;if($(`fwh-page-content-mvp · url:`,i,`siteId:`,t,`apiBase:`,n||`(empty)`),!i)return $(`fwh-page-content-mvp ✖ missing url`),`Missing url parameter.`;if(!t)return $(`fwh-page-content-mvp ✖ no siteId`),`Site is not configured.`;if(!n)return $(`fwh-page-content-mvp ✖ no api base`),`API base URL is missing; cannot fetch page content.`;try{let e=`${n}/api/site-tools/page-content`;$(`fwh-page-content-mvp → POST`,e);let r=await fetch(e,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({siteId:t,url:i})}),a=await r.json();if($(`fwh-page-content-mvp · HTTP`,r.status,`success:`,a.success),!a.success){let e=a.error||`Failed to get page content.`;return $(`fwh-page-content-mvp ✖`,e),`${e} FALLBACK REQUIRED: This page is not in the search index. Call fwh-read-content-mvp (no parameters) immediately to read the live page content directly from the visitor's browser — do NOT tell the visitor you cannot access the page.`}return $(`fwh-page-content-mvp ✓`,`title: ${a.title}`,`sections: ${(a.sections||[]).length}`,`content: ${(a.content||``).length} chars`),JSON.stringify({title:a.title,url:a.url,sections:a.sections,content:a.content})}catch(e){let t=`Page content error: ${e.message||`network`}`;return $(`fwh-page-content-mvp ✖`,t),t}},"fwh-search-mvp":async e=>{$(`fwh-search-mvp ▶`,e);let r=p_(e).query;if(!r)return $(`fwh-search-mvp ✖ missing query`),`Missing query parameter.`;if(!t)return $(`fwh-search-mvp ✖ no siteId`),`Search unavailable (no siteId configured).`;if(!n)return $(`fwh-search-mvp ✖ no api base`),`Search unavailable (no API base URL).`;try{let e=`${n}/api/site-tools/search`;$(`fwh-search-mvp → POST`,e);let i=await fetch(e,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({siteId:t,query:r,currentUrl:typeof window<`u`?window.location.href:``})}),a=await i.json();if($(`fwh-search-mvp · HTTP`,i.status,`success:`,a.success),!a.success||!a.results?.length)return`No results found for that query. Try rephrasing, or use fwh-read-content-mvp to read the current page.`;let o=a.results.slice(0,5).map((e,t)=>`${t+1}. ${e.title||e.url} (${e.url})${e.snippet?` — ${e.snippet}`:``}`).join(`
|
|
428
428
|
`);return $(`fwh-search-mvp ✓`,`${a.results.length} result(s)`),`SEARCH_RESULTS:\n${o}`}catch(e){return $(`fwh-search-mvp ✖`,e.message),`Search failed. Use fwh-read-content-mvp to read the current page instead.`}},"fwh-list-nav-links-mvp":async()=>{if($(`fwh-list-nav-links-mvp ▶`),typeof window>`u`)return`Cannot read navigation (no browser context).`;let e=new Map,t=[],n=[`nav a`,`[role='navigation'] a`,`header a`,`.navbar a`,`.nav a`,`.menu a`,`.navigation a`,`.header a`,`[class*='nav'] a`,`[class*='menu'] a`,`[class*='header'] a`];if(r_(t=>{for(let r of n)try{t.querySelectorAll(r).forEach(t=>{if(Rn(t))return;let n=t.getAttribute(`href`),r=(t.textContent||``).replace(/\s+/g,` `).trim();if(!r||!n||n===`#`||n.startsWith(`javascript:`))return;try{let e=t.parentElement;if(e&&n_(e).display===`none`&&!e.closest(`nav, [role='navigation'], header, [class*='nav'], [class*='menu']`))return}catch{}let i=r.toLowerCase()+`|`+n;e.has(i)||e.set(i,{text:r,href:n})})}catch{}t.querySelectorAll(`select`).forEach(t=>{if(Rn(t))return;let n=a_(t);/nav|menu|page|section|go to/i.test(n||t.name||t.id||``)&&Array.from(t.options).forEach(r=>{if(!r.value||r.disabled)return;let i=r.text.trim();if(!i)return;let a=i.toLowerCase()+`|`+r.value;e.has(a)||e.set(a,{text:i,href:r.value,isSelectOption:!0,selectLabel:n||t.name||`dropdown`})})})}),e.forEach(e=>t.push(e)),!t.length)return`NAV_LINKS: No navigation links found on this page. Use fwh-read-content-mvp to read page content or fwh-search-mvp to search the site.`;let r=t.slice(0,50).map((e,t)=>`${t+1}. "${e.text}"${e.isSelectOption?` (dropdown option in "${e.selectLabel}")`:``} → ${e.href}`).join(`
|
|
429
429
|
`);return $(`fwh-list-nav-links-mvp ✓`,`${t.length} links`),`NAV_LINKS (${t.length} total, showing up to 50):\n${r}\n\nIMPORTANT: Use the EXACT text shown above when reading link names to the visitor. Do NOT rename, abbreviate, or invent options not listed here. To navigate, call fwh-navigate-mvp with the href value.`},"fwh-end-session-mvp":async()=>($(`fwh-end-session-mvp ▶`),typeof e.requestEndSession==`function`&&e.requestEndSession(),`Voice session is ending.`),"fwh-request-email-mvp":async()=>($(`fwh-request-email-mvp ▶`),typeof e.requestEmailCollection==`function`&&e.requestEmailCollection(),`EMAIL_BOX_SHOWN. An email input text box is now visible to the visitor. Say EXACTLY: "Please enter your email in the text box to continue." Then stop and wait silently — do NOT ask them to say it aloud, do NOT repeat, and do NOT answer any other question until you receive the EMAIL_SAVED message.`),"fwh-get-current-page-mvp":async()=>{if($(`fwh-get-current-page-mvp ▶`),typeof window>`u`)return`Cannot determine current page (no browser context).`;let e=window.location.href,t=window.location.pathname,n=document.title||``;if(Or())return $(`fwh-get-current-page-mvp ✓ 404 detected`,{url:e}),`CURRENT_PAGE_NOT_FOUND. The visitor is currently on a 404 / page-not-found error page (${e}). There is no real content here. Do NOT describe this page, do NOT list sections, do NOT offer a walkthrough, and do NOT describe it from your own knowledge. Tell the visitor plainly that the page they are on doesn't exist, and offer to take them somewhere that does.`;let{headings:r,pageTitleHeading:i,introText:a}=g_();$(`fwh-get-current-page-mvp ✓`,{url:e,path:t,title:n}),$(`fwh-get-current-page-mvp → page headings collected:`,r,`titleHeading:`,i);let o=i?` NOTE: "${i}" is the PAGE TITLE, not a content section — do NOT fwh-highlight-mvp it or list it as a separate section in a walkthrough. Instead, open with a brief 1-2 sentence summary of this intro text before covering the real sections: "${a||`(no intro text captured)`}"`:a?` NOTE: no separate page-title heading was detected, but this intro text appears before the first section — open your walkthrough with a brief 1-2 sentence summary of it before covering sections: "${a}"`:``;if(!r.length)return`CURRENT_PAGE. URL: ${e} | Title: ${n} |${o} No structured section headings found on this page. If there is intro text above, describe it; otherwise describe the page from your knowledge. Do not call this tool again.`;let s=r.map(e=>`"${e}"`).join(` | `);return`CURRENT_PAGE. URL: ${e} | Title: ${n} | Page sections (${r.length} total): ${s}.${o} *** WALKTHROUGH: do NOT drive it yourself. Do not call fwh-highlight-mvp and do not decide the order — highlighting and section order are handled for you. You will be told which section to describe, one at a time. When told, describe ONLY that section in 3-4 sentences and then stop; never announce another section, never ask whether to continue. ***`},"fwh-list-forms-mvp":async()=>{let e=s_(),t=c_();$(`fwh-list-forms-mvp ✓`,`${e.length} field(s)`,`${t.length} button(s)`);let n=e.map(e=>({...e,inputCategory:e.tag===`SELECT`?`dropdown_select`:`text_input`}));return JSON.stringify({fields:n,buttons:t,hint:`field_key: use the label/placeholder/name shown above. CRITICAL: fields with inputCategory='dropdown_select' are SELECT elements — their field_key must match the SELECT's own label (e.g. 'Company Size'), NOT the option text (e.g. 'Small Business') and NOT a nearby text input label. Never fill a SELECT option value into a text input.`})},"fwh-fill-form-mvp":async()=>`FORM_FILLING_DISABLED: Form filling is not available at this time. Tell the visitor what fields are present (use fwh-list-forms-mvp) and ask them to fill the form themselves.`,"fwh-click-button-mvp":async e=>{let t=p_(e),n=(t.button_text??t.buttonText??t.text??``).trim(),r=(t.card_context??t.cardContext??``).trim();if($(`fwh-click-button-mvp ▶`,n,r?`(card_context: ${r})`:``),!n)return`Missing button_text: pass the visible label, e.g. Conduct Audit.`;let i=f_(n,a,r);if(!i)return $(`fwh-click-button-mvp ✖ no match`),`Could not find a button matching "${n}". Call fwh-list-forms-mvp to see button texts.`;try{Eh(`Clicking ${n}...`,{spinner:!0});try{i.scrollIntoView({block:`center`,inline:`nearest`})}catch{i.scrollIntoView(!0)}await Xh(i),i.focus?.(),await Qh(i),$h(i),i.click();let e=(i.textContent||i.value||i.getAttribute(`aria-label`)||``).trim().slice(0,80)||`button`,t=`OK_CLICK ${e}`;return $(`fwh-click-button-mvp ✓`,t),Eh(`Clicked ${e}`,{spinner:!1,icon:`✅`,duration:1200}),t}catch(e){let t=`Click failed: ${e?.message||String(e)}`;return $(`fwh-click-button-mvp ✖`,t),Eh(`Click failed`,{spinner:!1,icon:`⚠️`,duration:1800}),t}finally{Zh(),Dh(700)}},"fwh-cart-get-mvp":async()=>{$(`fwh-cart-get-mvp ▶`);try{let e=await Kr();if(!e)return`CART_UNAVAILABLE: this page is not a Shopify storefront, so there is no cart to read.`;let t=e.items.map((e,t)=>{let n=e.variantTitle&&e.variantTitle!==`Default Title`?` (${e.variantTitle})`:``;return`${t+1}. ${e.title}${n} — qty ${e.quantity} — ${e.linePrice}${e.handle?` — handle: ${e.handle}`:``}`}),n=`CART. Items: ${e.itemCount} | Subtotal: ${e.subtotal}${e.currency?` ${e.currency}`:``}${t.length?` | ${t.join(` | `)}`:` | (cart is empty)`}`;return $(`fwh-cart-get-mvp ✓`,n),n}catch(e){return $(`fwh-cart-get-mvp ✖`,e),`CART_ERROR: ${e?.userMessage||e?.message||`could not read the cart`}`}},"fwh-shop-search-mvp":async e=>{let t=String((e||{}).query||``).trim();if($(`fwh-shop-search-mvp ▶`,t),!t)return`Missing query: pass the product name or keyword to search.`;try{let e=await $r(t);if(!e.length)return`NO_PRODUCTS_FOUND for "${t}". Tell the visitor no matching product was found — do not invent products.`;let n=e.map(e=>`"${e.title}" — handle: ${e.handle}${e.price?` — price: ${e.price}`:``}`).join(` | `);return $(`fwh-shop-search-mvp ✓`,n),`PRODUCTS_FOUND: ${n}. Use the handle with fwh-cart-add-mvp or fwh-view-product-mvp.`}catch(e){return $(`fwh-shop-search-mvp ✖`,e),`SEARCH_ERROR: ${e?.message||`product search failed`}`}},"fwh-cart-add-mvp":async e=>{let t=e||{},n=String(t.product_handle||t.productHandle||``).trim().toLowerCase(),r=String(t.variant||``).trim()||null,i=Math.min(20,Math.max(1,parseInt(t.quantity,10)||1));if($(`fwh-cart-add-mvp ▶`,{handle:n,variantText:r,quantity:i}),!n)return`Missing product_handle: search the product first with fwh-shop-search-mvp.`;try{let{variant:e,product:t}=await qr(n,r);await Jr(e.id,i);let a=await Ur(),o=await s(a),c=e.title&&e.title!==`Default Title`?` (${e.title})`:``,l=`ADDED ${i} × ${t.title}${c}. Cart now has ${a.item_count} item(s), subtotal ${Gr(a.items_subtotal_price)}${a.currency?` ${a.currency}`:``}.${o}`;return $(`fwh-cart-add-mvp ✓`,l),l}catch(e){return $(`fwh-cart-add-mvp ✖`,e),`ADD_FAILED: ${e?.userMessage||e?.message||`could not add the item`}. Relay this reason to the visitor.`}},"fwh-cart-update-mvp":async e=>{let t=e||{},n=parseInt(t.line,10),r=Math.min(20,Math.max(0,parseInt(t.quantity,10)));if($(`fwh-cart-update-mvp ▶`,{line:n,quantity:r}),!Number.isFinite(n)||n<1)return`Missing/invalid line: call fwh-cart-get-mvp first and use the item's 1-based number.`;if(!Number.isFinite(r))return`Missing/invalid quantity.`;try{let e=await Ur(),t=e.items?.[n-1];if(!t)return`NO_SUCH_LINE: the cart has ${e.items?.length||0} item(s). Call fwh-cart-get-mvp for the current list.`;let i=await Yr(n,r),a=await s(i),o=`UPDATED ${t.product_title||t.title} to qty ${r}. Cart now has ${i.item_count} item(s), subtotal ${Gr(i.items_subtotal_price)}${i.currency?` ${i.currency}`:``}.${a}`;return $(`fwh-cart-update-mvp ✓`,o),o}catch(e){return $(`fwh-cart-update-mvp ✖`,e),`UPDATE_FAILED: ${e?.userMessage||e?.message||`could not update the item`}`}},"fwh-cart-remove-mvp":async e=>{let t=parseInt((e||{}).line,10);if($(`fwh-cart-remove-mvp ▶`,{line:t}),!Number.isFinite(t)||t<1)return`Missing/invalid line: call fwh-cart-get-mvp first and use the item's 1-based number.`;try{let e=await Ur(),n=e.items?.[t-1];if(!n)return`NO_SUCH_LINE: the cart has ${e.items?.length||0} item(s). Call fwh-cart-get-mvp for the current list.`;let r=await Yr(t,0),i=await s(r),a=`REMOVED ${n.product_title||n.title}. Cart now has ${r.item_count} item(s), subtotal ${Gr(r.items_subtotal_price)}${r.currency?` ${r.currency}`:``}.${i}`;return $(`fwh-cart-remove-mvp ✓`,a),a}catch(e){return $(`fwh-cart-remove-mvp ✖`,e),`REMOVE_FAILED: ${e?.userMessage||e?.message||`could not remove the item`}`}},"fwh-cart-open-mvp":async()=>($(`fwh-cart-open-mvp ▶`),c(Xr(),`Cart`)),"fwh-checkout-mvp":async()=>{$(`fwh-checkout-mvp ▶`);try{if(!(await Ur()).item_count)return`CART_EMPTY: do not go to checkout. Tell the visitor their cart is empty and offer to help find products.`}catch{}return c(Zr(),`Checkout`)},"fwh-view-product-mvp":async e=>{let t=e||{},n=String(t.product_handle||t.productHandle||``).trim().toLowerCase();return $(`fwh-view-product-mvp ▶`,{handle:n}),n?c(Qr(n),n.replace(/[-_]/g,` `)):`Missing product_handle: search the product first with fwh-shop-search-mvp.`}};async function s(e){try{let{sectionsSwapped:t}=await si(e);if(!t&&ei())return` NOTE: the cart list on this page could not be re-rendered — briefly tell the visitor the page may need a quick refresh to show the change.`}catch{}return``}function c(e,n){if(typeof window>`u`)return`NAV_FAILED: no browser context.`;let r=e;try{r=new URL(e,window.location.href).href}catch{}return t&&Le(t,window.location.href,r,n),Tr(r),Mr().then(()=>{window.location.assign(r)}),`OK_NAVIGATING to ${n}. The page is loading — say one short phrase like "Here you go." and nothing more.`}return{...o,highlightOnPage:o[`fwh-highlight-mvp`],getPageContent:o[`fwh-page-content-mvp`],endVoiceSession:o[`fwh-end-session-mvp`],listPageForms:o[`fwh-list-forms-mvp`],fillFormFields:o[`fwh-fill-form-mvp`],clickPageButton:o[`fwh-click-button-mvp`]}}var y_=/^(get in touch|contact us|let'?s (talk|connect)|subscribe|newsletter|follow us|quick links|company|solutions|what we do|resources|legal|sitemap|share this)/i;function b_(){try{let{headings:e,pageTitleHeading:t,containerHeadings:n}=g_(),r=new Set((n||[]).map(e=>e.trim())),i=(e||[]).map(e=>typeof e==`string`?e:e?.text||``).filter(Boolean).filter(e=>!t||e.trim()!==t.trim()).filter(e=>!y_.test(e.trim())),a=i.filter(e=>!r.has(e.trim()));return a.length?a:i}catch{return[]}}function x_(e){return!e||typeof e!=`object`?e:Object.fromEntries(Object.entries(e).map(([e,t])=>[e,async n=>{let r=Mn();r?console.log(`[FlowEngage:voice-tool] ▶`,e,n):console.info(`[FlowEngage] voice tool » ${e}`);let i;try{i=await t(n)}catch(t){let n=`Tool error: ${t?.message||String(t)}`;return console.error(`[FlowEngage] voice tool ${e} threw:`,t),n}let a=typeof i==`string`?i.length>220?`${i.slice(0,220)}…`:i:String(i);return r?console.log(`[FlowEngage:voice-tool] ◀`,e,a):console.info(`[FlowEngage] voice tool « ${e}:`,a),i}]))}var S_=`[PAGE_CONTEXT]`;function C_(e,t,n){return`${S_} SESSION_CONTEXT (NOT a tool result — do NOT treat as CURRENT_PAGE): Visitor is on: ${e} | Approx headings for awareness only: ${t||``}. ${n?`Forms: ${n}. `:``}The visitor is ALREADY on this page — do NOT offer to navigate there.`}var w_={blue:`#135bd8`,red:`#ef334b`,yellow:`#ffbd2e`,dark:`#0d1b2a`,highlight:`rgba(255,255,255,0.9)`,green:`#10b981`};function T_(e){if(!Array.isArray(e)||e.length<2)return`linear-gradient(145deg, #dce8f022 0%, #e8dff522 45%, #f5e0e022 100%), #f7f8fa`;let[t,n,r]=e;return e.length>=3&&r?[`radial-gradient(ellipse 80% 70% at -10% 50%, ${t}1a 0%, transparent 70%)`,`radial-gradient(ellipse 70% 80% at 110% 30%, ${n}1a 0%, transparent 70%)`,`radial-gradient(ellipse 80% 60% at 50% 110%, ${r}1a 0%, transparent 70%)`,`#f7f8fa`].join(`, `):[`radial-gradient(ellipse 80% 100% at -10% 50%, ${t}1a 0%, transparent 70%)`,`radial-gradient(ellipse 80% 100% at 110% 50%, ${n}1a 0%, transparent 70%)`,`#f7f8fa`].join(`, `)}function E_(e){if(!e||typeof e!=`string`)return`#1a1a1a`;let t=e.replace(`#`,``),n=t.length===3?t.split(``).map(e=>e+e).join(``):t,r=parseInt(n.slice(0,2),16)/255,i=parseInt(n.slice(2,4),16)/255,a=parseInt(n.slice(4,6),16)/255,o=e=>e<=.04045?e/12.92:((e+.055)/1.055)**2.4;return .2126*o(r)+.7152*o(i)+.0722*o(a)>.179?`#1a1a1a`:`#ffffff`}function D_(e){let[t,n]=b.useState(null),r=typeof e==`string`?e.trim():``;return b.useEffect(()=>{if(!r){n(null);return}n(null);let e=!1,t=new window.Image;return t.onload=()=>{e||n(r)},t.onerror=()=>{e||n(null)},t.src=r,()=>{e=!0}},[r]),t}function O_({logo:e,style:t}){let n=D_(typeof e==`string`?e:null);if(!e)return null;if(typeof e==`string`)return n?(0,V.jsx)(`img`,{src:n,alt:``,style:{objectFit:`contain`,flexShrink:0,display:`block`,...t}}):null;let r=typeof e==`function`?b.createElement(e):b.isValidElement(e)?e:null;return r?(0,V.jsx)(`span`,{className:`fe-logo-slot`,style:{display:`inline-flex`,alignItems:`center`,flexShrink:0,overflow:`hidden`,...t},children:r}):null}function k_({title:e}){return(0,V.jsx)(`div`,{style:{width:`100%`,height:`100%`,borderRadius:`16px`,background:`linear-gradient(135deg, rgba(59,130,246,0.16) 0%, rgba(59,130,246,0.08) 100%)`,color:`#2563eb`,display:`flex`,alignItems:`center`,justifyContent:`center`,fontSize:`18px`,fontWeight:700,letterSpacing:`-0.03em`},children:String(e||`FE`).split(/\s+/).filter(Boolean).slice(0,2).map(e=>e[0]?.toUpperCase()||``).join(``)||`FE`})}function A_(e,t){let n=(t||``).trim();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)||e===`bot`&&(/\b(that'?s all|we'?re done)\b/i.test(n)||/\b(goodbye|bye)\b/i.test(n)||/\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))):!1}function j_({orbColors:e,...t}){return(0,V.jsx)(Ug,{colors:e,...t})}function M_({copy:e,orbColors:t,primaryColor:n,onYes:r,onLater:i,isMobile:a,isClosing:o}){let s=n||w_.green,c=E_(s),l=a?72:94,u=(0,V.jsxs)(`div`,{style:{display:`flex`,alignItems:a?`flex-end`:`center`,gap:a?`7px`:`9px`},children:[(0,V.jsxs)(`div`,{style:{flex:1,minWidth:0},children:[(0,V.jsx)(Qm.p,{initial:{x:-16,opacity:0},animate:{x:0,opacity:1},transition:{type:`spring`,stiffness:260,damping:24,delay:.36},style:{margin:`0 0 14px`,fontSize:`14px`,fontWeight:700,color:`white`,lineHeight:1.4,textShadow:`0 1px 4px rgba(0,0,0,0.35)`,maxWidth:`210px`,wordBreak:`break-word`,overflow:`hidden`,display:`-webkit-box`,WebkitLineClamp:4,WebkitBoxOrient:`vertical`},children:e.entryPrompt}),(0,V.jsxs)(Qm.div,{initial:{y:10,opacity:0},animate:{y:0,opacity:1},transition:{type:`spring`,stiffness:260,damping:24,delay:.54},style:{display:`flex`,gap:`16px`,flexWrap:`nowrap`},children:[(0,V.jsx)(`button`,{onClick:r,style:{height:`31px`,boxSizing:`border-box`,padding:`0 16px`,borderRadius:`999px`,background:s,color:c,fontWeight:400,fontSize:`14px`,border:`none`,cursor:`pointer`,whiteSpace:`nowrap`,boxShadow:`0 4px 16px ${s}66`,transition:`transform 0.15s ease`},onMouseEnter:e=>{e.currentTarget.style.transform=`scale(1.04)`},onMouseLeave:e=>{e.currentTarget.style.transform=`scale(1)`},children:e.yesLabel}),(0,V.jsx)(`button`,{onClick:i,style:{height:`31px`,boxSizing:`border-box`,padding:`0 16px`,borderRadius:`999px`,background:`transparent`,color:`white`,fontWeight:400,fontSize:`14px`,border:`2px solid rgba(255,255,255,0.55)`,cursor:`pointer`,whiteSpace:`nowrap`,transition:`border-color 0.15s ease`},onMouseEnter:e=>{e.currentTarget.style.borderColor=`rgba(255,255,255,0.9)`},onMouseLeave:e=>{e.currentTarget.style.borderColor=`rgba(255,255,255,0.55)`},children:e.laterLabel})]})]}),(0,V.jsx)(Qm.div,{initial:{x:120,opacity:0},animate:{x:0,opacity:1},transition:{type:`spring`,stiffness:220,damping:22,delay:0},style:{flexShrink:0},children:(0,V.jsx)(j_,{size:l,orbColors:t})})]});return(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(Qm.div,{initial:{opacity:0},animate:{opacity:o?0:1},transition:{duration:.28},style:{position:`fixed`,inset:0,background:`rgba(0,0,0,0.72)`,zIndex:-1}}),a?(0,V.jsx)(`div`,{style:{position:`absolute`,bottom:0,left:0,right:0,padding:`24px 20px 40px`,overflow:`hidden`,animation:o?`fe-orb-slide-down 0.28s cubic-bezier(0.16,1,0.3,1) forwards`:``},children:u}):(0,V.jsx)(`div`,{style:{animation:o?`fe-orb-slide-down 0.28s cubic-bezier(0.16,1,0.3,1) forwards`:``},children:u})]})}function N_({copy:e,orbColors:t,primaryColor:n,onVoice:r,onText:i,onClose:a,isMobile:o,isClosing:s}){let c=n||w_.green,l=E_(c);return(0,V.jsxs)(Qm.div,{initial:{scale:50/350,borderRadius:`50%`},animate:s?{scale:50/350,opacity:0,borderRadius:`50%`}:{scale:1,borderRadius:`16px`,opacity:1},transition:{type:`spring`,stiffness:260,damping:26},style:{transformOrigin:o?`bottom center`:`bottom right`,background:`white`,width:o?`100%`:`350px`,height:`245px`,padding:`24px 40px 32px`,boxSizing:`border-box`,display:`flex`,flexDirection:`column`,alignItems:`center`,justifyContent:`space-between`,boxShadow:`0 20px 60px rgba(0,0,0,0.2)`,position:`relative`,overflow:`hidden`},children:[(0,V.jsx)(`button`,{onClick:a,style:{position:`absolute`,top:`14px`,right:`14px`,background:`none`,border:`none`,cursor:`pointer`,color:`#9ca3af`,padding:`4px`,display:`flex`,alignItems:`center`,justifyContent:`center`,borderRadius:`50%`,transition:`color 0.15s ease`},onMouseEnter:e=>{e.currentTarget.style.color=`#374151`},onMouseLeave:e=>{e.currentTarget.style.color=`#9ca3af`},children:(0,V.jsx)(`svg`,{width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2.2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:(0,V.jsx)(`polyline`,{points:`6 9 12 15 18 9`})})}),(0,V.jsx)(j_,{size:72,orbColors:t}),(0,V.jsxs)(`div`,{style:{textAlign:`center`},children:[(0,V.jsx)(`p`,{style:{margin:0,fontSize:`16px`,fontWeight:400,color:`#111827`,lineHeight:1.4},children:e.connectionTitle}),e.connectionSubtitle&&(0,V.jsx)(`p`,{style:{margin:`6px 0 0`,fontSize:`13px`,fontWeight:400,color:`#6b7280`,lineHeight:1.4},children:e.connectionSubtitle})]}),(0,V.jsxs)(`div`,{style:{display:`flex`,gap:`16px`,width:`100%`},children:[(0,V.jsx)(`button`,{onClick:r,style:{width:`127px`,height:`43px`,borderRadius:`999px`,background:c,color:l,fontWeight:400,fontSize:`16px`,border:`none`,cursor:`pointer`,transition:`transform 0.15s ease`,boxShadow:`0 4px 12px ${c}55`},onMouseEnter:e=>{e.currentTarget.style.transform=`scale(1.03)`},onMouseLeave:e=>{e.currentTarget.style.transform=`scale(1)`},children:e.voiceLabel}),(0,V.jsx)(`button`,{onClick:i,style:{width:`127px`,height:`43px`,borderRadius:`999px`,background:`transparent`,color:`#111827`,fontWeight:400,fontSize:`16px`,border:`1.5px solid #d1d5db`,cursor:`pointer`,transition:`border-color 0.15s ease`},onMouseEnter:e=>{e.currentTarget.style.borderColor=`#6b7280`},onMouseLeave:e=>{e.currentTarget.style.borderColor=`#d1d5db`},children:e.textLabel})]})]})}function P_({orbColors:e,onEnterChat:t,onEndChat:n,agentId:r,siteId:i,apiBaseUrl:a,chatId:o,historyRef:s,onMessageSync:c,beaconSyncVoiceTranscript:l,widgetPosition:u=`bottom-right`,voiceEndCounter:d,websiteName:f,voiceFirstMessage:p,captureVisitorEmail:m,emailRequired:h,emailCaptureEnabled:g=!0,listeningLabel:_=`Listening...`,thinkingLabel:v=`Thinking...`,enterChatModeLabel:y=`Enter Chat Mode`,endConversationLabel:x=`End Conversation`}){(0,b.useEffect)(()=>(console.log(`[FlowEngage:VoiceDebug] 🔵 VoiceMode MOUNTED`),()=>console.log(`[FlowEngage:VoiceDebug] 🔴 VoiceMode UNMOUNTED`)),[]);let[S,C]=(0,b.useState)(`idle`),[w,T]=(0,b.useState)(!1),[E,ee]=(0,b.useState)(!1),[D,te]=(0,b.useState)(()=>le(i)===`auto`),O=()=>{let e=D?`ask`:`auto`;ue(i,e),te(e===`auto`)},[ne,re]=(0,b.useState)(!1),[ie,ae]=(0,b.useState)(``),[oe,k]=(0,b.useState)(``),A=(0,b.useRef)(!1),se=(0,b.useRef)(!1),ce=(0,b.useRef)(null),de=(0,b.useRef)(0),j=(0,b.useRef)(``),fe=(0,b.useRef)(!1),pe=(0,b.useRef)(0),me=(0,b.useRef)(!1),he=(0,b.useRef)(!1),ge=(0,b.useRef)(!1),_e=(0,b.useRef)([]),ve=(0,b.useRef)(null),ye=(0,b.useRef)(``),M=(0,b.useRef)(null),be=e=>{me.current=e,e||(he.current=!1),N.current?.setWalkthroughActive?.(e)};M.current=async()=>{if(he.current)return;let e=typeof window<`u`?window.location.pathname:null;if(e&&ve.current&&e!==ve.current){console.log(`[FlowEngage] walkthrough ended — visitor left `+ve.current),ve.current=null,_e.current=[],be(!1);return}let t=_e.current;xe.current+=1;let n=xe.current,r=t[n];if(!r){if(be(!1),t.length)N.current?.sendUserMessage?.(`That was the last section. Say so in one short sentence and ask if they want to explore any section further.`);else if(n===0){let e=``;try{let t=await Se.current?.[`fwh-read-content-mvp`]?.({});typeof t==`string`&&t&&!/^Could not/i.test(t)&&(e=t.slice(0,2800))}catch{}N.current?.sendUserMessage?.(e?`Give the visitor a complete spoken overview of this page in 4-6 sentences, based ONLY on this page content:\n"""${e}"""\nThen ask which part they'd like to hear more about. Do not call any tool.`:`Give the visitor a complete spoken overview of this page in 4-6 sentences based on what you know about it, then ask which part they'd like to hear more about. Do not call any tool.`,{suppressQuestion:!0})}return}try{await Se.current?.highlightOnPage?.({section_text:r})}catch(e){console.warn(`[FlowEngage] walkthrough highlight failed:`,e?.message||e)}console.debug(`[FlowEngage] walkthrough step ${n+1}/${t.length}: ${r}`);let i=``;try{let e=await Se.current?.[`fwh-read-content-mvp`]?.({section_text:r});typeof e==`string`&&e&&!/^Could not/i.test(e)&&(i=e.slice(0,2500))}catch{}let a=i?`Here is this section's full content — describe it from THIS text only, and do not call any tool:\n"""${i}"""\n`:`read the section's content silently first (fwh-read-content-mvp) if you don't already have it. `;N.current?.sendUserMessage?.(`Describe the section titled "${r}" in 3-4 sentences, starting with ${n===0?`Starting with`:`Next —`} ${r}. Describe ONLY this section. If this section contains named sub-items (steps, checklist entries, cards, plans, FAQs, team members), name EVERY one of them in order with a short phrase each — never skip or collapse them into a summary; `+a+`This section is ON THE PAGE THE VISITOR IS ALREADY ON — do NOT call fwh-navigate-mvp, do not go to a product/detail page, and do not offer to, even if this section's name also matches a page name. Do not mention any other section, do not ask whether to continue, then stop.`,{suppressQuestion:!0})};let xe=(0,b.useRef)(0),N=(0,b.useRef)(null),Se=(0,b.useRef)(null),Ce=(0,b.useRef)(``),we=(0,b.useRef)(async()=>{}),Te=(0,b.useRef)(!1),ke=(0,b.useRef)(!1),je=(0,b.useRef)(!1),Me=(0,b.useRef)(!1),Ne=(0,b.useRef)(`minimized`),Pe=(0,b.useRef)(null),Ie=(0,b.useRef)(null),Le=(0,b.useRef)(null),ze=(0,b.useRef)(0),P=(0,b.useRef)(!1),Be=(0,b.useRef)(`idle`),Ve=(0,b.useRef)(null),He=(0,b.useRef)(!1),Ue=(0,b.useRef)(null),We=(0,b.useCallback)((e=`minimized`)=>{console.warn(`[FlowEngage:VoiceDebug] 🛑 requestSessionShutdown called`,{destination:e,stack:Error().stack?.split(`
|
|
430
|
-
`).slice(1,4).join(` | `)}),Ne.current=e,Me.current=!0,queueMicrotask(()=>{Te.current||(Te.current=!0,we.current?.())})},[s]),Ge=(0,b.useCallback)(()=>{ce.current&&=(clearTimeout(ce.current),null)},[]);(0,b.useEffect)(()=>{if(h){A.current=!1;return}if(A.current=!0,se.current=!1,re(!1),Ge(),fe.current){fe.current=!1,N.current?.setMicMuted?.(!1);let e=j.current;j.current=``,N.current?.injectAgentSpeech?.(`Thanks — I already have your email on file.`,`interrupt`),e&&N.current?.sendUserMessage?.(`The visitor's email is already on file — do not ask for it. Answer their question now: "${e}"`)}N.current?.sendContextualUpdate?.(`EMAIL REQUIREMENT SATISFIED — the visitor's email is already on file. All earlier instructions about requiring or asking for an email are VOID. Never ask for it again.`)},[h,Ge]);let Ke=/(walk\s*(me\s*)?through|walkthrough|take me through|(complete|full|detailed) (overview|understanding|walkthrough|picture|breakdown|explanation)|give me (a|an|the) (overview|tour|walk)|tell me about (each|all)|go through (it|them|each|all)|overview of|one by one|tell me everything|explain (me )?(this|the) (whole |entire |full )?page|everything (about|on) this page|show me (each|all|around))/i,qe=/(that covers everything|that was all|covered everything|that(?:'|’)?s everything)/i,Je=/^((uh+|um+|so|well|okay|ok|yeah)[,.\s]+)*(yes|yeah|yep|yup|sure|ok(ay)?|please|continue|go on|next|keep going|carry on|go ahead|resume|back to the tour)[\s.!,]*$/i,Ye=/(would you like (me )?to take you|shall i take you|want me to take you|would you like to (go|explore|visit|see)|take you there)/i,Xe=/^((uh+|um+|oh|ah|so|well)[,.\s]+)*(cool|nice|great|awesome|wow|lovely|perfect|got it|gotcha|i see|understood|makes sense|interesting|right|alright|sounds good|mm+-?hmm+|hmm+|k)[\s.!,]*$/i,Ze=e=>String(e||``).replace(/[\s.,!?]*((uh+|um+|er+|hmm+|mm+)[\s.,!?]*)+$/i,``).trim(),Qe=(0,b.useCallback)(()=>{Ge(),ce.current=setTimeout(()=>{if(!(!se.current||A.current))if(de.current<3){de.current+=1;try{N.current?.injectAgentSpeech?.(`I still need your email in the box below to continue — could you enter it, please?`,`queue`)}catch{}Qe()}else{try{N.current?.injectAgentSpeech?.(`Since I haven't received your email, I'll close this session for now. Feel free to come back anytime — take care!`,`queue`)}catch{}se.current=!1,re(!1),Ge(),P.current=!0}},1e4)},[Ge]),$e=(0,b.useCallback)(async e=>{Me.current=!0;try{In()}catch{}try{Bh()}catch{}if(i)try{Re(i)}catch{}try{await N.current?.endSession?.()}catch{}let t=Pe.current,n=Ie.current;if(t&&o&&a){let e=n?Math.round((Date.now()-n)/1e3):0;fetch(`${a}/api/voice/save-chat-recording`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({chatId:o,voiceSessionId:t,duration:e,siteId:i})}).catch(e=>console.warn(`[FlowEngage] save-chat-recording failed:`,e.message))}if(Pe.current=null,Ie.current=null,N.current=null,C(`idle`),T(!1),Te.current=!1,Lr(),i)try{Oe(i)}catch{}typeof c==`function`&&await c(s.current),e?.()},[s,c,i,o,a]);we.current=()=>$e(Ne.current===`chat`?t:n),(0,b.useEffect)(()=>{ke.current=w},[w]),(0,b.useEffect)(()=>{d&&We(`chat`)},[d]),(0,b.useEffect)(()=>{let e=!1,d=null,m=null,_=null;return(async()=>{if(!i){C(`error`);return}try{if((await navigator.mediaDevices.getUserMedia({audio:!0})).getTracks().forEach(e=>e.stop()),e)return;s.current=[],Ce.current=``,Te.current=!1,je.current=!1,Me.current=!1,Ne.current=`minimized`,Ir({onBeforeNavigate:()=>{if(i&&s.current.length>0)try{let e=Qg(s.current);e&&Ee(i,e)}catch{}if(!nr()){let e=Pe.current,t=Ie.current;if(e&&o&&a)try{let n=t?Math.round((Date.now()-t)/1e3):0,r=JSON.stringify({chatId:o,voiceSessionId:e,duration:n,siteId:i}),s=`${a}/api/voice/save-chat-recording`;fetch(s,{method:`POST`,headers:{"Content-Type":`application/json`},body:r,keepalive:!0}).catch(()=>{}),Pe.current=null,Ie.current=null}catch{}}Ae()}});let t=x_(v_({siteId:i||null,apiBaseUrl:a||null,chatId:o||null,getLastUserVoiceText:()=>Ce.current,beaconSyncFullVoiceHistory:()=>l?.(s.current),isWalkthroughActive:()=>me.current&&!he.current,requestEndSession:()=>{P.current=!0},requestEmailCollection:()=>{g&&(A.current||se.current||(se.current=!0,de.current=0,re(!0),Qe()))}})),n=i?De(i):null,r=!!n?.transcript,c=i?Re(i):null,v=!!c,y=typeof window<`u`?window.location.pathname:`/`,b=typeof document<`u`?document.title:``,x=y.replace(/^\/[a-z]{2}(?:-[a-z]{2})?(?=\/|$)/i,``)||`/`,S=x===`/`||x===``,w={apiBaseUrl:a,siteId:i,chatId:o,clientTools:t};Se.current=t;let E=(y.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()),ee=``;b&&b.includes(`|`)&&(ee=b.split(`|`).map(e=>e.trim()).filter(Boolean).reduce((e,t)=>e.length<=t.length?e:t));let D=ee||E||``,te=D?`the ${D} page`:`this page`,O=(()=>{if(!c?.targetUrl)return!0;try{return new URL(c.targetUrl,window.location.href).pathname.replace(/\/$/,``)===window.location.pathname.replace(/\/$/,``)}catch{return!0}})(),ne=(()=>{if(!c?.targetUrl)return null;try{return new URL(c.targetUrl,window.location.href).pathname}catch{return null}})();if(v&&!O)w.greeting=`You're on ${te}. What would you like to know?`,console.warn(`[FlowEngage] nav target "${c?.pageName}" not reached — on "${D}" instead; not claiming navigation`);else if(v){let e=c?.pageName?`the ${c.pageName} page`:te;w.greeting=`I have navigated you to ${e}. Do you want to know about any specific section or want a complete overview?`,console.log(`[FlowEngage] 🔄 Agent-nav resume: greeting set for "${e}" (${y})`)}else if(r)w.greeting=`Welcome back! I see you're now on ${te}. Would you like me to continue what I was explaining before, or would you like to know about this page instead?`;else{let e=f||``;w.greeting=p||(S?e?`Hi, welcome to ${e}! How may I help you?`:`Hi, welcome! How may I help you?`:`Welcome to ${te}! How may I help you?`),S||console.log(`[FlowEngage] 🏠 Page-context greeting: "${D}" for ${y}`)}let ie=!1,ae=v?`NAVIGATION COMPLETE — The exact phrase "I have navigated you to the [page] page. Do you want to know about any specific section or want a complete overview?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript?`Prior transcript:\n${n.transcript}\n`:`(no prior transcript)`}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until the visitor responds. When they reply: if they name a specific section → call fwh-get-current-page-mvp then answer immediately; if they say "overview" or anything non-specific → call fwh-get-current-page-mvp (zero speech) then do a full highlighted walkthrough.`:null,oe=!1,k=!v&&!r?`Your greeting has been spoken. Wait for the visitor to ask a question or tell you what they need. Do NOT auto-start a walkthrough or highlight any sections — let the visitor lead.`:null,ce=r&&!v,ue=!1,we=ce?`The question "Would you like me to continue what I was explaining before, or would you like to know about this page instead?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript?`Prior transcript:\n${n.transcript}\n`:`(no prior transcript, or the visitor navigated before saying much)`}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until they respond. When they reply: if they want you to CONTINUE the previous topic → resume verbally from the prior transcript above, do NOT call fwh-get-current-page-mvp or describe the new page unless they ask. If they want to know about THIS page → call fwh-get-current-page-mvp (zero speech) then proceed as a normal fresh-page walkthrough/description. If their reply is ambiguous, ask them to clarify which they'd prefer rather than guessing.`:null,Oe=!1,Le=!1,$e=g&&!!h&&!ge.current,F=await xh.startSession({...w,onConnect:({conversationId:e})=>{Pe.current=e,Ie.current=Date.now(),console.log(`[FlowEngage] 🎙️ AI chat session connected:`,e)},onUnhandledClientToolCall:e=>console.error(`[FlowEngage] Unhandled tool:`,e?.tool_name),onSessionInfo:({emailOnFile:e})=>{ge.current=!!e,e&&(A.current=!0,se.current=!1,re(!1),Ge(),console.log(`[FlowEngage] email already on file per server — gate disabled`))},onAgentTurnEnd:()=>{e||me.current&&(he.current||fe.current||setTimeout(()=>M.current?.(),250))},onSessionEnding:()=>{e||We(`minimized`)},onPanel:({title:t,items:n})=>{e||Rh(n,{title:t||`Products`,side:u===`bottom-left`?`left`:`right`,productUrl:e=>e.handle?`${window.location.origin}/products/${encodeURIComponent(e.handle)}`:null})},onModeChange:({mode:t,tentative:n})=>{if(!e){if(t===`speaking`){Ve.current&&=(clearTimeout(Ve.current),null),Ue.current&&=(clearTimeout(Ue.current),null),C(`speaking`),Be.current=`speaking`,He.current=!1;return}if(t===`user-speaking`){if(se.current&&!A.current&&Qe(),Ue.current&&=(clearTimeout(Ue.current),null),Be.current===`speaking`)return;try{In()}catch{}Ve.current&&=(clearTimeout(Ve.current),null),Be.current!==`listening`&&(C(`listening`),Be.current=`listening`);return}if(t===`thinking`){if(Ve.current&&=(clearTimeout(Ve.current),null),Ue.current&&=(clearTimeout(Ue.current),null),Be.current===`speaking`)return;C(`thinking`),Be.current=`thinking`,n&&(Ue.current=setTimeout(()=>{Ue.current=null,!(e||Be.current!==`thinking`)&&(Be.current=`listening`,C(`listening`))},4e3));return}if(Be.current=`listening`,P.current){P.current=!1,We();return}if($e&&!Le&&!A.current&&!ge.current){Le=!0,ie=!0,oe=!0;try{N.current?.sendContextualUpdate?.(`CRITICAL — EMAIL REQUIRED BEFORE ANYTHING ELSE. The visitor has not provided their email. The app itself speaks the email request out loud — you must NOT say it, and you must NOT answer. Stay completely silent from now until you receive an EMAIL_SAVED message: produce no speech and call no tools, no matter what the visitor says. Remember their question. On EMAIL_SAVED, thank them briefly and answer that remembered question in full.`)}catch{}}if(ae&&!ie){ie=!0;try{N.current?.sendContextualUpdate?.(ae)}catch{}}if(we&&!ue){ue=!0;try{N.current?.sendContextualUpdate?.(we)}catch{}}if(k&&!oe){oe=!0;try{N.current?.sendContextualUpdate?.(k)}catch{}}Ve.current&&clearTimeout(Ve.current),Ve.current=setTimeout(()=>{e||Be.current!==`speaking`&&(He.current||(Ve.current=null,Be.current=`listening`,C(`listening`)))},1200)}},onMessage:t=>{if(e||fe.current&&t.role!==`user`&&!/email/i.test(String(t.message||``)))return;let n=t.role===`user`&&/^(Describe the section titled "|\[SYSTEM:|Give the visitor a complete spoken overview|That was the last section)/.test(String(t.message||``).trim()),r=/\b(?:this|that|the)\s+(?:case\s*stud(?:y|ies)|product|article|blog|post|item|service)\b/i.test(String(t.message||``));if(!n&&!r&&t.role===`user`&&Ke.test(String(t.message||``)))typeof window<`u`&&(window.__fe_user_nav_during_speech=null),be(!0),he.current=!1,pe.current=0,xe.current=-1,_e.current=b_(),ve.current=typeof window<`u`?window.location.pathname:null,setTimeout(()=>M.current?.(),400),console.log(`[FlowEngage] walkthrough armed — ${_e.current.length} sections on this page`);else if(!n&&t.role===`user`&&me.current){let e=String(t.message||``).trim(),n=Ze(e),r=e=>!!e&&(Je.test(e)||Xe.test(e));Ye.test(ye.current||``)?(console.log(`[FlowEngage] walkthrough ended — visitor accepted a navigation offer`),ve.current=null,_e.current=[],be(!1)):!n||r(e)||r(n)?he.current&&(he.current=!1,console.debug(`[FlowEngage] walkthrough resumed by visitor`),setTimeout(()=>M.current?.(),300)):(console.log(`[FlowEngage] walkthrough ended — visitor moved to a new topic`),ve.current=null,_e.current=[],be(!1))}if(t.role!==`user`&&!n&&(ye.current=String(t.message||``)),t.role!==`user`&&qe.test(String(t.message||``))){let e=_e.current.length;!e||xe.current>=e-1?be(!1):console.debug(`[FlowEngage] agent claimed completion at step ${xe.current+1}/${e} — ignoring, tour continues`)}let a=t.role===`user`?`user`:`bot`,o=t.message||t.text||t.content||``;if(o){if(a===`user`&&(Ce.current=o,typeof window<`u`&&(window.__fe_user_nav_during_speech=null),He.current=!0,Ve.current&&=(clearTimeout(Ve.current),null),Be.current!==`speaking`&&(C(`thinking`),Be.current=`thinking`),$e&&!Oe&&!A.current&&!ge.current&&(Oe=!0,j.current=o,fe.current=!0,N.current?.injectAgentSpeech?.(`Before I answer that, could you please enter your email in the text box below?`,`interrupt`),N.current?.setMicMuted?.(!0),se.current||(se.current=!0,de.current=0,re(!0),Qe()))),s.current=[...s.current,{sender:a,message:o,timestamp:new Date().toISOString(),debug:t.debug}],i)try{let e=Qg(s.current);e&&Ee(i,e)}catch{}if(A_(a,o)){P.current=!0;return}if(ke.current&&a===`bot`&&/are you still there|still there\??|hello\?|anyone there|checking in|you there|are you available|is anyone|did you step away/i.test(o))if(ze.current+=1,ze.current===2)try{N.current?.sendContextualUpdate?.(`IMPORTANT: The visitor is muted and has not responded to your last 2 checks. Make exactly ONE more check. If there is still no response, say: 'I feel like you are not active, so I'm ending this session. You can connect with me anytime.' then immediately call the fwh-end-session-mvp tool to end the voice session. Do NOT ask again after that.`)}catch{}else ze.current>=5&&We(`minimized`)}},onError:t=>{if(!e&&!Me.current&&!je.current&&!Fe()){je.current=!0,console.error(`[FlowEngageVoiceCall] session:error`,{message:t?.message,name:t?.name,raw:t});try{N.current?.setMicMuted?.(!0)}catch{}T(!0),C(`error`),We(`chat`)}},onDisconnect:t=>{if(e||Me.current||je.current||Fe())return;let n=s.current?.length||0;console.warn(`[FlowEngageVoiceCall] session:disconnected`,{historyCount:n,reason:t?.reason,closeCode:t?.context?.code,closeReason:t?.context?.reason||t?.message,lastUserText:Ce.current||``,wasMuted:ke.current}),je.current=!0;try{N.current?.setMicMuted?.(!0)}catch{}T(!0),We(`chat`)}});if(e){console.warn(`[FlowEngage:VoiceDebug] ❌ cancelled=true after startSession resolved — killing session immediately`),F.endSession().catch(()=>{});return}d=F,N.current=F,C(`listening`);try{sessionStorage.setItem(`el_conv_${i}`,F.getId())}catch{}parent.postMessage({type:`AGENT_EXPAND`},`*`);try{let e=typeof window<`u`?window.location.href:``,a=`[]`;try{a=await t[`fwh-list-forms-mvp`]()}catch{}let o=`none`;if(typeof window<`u`&&typeof document<`u`)try{let e=`#flowengage-root, .flowengage-shell, .flowengage-widget-root`,t=document.querySelector(`main, [role='main']`)||document.body,n=Array.from(t.querySelectorAll(`h1, h2, h3`)).filter(t=>!t.closest(e)).map(e=>e.textContent.replace(/\s+/g,` `).trim()).filter(e=>e.length>2&&e.length<200);n.length||(n=Array.from(document.body.querySelectorAll(`h1, h2, h3`)).filter(t=>!t.closest(e)).map(e=>e.textContent.replace(/\s+/g,` `).trim()).filter(e=>e.length>2&&e.length<200)),n.length&&(o=[...new Set(n)].slice(0,15).join(` | `))}catch{}let s=r&&!v?`RESUMING PRIOR SESSION — DO NOT GREET. The visitor is continuing the same conversation after a page reload. Do not introduce yourself or re-state your capabilities. Wait silently until the visitor speaks; if you must speak first, briefly acknowledge where you left off in one short sentence.\nPrior transcript:\n${n?.transcript||``}\n\n`:``;F.sendContextualUpdate(`${s}SiteId: ${i||`unknown`}. ${h?``:`Visitor's email is already on file from a prior session — do NOT ask for it again. `}WALKTHROUGH RULE: When the visitor asks for a walkthrough or overview of this page, you MUST IMMEDIATELY call fwh-get-current-page-mvp (zero speech — call it silently with NO words before it) to get the exact highlightable headings. Do NOT skip fwh-get-current-page-mvp. Voice: call end_voice_session or say you are ending the session to stop.`),F.sendContextualUpdate(C_(e,o,a))}catch{}if(typeof window<`u`){let t=window.location.pathname,n=Date.now(),r=e=>{try{return(decodeURIComponent(e||`/`).replace(/\/+$/,``)||`/`).toLowerCase()}catch{return((e||`/`).replace(/\/+$/,``)||`/`).toLowerCase()}},a=()=>{if(e)return;let a=window.location.pathname;if(r(a)===r(t)){t=a;return}if(Date.now()-n<8e3){t=a;return}let o=typeof window<`u`&&window.__fe_agent_nav||null,s=ne&&r(a)===r(ne)||Jn()||o&&Date.now()-o.ts<15e3&&r(o.path)===r(a),c=t;t=a;let l=window.location.href,u=typeof document<`u`?document.title:``,d=typeof document<`u`?Array.from(document.querySelectorAll(`h1, h2, h3, h4`)).filter(e=>!e.closest(`#flowengage-root, .flowengage-shell`)).map(e=>({tag:e.tagName,text:e.textContent.trim()})).filter(e=>e.text.length>0&&e.text.length<200).slice(0,20):[];try{In()}catch{}let f=d.map(e=>e.text).join(` | `),p=e=>(e.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase())||`Home`,m=p(c),h=p(a);if(s){if(typeof window<`u`&&(window.__fe_agent_nav=null),console.log(`[FlowEngage] agent-driven navigation settled: "${c}" → "${a}"`),le(i)!==`auto`)try{N.current?.sendUserMessage?.(`[SYSTEM: YOU just navigated the visitor to the "${h}" page (${l}) at their request — this was YOUR action, not theirs. Page title: "${u}". Sections: ${JSON.stringify(d)}. Say: "Here we are on the ${h} page." Then introduce this page in 2-3 sentences, and finish by asking: "Would you like me to walk you through it section by section?" NEVER say "I see you've navigated" — the visitor did not navigate, you did. Do NOT call fwh-navigate-mvp again. Wait for their answer before describing sections.]`,{suppressQuestion:!0})}catch{}else console.log(`[FlowEngage] auto-nav — skipping the arrival acknowledgment turn`);try{N.current?.sendContextualUpdate?.(C_(l,f))}catch{}return}typeof window<`u`&&(window.__fe_user_nav_during_speech={fromPath:c,toPath:a,fromPageName:m,toPageName:h,ts:Date.now()}),console.log(`[FlowEngage] visitor navigation detected: "${c}" → "${a}" (agent target: ${ne||`none`})`);try{N.current?.sendUserMessage?.(`[SYSTEM: I navigated away mid-walkthrough — I am now on the "${h}" page (${l}). Page title: "${u}". Sections: ${JSON.stringify(d)}. STOP everything. Do NOT describe the new page. Say EXACTLY: "I see you've navigated to the ${h} page — would you like me to continue what I was explaining, or shall I walk you through this page?" Then wait for my answer. IF I say continue/yes/carry on: resume speaking verbally from where you left off — do NOT call fwh-navigate-mvp, do NOT call fwh-highlight-mvp, the visitor will navigate themselves if needed. IF I want to hear about this page: call fwh-highlight-mvp for sections of "${h}" only.]`)}catch{}try{N.current?.sendContextualUpdate?.(C_(l,f))}catch{}};m=window.history.pushState.bind(window.history),window.history.pushState=(...e)=>{m(...e),setTimeout(a,50)},_=()=>setTimeout(a,50),window.addEventListener(`popstate`,_)}}catch(r){e||(console.error(`[FlowEngage] startSession failed`,r),r.message&&r.message.includes(`Too many voice sessions`)?(alert(r.message),n&&n(`minimized`)):(C(`error`),Ne.current=`chat`,window.setTimeout(()=>{e||t?.()},0)))}})(),()=>{console.warn(`[FlowEngage:VoiceDebug] ⚠️ useEffect CLEANUP fired — session will be killed`,{agentId:r,siteId:i,apiBaseUrl:a}),e=!0,Me.current=!0;try{In()}catch{}if(i)try{Re(i)}catch{}typeof window<`u`&&(window.__fe_user_nav_during_speech=null),Lr(),Ve.current&&=(clearTimeout(Ve.current),null),Ue.current&&=(clearTimeout(Ue.current),null),ce.current&&=(clearTimeout(ce.current),null),He.current=!1,Be.current=`idle`,m&&typeof window<`u`&&(window.history.pushState=m),_&&typeof window<`u`&&window.removeEventListener(`popstate`,_);let t=d||N.current,n=Pe.current,l=Ie.current;if(t?.endSession?.().catch(()=>{}),n&&o&&a){let e=l?Math.round((Date.now()-l)/1e3):0,t=JSON.stringify({chatId:o,voiceSessionId:n,duration:e,siteId:i}),r=`${a}/api/voice/save-chat-recording`;typeof navigator<`u`&&navigator.sendBeacon&&navigator.sendBeacon(r,new Blob([t],{type:`application/json`}))||fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`},body:t,keepalive:!0}).catch(()=>{}),Pe.current=null,Ie.current=null}typeof c==`function`&&s.current.length>0&&c(s.current).catch?.(()=>{})}},[r,i,a,We]),(0,b.useEffect)(()=>{if(!E)return;let e=e=>{let t=e.composedPath?.()?.[0]||e.target;Le.current&&!Le.current.contains(t)&&ee(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[E]);let F=async()=>{if(N.current)try{if(w){ze.current=0,await N.current.setMicMuted?.(!1),T(!1);try{N.current.sendContextualUpdate?.(`The visitor has unmuted. Resume normal interaction and wait for them to speak.`)}catch{}}else{ze.current=0,await N.current.setMicMuted?.(!0),T(!0);try{N.current.sendContextualUpdate?.(`The visitor has muted their microphone. Do NOT speak, ask if they are there, or prompt them in any way. Stay completely silent until they unmute.`)}catch{}}}catch{}},I=(0,b.useCallback)(async e=>{e?.preventDefault?.();let t=ie.trim().toLowerCase();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)){k(`Please enter a valid email address.`);return}k(``),A.current=!0,se.current=!1,Ge(),re(!1),ae(``),fe.current=!1;try{typeof m==`function`&&await m(t)}catch{}N.current?.setMicMuted?.(!1);let n=j.current;j.current=``;try{N.current?.injectAgentSpeech?.(`Thank you for submitting your email.`),N.current?.sendUserMessage?.(n||`The visitor's email is on file. Carry on helping them and never ask for it again.`)}catch{}},[ie,m,Ge]),L=Array.isArray(e)&&e[0]||`#3B82F6`;return(0,V.jsxs)(`div`,{ref:Le,style:{position:`relative`,display:`flex`,flexDirection:`column`,alignItems:`center`,gap:`14px`,animation:`fe-orb-slide-up 0.42s cubic-bezier(0.16,1,0.3,1)`},children:[ne&&(0,V.jsxs)(`form`,{onSubmit:I,style:{width:`270px`,maxWidth:`calc(100vw - 40px)`,boxSizing:`border-box`,background:`rgba(15,22,36,0.97)`,backdropFilter:`blur(14px)`,WebkitBackdropFilter:`blur(14px)`,borderRadius:`16px`,boxShadow:`0 10px 40px rgba(0,0,0,0.45)`,padding:`16px`,display:`flex`,flexDirection:`column`,gap:`10px`,animation:`fe-orb-fade-in 0.18s ease`},children:[(0,V.jsx)(`span`,{style:{color:`white`,fontSize:`13px`,fontWeight:600,lineHeight:1.4},children:`Please enter your email to continue`}),(0,V.jsx)(`input`,{type:`email`,inputMode:`email`,autoComplete:`email`,autoFocus:!0,value:ie,onChange:e=>{ae(e.target.value),oe&&k(``)},placeholder:`you@example.com`,style:{width:`100%`,boxSizing:`border-box`,padding:`10px 12px`,borderRadius:`10px`,border:oe?`1.5px solid #ef4444`:`1.5px solid #4b5563`,background:`#1f2937`,color:`white`,fontSize:`14px`,outline:`none`}}),oe&&(0,V.jsx)(`span`,{style:{color:`#f87171`,fontSize:`12px`},children:oe}),(0,V.jsx)(`button`,{type:`submit`,disabled:!ie.trim(),style:{width:`100%`,padding:`10px 12px`,borderRadius:`10px`,border:`none`,background:ie.trim()?L:`#374151`,color:`white`,fontSize:`14px`,fontWeight:600,cursor:ie.trim()?`pointer`:`default`,transition:`background 0.2s ease`},children:`Submit`})]}),(0,V.jsxs)(`div`,{style:{position:`relative`},children:[(S===`listening`||S===`thinking`)&&!w&&(0,V.jsx)(`span`,{style:{position:`absolute`,right:`100%`,marginRight:`18px`,top:`50%`,transform:`translateY(-50%)`,color:`white`,fontSize:`18px`,fontWeight:600,letterSpacing:`0.01em`,textShadow:`0 2px 4px rgba(0,0,0,0.4)`,whiteSpace:`nowrap`},children:S===`thinking`?v:_}),w&&(0,V.jsx)(`span`,{style:{position:`absolute`,right:`100%`,marginRight:`18px`,top:`50%`,transform:`translateY(-50%)`,color:`#f87171`,fontSize:`16px`,fontWeight:600,letterSpacing:`0.01em`,textShadow:`0 2px 4px rgba(0,0,0,0.4)`,whiteSpace:`nowrap`},children:`Muted`}),(0,V.jsx)(j_,{size:72,voiceState:S,isMuted:w,orbColors:e})]}),S===`error`&&(0,V.jsx)(`span`,{style:{color:`#f87171`,fontSize:`13px`,fontWeight:500},children:`Could not start voice session.`}),(0,V.jsxs)(`div`,{style:{display:`flex`,gap:`12px`,alignItems:`center`},children:[(0,V.jsx)(`button`,{onClick:F,title:w?`Unmute`:`Mute`,style:{width:`24px`,height:`24px`,borderRadius:`50%`,background:w?`#7f1d1d`:`#1f2937`,border:w?`1.5px solid #ef4444`:`1.5px solid #4b5563`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:w?`#f87171`:`white`,transition:`all 0.2s ease`,boxShadow:`0 2px 8px rgba(0,0,0,0.4)`,padding:0,flexShrink:0},children:w?(0,V.jsxs)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,V.jsx)(`line`,{x1:`1`,y1:`1`,x2:`23`,y2:`23`}),(0,V.jsx)(`path`,{d:`M9 9v3a3 3 0 005.12 2.12M15 9.34V4a3 3 0 00-5.94-.6`}),(0,V.jsx)(`path`,{d:`M17 16.95A7 7 0 015 12v-2m14 0v2a7 7 0 01-.11 1.23`}),(0,V.jsx)(`line`,{x1:`12`,y1:`19`,x2:`12`,y2:`23`}),(0,V.jsx)(`line`,{x1:`8`,y1:`23`,x2:`16`,y2:`23`})]}):(0,V.jsx)(tg,{size:16,color:`currentColor`})}),(0,V.jsx)(`button`,{onClick:()=>ee(e=>!e),style:{width:`24px`,height:`24px`,borderRadius:`50%`,background:`#1f2937`,border:`1.5px solid #4b5563`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:`white`,transition:`background 0.2s ease`,boxShadow:`0 2px 8px rgba(0,0,0,0.4)`,padding:0,flexShrink:0},children:(0,V.jsx)(lg,{size:16,color:`currentColor`})})]}),E&&(0,V.jsxs)(`div`,{style:{position:`absolute`,bottom:0,right:0,width:`230px`,zIndex:9999,background:`rgba(15,22,36,0.96)`,backdropFilter:`blur(14px)`,WebkitBackdropFilter:`blur(14px)`,borderRadius:`16px`,boxShadow:`0 10px 40px rgba(0,0,0,0.45)`,animation:`fe-orb-fade-in 0.18s ease`,padding:`12px 12px 40px`},children:[(0,V.jsxs)(`button`,{onClick:()=>{ee(!1),Ne.current=`chat`,$e(t)},style:{display:`flex`,alignItems:`center`,gap:`12px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`white`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`,whiteSpace:`nowrap`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.08)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,V.jsx)(ug,{}),y]}),i&&(0,V.jsxs)(`button`,{onClick:()=>O(),style:{display:`flex`,alignItems:`center`,justifyContent:`space-between`,gap:`8px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`white`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.08)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,V.jsxs)(`span`,{style:{display:`flex`,alignItems:`center`,gap:`12px`,minWidth:0,overflow:`hidden`},children:[(0,V.jsx)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,style:{flexShrink:0},children:(0,V.jsx)(`polygon`,{points:`3 11 22 2 13 21 11 13 3 11`})}),(0,V.jsx)(`span`,{style:{overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:`Auto-navigate`})]}),(0,V.jsx)(`span`,{"aria-hidden":!0,style:{width:`34px`,height:`19px`,borderRadius:`999px`,background:D?`#22c55e`:`rgba(255,255,255,0.2)`,position:`relative`,flexShrink:0,transition:`background 0.15s ease`},children:(0,V.jsx)(`span`,{style:{position:`absolute`,top:`2px`,left:D?`17px`:`2px`,width:`15px`,height:`15px`,borderRadius:`50%`,background:`white`,transition:`left 0.15s ease`}})})]}),(0,V.jsxs)(`button`,{onClick:()=>{ee(!1),Ne.current=`minimized`,$e(n)},style:{display:`flex`,alignItems:`center`,gap:`12px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`#E30C12`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`,whiteSpace:`nowrap`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(227,12,18,0.1)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,V.jsx)(dg,{}),x]}),(0,V.jsx)(`button`,{onClick:()=>ee(!1),style:{position:`absolute`,bottom:`8px`,right:`8px`,width:`24px`,height:`24px`,borderRadius:`50%`,background:`rgba(255,255,255,0.15)`,border:`none`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:`white`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.25)`},onMouseLeave:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.15)`},children:(0,V.jsxs)(`svg`,{width:`12`,height:`12`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,V.jsx)(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),(0,V.jsx)(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})]})})]})]})}function F_({orbColors:e}){let t=Array.isArray(e)&&e.length>0?e:[`#135bd8`,`#ef334b`,`#ffbd2e`],n=t[0],r=t[1]||t[0],i=t[2]||t[0];return(0,V.jsx)(`div`,{style:{width:`32px`,height:`32px`,borderRadius:`50%`,flexShrink:0,background:`
|
|
430
|
+
`).slice(1,4).join(` | `)}),Ne.current=e,Me.current=!0,queueMicrotask(()=>{Te.current||(Te.current=!0,we.current?.())})},[s]),Ge=(0,b.useCallback)(()=>{ce.current&&=(clearTimeout(ce.current),null)},[]);(0,b.useEffect)(()=>{if(h){A.current=!1;return}if(A.current=!0,se.current=!1,re(!1),Ge(),fe.current){fe.current=!1,N.current?.setMicMuted?.(!1);let e=j.current;j.current=``,N.current?.injectAgentSpeech?.(`Thanks — I already have your email on file.`,`interrupt`),e&&N.current?.sendUserMessage?.(`The visitor's email is already on file — do not ask for it. Answer their question now: "${e}"`)}N.current?.sendContextualUpdate?.(`EMAIL REQUIREMENT SATISFIED — the visitor's email is already on file. All earlier instructions about requiring or asking for an email are VOID. Never ask for it again.`)},[h,Ge]);let Ke=/(walk\s*(me\s*)?through|walkthrough|take me through|(complete|full|detailed) (overview|understanding|walkthrough|picture|breakdown|explanation)|give me (a|an|the) (overview|tour|walk)|tell me about (each|all)|go through (it|them|each|all)|overview of|one by one|tell me everything|explain (me )?(this|the) (whole |entire |full )?page|everything (about|on) this page|show me (each|all|around))/i,qe=/(that covers everything|that was all|covered everything|that(?:'|’)?s everything)/i,Je=/^((uh+|um+|so|well|okay|ok|yeah)[,.\s]+)*(yes|yeah|yep|yup|sure|ok(ay)?|please|continue|go on|next|keep going|carry on|go ahead|resume|back to the tour)[\s.!,]*$/i,Ye=/(would you like (me )?to take you|shall i take you|want me to take you|would you like to (go|explore|visit|see)|take you there)/i,Xe=/^((uh+|um+|oh|ah|so|well)[,.\s]+)*(cool|nice|great|awesome|wow|lovely|perfect|got it|gotcha|i see|understood|makes sense|interesting|right|alright|sounds good|mm+-?hmm+|hmm+|k)[\s.!,]*$/i,Ze=e=>String(e||``).replace(/[\s.,!?]*((uh+|um+|er+|hmm+|mm+)[\s.,!?]*)+$/i,``).trim(),Qe=(0,b.useCallback)(()=>{Ge(),ce.current=setTimeout(()=>{if(!(!se.current||A.current))if(de.current<3){de.current+=1;try{N.current?.injectAgentSpeech?.(`I still need your email in the box below to continue — could you enter it, please?`,`queue`)}catch{}Qe()}else{try{N.current?.injectAgentSpeech?.(`Since I haven't received your email, I'll close this session for now. Feel free to come back anytime — take care!`,`queue`)}catch{}se.current=!1,re(!1),Ge(),P.current=!0}},1e4)},[Ge]),$e=(0,b.useCallback)(async e=>{Me.current=!0;try{In()}catch{}try{Bh()}catch{}if(i)try{Re(i)}catch{}try{await N.current?.endSession?.()}catch{}let t=Pe.current,n=Ie.current;if(t&&o&&a){let e=n?Math.round((Date.now()-n)/1e3):0;fetch(`${a}/api/voice/save-chat-recording`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({chatId:o,voiceSessionId:t,duration:e,siteId:i})}).catch(e=>console.warn(`[FlowEngage] save-chat-recording failed:`,e.message))}if(Pe.current=null,Ie.current=null,N.current=null,C(`idle`),T(!1),Te.current=!1,Lr(),i)try{Oe(i)}catch{}typeof c==`function`&&await c(s.current),e?.()},[s,c,i,o,a]);we.current=()=>$e(Ne.current===`chat`?t:n),(0,b.useEffect)(()=>{ke.current=w},[w]),(0,b.useEffect)(()=>{d&&We(`chat`)},[d]),(0,b.useEffect)(()=>{let e=!1,d=null,m=null,_=null;return(async()=>{if(!i){C(`error`);return}try{if((await navigator.mediaDevices.getUserMedia({audio:!0})).getTracks().forEach(e=>e.stop()),e)return;s.current=[],Ce.current=``,Te.current=!1,je.current=!1,Me.current=!1,Ne.current=`minimized`,Ir({onBeforeNavigate:()=>{if(i&&s.current.length>0)try{let e=Qg(s.current);e&&Ee(i,e)}catch{}if(!nr()){let e=Pe.current,t=Ie.current;if(e&&o&&a)try{let n=t?Math.round((Date.now()-t)/1e3):0,r=JSON.stringify({chatId:o,voiceSessionId:e,duration:n,siteId:i}),s=`${a}/api/voice/save-chat-recording`;fetch(s,{method:`POST`,headers:{"Content-Type":`application/json`},body:r,keepalive:!0}).catch(()=>{}),Pe.current=null,Ie.current=null}catch{}}Ae()}});let t=x_(v_({siteId:i||null,apiBaseUrl:a||null,chatId:o||null,getLastUserVoiceText:()=>Ce.current,beaconSyncFullVoiceHistory:()=>l?.(s.current),isWalkthroughActive:()=>me.current&&!he.current,requestEndSession:()=>{P.current=!0},requestEmailCollection:()=>{g&&(A.current||se.current||(se.current=!0,de.current=0,re(!0),Qe()))}})),n=i?De(i):null,r=!!n?.transcript,c=i?Re(i):null,v=!!c,y=typeof window<`u`?window.location.pathname:`/`,b=typeof document<`u`?document.title:``,x=y.replace(/^\/[a-z]{2}(?:-[a-z]{2})?(?=\/|$)/i,``)||`/`,S=x===`/`||x===``,w={apiBaseUrl:a,siteId:i,chatId:o,clientTools:t};Se.current=t;let E=(y.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()),ee=``;b&&b.includes(`|`)&&(ee=b.split(`|`).map(e=>e.trim()).filter(Boolean).reduce((e,t)=>e.length<=t.length?e:t));let D=ee||E||``,te=D?`the ${D} page`:`this page`,O=(()=>{if(!c?.targetUrl)return!0;try{return new URL(c.targetUrl,window.location.href).pathname.replace(/\/$/,``)===window.location.pathname.replace(/\/$/,``)}catch{return!0}})(),ne=(()=>{if(!c?.targetUrl)return null;try{return new URL(c.targetUrl,window.location.href).pathname}catch{return null}})();if(v&&!O)w.greeting=`You're on ${te}. What would you like to know?`,console.warn(`[FlowEngage] nav target "${c?.pageName}" not reached — on "${D}" instead; not claiming navigation`);else if(v){let e=c?.pageName?`the ${c.pageName} page`:te;w.greeting=`I have navigated you to ${e}. Do you want to know about any specific section or want a complete overview?`,console.log(`[FlowEngage] 🔄 Agent-nav resume: greeting set for "${e}" (${y})`)}else if(r)w.greeting=`Welcome back! I see you're now on ${te}. Would you like me to continue what I was explaining before, or would you like to know about this page instead?`;else{let e=f||``;w.greeting=p||(S?e?`Hi, welcome to ${e}! How may I help you?`:`Hi, welcome! How may I help you?`:`Welcome to ${te}! How may I help you?`),S||console.log(`[FlowEngage] 🏠 Page-context greeting: "${D}" for ${y}`)}let ie=!1,ae=v?`NAVIGATION COMPLETE — The exact phrase "I have navigated you to the [page] page. Do you want to know about any specific section or want a complete overview?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript?`Prior transcript:\n${n.transcript}\n`:`(no prior transcript)`}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until the visitor responds. When they reply: if they name a specific section → call fwh-get-current-page-mvp then answer immediately; if they say "overview" or anything non-specific → call fwh-get-current-page-mvp (zero speech) then do a full highlighted walkthrough.`:null,oe=!1,k=!v&&!r?`Your greeting has been spoken. Wait for the visitor to ask a question or tell you what they need. Do NOT auto-start a walkthrough or highlight any sections — let the visitor lead.`:null,ce=r&&!v,ue=!1,we=ce?`The question "Would you like me to continue what I was explaining before, or would you like to know about this page instead?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript?`Prior transcript:\n${n.transcript}\n`:`(no prior transcript, or the visitor navigated before saying much)`}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until they respond. When they reply: if they want you to CONTINUE the previous topic → resume verbally from the prior transcript above, do NOT call fwh-get-current-page-mvp or describe the new page unless they ask. If they want to know about THIS page → call fwh-get-current-page-mvp (zero speech) then proceed as a normal fresh-page walkthrough/description. If their reply is ambiguous, ask them to clarify which they'd prefer rather than guessing.`:null,Oe=!1,Le=!1,$e=g&&!!h&&!ge.current,F=await xh.startSession({...w,onConnect:({conversationId:e})=>{Pe.current=e,Ie.current=Date.now(),console.log(`[FlowEngage] 🎙️ AI chat session connected:`,e)},onUnhandledClientToolCall:e=>console.error(`[FlowEngage] Unhandled tool:`,e?.tool_name),onSessionInfo:({emailOnFile:e})=>{ge.current=!!e,e&&(A.current=!0,se.current=!1,re(!1),Ge(),console.log(`[FlowEngage] email already on file per server — gate disabled`))},onAgentTurnEnd:()=>{e||me.current&&(he.current||fe.current||setTimeout(()=>M.current?.(),250))},onSessionEnding:()=>{e||We(`minimized`)},onPanel:({title:t,items:n})=>{e||Rh(n,{title:t||`Products`,side:u===`bottom-left`?`left`:`right`,productUrl:e=>e.handle?`${window.location.origin}/products/${encodeURIComponent(e.handle)}`:null})},onModeChange:({mode:t,tentative:n})=>{if(!e){if(t===`speaking`){Ve.current&&=(clearTimeout(Ve.current),null),Ue.current&&=(clearTimeout(Ue.current),null),C(`speaking`),Be.current=`speaking`,He.current=!1;return}if(t===`user-speaking`){if(se.current&&!A.current&&Qe(),Ue.current&&=(clearTimeout(Ue.current),null),Be.current===`speaking`)return;try{In()}catch{}Ve.current&&=(clearTimeout(Ve.current),null),Be.current!==`listening`&&(C(`listening`),Be.current=`listening`);return}if(t===`thinking`){if(Ve.current&&=(clearTimeout(Ve.current),null),Ue.current&&=(clearTimeout(Ue.current),null),Be.current===`speaking`)return;C(`thinking`),Be.current=`thinking`,n&&(Ue.current=setTimeout(()=>{Ue.current=null,!(e||Be.current!==`thinking`)&&(Be.current=`listening`,C(`listening`))},4e3));return}if(Be.current=`listening`,P.current){P.current=!1,We();return}if($e&&!Le&&!A.current&&!ge.current){Le=!0,ie=!0,oe=!0;try{N.current?.sendContextualUpdate?.(`CRITICAL — EMAIL REQUIRED BEFORE ANYTHING ELSE. The visitor has not provided their email. The app itself speaks the email request out loud — you must NOT say it, and you must NOT answer. Stay completely silent from now until you receive an EMAIL_SAVED message: produce no speech and call no tools, no matter what the visitor says. Remember their question. On EMAIL_SAVED, thank them briefly and answer that remembered question in full.`)}catch{}}if(ae&&!ie){ie=!0;try{N.current?.sendContextualUpdate?.(ae)}catch{}}if(we&&!ue){ue=!0;try{N.current?.sendContextualUpdate?.(we)}catch{}}if(k&&!oe){oe=!0;try{N.current?.sendContextualUpdate?.(k)}catch{}}Ve.current&&clearTimeout(Ve.current),Ve.current=setTimeout(()=>{e||Be.current!==`speaking`&&(He.current||(Ve.current=null,Be.current=`listening`,C(`listening`)))},1200)}},onMessage:t=>{if(e||fe.current&&t.role!==`user`&&!/email/i.test(String(t.message||``)))return;let n=t.role===`user`&&/^(Describe the section titled "|\[SYSTEM:|Give the visitor a complete spoken overview|That was the last section)/.test(String(t.message||``).trim());if(t.role===`user`&&!n)try{Bh()}catch{}let r=/\b(?:this|that|the)\s+(?:case\s*stud(?:y|ies)|product|article|blog|post|item|service)\b/i.test(String(t.message||``));if(!n&&!r&&t.role===`user`&&Ke.test(String(t.message||``)))typeof window<`u`&&(window.__fe_user_nav_during_speech=null),be(!0),he.current=!1,pe.current=0,xe.current=-1,_e.current=b_(),ve.current=typeof window<`u`?window.location.pathname:null,setTimeout(()=>M.current?.(),400),console.log(`[FlowEngage] walkthrough armed — ${_e.current.length} sections on this page`);else if(!n&&t.role===`user`&&me.current){let e=String(t.message||``).trim(),n=Ze(e),r=e=>!!e&&(Je.test(e)||Xe.test(e));Ye.test(ye.current||``)?(console.log(`[FlowEngage] walkthrough ended — visitor accepted a navigation offer`),ve.current=null,_e.current=[],be(!1)):!n||r(e)||r(n)?he.current&&(he.current=!1,console.debug(`[FlowEngage] walkthrough resumed by visitor`),setTimeout(()=>M.current?.(),300)):(console.log(`[FlowEngage] walkthrough ended — visitor moved to a new topic`),ve.current=null,_e.current=[],be(!1))}if(t.role!==`user`&&!n&&(ye.current=String(t.message||``)),t.role!==`user`&&qe.test(String(t.message||``))){let e=_e.current.length;!e||xe.current>=e-1?be(!1):console.debug(`[FlowEngage] agent claimed completion at step ${xe.current+1}/${e} — ignoring, tour continues`)}let a=t.role===`user`?`user`:`bot`,o=t.message||t.text||t.content||``;if(o){if(a===`user`&&(Ce.current=o,typeof window<`u`&&(window.__fe_user_nav_during_speech=null),He.current=!0,Ve.current&&=(clearTimeout(Ve.current),null),Be.current!==`speaking`&&(C(`thinking`),Be.current=`thinking`),$e&&!Oe&&!A.current&&!ge.current&&(Oe=!0,j.current=o,fe.current=!0,N.current?.injectAgentSpeech?.(`Before I answer that, could you please enter your email in the text box below?`,`interrupt`),N.current?.setMicMuted?.(!0),se.current||(se.current=!0,de.current=0,re(!0),Qe()))),s.current=[...s.current,{sender:a,message:o,timestamp:new Date().toISOString(),debug:t.debug}],i)try{let e=Qg(s.current);e&&Ee(i,e)}catch{}if(A_(a,o)){P.current=!0;return}if(ke.current&&a===`bot`&&/are you still there|still there\??|hello\?|anyone there|checking in|you there|are you available|is anyone|did you step away/i.test(o))if(ze.current+=1,ze.current===2)try{N.current?.sendContextualUpdate?.(`IMPORTANT: The visitor is muted and has not responded to your last 2 checks. Make exactly ONE more check. If there is still no response, say: 'I feel like you are not active, so I'm ending this session. You can connect with me anytime.' then immediately call the fwh-end-session-mvp tool to end the voice session. Do NOT ask again after that.`)}catch{}else ze.current>=5&&We(`minimized`)}},onError:t=>{if(!e&&!Me.current&&!je.current&&!Fe()){je.current=!0,console.error(`[FlowEngageVoiceCall] session:error`,{message:t?.message,name:t?.name,raw:t});try{N.current?.setMicMuted?.(!0)}catch{}T(!0),C(`error`),We(`chat`)}},onDisconnect:t=>{if(e||Me.current||je.current||Fe())return;let n=s.current?.length||0;console.warn(`[FlowEngageVoiceCall] session:disconnected`,{historyCount:n,reason:t?.reason,closeCode:t?.context?.code,closeReason:t?.context?.reason||t?.message,lastUserText:Ce.current||``,wasMuted:ke.current}),je.current=!0;try{N.current?.setMicMuted?.(!0)}catch{}T(!0),We(`chat`)}});if(e){console.warn(`[FlowEngage:VoiceDebug] ❌ cancelled=true after startSession resolved — killing session immediately`),F.endSession().catch(()=>{});return}d=F,N.current=F,C(`listening`);try{sessionStorage.setItem(`el_conv_${i}`,F.getId())}catch{}parent.postMessage({type:`AGENT_EXPAND`},`*`);try{let e=typeof window<`u`?window.location.href:``,a=`[]`;try{a=await t[`fwh-list-forms-mvp`]()}catch{}let o=`none`;if(typeof window<`u`&&typeof document<`u`)try{let e=`#flowengage-root, .flowengage-shell, .flowengage-widget-root`,t=document.querySelector(`main, [role='main']`)||document.body,n=Array.from(t.querySelectorAll(`h1, h2, h3`)).filter(t=>!t.closest(e)).map(e=>e.textContent.replace(/\s+/g,` `).trim()).filter(e=>e.length>2&&e.length<200);n.length||(n=Array.from(document.body.querySelectorAll(`h1, h2, h3`)).filter(t=>!t.closest(e)).map(e=>e.textContent.replace(/\s+/g,` `).trim()).filter(e=>e.length>2&&e.length<200)),n.length&&(o=[...new Set(n)].slice(0,15).join(` | `))}catch{}let s=r&&!v?`RESUMING PRIOR SESSION — DO NOT GREET. The visitor is continuing the same conversation after a page reload. Do not introduce yourself or re-state your capabilities. Wait silently until the visitor speaks; if you must speak first, briefly acknowledge where you left off in one short sentence.\nPrior transcript:\n${n?.transcript||``}\n\n`:``;F.sendContextualUpdate(`${s}SiteId: ${i||`unknown`}. ${h?``:`Visitor's email is already on file from a prior session — do NOT ask for it again. `}WALKTHROUGH RULE: When the visitor asks for a walkthrough or overview of this page, you MUST IMMEDIATELY call fwh-get-current-page-mvp (zero speech — call it silently with NO words before it) to get the exact highlightable headings. Do NOT skip fwh-get-current-page-mvp. Voice: call end_voice_session or say you are ending the session to stop.`),F.sendContextualUpdate(C_(e,o,a))}catch{}if(typeof window<`u`){let t=window.location.pathname,n=Date.now(),r=e=>{try{return(decodeURIComponent(e||`/`).replace(/\/+$/,``)||`/`).toLowerCase()}catch{return((e||`/`).replace(/\/+$/,``)||`/`).toLowerCase()}},a=()=>{if(e)return;let a=window.location.pathname;if(r(a)===r(t)){t=a;return}if(Date.now()-n<8e3){t=a;return}let o=typeof window<`u`&&window.__fe_agent_nav||null,s=ne&&r(a)===r(ne)||Jn()||o&&Date.now()-o.ts<15e3&&r(o.path)===r(a),c=t;t=a;let l=window.location.href,u=typeof document<`u`?document.title:``,d=typeof document<`u`?Array.from(document.querySelectorAll(`h1, h2, h3, h4`)).filter(e=>!e.closest(`#flowengage-root, .flowengage-shell`)).map(e=>({tag:e.tagName,text:e.textContent.trim()})).filter(e=>e.text.length>0&&e.text.length<200).slice(0,20):[];try{In()}catch{}let f=d.map(e=>e.text).join(` | `),p=e=>(e.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase())||`Home`,m=p(c),h=p(a);if(s){if(typeof window<`u`&&(window.__fe_agent_nav=null),console.log(`[FlowEngage] agent-driven navigation settled: "${c}" → "${a}"`),le(i)!==`auto`)try{N.current?.sendUserMessage?.(`[SYSTEM: YOU just navigated the visitor to the "${h}" page (${l}) at their request — this was YOUR action, not theirs. Page title: "${u}". Sections: ${JSON.stringify(d)}. Say: "Here we are on the ${h} page." Then introduce this page in 2-3 sentences, and finish by asking: "Would you like me to walk you through it section by section?" NEVER say "I see you've navigated" — the visitor did not navigate, you did. Do NOT call fwh-navigate-mvp again. Wait for their answer before describing sections.]`,{suppressQuestion:!0})}catch{}else console.log(`[FlowEngage] auto-nav — skipping the arrival acknowledgment turn`);try{N.current?.sendContextualUpdate?.(C_(l,f))}catch{}return}typeof window<`u`&&(window.__fe_user_nav_during_speech={fromPath:c,toPath:a,fromPageName:m,toPageName:h,ts:Date.now()}),console.log(`[FlowEngage] visitor navigation detected: "${c}" → "${a}" (agent target: ${ne||`none`})`);try{N.current?.sendUserMessage?.(`[SYSTEM: I navigated away mid-walkthrough — I am now on the "${h}" page (${l}). Page title: "${u}". Sections: ${JSON.stringify(d)}. STOP everything. Do NOT describe the new page. Say EXACTLY: "I see you've navigated to the ${h} page — would you like me to continue what I was explaining, or shall I walk you through this page?" Then wait for my answer. IF I say continue/yes/carry on: resume speaking verbally from where you left off — do NOT call fwh-navigate-mvp, do NOT call fwh-highlight-mvp, the visitor will navigate themselves if needed. IF I want to hear about this page: call fwh-highlight-mvp for sections of "${h}" only.]`)}catch{}try{N.current?.sendContextualUpdate?.(C_(l,f))}catch{}};m=window.history.pushState.bind(window.history),window.history.pushState=(...e)=>{m(...e),setTimeout(a,50)},_=()=>setTimeout(a,50),window.addEventListener(`popstate`,_)}}catch(r){e||(console.error(`[FlowEngage] startSession failed`,r),r.message&&r.message.includes(`Too many voice sessions`)?(alert(r.message),n&&n(`minimized`)):(C(`error`),Ne.current=`chat`,window.setTimeout(()=>{e||t?.()},0)))}})(),()=>{console.warn(`[FlowEngage:VoiceDebug] ⚠️ useEffect CLEANUP fired — session will be killed`,{agentId:r,siteId:i,apiBaseUrl:a}),e=!0,Me.current=!0;try{In()}catch{}if(i)try{Re(i)}catch{}typeof window<`u`&&(window.__fe_user_nav_during_speech=null),Lr(),Ve.current&&=(clearTimeout(Ve.current),null),Ue.current&&=(clearTimeout(Ue.current),null),ce.current&&=(clearTimeout(ce.current),null),He.current=!1,Be.current=`idle`,m&&typeof window<`u`&&(window.history.pushState=m),_&&typeof window<`u`&&window.removeEventListener(`popstate`,_);let t=d||N.current,n=Pe.current,l=Ie.current;if(t?.endSession?.().catch(()=>{}),n&&o&&a){let e=l?Math.round((Date.now()-l)/1e3):0,t=JSON.stringify({chatId:o,voiceSessionId:n,duration:e,siteId:i}),r=`${a}/api/voice/save-chat-recording`;typeof navigator<`u`&&navigator.sendBeacon&&navigator.sendBeacon(r,new Blob([t],{type:`application/json`}))||fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`},body:t,keepalive:!0}).catch(()=>{}),Pe.current=null,Ie.current=null}typeof c==`function`&&s.current.length>0&&c(s.current).catch?.(()=>{})}},[r,i,a,We]),(0,b.useEffect)(()=>{if(!E)return;let e=e=>{let t=e.composedPath?.()?.[0]||e.target;Le.current&&!Le.current.contains(t)&&ee(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[E]);let F=async()=>{if(N.current)try{if(w){ze.current=0,await N.current.setMicMuted?.(!1),T(!1);try{N.current.sendContextualUpdate?.(`The visitor has unmuted. Resume normal interaction and wait for them to speak.`)}catch{}}else{ze.current=0,await N.current.setMicMuted?.(!0),T(!0);try{N.current.sendContextualUpdate?.(`The visitor has muted their microphone. Do NOT speak, ask if they are there, or prompt them in any way. Stay completely silent until they unmute.`)}catch{}}}catch{}},I=(0,b.useCallback)(async e=>{e?.preventDefault?.();let t=ie.trim().toLowerCase();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)){k(`Please enter a valid email address.`);return}k(``),A.current=!0,se.current=!1,Ge(),re(!1),ae(``),fe.current=!1;try{typeof m==`function`&&await m(t)}catch{}N.current?.setMicMuted?.(!1);let n=j.current;j.current=``;try{N.current?.injectAgentSpeech?.(`Thank you for submitting your email.`),N.current?.sendUserMessage?.(n||`The visitor's email is on file. Carry on helping them and never ask for it again.`)}catch{}},[ie,m,Ge]),L=Array.isArray(e)&&e[0]||`#3B82F6`;return(0,V.jsxs)(`div`,{ref:Le,style:{position:`relative`,display:`flex`,flexDirection:`column`,alignItems:`center`,gap:`14px`,animation:`fe-orb-slide-up 0.42s cubic-bezier(0.16,1,0.3,1)`},children:[ne&&(0,V.jsxs)(`form`,{onSubmit:I,style:{width:`270px`,maxWidth:`calc(100vw - 40px)`,boxSizing:`border-box`,background:`rgba(15,22,36,0.97)`,backdropFilter:`blur(14px)`,WebkitBackdropFilter:`blur(14px)`,borderRadius:`16px`,boxShadow:`0 10px 40px rgba(0,0,0,0.45)`,padding:`16px`,display:`flex`,flexDirection:`column`,gap:`10px`,animation:`fe-orb-fade-in 0.18s ease`},children:[(0,V.jsx)(`span`,{style:{color:`white`,fontSize:`13px`,fontWeight:600,lineHeight:1.4},children:`Please enter your email to continue`}),(0,V.jsx)(`input`,{type:`email`,inputMode:`email`,autoComplete:`email`,autoFocus:!0,value:ie,onChange:e=>{ae(e.target.value),oe&&k(``)},placeholder:`you@example.com`,style:{width:`100%`,boxSizing:`border-box`,padding:`10px 12px`,borderRadius:`10px`,border:oe?`1.5px solid #ef4444`:`1.5px solid #4b5563`,background:`#1f2937`,color:`white`,fontSize:`14px`,outline:`none`}}),oe&&(0,V.jsx)(`span`,{style:{color:`#f87171`,fontSize:`12px`},children:oe}),(0,V.jsx)(`button`,{type:`submit`,disabled:!ie.trim(),style:{width:`100%`,padding:`10px 12px`,borderRadius:`10px`,border:`none`,background:ie.trim()?L:`#374151`,color:`white`,fontSize:`14px`,fontWeight:600,cursor:ie.trim()?`pointer`:`default`,transition:`background 0.2s ease`},children:`Submit`})]}),(0,V.jsxs)(`div`,{style:{position:`relative`},children:[(S===`listening`||S===`thinking`)&&!w&&(0,V.jsx)(`span`,{style:{position:`absolute`,right:`100%`,marginRight:`18px`,top:`50%`,transform:`translateY(-50%)`,color:`white`,fontSize:`18px`,fontWeight:600,letterSpacing:`0.01em`,textShadow:`0 2px 4px rgba(0,0,0,0.4)`,whiteSpace:`nowrap`},children:S===`thinking`?v:_}),w&&(0,V.jsx)(`span`,{style:{position:`absolute`,right:`100%`,marginRight:`18px`,top:`50%`,transform:`translateY(-50%)`,color:`#f87171`,fontSize:`16px`,fontWeight:600,letterSpacing:`0.01em`,textShadow:`0 2px 4px rgba(0,0,0,0.4)`,whiteSpace:`nowrap`},children:`Muted`}),(0,V.jsx)(j_,{size:72,voiceState:S,isMuted:w,orbColors:e})]}),S===`error`&&(0,V.jsx)(`span`,{style:{color:`#f87171`,fontSize:`13px`,fontWeight:500},children:`Could not start voice session.`}),(0,V.jsxs)(`div`,{style:{display:`flex`,gap:`12px`,alignItems:`center`},children:[(0,V.jsx)(`button`,{onClick:F,title:w?`Unmute`:`Mute`,style:{width:`24px`,height:`24px`,borderRadius:`50%`,background:w?`#7f1d1d`:`#1f2937`,border:w?`1.5px solid #ef4444`:`1.5px solid #4b5563`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:w?`#f87171`:`white`,transition:`all 0.2s ease`,boxShadow:`0 2px 8px rgba(0,0,0,0.4)`,padding:0,flexShrink:0},children:w?(0,V.jsxs)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,V.jsx)(`line`,{x1:`1`,y1:`1`,x2:`23`,y2:`23`}),(0,V.jsx)(`path`,{d:`M9 9v3a3 3 0 005.12 2.12M15 9.34V4a3 3 0 00-5.94-.6`}),(0,V.jsx)(`path`,{d:`M17 16.95A7 7 0 015 12v-2m14 0v2a7 7 0 01-.11 1.23`}),(0,V.jsx)(`line`,{x1:`12`,y1:`19`,x2:`12`,y2:`23`}),(0,V.jsx)(`line`,{x1:`8`,y1:`23`,x2:`16`,y2:`23`})]}):(0,V.jsx)(tg,{size:16,color:`currentColor`})}),(0,V.jsx)(`button`,{onClick:()=>ee(e=>!e),style:{width:`24px`,height:`24px`,borderRadius:`50%`,background:`#1f2937`,border:`1.5px solid #4b5563`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:`white`,transition:`background 0.2s ease`,boxShadow:`0 2px 8px rgba(0,0,0,0.4)`,padding:0,flexShrink:0},children:(0,V.jsx)(lg,{size:16,color:`currentColor`})})]}),E&&(0,V.jsxs)(`div`,{style:{position:`absolute`,bottom:0,right:0,width:`230px`,zIndex:9999,background:`rgba(15,22,36,0.96)`,backdropFilter:`blur(14px)`,WebkitBackdropFilter:`blur(14px)`,borderRadius:`16px`,boxShadow:`0 10px 40px rgba(0,0,0,0.45)`,animation:`fe-orb-fade-in 0.18s ease`,padding:`12px 12px 40px`},children:[(0,V.jsxs)(`button`,{onClick:()=>{ee(!1),Ne.current=`chat`,$e(t)},style:{display:`flex`,alignItems:`center`,gap:`12px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`white`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`,whiteSpace:`nowrap`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.08)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,V.jsx)(ug,{}),y]}),i&&(0,V.jsxs)(`button`,{onClick:()=>O(),style:{display:`flex`,alignItems:`center`,justifyContent:`space-between`,gap:`8px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`white`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.08)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,V.jsxs)(`span`,{style:{display:`flex`,alignItems:`center`,gap:`12px`,minWidth:0,overflow:`hidden`},children:[(0,V.jsx)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,style:{flexShrink:0},children:(0,V.jsx)(`polygon`,{points:`3 11 22 2 13 21 11 13 3 11`})}),(0,V.jsx)(`span`,{style:{overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:`Auto-navigate`})]}),(0,V.jsx)(`span`,{"aria-hidden":!0,style:{width:`34px`,height:`19px`,borderRadius:`999px`,background:D?`#22c55e`:`rgba(255,255,255,0.2)`,position:`relative`,flexShrink:0,transition:`background 0.15s ease`},children:(0,V.jsx)(`span`,{style:{position:`absolute`,top:`2px`,left:D?`17px`:`2px`,width:`15px`,height:`15px`,borderRadius:`50%`,background:`white`,transition:`left 0.15s ease`}})})]}),(0,V.jsxs)(`button`,{onClick:()=>{ee(!1),Ne.current=`minimized`,$e(n)},style:{display:`flex`,alignItems:`center`,gap:`12px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`#E30C12`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`,whiteSpace:`nowrap`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(227,12,18,0.1)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,V.jsx)(dg,{}),x]}),(0,V.jsx)(`button`,{onClick:()=>ee(!1),style:{position:`absolute`,bottom:`8px`,right:`8px`,width:`24px`,height:`24px`,borderRadius:`50%`,background:`rgba(255,255,255,0.15)`,border:`none`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:`white`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.25)`},onMouseLeave:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.15)`},children:(0,V.jsxs)(`svg`,{width:`12`,height:`12`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,V.jsx)(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),(0,V.jsx)(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})]})})]})]})}function F_({orbColors:e}){let t=Array.isArray(e)&&e.length>0?e:[`#135bd8`,`#ef334b`,`#ffbd2e`],n=t[0],r=t[1]||t[0],i=t[2]||t[0];return(0,V.jsx)(`div`,{style:{width:`32px`,height:`32px`,borderRadius:`50%`,flexShrink:0,background:`
|
|
431
431
|
radial-gradient(circle at 18% 50%, ${n} 0%, ${n}99 35%, transparent 65%),
|
|
432
432
|
radial-gradient(circle at 82% 50%, ${r} 0%, ${r}99 35%, transparent 65%),
|
|
433
433
|
radial-gradient(circle at 50% 82%, ${i} 0%, ${i}99 30%, transparent 58%),
|
package/dist/flowengage-embed.js
CHANGED
|
@@ -427,7 +427,7 @@ Error generating stack: `+e.message+`
|
|
|
427
427
|
`).trim()}function v_(e){let t=e.siteId||null,n=(e.apiBaseUrl||``).replace(/\/$/,``),r=typeof e.getLastUserVoiceText==`function`?e.getLastUserVoiceText:null,i=typeof e.beaconSyncFullVoiceHistory==`function`?e.beaconSyncFullVoiceHistory:null,a=[],o={"fwh-navigate-mvp":async n=>{if($(`fwh-navigate-mvp ▶ raw parameters:`,n),e?.isWalkthroughActive?.())return $(`fwh-navigate-mvp ✖ blocked — walkthrough in progress`),`NAVIGATION_BLOCKED_DURING_WALKTHROUGH. A walkthrough of the current page is in progress, so navigation is not allowed. Do NOT tell the visitor you navigated or offer to. Describe the requested section using the CURRENT page's own content (fwh-read-content-mvp), then stop.`;let a=p_(n),o=m_(a),s=a.section_text??a.sectionText??a.section,c=Array.isArray(a.expected_landmarks)?a.expected_landmarks:Array.isArray(a.expectedLandmarks)?a.expectedLandmarks:[];if($(`fwh-navigate-mvp · coerced:`,{url:o,sectionText:s,expectedLandmarks:c}),!o){let e=`Missing url: pass a string url, path like /pricing, or JSON with a url field.`;return $(`fwh-navigate-mvp ✖`,e),e}if(typeof window<`u`)try{let e=new URL(o,window.location.href);if(e.hostname===window.location.hostname&&e.origin!==window.location.origin)o=e.pathname+e.search+e.hash,$(`fwh-navigate-mvp · cross-protocol same-host URL normalized →`,o);else if(e.origin!==window.location.origin&&/^https?:$/i.test(e.protocol)){let t=e.hostname;return $(`fwh-navigate-mvp ✖ external URL blocked:`,e.href),`EXTERNAL_LINK_BLOCKED: The URL "${o}" points to an external site (${t}) that opens in a new tab. I cannot open new tabs or navigate to external sites. Tell the visitor exactly this: "That link opens an external app — I'm not able to open it for you, but you can click the link directly in the navigation header."`}}catch{}if(typeof window<`u`){try{let e=new URL(o,window.location.href);if(e.pathname===window.location.pathname&&e.search===window.location.search)return $(`fwh-navigate-mvp ✖ already on page, skipping`),`OK_ALREADY_ON_PAGE. The visitor is already on this page. Do not navigate. Proceed directly with highlights, answering the question, or reading page content.`}catch{}let e=window.__fe_user_nav_during_speech;if(e&&Date.now()-e.ts<3e4)try{if(new URL(o,window.location.href).pathname===e.fromPath)return $(`flowengage_navigate ✖ blocked — would navigate back to page visitor just left`,e),`NAV_BLOCKED: The visitor just left "${e.fromPageName}" and is now on "${e.toPageName}". Do NOT navigate them back. If they said "continue", resume verbally from where you left off — no navigation needed. If they want to return, they will navigate themselves.`}catch{}}let{allowed:l,correctedUrl:u}=await Sr(o,{siteId:t,apiBaseUrl:e.apiBaseUrl});if(!l){let e=`PAGE_NOT_IN_SITE: "${o}" is NOT a real page on this site — do NOT guess or invent URL paths. Do NOT tell the visitor you are taking them there. Instead, find the REAL page: look at your Popular Pages / Site Pages list for the closest actual match, or call fwh-list-nav-links-mvp (to read the site's real menu links) or fwh-search-mvp (to find the right page). If a close match exists (e.g. the visitor said "support" and the site has "Technical Support"), tell them the ACTUAL page name and ask "we don't have a page called that, but we do have [real page] — would you like me to take you there?" Then navigate only to that verified URL.`;return $(`fwh-navigate-mvp ✖ blocked`,e),e}u&&u!==o&&($(`fwh-navigate-mvp · corrected url:`,{requested:o,correctedUrl:u}),o=u);try{Eh(`Navigating to ${o}...`,{spinner:!0}),Wh(),In();try{i?.()}catch{}try{let e=r?r():``;if(e&&t){let n=Qg([{sender:`user`,message:e}]);n&&Ee(t,n)}}catch{}Ae();let e=o;try{e=new URL(o,typeof window<`u`?window.location.href:``).href}catch{}let n=`this`;try{let e=new URL(o,window.location.href).pathname.split(`/`).filter(Boolean).pop()||``;n=e?e.replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()):`Home`}catch{}t&&typeof window<`u`&&Le(t,window.location.href,e,n),Tr(e);try{window.__fe_agent_nav={path:new URL(e,window.location.href).pathname,ts:Date.now()}}catch{}$(`fwh-navigate-mvp → real navigation`,o);let a=typeof window<`u`&&!window.Shopify&&!!nr();if(typeof window<`u`&&window.__fe_triggerVoiceNavHooks&&window.__fe_triggerVoiceNavHooks(),a&&qn(12e3),Nr(o,{voiceActive:!0}).catch(()=>{}),a)try{let e=new URL(o,window.location.origin).pathname,t=window.location.pathname,n=Date.now()+2e3;for(;Date.now()<n&&!(typeof window<`u`&&window.location.pathname===e);)await new Promise(e=>setTimeout(e,80));let r=typeof window<`u`?window.location.pathname:e,i=typeof window<`u`?window.location.href:o;(r.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()),$(`fwh-navigate-mvp → SPA nav complete. target:`,e,`actual:`,r);try{typeof window<`u`&&window.scrollTo(0,0)}catch{}let a=[],s=null,c=``,l=Date.now()+12e3;for(;Date.now()<l&&(await new Promise(e=>setTimeout(e,300)),{headings:a,pageTitleHeading:s,introText:c}=g_(),!(a.length>=1)););h_(),$(`fwh-navigate-mvp → page headings collected:`,a,`titleHeading:`,s);let u=a.length?a.join(` | `):`none`,d=a.length?`Use ONLY these exact heading strings for fwh-highlight-mvp — never invent or abbreviate.`:`No headings collected yet — call fwh-get-current-page-mvp immediately (no speech) to get the live headings before speaking or highlighting.`,f=s?` "${s}" is the PAGE TITLE, not a content section — do not fwh-highlight-mvp it separately; if doing a walkthrough, just fold this intro into your opening line: "${c||`(no intro text found)`}"`:c?` No separate page-title heading detected, but this intro text precedes the first section — mention it briefly before covering sections: "${c}"`:``,p=``;try{if(a.length>0){let e=a.slice(0,3),t=[];for(let n of e){let e=_r(null,n);if(e){let r=__(e).substring(0,800);r&&t.push(`[Section: ${n}] ${r}`)}}t.length>0&&(p=` CONTENT FOR FIRST FEW SECTIONS: ${t.join(` | `)}. Use this content to answer immediately if the visitor asks about one of these sections.`)}}catch{}if(Or())return Er(),$(`fwh-navigate-mvp ✖ landed on a not-found/404 page:`,i),`NAVIGATION_PAGE_NOT_FOUND. The page at ${i} does not exist — it shows a "not found"/404 error, not real content. Do NOT offer a page overview, do NOT describe or highlight any sections, do NOT claim the navigation succeeded. Tell the visitor EXACTLY: "It looks like that page doesn't exist — I couldn't find it. Would you like me to take you somewhere else?" Then STOP and wait for their reply.`;let m=Date.now()+4e3,h=typeof window<`u`?window.location.pathname:r;for(;Date.now()<m&&!t_(h,e)&&t_(h,t);)await new Promise(e=>setTimeout(e,120)),h=window.location.pathname;$(`fwh-navigate-mvp route settle:`,{startPath:t,targetPath:e,settledPath:h});let g=typeof window<`u`?window.location.href:i,_=(h.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase())||`Home`,v=t_(h,e),y=!t_(h,t),b=v||y;return $(`fwh-navigate-mvp settled:`,h,`target:`,e,`match:`,b),b?(Er(),`OK_NAVIGATE_SPA. Visitor is now on: ${g} (page: "${_}") | Section headings: ${u}.${f} ${d}${p} Now confirm the arrival in ONE short sentence phrased for what the visitor actually asked for — mention the page by name and what it lets them do (e.g. for a contact request: "I've taken you to the Contact Us page — filling in this form sends your query straight to our team."). Do NOT use a fixed template. Then, in the same breath, offer either a specific section or a full overview. Then STOP — wait for their reply.`):(Er(),`NAVIGATION_DID_NOT_HAPPEN. The visitor is STILL on ${g} (page: "${_}") — the navigation did not go through. Do NOT say "I have navigated you". Do NOT claim success. Tell the visitor EXACTLY: "Sorry, I couldn't open that page just now. Would you like me to try again, or is there something else I can help with?" Then STOP and wait for their reply.`)}catch{}return`OK_NAVIGATE_RELOADING. The page is reloading now and this session is ending. Say NOTHING further — do not confirm the navigation, do not describe the page. The arrival greeting is spoken automatically on the new page; you resume from the visitor's next reply.`}catch(e){let t=`Navigation error: ${e?.message||String(e)}`;return $(`fwh-navigate-mvp ✖`,t),Eh(`Navigation failed`,{spinner:!1,icon:`⚠️`,duration:1800}),t}finally{Gh(),Dh(900)}},"fwh-highlight-mvp":async e=>{$(`fwh-highlight-mvp ▶`,e);let t=p_(e),n=t?.section_id||t?.sectionId,r=t?.section_text||t?.sectionText;if(qn(3e3),typeof window<`u`&&window.__fe_user_nav_during_speech){let e=window.__fe_user_nav_during_speech,t=window.location.pathname;if(Date.now()-e.ts>=3e4)window.__fe_user_nav_during_speech=null;else return $(`flowengage_highlight ✖ blocked — user navigated during speech`,e),`NAV_CHANGED: The visitor navigated from "${e.fromPageName}" to "${e.toPageName}" (current path: ${t}) while you were speaking. STOP the current walkthrough immediately. DO NOT call fwh-highlight-mvp again. Say out loud: "I see you've navigated to the ${e.toPageName} page — do you want me to continue what I was explaining, or shall I guide you through this page?" Wait for the visitor's answer before doing anything else.`}h_();let i=(e=>e&&e.replace(/^\d+\.\s*/,``).replace(/^["']|["']$/g,``).trim())(r),a=_r(n||null,i||null);if(!a&&i){let e=Date.now()+600;for(;!a&&Date.now()<e;)await new Promise(e=>setTimeout(e,50)),h_(),a=_r(null,i)}if(!a){$(`fwh-highlight-mvp ✖ no element for:`,i||n);let{headings:e}=g_(),t=e.length?`Page headings: ${e.map(e=>`"${e}"`).join(` | `)}. Use one of these EXACT strings for section_text — no numbers, no quotes around them.`:`No headings found — skip highlight and continue speaking.`;return`HIGHLIGHT_NOT_FOUND: "${i||n}" on the current page. ${t}`}return Fn(a),$(`fwh-highlight-mvp ✓`,i||n),`OK_HIGHLIGHT: "${i||n}". Your NEXT response must be speech only (no tool call) describing THIS section — "${i||n}" — using its own content. Do not ask whether to continue.`},"fwh-read-content-mvp":async e=>{$(`fwh-read-content-mvp ▶`,e);let t=p_(e),n=t?.section_text||t?.sectionText||``;try{let e=``;if(n){let t=_r(null,n);if(!t)return`Could not find section "${n}" on the page.`;e=__(t)}else{let t=(document.querySelector(`main`)||document.querySelector(`article`)||document.querySelector(`[role="main"]`)||document.querySelector(`#content`)||document.querySelector(`.content`)||document.body).cloneNode(!0);t.querySelectorAll(`nav, footer, header, aside, script, style, noscript, .nav, .footer, .header, .sidebar, #flowengage-root, .flowengage-shell, .flowengage-widget-root`).forEach(e=>e.remove()),e=t.textContent.replace(/\s+/g,` `).trim()}return e.length>8e3&&(e=e.substring(0,8e3)+`... (truncated)`),$(`fwh-read-content-mvp ✓`,`${e.length} chars`),e||`No readable content found on this page.`}catch(e){return $(`fwh-read-content-mvp ✖`,e.message),`Could not read page content: ${e.message}`}},"fwh-page-content-mvp":async e=>{$(`fwh-page-content-mvp ▶`,e);let r=p_(e),i=r?.url||r?.page_url||``;if($(`fwh-page-content-mvp · url:`,i,`siteId:`,t,`apiBase:`,n||`(empty)`),!i)return $(`fwh-page-content-mvp ✖ missing url`),`Missing url parameter.`;if(!t)return $(`fwh-page-content-mvp ✖ no siteId`),`Site is not configured.`;if(!n)return $(`fwh-page-content-mvp ✖ no api base`),`API base URL is missing; cannot fetch page content.`;try{let e=`${n}/api/site-tools/page-content`;$(`fwh-page-content-mvp → POST`,e);let r=await fetch(e,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({siteId:t,url:i})}),a=await r.json();if($(`fwh-page-content-mvp · HTTP`,r.status,`success:`,a.success),!a.success){let e=a.error||`Failed to get page content.`;return $(`fwh-page-content-mvp ✖`,e),`${e} FALLBACK REQUIRED: This page is not in the search index. Call fwh-read-content-mvp (no parameters) immediately to read the live page content directly from the visitor's browser — do NOT tell the visitor you cannot access the page.`}return $(`fwh-page-content-mvp ✓`,`title: ${a.title}`,`sections: ${(a.sections||[]).length}`,`content: ${(a.content||``).length} chars`),JSON.stringify({title:a.title,url:a.url,sections:a.sections,content:a.content})}catch(e){let t=`Page content error: ${e.message||`network`}`;return $(`fwh-page-content-mvp ✖`,t),t}},"fwh-search-mvp":async e=>{$(`fwh-search-mvp ▶`,e);let r=p_(e).query;if(!r)return $(`fwh-search-mvp ✖ missing query`),`Missing query parameter.`;if(!t)return $(`fwh-search-mvp ✖ no siteId`),`Search unavailable (no siteId configured).`;if(!n)return $(`fwh-search-mvp ✖ no api base`),`Search unavailable (no API base URL).`;try{let e=`${n}/api/site-tools/search`;$(`fwh-search-mvp → POST`,e);let i=await fetch(e,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({siteId:t,query:r,currentUrl:typeof window<`u`?window.location.href:``})}),a=await i.json();if($(`fwh-search-mvp · HTTP`,i.status,`success:`,a.success),!a.success||!a.results?.length)return`No results found for that query. Try rephrasing, or use fwh-read-content-mvp to read the current page.`;let o=a.results.slice(0,5).map((e,t)=>`${t+1}. ${e.title||e.url} (${e.url})${e.snippet?` — ${e.snippet}`:``}`).join(`
|
|
428
428
|
`);return $(`fwh-search-mvp ✓`,`${a.results.length} result(s)`),`SEARCH_RESULTS:\n${o}`}catch(e){return $(`fwh-search-mvp ✖`,e.message),`Search failed. Use fwh-read-content-mvp to read the current page instead.`}},"fwh-list-nav-links-mvp":async()=>{if($(`fwh-list-nav-links-mvp ▶`),typeof window>`u`)return`Cannot read navigation (no browser context).`;let e=new Map,t=[],n=[`nav a`,`[role='navigation'] a`,`header a`,`.navbar a`,`.nav a`,`.menu a`,`.navigation a`,`.header a`,`[class*='nav'] a`,`[class*='menu'] a`,`[class*='header'] a`];if(r_(t=>{for(let r of n)try{t.querySelectorAll(r).forEach(t=>{if(Rn(t))return;let n=t.getAttribute(`href`),r=(t.textContent||``).replace(/\s+/g,` `).trim();if(!r||!n||n===`#`||n.startsWith(`javascript:`))return;try{let e=t.parentElement;if(e&&n_(e).display===`none`&&!e.closest(`nav, [role='navigation'], header, [class*='nav'], [class*='menu']`))return}catch{}let i=r.toLowerCase()+`|`+n;e.has(i)||e.set(i,{text:r,href:n})})}catch{}t.querySelectorAll(`select`).forEach(t=>{if(Rn(t))return;let n=a_(t);/nav|menu|page|section|go to/i.test(n||t.name||t.id||``)&&Array.from(t.options).forEach(r=>{if(!r.value||r.disabled)return;let i=r.text.trim();if(!i)return;let a=i.toLowerCase()+`|`+r.value;e.has(a)||e.set(a,{text:i,href:r.value,isSelectOption:!0,selectLabel:n||t.name||`dropdown`})})})}),e.forEach(e=>t.push(e)),!t.length)return`NAV_LINKS: No navigation links found on this page. Use fwh-read-content-mvp to read page content or fwh-search-mvp to search the site.`;let r=t.slice(0,50).map((e,t)=>`${t+1}. "${e.text}"${e.isSelectOption?` (dropdown option in "${e.selectLabel}")`:``} → ${e.href}`).join(`
|
|
429
429
|
`);return $(`fwh-list-nav-links-mvp ✓`,`${t.length} links`),`NAV_LINKS (${t.length} total, showing up to 50):\n${r}\n\nIMPORTANT: Use the EXACT text shown above when reading link names to the visitor. Do NOT rename, abbreviate, or invent options not listed here. To navigate, call fwh-navigate-mvp with the href value.`},"fwh-end-session-mvp":async()=>($(`fwh-end-session-mvp ▶`),typeof e.requestEndSession==`function`&&e.requestEndSession(),`Voice session is ending.`),"fwh-request-email-mvp":async()=>($(`fwh-request-email-mvp ▶`),typeof e.requestEmailCollection==`function`&&e.requestEmailCollection(),`EMAIL_BOX_SHOWN. An email input text box is now visible to the visitor. Say EXACTLY: "Please enter your email in the text box to continue." Then stop and wait silently — do NOT ask them to say it aloud, do NOT repeat, and do NOT answer any other question until you receive the EMAIL_SAVED message.`),"fwh-get-current-page-mvp":async()=>{if($(`fwh-get-current-page-mvp ▶`),typeof window>`u`)return`Cannot determine current page (no browser context).`;let e=window.location.href,t=window.location.pathname,n=document.title||``;if(Or())return $(`fwh-get-current-page-mvp ✓ 404 detected`,{url:e}),`CURRENT_PAGE_NOT_FOUND. The visitor is currently on a 404 / page-not-found error page (${e}). There is no real content here. Do NOT describe this page, do NOT list sections, do NOT offer a walkthrough, and do NOT describe it from your own knowledge. Tell the visitor plainly that the page they are on doesn't exist, and offer to take them somewhere that does.`;let{headings:r,pageTitleHeading:i,introText:a}=g_();$(`fwh-get-current-page-mvp ✓`,{url:e,path:t,title:n}),$(`fwh-get-current-page-mvp → page headings collected:`,r,`titleHeading:`,i);let o=i?` NOTE: "${i}" is the PAGE TITLE, not a content section — do NOT fwh-highlight-mvp it or list it as a separate section in a walkthrough. Instead, open with a brief 1-2 sentence summary of this intro text before covering the real sections: "${a||`(no intro text captured)`}"`:a?` NOTE: no separate page-title heading was detected, but this intro text appears before the first section — open your walkthrough with a brief 1-2 sentence summary of it before covering sections: "${a}"`:``;if(!r.length)return`CURRENT_PAGE. URL: ${e} | Title: ${n} |${o} No structured section headings found on this page. If there is intro text above, describe it; otherwise describe the page from your knowledge. Do not call this tool again.`;let s=r.map(e=>`"${e}"`).join(` | `);return`CURRENT_PAGE. URL: ${e} | Title: ${n} | Page sections (${r.length} total): ${s}.${o} *** WALKTHROUGH: do NOT drive it yourself. Do not call fwh-highlight-mvp and do not decide the order — highlighting and section order are handled for you. You will be told which section to describe, one at a time. When told, describe ONLY that section in 3-4 sentences and then stop; never announce another section, never ask whether to continue. ***`},"fwh-list-forms-mvp":async()=>{let e=s_(),t=c_();$(`fwh-list-forms-mvp ✓`,`${e.length} field(s)`,`${t.length} button(s)`);let n=e.map(e=>({...e,inputCategory:e.tag===`SELECT`?`dropdown_select`:`text_input`}));return JSON.stringify({fields:n,buttons:t,hint:`field_key: use the label/placeholder/name shown above. CRITICAL: fields with inputCategory='dropdown_select' are SELECT elements — their field_key must match the SELECT's own label (e.g. 'Company Size'), NOT the option text (e.g. 'Small Business') and NOT a nearby text input label. Never fill a SELECT option value into a text input.`})},"fwh-fill-form-mvp":async()=>`FORM_FILLING_DISABLED: Form filling is not available at this time. Tell the visitor what fields are present (use fwh-list-forms-mvp) and ask them to fill the form themselves.`,"fwh-click-button-mvp":async e=>{let t=p_(e),n=(t.button_text??t.buttonText??t.text??``).trim(),r=(t.card_context??t.cardContext??``).trim();if($(`fwh-click-button-mvp ▶`,n,r?`(card_context: ${r})`:``),!n)return`Missing button_text: pass the visible label, e.g. Conduct Audit.`;let i=f_(n,a,r);if(!i)return $(`fwh-click-button-mvp ✖ no match`),`Could not find a button matching "${n}". Call fwh-list-forms-mvp to see button texts.`;try{Eh(`Clicking ${n}...`,{spinner:!0});try{i.scrollIntoView({block:`center`,inline:`nearest`})}catch{i.scrollIntoView(!0)}await Xh(i),i.focus?.(),await Qh(i),$h(i),i.click();let e=(i.textContent||i.value||i.getAttribute(`aria-label`)||``).trim().slice(0,80)||`button`,t=`OK_CLICK ${e}`;return $(`fwh-click-button-mvp ✓`,t),Eh(`Clicked ${e}`,{spinner:!1,icon:`✅`,duration:1200}),t}catch(e){let t=`Click failed: ${e?.message||String(e)}`;return $(`fwh-click-button-mvp ✖`,t),Eh(`Click failed`,{spinner:!1,icon:`⚠️`,duration:1800}),t}finally{Zh(),Dh(700)}},"fwh-cart-get-mvp":async()=>{$(`fwh-cart-get-mvp ▶`);try{let e=await Kr();if(!e)return`CART_UNAVAILABLE: this page is not a Shopify storefront, so there is no cart to read.`;let t=e.items.map((e,t)=>{let n=e.variantTitle&&e.variantTitle!==`Default Title`?` (${e.variantTitle})`:``;return`${t+1}. ${e.title}${n} — qty ${e.quantity} — ${e.linePrice}${e.handle?` — handle: ${e.handle}`:``}`}),n=`CART. Items: ${e.itemCount} | Subtotal: ${e.subtotal}${e.currency?` ${e.currency}`:``}${t.length?` | ${t.join(` | `)}`:` | (cart is empty)`}`;return $(`fwh-cart-get-mvp ✓`,n),n}catch(e){return $(`fwh-cart-get-mvp ✖`,e),`CART_ERROR: ${e?.userMessage||e?.message||`could not read the cart`}`}},"fwh-shop-search-mvp":async e=>{let t=String((e||{}).query||``).trim();if($(`fwh-shop-search-mvp ▶`,t),!t)return`Missing query: pass the product name or keyword to search.`;try{let e=await $r(t);if(!e.length)return`NO_PRODUCTS_FOUND for "${t}". Tell the visitor no matching product was found — do not invent products.`;let n=e.map(e=>`"${e.title}" — handle: ${e.handle}${e.price?` — price: ${e.price}`:``}`).join(` | `);return $(`fwh-shop-search-mvp ✓`,n),`PRODUCTS_FOUND: ${n}. Use the handle with fwh-cart-add-mvp or fwh-view-product-mvp.`}catch(e){return $(`fwh-shop-search-mvp ✖`,e),`SEARCH_ERROR: ${e?.message||`product search failed`}`}},"fwh-cart-add-mvp":async e=>{let t=e||{},n=String(t.product_handle||t.productHandle||``).trim().toLowerCase(),r=String(t.variant||``).trim()||null,i=Math.min(20,Math.max(1,parseInt(t.quantity,10)||1));if($(`fwh-cart-add-mvp ▶`,{handle:n,variantText:r,quantity:i}),!n)return`Missing product_handle: search the product first with fwh-shop-search-mvp.`;try{let{variant:e,product:t}=await qr(n,r);await Jr(e.id,i);let a=await Ur(),o=await s(a),c=e.title&&e.title!==`Default Title`?` (${e.title})`:``,l=`ADDED ${i} × ${t.title}${c}. Cart now has ${a.item_count} item(s), subtotal ${Gr(a.items_subtotal_price)}${a.currency?` ${a.currency}`:``}.${o}`;return $(`fwh-cart-add-mvp ✓`,l),l}catch(e){return $(`fwh-cart-add-mvp ✖`,e),`ADD_FAILED: ${e?.userMessage||e?.message||`could not add the item`}. Relay this reason to the visitor.`}},"fwh-cart-update-mvp":async e=>{let t=e||{},n=parseInt(t.line,10),r=Math.min(20,Math.max(0,parseInt(t.quantity,10)));if($(`fwh-cart-update-mvp ▶`,{line:n,quantity:r}),!Number.isFinite(n)||n<1)return`Missing/invalid line: call fwh-cart-get-mvp first and use the item's 1-based number.`;if(!Number.isFinite(r))return`Missing/invalid quantity.`;try{let e=await Ur(),t=e.items?.[n-1];if(!t)return`NO_SUCH_LINE: the cart has ${e.items?.length||0} item(s). Call fwh-cart-get-mvp for the current list.`;let i=await Yr(n,r),a=await s(i),o=`UPDATED ${t.product_title||t.title} to qty ${r}. Cart now has ${i.item_count} item(s), subtotal ${Gr(i.items_subtotal_price)}${i.currency?` ${i.currency}`:``}.${a}`;return $(`fwh-cart-update-mvp ✓`,o),o}catch(e){return $(`fwh-cart-update-mvp ✖`,e),`UPDATE_FAILED: ${e?.userMessage||e?.message||`could not update the item`}`}},"fwh-cart-remove-mvp":async e=>{let t=parseInt((e||{}).line,10);if($(`fwh-cart-remove-mvp ▶`,{line:t}),!Number.isFinite(t)||t<1)return`Missing/invalid line: call fwh-cart-get-mvp first and use the item's 1-based number.`;try{let e=await Ur(),n=e.items?.[t-1];if(!n)return`NO_SUCH_LINE: the cart has ${e.items?.length||0} item(s). Call fwh-cart-get-mvp for the current list.`;let r=await Yr(t,0),i=await s(r),a=`REMOVED ${n.product_title||n.title}. Cart now has ${r.item_count} item(s), subtotal ${Gr(r.items_subtotal_price)}${r.currency?` ${r.currency}`:``}.${i}`;return $(`fwh-cart-remove-mvp ✓`,a),a}catch(e){return $(`fwh-cart-remove-mvp ✖`,e),`REMOVE_FAILED: ${e?.userMessage||e?.message||`could not remove the item`}`}},"fwh-cart-open-mvp":async()=>($(`fwh-cart-open-mvp ▶`),c(Xr(),`Cart`)),"fwh-checkout-mvp":async()=>{$(`fwh-checkout-mvp ▶`);try{if(!(await Ur()).item_count)return`CART_EMPTY: do not go to checkout. Tell the visitor their cart is empty and offer to help find products.`}catch{}return c(Zr(),`Checkout`)},"fwh-view-product-mvp":async e=>{let t=e||{},n=String(t.product_handle||t.productHandle||``).trim().toLowerCase();return $(`fwh-view-product-mvp ▶`,{handle:n}),n?c(Qr(n),n.replace(/[-_]/g,` `)):`Missing product_handle: search the product first with fwh-shop-search-mvp.`}};async function s(e){try{let{sectionsSwapped:t}=await si(e);if(!t&&ei())return` NOTE: the cart list on this page could not be re-rendered — briefly tell the visitor the page may need a quick refresh to show the change.`}catch{}return``}function c(e,n){if(typeof window>`u`)return`NAV_FAILED: no browser context.`;let r=e;try{r=new URL(e,window.location.href).href}catch{}return t&&Le(t,window.location.href,r,n),Tr(r),Mr().then(()=>{window.location.assign(r)}),`OK_NAVIGATING to ${n}. The page is loading — say one short phrase like "Here you go." and nothing more.`}return{...o,highlightOnPage:o[`fwh-highlight-mvp`],getPageContent:o[`fwh-page-content-mvp`],endVoiceSession:o[`fwh-end-session-mvp`],listPageForms:o[`fwh-list-forms-mvp`],fillFormFields:o[`fwh-fill-form-mvp`],clickPageButton:o[`fwh-click-button-mvp`]}}var y_=/^(get in touch|contact us|let'?s (talk|connect)|subscribe|newsletter|follow us|quick links|company|solutions|what we do|resources|legal|sitemap|share this)/i;function b_(){try{let{headings:e,pageTitleHeading:t,containerHeadings:n}=g_(),r=new Set((n||[]).map(e=>e.trim())),i=(e||[]).map(e=>typeof e==`string`?e:e?.text||``).filter(Boolean).filter(e=>!t||e.trim()!==t.trim()).filter(e=>!y_.test(e.trim())),a=i.filter(e=>!r.has(e.trim()));return a.length?a:i}catch{return[]}}function x_(e){return!e||typeof e!=`object`?e:Object.fromEntries(Object.entries(e).map(([e,t])=>[e,async n=>{let r=Mn();r?console.log(`[FlowEngage:voice-tool] ▶`,e,n):console.info(`[FlowEngage] voice tool » ${e}`);let i;try{i=await t(n)}catch(t){let n=`Tool error: ${t?.message||String(t)}`;return console.error(`[FlowEngage] voice tool ${e} threw:`,t),n}let a=typeof i==`string`?i.length>220?`${i.slice(0,220)}…`:i:String(i);return r?console.log(`[FlowEngage:voice-tool] ◀`,e,a):console.info(`[FlowEngage] voice tool « ${e}:`,a),i}]))}var S_=`[PAGE_CONTEXT]`;function C_(e,t,n){return`${S_} SESSION_CONTEXT (NOT a tool result — do NOT treat as CURRENT_PAGE): Visitor is on: ${e} | Approx headings for awareness only: ${t||``}. ${n?`Forms: ${n}. `:``}The visitor is ALREADY on this page — do NOT offer to navigate there.`}var w_={blue:`#135bd8`,red:`#ef334b`,yellow:`#ffbd2e`,dark:`#0d1b2a`,highlight:`rgba(255,255,255,0.9)`,green:`#10b981`};function T_(e){if(!Array.isArray(e)||e.length<2)return`linear-gradient(145deg, #dce8f022 0%, #e8dff522 45%, #f5e0e022 100%), #f7f8fa`;let[t,n,r]=e;return e.length>=3&&r?[`radial-gradient(ellipse 80% 70% at -10% 50%, ${t}1a 0%, transparent 70%)`,`radial-gradient(ellipse 70% 80% at 110% 30%, ${n}1a 0%, transparent 70%)`,`radial-gradient(ellipse 80% 60% at 50% 110%, ${r}1a 0%, transparent 70%)`,`#f7f8fa`].join(`, `):[`radial-gradient(ellipse 80% 100% at -10% 50%, ${t}1a 0%, transparent 70%)`,`radial-gradient(ellipse 80% 100% at 110% 50%, ${n}1a 0%, transparent 70%)`,`#f7f8fa`].join(`, `)}function E_(e){if(!e||typeof e!=`string`)return`#1a1a1a`;let t=e.replace(`#`,``),n=t.length===3?t.split(``).map(e=>e+e).join(``):t,r=parseInt(n.slice(0,2),16)/255,i=parseInt(n.slice(2,4),16)/255,a=parseInt(n.slice(4,6),16)/255,o=e=>e<=.04045?e/12.92:((e+.055)/1.055)**2.4;return .2126*o(r)+.7152*o(i)+.0722*o(a)>.179?`#1a1a1a`:`#ffffff`}function D_(e){let[t,n]=b.useState(null),r=typeof e==`string`?e.trim():``;return b.useEffect(()=>{if(!r){n(null);return}n(null);let e=!1,t=new window.Image;return t.onload=()=>{e||n(r)},t.onerror=()=>{e||n(null)},t.src=r,()=>{e=!0}},[r]),t}function O_({logo:e,style:t}){let n=D_(typeof e==`string`?e:null);if(!e)return null;if(typeof e==`string`)return n?(0,V.jsx)(`img`,{src:n,alt:``,style:{objectFit:`contain`,flexShrink:0,display:`block`,...t}}):null;let r=typeof e==`function`?b.createElement(e):b.isValidElement(e)?e:null;return r?(0,V.jsx)(`span`,{className:`fe-logo-slot`,style:{display:`inline-flex`,alignItems:`center`,flexShrink:0,overflow:`hidden`,...t},children:r}):null}function k_({title:e}){return(0,V.jsx)(`div`,{style:{width:`100%`,height:`100%`,borderRadius:`16px`,background:`linear-gradient(135deg, rgba(59,130,246,0.16) 0%, rgba(59,130,246,0.08) 100%)`,color:`#2563eb`,display:`flex`,alignItems:`center`,justifyContent:`center`,fontSize:`18px`,fontWeight:700,letterSpacing:`-0.03em`},children:String(e||`FE`).split(/\s+/).filter(Boolean).slice(0,2).map(e=>e[0]?.toUpperCase()||``).join(``)||`FE`})}function A_(e,t){let n=(t||``).trim();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)||e===`bot`&&(/\b(that'?s all|we'?re done)\b/i.test(n)||/\b(goodbye|bye)\b/i.test(n)||/\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))):!1}function j_({orbColors:e,...t}){return(0,V.jsx)(Ug,{colors:e,...t})}function M_({copy:e,orbColors:t,primaryColor:n,onYes:r,onLater:i,isMobile:a,isClosing:o}){let s=n||w_.green,c=E_(s),l=a?72:94,u=(0,V.jsxs)(`div`,{style:{display:`flex`,alignItems:a?`flex-end`:`center`,gap:a?`7px`:`9px`},children:[(0,V.jsxs)(`div`,{style:{flex:1,minWidth:0},children:[(0,V.jsx)(Qm.p,{initial:{x:-16,opacity:0},animate:{x:0,opacity:1},transition:{type:`spring`,stiffness:260,damping:24,delay:.36},style:{margin:`0 0 14px`,fontSize:`14px`,fontWeight:700,color:`white`,lineHeight:1.4,textShadow:`0 1px 4px rgba(0,0,0,0.35)`,maxWidth:`210px`,wordBreak:`break-word`,overflow:`hidden`,display:`-webkit-box`,WebkitLineClamp:4,WebkitBoxOrient:`vertical`},children:e.entryPrompt}),(0,V.jsxs)(Qm.div,{initial:{y:10,opacity:0},animate:{y:0,opacity:1},transition:{type:`spring`,stiffness:260,damping:24,delay:.54},style:{display:`flex`,gap:`16px`,flexWrap:`nowrap`},children:[(0,V.jsx)(`button`,{onClick:r,style:{height:`31px`,boxSizing:`border-box`,padding:`0 16px`,borderRadius:`999px`,background:s,color:c,fontWeight:400,fontSize:`14px`,border:`none`,cursor:`pointer`,whiteSpace:`nowrap`,boxShadow:`0 4px 16px ${s}66`,transition:`transform 0.15s ease`},onMouseEnter:e=>{e.currentTarget.style.transform=`scale(1.04)`},onMouseLeave:e=>{e.currentTarget.style.transform=`scale(1)`},children:e.yesLabel}),(0,V.jsx)(`button`,{onClick:i,style:{height:`31px`,boxSizing:`border-box`,padding:`0 16px`,borderRadius:`999px`,background:`transparent`,color:`white`,fontWeight:400,fontSize:`14px`,border:`2px solid rgba(255,255,255,0.55)`,cursor:`pointer`,whiteSpace:`nowrap`,transition:`border-color 0.15s ease`},onMouseEnter:e=>{e.currentTarget.style.borderColor=`rgba(255,255,255,0.9)`},onMouseLeave:e=>{e.currentTarget.style.borderColor=`rgba(255,255,255,0.55)`},children:e.laterLabel})]})]}),(0,V.jsx)(Qm.div,{initial:{x:120,opacity:0},animate:{x:0,opacity:1},transition:{type:`spring`,stiffness:220,damping:22,delay:0},style:{flexShrink:0},children:(0,V.jsx)(j_,{size:l,orbColors:t})})]});return(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(Qm.div,{initial:{opacity:0},animate:{opacity:o?0:1},transition:{duration:.28},style:{position:`fixed`,inset:0,background:`rgba(0,0,0,0.72)`,zIndex:-1}}),a?(0,V.jsx)(`div`,{style:{position:`absolute`,bottom:0,left:0,right:0,padding:`24px 20px 40px`,overflow:`hidden`,animation:o?`fe-orb-slide-down 0.28s cubic-bezier(0.16,1,0.3,1) forwards`:``},children:u}):(0,V.jsx)(`div`,{style:{animation:o?`fe-orb-slide-down 0.28s cubic-bezier(0.16,1,0.3,1) forwards`:``},children:u})]})}function N_({copy:e,orbColors:t,primaryColor:n,onVoice:r,onText:i,onClose:a,isMobile:o,isClosing:s}){let c=n||w_.green,l=E_(c);return(0,V.jsxs)(Qm.div,{initial:{scale:50/350,borderRadius:`50%`},animate:s?{scale:50/350,opacity:0,borderRadius:`50%`}:{scale:1,borderRadius:`16px`,opacity:1},transition:{type:`spring`,stiffness:260,damping:26},style:{transformOrigin:o?`bottom center`:`bottom right`,background:`white`,width:o?`100%`:`350px`,height:`245px`,padding:`24px 40px 32px`,boxSizing:`border-box`,display:`flex`,flexDirection:`column`,alignItems:`center`,justifyContent:`space-between`,boxShadow:`0 20px 60px rgba(0,0,0,0.2)`,position:`relative`,overflow:`hidden`},children:[(0,V.jsx)(`button`,{onClick:a,style:{position:`absolute`,top:`14px`,right:`14px`,background:`none`,border:`none`,cursor:`pointer`,color:`#9ca3af`,padding:`4px`,display:`flex`,alignItems:`center`,justifyContent:`center`,borderRadius:`50%`,transition:`color 0.15s ease`},onMouseEnter:e=>{e.currentTarget.style.color=`#374151`},onMouseLeave:e=>{e.currentTarget.style.color=`#9ca3af`},children:(0,V.jsx)(`svg`,{width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2.2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:(0,V.jsx)(`polyline`,{points:`6 9 12 15 18 9`})})}),(0,V.jsx)(j_,{size:72,orbColors:t}),(0,V.jsxs)(`div`,{style:{textAlign:`center`},children:[(0,V.jsx)(`p`,{style:{margin:0,fontSize:`16px`,fontWeight:400,color:`#111827`,lineHeight:1.4},children:e.connectionTitle}),e.connectionSubtitle&&(0,V.jsx)(`p`,{style:{margin:`6px 0 0`,fontSize:`13px`,fontWeight:400,color:`#6b7280`,lineHeight:1.4},children:e.connectionSubtitle})]}),(0,V.jsxs)(`div`,{style:{display:`flex`,gap:`16px`,width:`100%`},children:[(0,V.jsx)(`button`,{onClick:r,style:{width:`127px`,height:`43px`,borderRadius:`999px`,background:c,color:l,fontWeight:400,fontSize:`16px`,border:`none`,cursor:`pointer`,transition:`transform 0.15s ease`,boxShadow:`0 4px 12px ${c}55`},onMouseEnter:e=>{e.currentTarget.style.transform=`scale(1.03)`},onMouseLeave:e=>{e.currentTarget.style.transform=`scale(1)`},children:e.voiceLabel}),(0,V.jsx)(`button`,{onClick:i,style:{width:`127px`,height:`43px`,borderRadius:`999px`,background:`transparent`,color:`#111827`,fontWeight:400,fontSize:`16px`,border:`1.5px solid #d1d5db`,cursor:`pointer`,transition:`border-color 0.15s ease`},onMouseEnter:e=>{e.currentTarget.style.borderColor=`#6b7280`},onMouseLeave:e=>{e.currentTarget.style.borderColor=`#d1d5db`},children:e.textLabel})]})]})}function P_({orbColors:e,onEnterChat:t,onEndChat:n,agentId:r,siteId:i,apiBaseUrl:a,chatId:o,historyRef:s,onMessageSync:c,beaconSyncVoiceTranscript:l,widgetPosition:u=`bottom-right`,voiceEndCounter:d,websiteName:f,voiceFirstMessage:p,captureVisitorEmail:m,emailRequired:h,emailCaptureEnabled:g=!0,listeningLabel:_=`Listening...`,thinkingLabel:v=`Thinking...`,enterChatModeLabel:y=`Enter Chat Mode`,endConversationLabel:x=`End Conversation`}){(0,b.useEffect)(()=>(console.log(`[FlowEngage:VoiceDebug] 🔵 VoiceMode MOUNTED`),()=>console.log(`[FlowEngage:VoiceDebug] 🔴 VoiceMode UNMOUNTED`)),[]);let[S,C]=(0,b.useState)(`idle`),[w,T]=(0,b.useState)(!1),[E,ee]=(0,b.useState)(!1),[D,te]=(0,b.useState)(()=>le(i)===`auto`),O=()=>{let e=D?`ask`:`auto`;ue(i,e),te(e===`auto`)},[ne,re]=(0,b.useState)(!1),[ie,ae]=(0,b.useState)(``),[oe,k]=(0,b.useState)(``),A=(0,b.useRef)(!1),se=(0,b.useRef)(!1),ce=(0,b.useRef)(null),de=(0,b.useRef)(0),j=(0,b.useRef)(``),fe=(0,b.useRef)(!1),pe=(0,b.useRef)(0),me=(0,b.useRef)(!1),he=(0,b.useRef)(!1),ge=(0,b.useRef)(!1),_e=(0,b.useRef)([]),ve=(0,b.useRef)(null),ye=(0,b.useRef)(``),M=(0,b.useRef)(null),be=e=>{me.current=e,e||(he.current=!1),N.current?.setWalkthroughActive?.(e)};M.current=async()=>{if(he.current)return;let e=typeof window<`u`?window.location.pathname:null;if(e&&ve.current&&e!==ve.current){console.log(`[FlowEngage] walkthrough ended — visitor left `+ve.current),ve.current=null,_e.current=[],be(!1);return}let t=_e.current;xe.current+=1;let n=xe.current,r=t[n];if(!r){if(be(!1),t.length)N.current?.sendUserMessage?.(`That was the last section. Say so in one short sentence and ask if they want to explore any section further.`);else if(n===0){let e=``;try{let t=await Se.current?.[`fwh-read-content-mvp`]?.({});typeof t==`string`&&t&&!/^Could not/i.test(t)&&(e=t.slice(0,2800))}catch{}N.current?.sendUserMessage?.(e?`Give the visitor a complete spoken overview of this page in 4-6 sentences, based ONLY on this page content:\n"""${e}"""\nThen ask which part they'd like to hear more about. Do not call any tool.`:`Give the visitor a complete spoken overview of this page in 4-6 sentences based on what you know about it, then ask which part they'd like to hear more about. Do not call any tool.`,{suppressQuestion:!0})}return}try{await Se.current?.highlightOnPage?.({section_text:r})}catch(e){console.warn(`[FlowEngage] walkthrough highlight failed:`,e?.message||e)}console.debug(`[FlowEngage] walkthrough step ${n+1}/${t.length}: ${r}`);let i=``;try{let e=await Se.current?.[`fwh-read-content-mvp`]?.({section_text:r});typeof e==`string`&&e&&!/^Could not/i.test(e)&&(i=e.slice(0,2500))}catch{}let a=i?`Here is this section's full content — describe it from THIS text only, and do not call any tool:\n"""${i}"""\n`:`read the section's content silently first (fwh-read-content-mvp) if you don't already have it. `;N.current?.sendUserMessage?.(`Describe the section titled "${r}" in 3-4 sentences, starting with ${n===0?`Starting with`:`Next —`} ${r}. Describe ONLY this section. If this section contains named sub-items (steps, checklist entries, cards, plans, FAQs, team members), name EVERY one of them in order with a short phrase each — never skip or collapse them into a summary; `+a+`This section is ON THE PAGE THE VISITOR IS ALREADY ON — do NOT call fwh-navigate-mvp, do not go to a product/detail page, and do not offer to, even if this section's name also matches a page name. Do not mention any other section, do not ask whether to continue, then stop.`,{suppressQuestion:!0})};let xe=(0,b.useRef)(0),N=(0,b.useRef)(null),Se=(0,b.useRef)(null),Ce=(0,b.useRef)(``),we=(0,b.useRef)(async()=>{}),Te=(0,b.useRef)(!1),ke=(0,b.useRef)(!1),je=(0,b.useRef)(!1),Me=(0,b.useRef)(!1),Ne=(0,b.useRef)(`minimized`),Pe=(0,b.useRef)(null),Ie=(0,b.useRef)(null),Le=(0,b.useRef)(null),ze=(0,b.useRef)(0),P=(0,b.useRef)(!1),Be=(0,b.useRef)(`idle`),Ve=(0,b.useRef)(null),He=(0,b.useRef)(!1),Ue=(0,b.useRef)(null),We=(0,b.useCallback)((e=`minimized`)=>{console.warn(`[FlowEngage:VoiceDebug] 🛑 requestSessionShutdown called`,{destination:e,stack:Error().stack?.split(`
|
|
430
|
-
`).slice(1,4).join(` | `)}),Ne.current=e,Me.current=!0,queueMicrotask(()=>{Te.current||(Te.current=!0,we.current?.())})},[s]),Ge=(0,b.useCallback)(()=>{ce.current&&=(clearTimeout(ce.current),null)},[]);(0,b.useEffect)(()=>{if(h){A.current=!1;return}if(A.current=!0,se.current=!1,re(!1),Ge(),fe.current){fe.current=!1,N.current?.setMicMuted?.(!1);let e=j.current;j.current=``,N.current?.injectAgentSpeech?.(`Thanks — I already have your email on file.`,`interrupt`),e&&N.current?.sendUserMessage?.(`The visitor's email is already on file — do not ask for it. Answer their question now: "${e}"`)}N.current?.sendContextualUpdate?.(`EMAIL REQUIREMENT SATISFIED — the visitor's email is already on file. All earlier instructions about requiring or asking for an email are VOID. Never ask for it again.`)},[h,Ge]);let Ke=/(walk\s*(me\s*)?through|walkthrough|take me through|(complete|full|detailed) (overview|understanding|walkthrough|picture|breakdown|explanation)|give me (a|an|the) (overview|tour|walk)|tell me about (each|all)|go through (it|them|each|all)|overview of|one by one|tell me everything|explain (me )?(this|the) (whole |entire |full )?page|everything (about|on) this page|show me (each|all|around))/i,qe=/(that covers everything|that was all|covered everything|that(?:'|’)?s everything)/i,Je=/^((uh+|um+|so|well|okay|ok|yeah)[,.\s]+)*(yes|yeah|yep|yup|sure|ok(ay)?|please|continue|go on|next|keep going|carry on|go ahead|resume|back to the tour)[\s.!,]*$/i,Ye=/(would you like (me )?to take you|shall i take you|want me to take you|would you like to (go|explore|visit|see)|take you there)/i,Xe=/^((uh+|um+|oh|ah|so|well)[,.\s]+)*(cool|nice|great|awesome|wow|lovely|perfect|got it|gotcha|i see|understood|makes sense|interesting|right|alright|sounds good|mm+-?hmm+|hmm+|k)[\s.!,]*$/i,Ze=e=>String(e||``).replace(/[\s.,!?]*((uh+|um+|er+|hmm+|mm+)[\s.,!?]*)+$/i,``).trim(),Qe=(0,b.useCallback)(()=>{Ge(),ce.current=setTimeout(()=>{if(!(!se.current||A.current))if(de.current<3){de.current+=1;try{N.current?.injectAgentSpeech?.(`I still need your email in the box below to continue — could you enter it, please?`,`queue`)}catch{}Qe()}else{try{N.current?.injectAgentSpeech?.(`Since I haven't received your email, I'll close this session for now. Feel free to come back anytime — take care!`,`queue`)}catch{}se.current=!1,re(!1),Ge(),P.current=!0}},1e4)},[Ge]),$e=(0,b.useCallback)(async e=>{Me.current=!0;try{In()}catch{}try{Bh()}catch{}if(i)try{Re(i)}catch{}try{await N.current?.endSession?.()}catch{}let t=Pe.current,n=Ie.current;if(t&&o&&a){let e=n?Math.round((Date.now()-n)/1e3):0;fetch(`${a}/api/voice/save-chat-recording`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({chatId:o,voiceSessionId:t,duration:e,siteId:i})}).catch(e=>console.warn(`[FlowEngage] save-chat-recording failed:`,e.message))}if(Pe.current=null,Ie.current=null,N.current=null,C(`idle`),T(!1),Te.current=!1,Lr(),i)try{Oe(i)}catch{}typeof c==`function`&&await c(s.current),e?.()},[s,c,i,o,a]);we.current=()=>$e(Ne.current===`chat`?t:n),(0,b.useEffect)(()=>{ke.current=w},[w]),(0,b.useEffect)(()=>{d&&We(`chat`)},[d]),(0,b.useEffect)(()=>{let e=!1,d=null,m=null,_=null;return(async()=>{if(!i){C(`error`);return}try{if((await navigator.mediaDevices.getUserMedia({audio:!0})).getTracks().forEach(e=>e.stop()),e)return;s.current=[],Ce.current=``,Te.current=!1,je.current=!1,Me.current=!1,Ne.current=`minimized`,Ir({onBeforeNavigate:()=>{if(i&&s.current.length>0)try{let e=Qg(s.current);e&&Ee(i,e)}catch{}if(!nr()){let e=Pe.current,t=Ie.current;if(e&&o&&a)try{let n=t?Math.round((Date.now()-t)/1e3):0,r=JSON.stringify({chatId:o,voiceSessionId:e,duration:n,siteId:i}),s=`${a}/api/voice/save-chat-recording`;fetch(s,{method:`POST`,headers:{"Content-Type":`application/json`},body:r,keepalive:!0}).catch(()=>{}),Pe.current=null,Ie.current=null}catch{}}Ae()}});let t=x_(v_({siteId:i||null,apiBaseUrl:a||null,chatId:o||null,getLastUserVoiceText:()=>Ce.current,beaconSyncFullVoiceHistory:()=>l?.(s.current),isWalkthroughActive:()=>me.current&&!he.current,requestEndSession:()=>{P.current=!0},requestEmailCollection:()=>{g&&(A.current||se.current||(se.current=!0,de.current=0,re(!0),Qe()))}})),n=i?De(i):null,r=!!n?.transcript,c=i?Re(i):null,v=!!c,y=typeof window<`u`?window.location.pathname:`/`,b=typeof document<`u`?document.title:``,x=y.replace(/^\/[a-z]{2}(?:-[a-z]{2})?(?=\/|$)/i,``)||`/`,S=x===`/`||x===``,w={apiBaseUrl:a,siteId:i,chatId:o,clientTools:t};Se.current=t;let E=(y.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()),ee=``;b&&b.includes(`|`)&&(ee=b.split(`|`).map(e=>e.trim()).filter(Boolean).reduce((e,t)=>e.length<=t.length?e:t));let D=ee||E||``,te=D?`the ${D} page`:`this page`,O=(()=>{if(!c?.targetUrl)return!0;try{return new URL(c.targetUrl,window.location.href).pathname.replace(/\/$/,``)===window.location.pathname.replace(/\/$/,``)}catch{return!0}})(),ne=(()=>{if(!c?.targetUrl)return null;try{return new URL(c.targetUrl,window.location.href).pathname}catch{return null}})();if(v&&!O)w.greeting=`You're on ${te}. What would you like to know?`,console.warn(`[FlowEngage] nav target "${c?.pageName}" not reached — on "${D}" instead; not claiming navigation`);else if(v){let e=c?.pageName?`the ${c.pageName} page`:te;w.greeting=`I have navigated you to ${e}. Do you want to know about any specific section or want a complete overview?`,console.log(`[FlowEngage] 🔄 Agent-nav resume: greeting set for "${e}" (${y})`)}else if(r)w.greeting=`Welcome back! I see you're now on ${te}. Would you like me to continue what I was explaining before, or would you like to know about this page instead?`;else{let e=f||``;w.greeting=p||(S?e?`Hi, welcome to ${e}! How may I help you?`:`Hi, welcome! How may I help you?`:`Welcome to ${te}! How may I help you?`),S||console.log(`[FlowEngage] 🏠 Page-context greeting: "${D}" for ${y}`)}let ie=!1,ae=v?`NAVIGATION COMPLETE — The exact phrase "I have navigated you to the [page] page. Do you want to know about any specific section or want a complete overview?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript?`Prior transcript:\n${n.transcript}\n`:`(no prior transcript)`}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until the visitor responds. When they reply: if they name a specific section → call fwh-get-current-page-mvp then answer immediately; if they say "overview" or anything non-specific → call fwh-get-current-page-mvp (zero speech) then do a full highlighted walkthrough.`:null,oe=!1,k=!v&&!r?`Your greeting has been spoken. Wait for the visitor to ask a question or tell you what they need. Do NOT auto-start a walkthrough or highlight any sections — let the visitor lead.`:null,ce=r&&!v,ue=!1,we=ce?`The question "Would you like me to continue what I was explaining before, or would you like to know about this page instead?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript?`Prior transcript:\n${n.transcript}\n`:`(no prior transcript, or the visitor navigated before saying much)`}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until they respond. When they reply: if they want you to CONTINUE the previous topic → resume verbally from the prior transcript above, do NOT call fwh-get-current-page-mvp or describe the new page unless they ask. If they want to know about THIS page → call fwh-get-current-page-mvp (zero speech) then proceed as a normal fresh-page walkthrough/description. If their reply is ambiguous, ask them to clarify which they'd prefer rather than guessing.`:null,Oe=!1,Le=!1,$e=g&&!!h&&!ge.current,F=await xh.startSession({...w,onConnect:({conversationId:e})=>{Pe.current=e,Ie.current=Date.now(),console.log(`[FlowEngage] 🎙️ AI chat session connected:`,e)},onUnhandledClientToolCall:e=>console.error(`[FlowEngage] Unhandled tool:`,e?.tool_name),onSessionInfo:({emailOnFile:e})=>{ge.current=!!e,e&&(A.current=!0,se.current=!1,re(!1),Ge(),console.log(`[FlowEngage] email already on file per server — gate disabled`))},onAgentTurnEnd:()=>{e||me.current&&(he.current||fe.current||setTimeout(()=>M.current?.(),250))},onSessionEnding:()=>{e||We(`minimized`)},onPanel:({title:t,items:n})=>{e||Rh(n,{title:t||`Products`,side:u===`bottom-left`?`left`:`right`,productUrl:e=>e.handle?`${window.location.origin}/products/${encodeURIComponent(e.handle)}`:null})},onModeChange:({mode:t,tentative:n})=>{if(!e){if(t===`speaking`){Ve.current&&=(clearTimeout(Ve.current),null),Ue.current&&=(clearTimeout(Ue.current),null),C(`speaking`),Be.current=`speaking`,He.current=!1;return}if(t===`user-speaking`){if(se.current&&!A.current&&Qe(),Ue.current&&=(clearTimeout(Ue.current),null),Be.current===`speaking`)return;try{In()}catch{}Ve.current&&=(clearTimeout(Ve.current),null),Be.current!==`listening`&&(C(`listening`),Be.current=`listening`);return}if(t===`thinking`){if(Ve.current&&=(clearTimeout(Ve.current),null),Ue.current&&=(clearTimeout(Ue.current),null),Be.current===`speaking`)return;C(`thinking`),Be.current=`thinking`,n&&(Ue.current=setTimeout(()=>{Ue.current=null,!(e||Be.current!==`thinking`)&&(Be.current=`listening`,C(`listening`))},4e3));return}if(Be.current=`listening`,P.current){P.current=!1,We();return}if($e&&!Le&&!A.current&&!ge.current){Le=!0,ie=!0,oe=!0;try{N.current?.sendContextualUpdate?.(`CRITICAL — EMAIL REQUIRED BEFORE ANYTHING ELSE. The visitor has not provided their email. The app itself speaks the email request out loud — you must NOT say it, and you must NOT answer. Stay completely silent from now until you receive an EMAIL_SAVED message: produce no speech and call no tools, no matter what the visitor says. Remember their question. On EMAIL_SAVED, thank them briefly and answer that remembered question in full.`)}catch{}}if(ae&&!ie){ie=!0;try{N.current?.sendContextualUpdate?.(ae)}catch{}}if(we&&!ue){ue=!0;try{N.current?.sendContextualUpdate?.(we)}catch{}}if(k&&!oe){oe=!0;try{N.current?.sendContextualUpdate?.(k)}catch{}}Ve.current&&clearTimeout(Ve.current),Ve.current=setTimeout(()=>{e||Be.current!==`speaking`&&(He.current||(Ve.current=null,Be.current=`listening`,C(`listening`)))},1200)}},onMessage:t=>{if(e||fe.current&&t.role!==`user`&&!/email/i.test(String(t.message||``)))return;let n=t.role===`user`&&/^(Describe the section titled "|\[SYSTEM:|Give the visitor a complete spoken overview|That was the last section)/.test(String(t.message||``).trim()),r=/\b(?:this|that|the)\s+(?:case\s*stud(?:y|ies)|product|article|blog|post|item|service)\b/i.test(String(t.message||``));if(!n&&!r&&t.role===`user`&&Ke.test(String(t.message||``)))typeof window<`u`&&(window.__fe_user_nav_during_speech=null),be(!0),he.current=!1,pe.current=0,xe.current=-1,_e.current=b_(),ve.current=typeof window<`u`?window.location.pathname:null,setTimeout(()=>M.current?.(),400),console.log(`[FlowEngage] walkthrough armed — ${_e.current.length} sections on this page`);else if(!n&&t.role===`user`&&me.current){let e=String(t.message||``).trim(),n=Ze(e),r=e=>!!e&&(Je.test(e)||Xe.test(e));Ye.test(ye.current||``)?(console.log(`[FlowEngage] walkthrough ended — visitor accepted a navigation offer`),ve.current=null,_e.current=[],be(!1)):!n||r(e)||r(n)?he.current&&(he.current=!1,console.debug(`[FlowEngage] walkthrough resumed by visitor`),setTimeout(()=>M.current?.(),300)):(console.log(`[FlowEngage] walkthrough ended — visitor moved to a new topic`),ve.current=null,_e.current=[],be(!1))}if(t.role!==`user`&&!n&&(ye.current=String(t.message||``)),t.role!==`user`&&qe.test(String(t.message||``))){let e=_e.current.length;!e||xe.current>=e-1?be(!1):console.debug(`[FlowEngage] agent claimed completion at step ${xe.current+1}/${e} — ignoring, tour continues`)}let a=t.role===`user`?`user`:`bot`,o=t.message||t.text||t.content||``;if(o){if(a===`user`&&(Ce.current=o,typeof window<`u`&&(window.__fe_user_nav_during_speech=null),He.current=!0,Ve.current&&=(clearTimeout(Ve.current),null),Be.current!==`speaking`&&(C(`thinking`),Be.current=`thinking`),$e&&!Oe&&!A.current&&!ge.current&&(Oe=!0,j.current=o,fe.current=!0,N.current?.injectAgentSpeech?.(`Before I answer that, could you please enter your email in the text box below?`,`interrupt`),N.current?.setMicMuted?.(!0),se.current||(se.current=!0,de.current=0,re(!0),Qe()))),s.current=[...s.current,{sender:a,message:o,timestamp:new Date().toISOString(),debug:t.debug}],i)try{let e=Qg(s.current);e&&Ee(i,e)}catch{}if(A_(a,o)){P.current=!0;return}if(ke.current&&a===`bot`&&/are you still there|still there\??|hello\?|anyone there|checking in|you there|are you available|is anyone|did you step away/i.test(o))if(ze.current+=1,ze.current===2)try{N.current?.sendContextualUpdate?.(`IMPORTANT: The visitor is muted and has not responded to your last 2 checks. Make exactly ONE more check. If there is still no response, say: 'I feel like you are not active, so I'm ending this session. You can connect with me anytime.' then immediately call the fwh-end-session-mvp tool to end the voice session. Do NOT ask again after that.`)}catch{}else ze.current>=5&&We(`minimized`)}},onError:t=>{if(!e&&!Me.current&&!je.current&&!Fe()){je.current=!0,console.error(`[FlowEngageVoiceCall] session:error`,{message:t?.message,name:t?.name,raw:t});try{N.current?.setMicMuted?.(!0)}catch{}T(!0),C(`error`),We(`chat`)}},onDisconnect:t=>{if(e||Me.current||je.current||Fe())return;let n=s.current?.length||0;console.warn(`[FlowEngageVoiceCall] session:disconnected`,{historyCount:n,reason:t?.reason,closeCode:t?.context?.code,closeReason:t?.context?.reason||t?.message,lastUserText:Ce.current||``,wasMuted:ke.current}),je.current=!0;try{N.current?.setMicMuted?.(!0)}catch{}T(!0),We(`chat`)}});if(e){console.warn(`[FlowEngage:VoiceDebug] ❌ cancelled=true after startSession resolved — killing session immediately`),F.endSession().catch(()=>{});return}d=F,N.current=F,C(`listening`);try{sessionStorage.setItem(`el_conv_${i}`,F.getId())}catch{}parent.postMessage({type:`AGENT_EXPAND`},`*`);try{let e=typeof window<`u`?window.location.href:``,a=`[]`;try{a=await t[`fwh-list-forms-mvp`]()}catch{}let o=`none`;if(typeof window<`u`&&typeof document<`u`)try{let e=`#flowengage-root, .flowengage-shell, .flowengage-widget-root`,t=document.querySelector(`main, [role='main']`)||document.body,n=Array.from(t.querySelectorAll(`h1, h2, h3`)).filter(t=>!t.closest(e)).map(e=>e.textContent.replace(/\s+/g,` `).trim()).filter(e=>e.length>2&&e.length<200);n.length||(n=Array.from(document.body.querySelectorAll(`h1, h2, h3`)).filter(t=>!t.closest(e)).map(e=>e.textContent.replace(/\s+/g,` `).trim()).filter(e=>e.length>2&&e.length<200)),n.length&&(o=[...new Set(n)].slice(0,15).join(` | `))}catch{}let s=r&&!v?`RESUMING PRIOR SESSION — DO NOT GREET. The visitor is continuing the same conversation after a page reload. Do not introduce yourself or re-state your capabilities. Wait silently until the visitor speaks; if you must speak first, briefly acknowledge where you left off in one short sentence.\nPrior transcript:\n${n?.transcript||``}\n\n`:``;F.sendContextualUpdate(`${s}SiteId: ${i||`unknown`}. ${h?``:`Visitor's email is already on file from a prior session — do NOT ask for it again. `}WALKTHROUGH RULE: When the visitor asks for a walkthrough or overview of this page, you MUST IMMEDIATELY call fwh-get-current-page-mvp (zero speech — call it silently with NO words before it) to get the exact highlightable headings. Do NOT skip fwh-get-current-page-mvp. Voice: call end_voice_session or say you are ending the session to stop.`),F.sendContextualUpdate(C_(e,o,a))}catch{}if(typeof window<`u`){let t=window.location.pathname,n=Date.now(),r=e=>{try{return(decodeURIComponent(e||`/`).replace(/\/+$/,``)||`/`).toLowerCase()}catch{return((e||`/`).replace(/\/+$/,``)||`/`).toLowerCase()}},a=()=>{if(e)return;let a=window.location.pathname;if(r(a)===r(t)){t=a;return}if(Date.now()-n<8e3){t=a;return}let o=typeof window<`u`&&window.__fe_agent_nav||null,s=ne&&r(a)===r(ne)||Jn()||o&&Date.now()-o.ts<15e3&&r(o.path)===r(a),c=t;t=a;let l=window.location.href,u=typeof document<`u`?document.title:``,d=typeof document<`u`?Array.from(document.querySelectorAll(`h1, h2, h3, h4`)).filter(e=>!e.closest(`#flowengage-root, .flowengage-shell`)).map(e=>({tag:e.tagName,text:e.textContent.trim()})).filter(e=>e.text.length>0&&e.text.length<200).slice(0,20):[];try{In()}catch{}let f=d.map(e=>e.text).join(` | `),p=e=>(e.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase())||`Home`,m=p(c),h=p(a);if(s){if(typeof window<`u`&&(window.__fe_agent_nav=null),console.log(`[FlowEngage] agent-driven navigation settled: "${c}" → "${a}"`),le(i)!==`auto`)try{N.current?.sendUserMessage?.(`[SYSTEM: YOU just navigated the visitor to the "${h}" page (${l}) at their request — this was YOUR action, not theirs. Page title: "${u}". Sections: ${JSON.stringify(d)}. Say: "Here we are on the ${h} page." Then introduce this page in 2-3 sentences, and finish by asking: "Would you like me to walk you through it section by section?" NEVER say "I see you've navigated" — the visitor did not navigate, you did. Do NOT call fwh-navigate-mvp again. Wait for their answer before describing sections.]`,{suppressQuestion:!0})}catch{}else console.log(`[FlowEngage] auto-nav — skipping the arrival acknowledgment turn`);try{N.current?.sendContextualUpdate?.(C_(l,f))}catch{}return}typeof window<`u`&&(window.__fe_user_nav_during_speech={fromPath:c,toPath:a,fromPageName:m,toPageName:h,ts:Date.now()}),console.log(`[FlowEngage] visitor navigation detected: "${c}" → "${a}" (agent target: ${ne||`none`})`);try{N.current?.sendUserMessage?.(`[SYSTEM: I navigated away mid-walkthrough — I am now on the "${h}" page (${l}). Page title: "${u}". Sections: ${JSON.stringify(d)}. STOP everything. Do NOT describe the new page. Say EXACTLY: "I see you've navigated to the ${h} page — would you like me to continue what I was explaining, or shall I walk you through this page?" Then wait for my answer. IF I say continue/yes/carry on: resume speaking verbally from where you left off — do NOT call fwh-navigate-mvp, do NOT call fwh-highlight-mvp, the visitor will navigate themselves if needed. IF I want to hear about this page: call fwh-highlight-mvp for sections of "${h}" only.]`)}catch{}try{N.current?.sendContextualUpdate?.(C_(l,f))}catch{}};m=window.history.pushState.bind(window.history),window.history.pushState=(...e)=>{m(...e),setTimeout(a,50)},_=()=>setTimeout(a,50),window.addEventListener(`popstate`,_)}}catch(r){e||(console.error(`[FlowEngage] startSession failed`,r),r.message&&r.message.includes(`Too many voice sessions`)?(alert(r.message),n&&n(`minimized`)):(C(`error`),Ne.current=`chat`,window.setTimeout(()=>{e||t?.()},0)))}})(),()=>{console.warn(`[FlowEngage:VoiceDebug] ⚠️ useEffect CLEANUP fired — session will be killed`,{agentId:r,siteId:i,apiBaseUrl:a}),e=!0,Me.current=!0;try{In()}catch{}if(i)try{Re(i)}catch{}typeof window<`u`&&(window.__fe_user_nav_during_speech=null),Lr(),Ve.current&&=(clearTimeout(Ve.current),null),Ue.current&&=(clearTimeout(Ue.current),null),ce.current&&=(clearTimeout(ce.current),null),He.current=!1,Be.current=`idle`,m&&typeof window<`u`&&(window.history.pushState=m),_&&typeof window<`u`&&window.removeEventListener(`popstate`,_);let t=d||N.current,n=Pe.current,l=Ie.current;if(t?.endSession?.().catch(()=>{}),n&&o&&a){let e=l?Math.round((Date.now()-l)/1e3):0,t=JSON.stringify({chatId:o,voiceSessionId:n,duration:e,siteId:i}),r=`${a}/api/voice/save-chat-recording`;typeof navigator<`u`&&navigator.sendBeacon&&navigator.sendBeacon(r,new Blob([t],{type:`application/json`}))||fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`},body:t,keepalive:!0}).catch(()=>{}),Pe.current=null,Ie.current=null}typeof c==`function`&&s.current.length>0&&c(s.current).catch?.(()=>{})}},[r,i,a,We]),(0,b.useEffect)(()=>{if(!E)return;let e=e=>{let t=e.composedPath?.()?.[0]||e.target;Le.current&&!Le.current.contains(t)&&ee(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[E]);let F=async()=>{if(N.current)try{if(w){ze.current=0,await N.current.setMicMuted?.(!1),T(!1);try{N.current.sendContextualUpdate?.(`The visitor has unmuted. Resume normal interaction and wait for them to speak.`)}catch{}}else{ze.current=0,await N.current.setMicMuted?.(!0),T(!0);try{N.current.sendContextualUpdate?.(`The visitor has muted their microphone. Do NOT speak, ask if they are there, or prompt them in any way. Stay completely silent until they unmute.`)}catch{}}}catch{}},I=(0,b.useCallback)(async e=>{e?.preventDefault?.();let t=ie.trim().toLowerCase();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)){k(`Please enter a valid email address.`);return}k(``),A.current=!0,se.current=!1,Ge(),re(!1),ae(``),fe.current=!1;try{typeof m==`function`&&await m(t)}catch{}N.current?.setMicMuted?.(!1);let n=j.current;j.current=``;try{N.current?.injectAgentSpeech?.(`Thank you for submitting your email.`),N.current?.sendUserMessage?.(n||`The visitor's email is on file. Carry on helping them and never ask for it again.`)}catch{}},[ie,m,Ge]),L=Array.isArray(e)&&e[0]||`#3B82F6`;return(0,V.jsxs)(`div`,{ref:Le,style:{position:`relative`,display:`flex`,flexDirection:`column`,alignItems:`center`,gap:`14px`,animation:`fe-orb-slide-up 0.42s cubic-bezier(0.16,1,0.3,1)`},children:[ne&&(0,V.jsxs)(`form`,{onSubmit:I,style:{width:`270px`,maxWidth:`calc(100vw - 40px)`,boxSizing:`border-box`,background:`rgba(15,22,36,0.97)`,backdropFilter:`blur(14px)`,WebkitBackdropFilter:`blur(14px)`,borderRadius:`16px`,boxShadow:`0 10px 40px rgba(0,0,0,0.45)`,padding:`16px`,display:`flex`,flexDirection:`column`,gap:`10px`,animation:`fe-orb-fade-in 0.18s ease`},children:[(0,V.jsx)(`span`,{style:{color:`white`,fontSize:`13px`,fontWeight:600,lineHeight:1.4},children:`Please enter your email to continue`}),(0,V.jsx)(`input`,{type:`email`,inputMode:`email`,autoComplete:`email`,autoFocus:!0,value:ie,onChange:e=>{ae(e.target.value),oe&&k(``)},placeholder:`you@example.com`,style:{width:`100%`,boxSizing:`border-box`,padding:`10px 12px`,borderRadius:`10px`,border:oe?`1.5px solid #ef4444`:`1.5px solid #4b5563`,background:`#1f2937`,color:`white`,fontSize:`14px`,outline:`none`}}),oe&&(0,V.jsx)(`span`,{style:{color:`#f87171`,fontSize:`12px`},children:oe}),(0,V.jsx)(`button`,{type:`submit`,disabled:!ie.trim(),style:{width:`100%`,padding:`10px 12px`,borderRadius:`10px`,border:`none`,background:ie.trim()?L:`#374151`,color:`white`,fontSize:`14px`,fontWeight:600,cursor:ie.trim()?`pointer`:`default`,transition:`background 0.2s ease`},children:`Submit`})]}),(0,V.jsxs)(`div`,{style:{position:`relative`},children:[(S===`listening`||S===`thinking`)&&!w&&(0,V.jsx)(`span`,{style:{position:`absolute`,right:`100%`,marginRight:`18px`,top:`50%`,transform:`translateY(-50%)`,color:`white`,fontSize:`18px`,fontWeight:600,letterSpacing:`0.01em`,textShadow:`0 2px 4px rgba(0,0,0,0.4)`,whiteSpace:`nowrap`},children:S===`thinking`?v:_}),w&&(0,V.jsx)(`span`,{style:{position:`absolute`,right:`100%`,marginRight:`18px`,top:`50%`,transform:`translateY(-50%)`,color:`#f87171`,fontSize:`16px`,fontWeight:600,letterSpacing:`0.01em`,textShadow:`0 2px 4px rgba(0,0,0,0.4)`,whiteSpace:`nowrap`},children:`Muted`}),(0,V.jsx)(j_,{size:72,voiceState:S,isMuted:w,orbColors:e})]}),S===`error`&&(0,V.jsx)(`span`,{style:{color:`#f87171`,fontSize:`13px`,fontWeight:500},children:`Could not start voice session.`}),(0,V.jsxs)(`div`,{style:{display:`flex`,gap:`12px`,alignItems:`center`},children:[(0,V.jsx)(`button`,{onClick:F,title:w?`Unmute`:`Mute`,style:{width:`24px`,height:`24px`,borderRadius:`50%`,background:w?`#7f1d1d`:`#1f2937`,border:w?`1.5px solid #ef4444`:`1.5px solid #4b5563`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:w?`#f87171`:`white`,transition:`all 0.2s ease`,boxShadow:`0 2px 8px rgba(0,0,0,0.4)`,padding:0,flexShrink:0},children:w?(0,V.jsxs)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,V.jsx)(`line`,{x1:`1`,y1:`1`,x2:`23`,y2:`23`}),(0,V.jsx)(`path`,{d:`M9 9v3a3 3 0 005.12 2.12M15 9.34V4a3 3 0 00-5.94-.6`}),(0,V.jsx)(`path`,{d:`M17 16.95A7 7 0 015 12v-2m14 0v2a7 7 0 01-.11 1.23`}),(0,V.jsx)(`line`,{x1:`12`,y1:`19`,x2:`12`,y2:`23`}),(0,V.jsx)(`line`,{x1:`8`,y1:`23`,x2:`16`,y2:`23`})]}):(0,V.jsx)(tg,{size:16,color:`currentColor`})}),(0,V.jsx)(`button`,{onClick:()=>ee(e=>!e),style:{width:`24px`,height:`24px`,borderRadius:`50%`,background:`#1f2937`,border:`1.5px solid #4b5563`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:`white`,transition:`background 0.2s ease`,boxShadow:`0 2px 8px rgba(0,0,0,0.4)`,padding:0,flexShrink:0},children:(0,V.jsx)(lg,{size:16,color:`currentColor`})})]}),E&&(0,V.jsxs)(`div`,{style:{position:`absolute`,bottom:0,right:0,width:`230px`,zIndex:9999,background:`rgba(15,22,36,0.96)`,backdropFilter:`blur(14px)`,WebkitBackdropFilter:`blur(14px)`,borderRadius:`16px`,boxShadow:`0 10px 40px rgba(0,0,0,0.45)`,animation:`fe-orb-fade-in 0.18s ease`,padding:`12px 12px 40px`},children:[(0,V.jsxs)(`button`,{onClick:()=>{ee(!1),Ne.current=`chat`,$e(t)},style:{display:`flex`,alignItems:`center`,gap:`12px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`white`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`,whiteSpace:`nowrap`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.08)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,V.jsx)(ug,{}),y]}),i&&(0,V.jsxs)(`button`,{onClick:()=>O(),style:{display:`flex`,alignItems:`center`,justifyContent:`space-between`,gap:`8px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`white`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.08)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,V.jsxs)(`span`,{style:{display:`flex`,alignItems:`center`,gap:`12px`,minWidth:0,overflow:`hidden`},children:[(0,V.jsx)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,style:{flexShrink:0},children:(0,V.jsx)(`polygon`,{points:`3 11 22 2 13 21 11 13 3 11`})}),(0,V.jsx)(`span`,{style:{overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:`Auto-navigate`})]}),(0,V.jsx)(`span`,{"aria-hidden":!0,style:{width:`34px`,height:`19px`,borderRadius:`999px`,background:D?`#22c55e`:`rgba(255,255,255,0.2)`,position:`relative`,flexShrink:0,transition:`background 0.15s ease`},children:(0,V.jsx)(`span`,{style:{position:`absolute`,top:`2px`,left:D?`17px`:`2px`,width:`15px`,height:`15px`,borderRadius:`50%`,background:`white`,transition:`left 0.15s ease`}})})]}),(0,V.jsxs)(`button`,{onClick:()=>{ee(!1),Ne.current=`minimized`,$e(n)},style:{display:`flex`,alignItems:`center`,gap:`12px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`#E30C12`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`,whiteSpace:`nowrap`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(227,12,18,0.1)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,V.jsx)(dg,{}),x]}),(0,V.jsx)(`button`,{onClick:()=>ee(!1),style:{position:`absolute`,bottom:`8px`,right:`8px`,width:`24px`,height:`24px`,borderRadius:`50%`,background:`rgba(255,255,255,0.15)`,border:`none`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:`white`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.25)`},onMouseLeave:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.15)`},children:(0,V.jsxs)(`svg`,{width:`12`,height:`12`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,V.jsx)(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),(0,V.jsx)(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})]})})]})]})}function F_({orbColors:e}){let t=Array.isArray(e)&&e.length>0?e:[`#135bd8`,`#ef334b`,`#ffbd2e`],n=t[0],r=t[1]||t[0],i=t[2]||t[0];return(0,V.jsx)(`div`,{style:{width:`32px`,height:`32px`,borderRadius:`50%`,flexShrink:0,background:`
|
|
430
|
+
`).slice(1,4).join(` | `)}),Ne.current=e,Me.current=!0,queueMicrotask(()=>{Te.current||(Te.current=!0,we.current?.())})},[s]),Ge=(0,b.useCallback)(()=>{ce.current&&=(clearTimeout(ce.current),null)},[]);(0,b.useEffect)(()=>{if(h){A.current=!1;return}if(A.current=!0,se.current=!1,re(!1),Ge(),fe.current){fe.current=!1,N.current?.setMicMuted?.(!1);let e=j.current;j.current=``,N.current?.injectAgentSpeech?.(`Thanks — I already have your email on file.`,`interrupt`),e&&N.current?.sendUserMessage?.(`The visitor's email is already on file — do not ask for it. Answer their question now: "${e}"`)}N.current?.sendContextualUpdate?.(`EMAIL REQUIREMENT SATISFIED — the visitor's email is already on file. All earlier instructions about requiring or asking for an email are VOID. Never ask for it again.`)},[h,Ge]);let Ke=/(walk\s*(me\s*)?through|walkthrough|take me through|(complete|full|detailed) (overview|understanding|walkthrough|picture|breakdown|explanation)|give me (a|an|the) (overview|tour|walk)|tell me about (each|all)|go through (it|them|each|all)|overview of|one by one|tell me everything|explain (me )?(this|the) (whole |entire |full )?page|everything (about|on) this page|show me (each|all|around))/i,qe=/(that covers everything|that was all|covered everything|that(?:'|’)?s everything)/i,Je=/^((uh+|um+|so|well|okay|ok|yeah)[,.\s]+)*(yes|yeah|yep|yup|sure|ok(ay)?|please|continue|go on|next|keep going|carry on|go ahead|resume|back to the tour)[\s.!,]*$/i,Ye=/(would you like (me )?to take you|shall i take you|want me to take you|would you like to (go|explore|visit|see)|take you there)/i,Xe=/^((uh+|um+|oh|ah|so|well)[,.\s]+)*(cool|nice|great|awesome|wow|lovely|perfect|got it|gotcha|i see|understood|makes sense|interesting|right|alright|sounds good|mm+-?hmm+|hmm+|k)[\s.!,]*$/i,Ze=e=>String(e||``).replace(/[\s.,!?]*((uh+|um+|er+|hmm+|mm+)[\s.,!?]*)+$/i,``).trim(),Qe=(0,b.useCallback)(()=>{Ge(),ce.current=setTimeout(()=>{if(!(!se.current||A.current))if(de.current<3){de.current+=1;try{N.current?.injectAgentSpeech?.(`I still need your email in the box below to continue — could you enter it, please?`,`queue`)}catch{}Qe()}else{try{N.current?.injectAgentSpeech?.(`Since I haven't received your email, I'll close this session for now. Feel free to come back anytime — take care!`,`queue`)}catch{}se.current=!1,re(!1),Ge(),P.current=!0}},1e4)},[Ge]),$e=(0,b.useCallback)(async e=>{Me.current=!0;try{In()}catch{}try{Bh()}catch{}if(i)try{Re(i)}catch{}try{await N.current?.endSession?.()}catch{}let t=Pe.current,n=Ie.current;if(t&&o&&a){let e=n?Math.round((Date.now()-n)/1e3):0;fetch(`${a}/api/voice/save-chat-recording`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({chatId:o,voiceSessionId:t,duration:e,siteId:i})}).catch(e=>console.warn(`[FlowEngage] save-chat-recording failed:`,e.message))}if(Pe.current=null,Ie.current=null,N.current=null,C(`idle`),T(!1),Te.current=!1,Lr(),i)try{Oe(i)}catch{}typeof c==`function`&&await c(s.current),e?.()},[s,c,i,o,a]);we.current=()=>$e(Ne.current===`chat`?t:n),(0,b.useEffect)(()=>{ke.current=w},[w]),(0,b.useEffect)(()=>{d&&We(`chat`)},[d]),(0,b.useEffect)(()=>{let e=!1,d=null,m=null,_=null;return(async()=>{if(!i){C(`error`);return}try{if((await navigator.mediaDevices.getUserMedia({audio:!0})).getTracks().forEach(e=>e.stop()),e)return;s.current=[],Ce.current=``,Te.current=!1,je.current=!1,Me.current=!1,Ne.current=`minimized`,Ir({onBeforeNavigate:()=>{if(i&&s.current.length>0)try{let e=Qg(s.current);e&&Ee(i,e)}catch{}if(!nr()){let e=Pe.current,t=Ie.current;if(e&&o&&a)try{let n=t?Math.round((Date.now()-t)/1e3):0,r=JSON.stringify({chatId:o,voiceSessionId:e,duration:n,siteId:i}),s=`${a}/api/voice/save-chat-recording`;fetch(s,{method:`POST`,headers:{"Content-Type":`application/json`},body:r,keepalive:!0}).catch(()=>{}),Pe.current=null,Ie.current=null}catch{}}Ae()}});let t=x_(v_({siteId:i||null,apiBaseUrl:a||null,chatId:o||null,getLastUserVoiceText:()=>Ce.current,beaconSyncFullVoiceHistory:()=>l?.(s.current),isWalkthroughActive:()=>me.current&&!he.current,requestEndSession:()=>{P.current=!0},requestEmailCollection:()=>{g&&(A.current||se.current||(se.current=!0,de.current=0,re(!0),Qe()))}})),n=i?De(i):null,r=!!n?.transcript,c=i?Re(i):null,v=!!c,y=typeof window<`u`?window.location.pathname:`/`,b=typeof document<`u`?document.title:``,x=y.replace(/^\/[a-z]{2}(?:-[a-z]{2})?(?=\/|$)/i,``)||`/`,S=x===`/`||x===``,w={apiBaseUrl:a,siteId:i,chatId:o,clientTools:t};Se.current=t;let E=(y.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()),ee=``;b&&b.includes(`|`)&&(ee=b.split(`|`).map(e=>e.trim()).filter(Boolean).reduce((e,t)=>e.length<=t.length?e:t));let D=ee||E||``,te=D?`the ${D} page`:`this page`,O=(()=>{if(!c?.targetUrl)return!0;try{return new URL(c.targetUrl,window.location.href).pathname.replace(/\/$/,``)===window.location.pathname.replace(/\/$/,``)}catch{return!0}})(),ne=(()=>{if(!c?.targetUrl)return null;try{return new URL(c.targetUrl,window.location.href).pathname}catch{return null}})();if(v&&!O)w.greeting=`You're on ${te}. What would you like to know?`,console.warn(`[FlowEngage] nav target "${c?.pageName}" not reached — on "${D}" instead; not claiming navigation`);else if(v){let e=c?.pageName?`the ${c.pageName} page`:te;w.greeting=`I have navigated you to ${e}. Do you want to know about any specific section or want a complete overview?`,console.log(`[FlowEngage] 🔄 Agent-nav resume: greeting set for "${e}" (${y})`)}else if(r)w.greeting=`Welcome back! I see you're now on ${te}. Would you like me to continue what I was explaining before, or would you like to know about this page instead?`;else{let e=f||``;w.greeting=p||(S?e?`Hi, welcome to ${e}! How may I help you?`:`Hi, welcome! How may I help you?`:`Welcome to ${te}! How may I help you?`),S||console.log(`[FlowEngage] 🏠 Page-context greeting: "${D}" for ${y}`)}let ie=!1,ae=v?`NAVIGATION COMPLETE — The exact phrase "I have navigated you to the [page] page. Do you want to know about any specific section or want a complete overview?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript?`Prior transcript:\n${n.transcript}\n`:`(no prior transcript)`}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until the visitor responds. When they reply: if they name a specific section → call fwh-get-current-page-mvp then answer immediately; if they say "overview" or anything non-specific → call fwh-get-current-page-mvp (zero speech) then do a full highlighted walkthrough.`:null,oe=!1,k=!v&&!r?`Your greeting has been spoken. Wait for the visitor to ask a question or tell you what they need. Do NOT auto-start a walkthrough or highlight any sections — let the visitor lead.`:null,ce=r&&!v,ue=!1,we=ce?`The question "Would you like me to continue what I was explaining before, or would you like to know about this page instead?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript?`Prior transcript:\n${n.transcript}\n`:`(no prior transcript, or the visitor navigated before saying much)`}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until they respond. When they reply: if they want you to CONTINUE the previous topic → resume verbally from the prior transcript above, do NOT call fwh-get-current-page-mvp or describe the new page unless they ask. If they want to know about THIS page → call fwh-get-current-page-mvp (zero speech) then proceed as a normal fresh-page walkthrough/description. If their reply is ambiguous, ask them to clarify which they'd prefer rather than guessing.`:null,Oe=!1,Le=!1,$e=g&&!!h&&!ge.current,F=await xh.startSession({...w,onConnect:({conversationId:e})=>{Pe.current=e,Ie.current=Date.now(),console.log(`[FlowEngage] 🎙️ AI chat session connected:`,e)},onUnhandledClientToolCall:e=>console.error(`[FlowEngage] Unhandled tool:`,e?.tool_name),onSessionInfo:({emailOnFile:e})=>{ge.current=!!e,e&&(A.current=!0,se.current=!1,re(!1),Ge(),console.log(`[FlowEngage] email already on file per server — gate disabled`))},onAgentTurnEnd:()=>{e||me.current&&(he.current||fe.current||setTimeout(()=>M.current?.(),250))},onSessionEnding:()=>{e||We(`minimized`)},onPanel:({title:t,items:n})=>{e||Rh(n,{title:t||`Products`,side:u===`bottom-left`?`left`:`right`,productUrl:e=>e.handle?`${window.location.origin}/products/${encodeURIComponent(e.handle)}`:null})},onModeChange:({mode:t,tentative:n})=>{if(!e){if(t===`speaking`){Ve.current&&=(clearTimeout(Ve.current),null),Ue.current&&=(clearTimeout(Ue.current),null),C(`speaking`),Be.current=`speaking`,He.current=!1;return}if(t===`user-speaking`){if(se.current&&!A.current&&Qe(),Ue.current&&=(clearTimeout(Ue.current),null),Be.current===`speaking`)return;try{In()}catch{}Ve.current&&=(clearTimeout(Ve.current),null),Be.current!==`listening`&&(C(`listening`),Be.current=`listening`);return}if(t===`thinking`){if(Ve.current&&=(clearTimeout(Ve.current),null),Ue.current&&=(clearTimeout(Ue.current),null),Be.current===`speaking`)return;C(`thinking`),Be.current=`thinking`,n&&(Ue.current=setTimeout(()=>{Ue.current=null,!(e||Be.current!==`thinking`)&&(Be.current=`listening`,C(`listening`))},4e3));return}if(Be.current=`listening`,P.current){P.current=!1,We();return}if($e&&!Le&&!A.current&&!ge.current){Le=!0,ie=!0,oe=!0;try{N.current?.sendContextualUpdate?.(`CRITICAL — EMAIL REQUIRED BEFORE ANYTHING ELSE. The visitor has not provided their email. The app itself speaks the email request out loud — you must NOT say it, and you must NOT answer. Stay completely silent from now until you receive an EMAIL_SAVED message: produce no speech and call no tools, no matter what the visitor says. Remember their question. On EMAIL_SAVED, thank them briefly and answer that remembered question in full.`)}catch{}}if(ae&&!ie){ie=!0;try{N.current?.sendContextualUpdate?.(ae)}catch{}}if(we&&!ue){ue=!0;try{N.current?.sendContextualUpdate?.(we)}catch{}}if(k&&!oe){oe=!0;try{N.current?.sendContextualUpdate?.(k)}catch{}}Ve.current&&clearTimeout(Ve.current),Ve.current=setTimeout(()=>{e||Be.current!==`speaking`&&(He.current||(Ve.current=null,Be.current=`listening`,C(`listening`)))},1200)}},onMessage:t=>{if(e||fe.current&&t.role!==`user`&&!/email/i.test(String(t.message||``)))return;let n=t.role===`user`&&/^(Describe the section titled "|\[SYSTEM:|Give the visitor a complete spoken overview|That was the last section)/.test(String(t.message||``).trim());if(t.role===`user`&&!n)try{Bh()}catch{}let r=/\b(?:this|that|the)\s+(?:case\s*stud(?:y|ies)|product|article|blog|post|item|service)\b/i.test(String(t.message||``));if(!n&&!r&&t.role===`user`&&Ke.test(String(t.message||``)))typeof window<`u`&&(window.__fe_user_nav_during_speech=null),be(!0),he.current=!1,pe.current=0,xe.current=-1,_e.current=b_(),ve.current=typeof window<`u`?window.location.pathname:null,setTimeout(()=>M.current?.(),400),console.log(`[FlowEngage] walkthrough armed — ${_e.current.length} sections on this page`);else if(!n&&t.role===`user`&&me.current){let e=String(t.message||``).trim(),n=Ze(e),r=e=>!!e&&(Je.test(e)||Xe.test(e));Ye.test(ye.current||``)?(console.log(`[FlowEngage] walkthrough ended — visitor accepted a navigation offer`),ve.current=null,_e.current=[],be(!1)):!n||r(e)||r(n)?he.current&&(he.current=!1,console.debug(`[FlowEngage] walkthrough resumed by visitor`),setTimeout(()=>M.current?.(),300)):(console.log(`[FlowEngage] walkthrough ended — visitor moved to a new topic`),ve.current=null,_e.current=[],be(!1))}if(t.role!==`user`&&!n&&(ye.current=String(t.message||``)),t.role!==`user`&&qe.test(String(t.message||``))){let e=_e.current.length;!e||xe.current>=e-1?be(!1):console.debug(`[FlowEngage] agent claimed completion at step ${xe.current+1}/${e} — ignoring, tour continues`)}let a=t.role===`user`?`user`:`bot`,o=t.message||t.text||t.content||``;if(o){if(a===`user`&&(Ce.current=o,typeof window<`u`&&(window.__fe_user_nav_during_speech=null),He.current=!0,Ve.current&&=(clearTimeout(Ve.current),null),Be.current!==`speaking`&&(C(`thinking`),Be.current=`thinking`),$e&&!Oe&&!A.current&&!ge.current&&(Oe=!0,j.current=o,fe.current=!0,N.current?.injectAgentSpeech?.(`Before I answer that, could you please enter your email in the text box below?`,`interrupt`),N.current?.setMicMuted?.(!0),se.current||(se.current=!0,de.current=0,re(!0),Qe()))),s.current=[...s.current,{sender:a,message:o,timestamp:new Date().toISOString(),debug:t.debug}],i)try{let e=Qg(s.current);e&&Ee(i,e)}catch{}if(A_(a,o)){P.current=!0;return}if(ke.current&&a===`bot`&&/are you still there|still there\??|hello\?|anyone there|checking in|you there|are you available|is anyone|did you step away/i.test(o))if(ze.current+=1,ze.current===2)try{N.current?.sendContextualUpdate?.(`IMPORTANT: The visitor is muted and has not responded to your last 2 checks. Make exactly ONE more check. If there is still no response, say: 'I feel like you are not active, so I'm ending this session. You can connect with me anytime.' then immediately call the fwh-end-session-mvp tool to end the voice session. Do NOT ask again after that.`)}catch{}else ze.current>=5&&We(`minimized`)}},onError:t=>{if(!e&&!Me.current&&!je.current&&!Fe()){je.current=!0,console.error(`[FlowEngageVoiceCall] session:error`,{message:t?.message,name:t?.name,raw:t});try{N.current?.setMicMuted?.(!0)}catch{}T(!0),C(`error`),We(`chat`)}},onDisconnect:t=>{if(e||Me.current||je.current||Fe())return;let n=s.current?.length||0;console.warn(`[FlowEngageVoiceCall] session:disconnected`,{historyCount:n,reason:t?.reason,closeCode:t?.context?.code,closeReason:t?.context?.reason||t?.message,lastUserText:Ce.current||``,wasMuted:ke.current}),je.current=!0;try{N.current?.setMicMuted?.(!0)}catch{}T(!0),We(`chat`)}});if(e){console.warn(`[FlowEngage:VoiceDebug] ❌ cancelled=true after startSession resolved — killing session immediately`),F.endSession().catch(()=>{});return}d=F,N.current=F,C(`listening`);try{sessionStorage.setItem(`el_conv_${i}`,F.getId())}catch{}parent.postMessage({type:`AGENT_EXPAND`},`*`);try{let e=typeof window<`u`?window.location.href:``,a=`[]`;try{a=await t[`fwh-list-forms-mvp`]()}catch{}let o=`none`;if(typeof window<`u`&&typeof document<`u`)try{let e=`#flowengage-root, .flowengage-shell, .flowengage-widget-root`,t=document.querySelector(`main, [role='main']`)||document.body,n=Array.from(t.querySelectorAll(`h1, h2, h3`)).filter(t=>!t.closest(e)).map(e=>e.textContent.replace(/\s+/g,` `).trim()).filter(e=>e.length>2&&e.length<200);n.length||(n=Array.from(document.body.querySelectorAll(`h1, h2, h3`)).filter(t=>!t.closest(e)).map(e=>e.textContent.replace(/\s+/g,` `).trim()).filter(e=>e.length>2&&e.length<200)),n.length&&(o=[...new Set(n)].slice(0,15).join(` | `))}catch{}let s=r&&!v?`RESUMING PRIOR SESSION — DO NOT GREET. The visitor is continuing the same conversation after a page reload. Do not introduce yourself or re-state your capabilities. Wait silently until the visitor speaks; if you must speak first, briefly acknowledge where you left off in one short sentence.\nPrior transcript:\n${n?.transcript||``}\n\n`:``;F.sendContextualUpdate(`${s}SiteId: ${i||`unknown`}. ${h?``:`Visitor's email is already on file from a prior session — do NOT ask for it again. `}WALKTHROUGH RULE: When the visitor asks for a walkthrough or overview of this page, you MUST IMMEDIATELY call fwh-get-current-page-mvp (zero speech — call it silently with NO words before it) to get the exact highlightable headings. Do NOT skip fwh-get-current-page-mvp. Voice: call end_voice_session or say you are ending the session to stop.`),F.sendContextualUpdate(C_(e,o,a))}catch{}if(typeof window<`u`){let t=window.location.pathname,n=Date.now(),r=e=>{try{return(decodeURIComponent(e||`/`).replace(/\/+$/,``)||`/`).toLowerCase()}catch{return((e||`/`).replace(/\/+$/,``)||`/`).toLowerCase()}},a=()=>{if(e)return;let a=window.location.pathname;if(r(a)===r(t)){t=a;return}if(Date.now()-n<8e3){t=a;return}let o=typeof window<`u`&&window.__fe_agent_nav||null,s=ne&&r(a)===r(ne)||Jn()||o&&Date.now()-o.ts<15e3&&r(o.path)===r(a),c=t;t=a;let l=window.location.href,u=typeof document<`u`?document.title:``,d=typeof document<`u`?Array.from(document.querySelectorAll(`h1, h2, h3, h4`)).filter(e=>!e.closest(`#flowengage-root, .flowengage-shell`)).map(e=>({tag:e.tagName,text:e.textContent.trim()})).filter(e=>e.text.length>0&&e.text.length<200).slice(0,20):[];try{In()}catch{}let f=d.map(e=>e.text).join(` | `),p=e=>(e.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase())||`Home`,m=p(c),h=p(a);if(s){if(typeof window<`u`&&(window.__fe_agent_nav=null),console.log(`[FlowEngage] agent-driven navigation settled: "${c}" → "${a}"`),le(i)!==`auto`)try{N.current?.sendUserMessage?.(`[SYSTEM: YOU just navigated the visitor to the "${h}" page (${l}) at their request — this was YOUR action, not theirs. Page title: "${u}". Sections: ${JSON.stringify(d)}. Say: "Here we are on the ${h} page." Then introduce this page in 2-3 sentences, and finish by asking: "Would you like me to walk you through it section by section?" NEVER say "I see you've navigated" — the visitor did not navigate, you did. Do NOT call fwh-navigate-mvp again. Wait for their answer before describing sections.]`,{suppressQuestion:!0})}catch{}else console.log(`[FlowEngage] auto-nav — skipping the arrival acknowledgment turn`);try{N.current?.sendContextualUpdate?.(C_(l,f))}catch{}return}typeof window<`u`&&(window.__fe_user_nav_during_speech={fromPath:c,toPath:a,fromPageName:m,toPageName:h,ts:Date.now()}),console.log(`[FlowEngage] visitor navigation detected: "${c}" → "${a}" (agent target: ${ne||`none`})`);try{N.current?.sendUserMessage?.(`[SYSTEM: I navigated away mid-walkthrough — I am now on the "${h}" page (${l}). Page title: "${u}". Sections: ${JSON.stringify(d)}. STOP everything. Do NOT describe the new page. Say EXACTLY: "I see you've navigated to the ${h} page — would you like me to continue what I was explaining, or shall I walk you through this page?" Then wait for my answer. IF I say continue/yes/carry on: resume speaking verbally from where you left off — do NOT call fwh-navigate-mvp, do NOT call fwh-highlight-mvp, the visitor will navigate themselves if needed. IF I want to hear about this page: call fwh-highlight-mvp for sections of "${h}" only.]`)}catch{}try{N.current?.sendContextualUpdate?.(C_(l,f))}catch{}};m=window.history.pushState.bind(window.history),window.history.pushState=(...e)=>{m(...e),setTimeout(a,50)},_=()=>setTimeout(a,50),window.addEventListener(`popstate`,_)}}catch(r){e||(console.error(`[FlowEngage] startSession failed`,r),r.message&&r.message.includes(`Too many voice sessions`)?(alert(r.message),n&&n(`minimized`)):(C(`error`),Ne.current=`chat`,window.setTimeout(()=>{e||t?.()},0)))}})(),()=>{console.warn(`[FlowEngage:VoiceDebug] ⚠️ useEffect CLEANUP fired — session will be killed`,{agentId:r,siteId:i,apiBaseUrl:a}),e=!0,Me.current=!0;try{In()}catch{}if(i)try{Re(i)}catch{}typeof window<`u`&&(window.__fe_user_nav_during_speech=null),Lr(),Ve.current&&=(clearTimeout(Ve.current),null),Ue.current&&=(clearTimeout(Ue.current),null),ce.current&&=(clearTimeout(ce.current),null),He.current=!1,Be.current=`idle`,m&&typeof window<`u`&&(window.history.pushState=m),_&&typeof window<`u`&&window.removeEventListener(`popstate`,_);let t=d||N.current,n=Pe.current,l=Ie.current;if(t?.endSession?.().catch(()=>{}),n&&o&&a){let e=l?Math.round((Date.now()-l)/1e3):0,t=JSON.stringify({chatId:o,voiceSessionId:n,duration:e,siteId:i}),r=`${a}/api/voice/save-chat-recording`;typeof navigator<`u`&&navigator.sendBeacon&&navigator.sendBeacon(r,new Blob([t],{type:`application/json`}))||fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`},body:t,keepalive:!0}).catch(()=>{}),Pe.current=null,Ie.current=null}typeof c==`function`&&s.current.length>0&&c(s.current).catch?.(()=>{})}},[r,i,a,We]),(0,b.useEffect)(()=>{if(!E)return;let e=e=>{let t=e.composedPath?.()?.[0]||e.target;Le.current&&!Le.current.contains(t)&&ee(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[E]);let F=async()=>{if(N.current)try{if(w){ze.current=0,await N.current.setMicMuted?.(!1),T(!1);try{N.current.sendContextualUpdate?.(`The visitor has unmuted. Resume normal interaction and wait for them to speak.`)}catch{}}else{ze.current=0,await N.current.setMicMuted?.(!0),T(!0);try{N.current.sendContextualUpdate?.(`The visitor has muted their microphone. Do NOT speak, ask if they are there, or prompt them in any way. Stay completely silent until they unmute.`)}catch{}}}catch{}},I=(0,b.useCallback)(async e=>{e?.preventDefault?.();let t=ie.trim().toLowerCase();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)){k(`Please enter a valid email address.`);return}k(``),A.current=!0,se.current=!1,Ge(),re(!1),ae(``),fe.current=!1;try{typeof m==`function`&&await m(t)}catch{}N.current?.setMicMuted?.(!1);let n=j.current;j.current=``;try{N.current?.injectAgentSpeech?.(`Thank you for submitting your email.`),N.current?.sendUserMessage?.(n||`The visitor's email is on file. Carry on helping them and never ask for it again.`)}catch{}},[ie,m,Ge]),L=Array.isArray(e)&&e[0]||`#3B82F6`;return(0,V.jsxs)(`div`,{ref:Le,style:{position:`relative`,display:`flex`,flexDirection:`column`,alignItems:`center`,gap:`14px`,animation:`fe-orb-slide-up 0.42s cubic-bezier(0.16,1,0.3,1)`},children:[ne&&(0,V.jsxs)(`form`,{onSubmit:I,style:{width:`270px`,maxWidth:`calc(100vw - 40px)`,boxSizing:`border-box`,background:`rgba(15,22,36,0.97)`,backdropFilter:`blur(14px)`,WebkitBackdropFilter:`blur(14px)`,borderRadius:`16px`,boxShadow:`0 10px 40px rgba(0,0,0,0.45)`,padding:`16px`,display:`flex`,flexDirection:`column`,gap:`10px`,animation:`fe-orb-fade-in 0.18s ease`},children:[(0,V.jsx)(`span`,{style:{color:`white`,fontSize:`13px`,fontWeight:600,lineHeight:1.4},children:`Please enter your email to continue`}),(0,V.jsx)(`input`,{type:`email`,inputMode:`email`,autoComplete:`email`,autoFocus:!0,value:ie,onChange:e=>{ae(e.target.value),oe&&k(``)},placeholder:`you@example.com`,style:{width:`100%`,boxSizing:`border-box`,padding:`10px 12px`,borderRadius:`10px`,border:oe?`1.5px solid #ef4444`:`1.5px solid #4b5563`,background:`#1f2937`,color:`white`,fontSize:`14px`,outline:`none`}}),oe&&(0,V.jsx)(`span`,{style:{color:`#f87171`,fontSize:`12px`},children:oe}),(0,V.jsx)(`button`,{type:`submit`,disabled:!ie.trim(),style:{width:`100%`,padding:`10px 12px`,borderRadius:`10px`,border:`none`,background:ie.trim()?L:`#374151`,color:`white`,fontSize:`14px`,fontWeight:600,cursor:ie.trim()?`pointer`:`default`,transition:`background 0.2s ease`},children:`Submit`})]}),(0,V.jsxs)(`div`,{style:{position:`relative`},children:[(S===`listening`||S===`thinking`)&&!w&&(0,V.jsx)(`span`,{style:{position:`absolute`,right:`100%`,marginRight:`18px`,top:`50%`,transform:`translateY(-50%)`,color:`white`,fontSize:`18px`,fontWeight:600,letterSpacing:`0.01em`,textShadow:`0 2px 4px rgba(0,0,0,0.4)`,whiteSpace:`nowrap`},children:S===`thinking`?v:_}),w&&(0,V.jsx)(`span`,{style:{position:`absolute`,right:`100%`,marginRight:`18px`,top:`50%`,transform:`translateY(-50%)`,color:`#f87171`,fontSize:`16px`,fontWeight:600,letterSpacing:`0.01em`,textShadow:`0 2px 4px rgba(0,0,0,0.4)`,whiteSpace:`nowrap`},children:`Muted`}),(0,V.jsx)(j_,{size:72,voiceState:S,isMuted:w,orbColors:e})]}),S===`error`&&(0,V.jsx)(`span`,{style:{color:`#f87171`,fontSize:`13px`,fontWeight:500},children:`Could not start voice session.`}),(0,V.jsxs)(`div`,{style:{display:`flex`,gap:`12px`,alignItems:`center`},children:[(0,V.jsx)(`button`,{onClick:F,title:w?`Unmute`:`Mute`,style:{width:`24px`,height:`24px`,borderRadius:`50%`,background:w?`#7f1d1d`:`#1f2937`,border:w?`1.5px solid #ef4444`:`1.5px solid #4b5563`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:w?`#f87171`:`white`,transition:`all 0.2s ease`,boxShadow:`0 2px 8px rgba(0,0,0,0.4)`,padding:0,flexShrink:0},children:w?(0,V.jsxs)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,V.jsx)(`line`,{x1:`1`,y1:`1`,x2:`23`,y2:`23`}),(0,V.jsx)(`path`,{d:`M9 9v3a3 3 0 005.12 2.12M15 9.34V4a3 3 0 00-5.94-.6`}),(0,V.jsx)(`path`,{d:`M17 16.95A7 7 0 015 12v-2m14 0v2a7 7 0 01-.11 1.23`}),(0,V.jsx)(`line`,{x1:`12`,y1:`19`,x2:`12`,y2:`23`}),(0,V.jsx)(`line`,{x1:`8`,y1:`23`,x2:`16`,y2:`23`})]}):(0,V.jsx)(tg,{size:16,color:`currentColor`})}),(0,V.jsx)(`button`,{onClick:()=>ee(e=>!e),style:{width:`24px`,height:`24px`,borderRadius:`50%`,background:`#1f2937`,border:`1.5px solid #4b5563`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:`white`,transition:`background 0.2s ease`,boxShadow:`0 2px 8px rgba(0,0,0,0.4)`,padding:0,flexShrink:0},children:(0,V.jsx)(lg,{size:16,color:`currentColor`})})]}),E&&(0,V.jsxs)(`div`,{style:{position:`absolute`,bottom:0,right:0,width:`230px`,zIndex:9999,background:`rgba(15,22,36,0.96)`,backdropFilter:`blur(14px)`,WebkitBackdropFilter:`blur(14px)`,borderRadius:`16px`,boxShadow:`0 10px 40px rgba(0,0,0,0.45)`,animation:`fe-orb-fade-in 0.18s ease`,padding:`12px 12px 40px`},children:[(0,V.jsxs)(`button`,{onClick:()=>{ee(!1),Ne.current=`chat`,$e(t)},style:{display:`flex`,alignItems:`center`,gap:`12px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`white`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`,whiteSpace:`nowrap`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.08)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,V.jsx)(ug,{}),y]}),i&&(0,V.jsxs)(`button`,{onClick:()=>O(),style:{display:`flex`,alignItems:`center`,justifyContent:`space-between`,gap:`8px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`white`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.08)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,V.jsxs)(`span`,{style:{display:`flex`,alignItems:`center`,gap:`12px`,minWidth:0,overflow:`hidden`},children:[(0,V.jsx)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,style:{flexShrink:0},children:(0,V.jsx)(`polygon`,{points:`3 11 22 2 13 21 11 13 3 11`})}),(0,V.jsx)(`span`,{style:{overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:`Auto-navigate`})]}),(0,V.jsx)(`span`,{"aria-hidden":!0,style:{width:`34px`,height:`19px`,borderRadius:`999px`,background:D?`#22c55e`:`rgba(255,255,255,0.2)`,position:`relative`,flexShrink:0,transition:`background 0.15s ease`},children:(0,V.jsx)(`span`,{style:{position:`absolute`,top:`2px`,left:D?`17px`:`2px`,width:`15px`,height:`15px`,borderRadius:`50%`,background:`white`,transition:`left 0.15s ease`}})})]}),(0,V.jsxs)(`button`,{onClick:()=>{ee(!1),Ne.current=`minimized`,$e(n)},style:{display:`flex`,alignItems:`center`,gap:`12px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`#E30C12`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`,whiteSpace:`nowrap`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(227,12,18,0.1)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,V.jsx)(dg,{}),x]}),(0,V.jsx)(`button`,{onClick:()=>ee(!1),style:{position:`absolute`,bottom:`8px`,right:`8px`,width:`24px`,height:`24px`,borderRadius:`50%`,background:`rgba(255,255,255,0.15)`,border:`none`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:`white`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.25)`},onMouseLeave:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.15)`},children:(0,V.jsxs)(`svg`,{width:`12`,height:`12`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,V.jsx)(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),(0,V.jsx)(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})]})})]})]})}function F_({orbColors:e}){let t=Array.isArray(e)&&e.length>0?e:[`#135bd8`,`#ef334b`,`#ffbd2e`],n=t[0],r=t[1]||t[0],i=t[2]||t[0];return(0,V.jsx)(`div`,{style:{width:`32px`,height:`32px`,borderRadius:`50%`,flexShrink:0,background:`
|
|
431
431
|
radial-gradient(circle at 18% 50%, ${n} 0%, ${n}99 35%, transparent 65%),
|
|
432
432
|
radial-gradient(circle at 82% 50%, ${r} 0%, ${r}99 35%, transparent 65%),
|
|
433
433
|
radial-gradient(circle at 50% 82%, ${i} 0%, ${i}99 30%, transparent 58%),
|
|
@@ -419,7 +419,7 @@
|
|
|
419
419
|
`).trim()}function p_(e){let t=e.siteId||null,n=(e.apiBaseUrl||``).replace(/\/$/,``),r=typeof e.getLastUserVoiceText==`function`?e.getLastUserVoiceText:null,i=typeof e.beaconSyncFullVoiceHistory==`function`?e.beaconSyncFullVoiceHistory:null,a=[],o={"fwh-navigate-mvp":async n=>{if($(`fwh-navigate-mvp ▶ raw parameters:`,n),e?.isWalkthroughActive?.())return $(`fwh-navigate-mvp ✖ blocked — walkthrough in progress`),`NAVIGATION_BLOCKED_DURING_WALKTHROUGH. A walkthrough of the current page is in progress, so navigation is not allowed. Do NOT tell the visitor you navigated or offer to. Describe the requested section using the CURRENT page's own content (fwh-read-content-mvp), then stop.`;let a=c_(n),o=l_(a),s=a.section_text??a.sectionText??a.section,c=Array.isArray(a.expected_landmarks)?a.expected_landmarks:Array.isArray(a.expectedLandmarks)?a.expectedLandmarks:[];if($(`fwh-navigate-mvp · coerced:`,{url:o,sectionText:s,expectedLandmarks:c}),!o){let e=`Missing url: pass a string url, path like /pricing, or JSON with a url field.`;return $(`fwh-navigate-mvp ✖`,e),e}if(typeof window<`u`)try{let e=new URL(o,window.location.href);if(e.hostname===window.location.hostname&&e.origin!==window.location.origin)o=e.pathname+e.search+e.hash,$(`fwh-navigate-mvp · cross-protocol same-host URL normalized →`,o);else if(e.origin!==window.location.origin&&/^https?:$/i.test(e.protocol)){let t=e.hostname;return $(`fwh-navigate-mvp ✖ external URL blocked:`,e.href),`EXTERNAL_LINK_BLOCKED: The URL "${o}" points to an external site (${t}) that opens in a new tab. I cannot open new tabs or navigate to external sites. Tell the visitor exactly this: "That link opens an external app — I'm not able to open it for you, but you can click the link directly in the navigation header."`}}catch{}if(typeof window<`u`){try{let e=new URL(o,window.location.href);if(e.pathname===window.location.pathname&&e.search===window.location.search)return $(`fwh-navigate-mvp ✖ already on page, skipping`),`OK_ALREADY_ON_PAGE. The visitor is already on this page. Do not navigate. Proceed directly with highlights, answering the question, or reading page content.`}catch{}let e=window.__fe_user_nav_during_speech;if(e&&Date.now()-e.ts<3e4)try{if(new URL(o,window.location.href).pathname===e.fromPath)return $(`flowengage_navigate ✖ blocked — would navigate back to page visitor just left`,e),`NAV_BLOCKED: The visitor just left "${e.fromPageName}" and is now on "${e.toPageName}". Do NOT navigate them back. If they said "continue", resume verbally from where you left off — no navigation needed. If they want to return, they will navigate themselves.`}catch{}}let{allowed:l,correctedUrl:u}=await or(o,{siteId:t,apiBaseUrl:e.apiBaseUrl});if(!l){let e=`PAGE_NOT_IN_SITE: "${o}" is NOT a real page on this site — do NOT guess or invent URL paths. Do NOT tell the visitor you are taking them there. Instead, find the REAL page: look at your Popular Pages / Site Pages list for the closest actual match, or call fwh-list-nav-links-mvp (to read the site's real menu links) or fwh-search-mvp (to find the right page). If a close match exists (e.g. the visitor said "support" and the site has "Technical Support"), tell them the ACTUAL page name and ask "we don't have a page called that, but we do have [real page] — would you like me to take you there?" Then navigate only to that verified URL.`;return $(`fwh-navigate-mvp ✖ blocked`,e),e}u&&u!==o&&($(`fwh-navigate-mvp · corrected url:`,{requested:o,correctedUrl:u}),o=u);try{xh(`Navigating to ${o}...`,{spinner:!0}),zh(),xn();try{i?.()}catch{}try{let e=r?r():``;if(e&&t){let n=qg([{sender:`user`,message:e}]);n&&_e(t,n)}}catch{}F();let e=o;try{e=new URL(o,typeof window<`u`?window.location.href:``).href}catch{}let n=`this`;try{let e=new URL(o,window.location.href).pathname.split(`/`).filter(Boolean).pop()||``;n=e?e.replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()):`Home`}catch{}t&&typeof window<`u`&&Te(t,window.location.href,e,n),lr(e);try{window.__fe_agent_nav={path:new URL(e,window.location.href).pathname,ts:Date.now()}}catch{}$(`fwh-navigate-mvp → real navigation`,o);let a=typeof window<`u`&&!window.Shopify&&!!Bn();if(typeof window<`u`&&window.__fe_triggerVoiceNavHooks&&window.__fe_triggerVoiceNavHooks(),a&&jn(12e3),_r(o,{voiceActive:!0}).catch(()=>{}),a)try{let e=new URL(o,window.location.origin).pathname,t=window.location.pathname,n=Date.now()+2e3;for(;Date.now()<n&&!(typeof window<`u`&&window.location.pathname===e);)await new Promise(e=>setTimeout(e,80));let r=typeof window<`u`?window.location.pathname:e,i=typeof window<`u`?window.location.href:o;(r.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()),$(`fwh-navigate-mvp → SPA nav complete. target:`,e,`actual:`,r);try{typeof window<`u`&&window.scrollTo(0,0)}catch{}let a=[],s=null,c=``,l=Date.now()+12e3;for(;Date.now()<l&&(await new Promise(e=>setTimeout(e,300)),{headings:a,pageTitleHeading:s,introText:c}=d_(),!(a.length>=1)););u_(),$(`fwh-navigate-mvp → page headings collected:`,a,`titleHeading:`,s);let u=a.length?a.join(` | `):`none`,d=a.length?`Use ONLY these exact heading strings for fwh-highlight-mvp — never invent or abbreviate.`:`No headings collected yet — call fwh-get-current-page-mvp immediately (no speech) to get the live headings before speaking or highlighting.`,f=s?` "${s}" is the PAGE TITLE, not a content section — do not fwh-highlight-mvp it separately; if doing a walkthrough, just fold this intro into your opening line: "${c||`(no intro text found)`}"`:c?` No separate page-title heading detected, but this intro text precedes the first section — mention it briefly before covering sections: "${c}"`:``,p=``;try{if(a.length>0){let e=a.slice(0,3),t=[];for(let n of e){let e=tr(null,n);if(e){let r=f_(e).substring(0,800);r&&t.push(`[Section: ${n}] ${r}`)}}t.length>0&&(p=` CONTENT FOR FIRST FEW SECTIONS: ${t.join(` | `)}. Use this content to answer immediately if the visitor asks about one of these sections.`)}}catch{}if(fr())return ur(),$(`fwh-navigate-mvp ✖ landed on a not-found/404 page:`,i),`NAVIGATION_PAGE_NOT_FOUND. The page at ${i} does not exist — it shows a "not found"/404 error, not real content. Do NOT offer a page overview, do NOT describe or highlight any sections, do NOT claim the navigation succeeded. Tell the visitor EXACTLY: "It looks like that page doesn't exist — I couldn't find it. Would you like me to take you somewhere else?" Then STOP and wait for their reply.`;let m=Date.now()+4e3,h=typeof window<`u`?window.location.pathname:r;for(;Date.now()<m&&!Xg(h,e)&&Xg(h,t);)await new Promise(e=>setTimeout(e,120)),h=window.location.pathname;$(`fwh-navigate-mvp route settle:`,{startPath:t,targetPath:e,settledPath:h});let g=typeof window<`u`?window.location.href:i,_=(h.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase())||`Home`,v=Xg(h,e),y=!Xg(h,t),b=v||y;return $(`fwh-navigate-mvp settled:`,h,`target:`,e,`match:`,b),b?(ur(),`OK_NAVIGATE_SPA. Visitor is now on: ${g} (page: "${_}") | Section headings: ${u}.${f} ${d}${p} Now confirm the arrival in ONE short sentence phrased for what the visitor actually asked for — mention the page by name and what it lets them do (e.g. for a contact request: "I've taken you to the Contact Us page — filling in this form sends your query straight to our team."). Do NOT use a fixed template. Then, in the same breath, offer either a specific section or a full overview. Then STOP — wait for their reply.`):(ur(),`NAVIGATION_DID_NOT_HAPPEN. The visitor is STILL on ${g} (page: "${_}") — the navigation did not go through. Do NOT say "I have navigated you". Do NOT claim success. Tell the visitor EXACTLY: "Sorry, I couldn't open that page just now. Would you like me to try again, or is there something else I can help with?" Then STOP and wait for their reply.`)}catch{}return`OK_NAVIGATE_RELOADING. The page is reloading now and this session is ending. Say NOTHING further — do not confirm the navigation, do not describe the page. The arrival greeting is spoken automatically on the new page; you resume from the visitor's next reply.`}catch(e){let t=`Navigation error: ${e?.message||String(e)}`;return $(`fwh-navigate-mvp ✖`,t),xh(`Navigation failed`,{spinner:!1,icon:`⚠️`,duration:1800}),t}finally{Bh(),Sh(900)}},"fwh-highlight-mvp":async e=>{$(`fwh-highlight-mvp ▶`,e);let t=c_(e),n=t?.section_id||t?.sectionId,r=t?.section_text||t?.sectionText;if(jn(3e3),typeof window<`u`&&window.__fe_user_nav_during_speech){let e=window.__fe_user_nav_during_speech,t=window.location.pathname;if(Date.now()-e.ts>=3e4)window.__fe_user_nav_during_speech=null;else return $(`flowengage_highlight ✖ blocked — user navigated during speech`,e),`NAV_CHANGED: The visitor navigated from "${e.fromPageName}" to "${e.toPageName}" (current path: ${t}) while you were speaking. STOP the current walkthrough immediately. DO NOT call fwh-highlight-mvp again. Say out loud: "I see you've navigated to the ${e.toPageName} page — do you want me to continue what I was explaining, or shall I guide you through this page?" Wait for the visitor's answer before doing anything else.`}u_();let i=(e=>e&&e.replace(/^\d+\.\s*/,``).replace(/^["']|["']$/g,``).trim())(r),a=tr(n||null,i||null);if(!a&&i){let e=Date.now()+600;for(;!a&&Date.now()<e;)await new Promise(e=>setTimeout(e,50)),u_(),a=tr(null,i)}if(!a){$(`fwh-highlight-mvp ✖ no element for:`,i||n);let{headings:e}=d_(),t=e.length?`Page headings: ${e.map(e=>`"${e}"`).join(` | `)}. Use one of these EXACT strings for section_text — no numbers, no quotes around them.`:`No headings found — skip highlight and continue speaking.`;return`HIGHLIGHT_NOT_FOUND: "${i||n}" on the current page. ${t}`}return bn(a),$(`fwh-highlight-mvp ✓`,i||n),`OK_HIGHLIGHT: "${i||n}". Your NEXT response must be speech only (no tool call) describing THIS section — "${i||n}" — using its own content. Do not ask whether to continue.`},"fwh-read-content-mvp":async e=>{$(`fwh-read-content-mvp ▶`,e);let t=c_(e),n=t?.section_text||t?.sectionText||``;try{let e=``;if(n){let t=tr(null,n);if(!t)return`Could not find section "${n}" on the page.`;e=f_(t)}else{let t=(document.querySelector(`main`)||document.querySelector(`article`)||document.querySelector(`[role="main"]`)||document.querySelector(`#content`)||document.querySelector(`.content`)||document.body).cloneNode(!0);t.querySelectorAll(`nav, footer, header, aside, script, style, noscript, .nav, .footer, .header, .sidebar, #flowengage-root, .flowengage-shell, .flowengage-widget-root`).forEach(e=>e.remove()),e=t.textContent.replace(/\s+/g,` `).trim()}return e.length>8e3&&(e=e.substring(0,8e3)+`... (truncated)`),$(`fwh-read-content-mvp ✓`,`${e.length} chars`),e||`No readable content found on this page.`}catch(e){return $(`fwh-read-content-mvp ✖`,e.message),`Could not read page content: ${e.message}`}},"fwh-page-content-mvp":async e=>{$(`fwh-page-content-mvp ▶`,e);let r=c_(e),i=r?.url||r?.page_url||``;if($(`fwh-page-content-mvp · url:`,i,`siteId:`,t,`apiBase:`,n||`(empty)`),!i)return $(`fwh-page-content-mvp ✖ missing url`),`Missing url parameter.`;if(!t)return $(`fwh-page-content-mvp ✖ no siteId`),`Site is not configured.`;if(!n)return $(`fwh-page-content-mvp ✖ no api base`),`API base URL is missing; cannot fetch page content.`;try{let e=`${n}/api/site-tools/page-content`;$(`fwh-page-content-mvp → POST`,e);let r=await fetch(e,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({siteId:t,url:i})}),a=await r.json();if($(`fwh-page-content-mvp · HTTP`,r.status,`success:`,a.success),!a.success){let e=a.error||`Failed to get page content.`;return $(`fwh-page-content-mvp ✖`,e),`${e} FALLBACK REQUIRED: This page is not in the search index. Call fwh-read-content-mvp (no parameters) immediately to read the live page content directly from the visitor's browser — do NOT tell the visitor you cannot access the page.`}return $(`fwh-page-content-mvp ✓`,`title: ${a.title}`,`sections: ${(a.sections||[]).length}`,`content: ${(a.content||``).length} chars`),JSON.stringify({title:a.title,url:a.url,sections:a.sections,content:a.content})}catch(e){let t=`Page content error: ${e.message||`network`}`;return $(`fwh-page-content-mvp ✖`,t),t}},"fwh-search-mvp":async e=>{$(`fwh-search-mvp ▶`,e);let r=c_(e).query;if(!r)return $(`fwh-search-mvp ✖ missing query`),`Missing query parameter.`;if(!t)return $(`fwh-search-mvp ✖ no siteId`),`Search unavailable (no siteId configured).`;if(!n)return $(`fwh-search-mvp ✖ no api base`),`Search unavailable (no API base URL).`;try{let e=`${n}/api/site-tools/search`;$(`fwh-search-mvp → POST`,e);let i=await fetch(e,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({siteId:t,query:r,currentUrl:typeof window<`u`?window.location.href:``})}),a=await i.json();if($(`fwh-search-mvp · HTTP`,i.status,`success:`,a.success),!a.success||!a.results?.length)return`No results found for that query. Try rephrasing, or use fwh-read-content-mvp to read the current page.`;let o=a.results.slice(0,5).map((e,t)=>`${t+1}. ${e.title||e.url} (${e.url})${e.snippet?` — ${e.snippet}`:``}`).join(`
|
|
420
420
|
`);return $(`fwh-search-mvp ✓`,`${a.results.length} result(s)`),`SEARCH_RESULTS:\n${o}`}catch(e){return $(`fwh-search-mvp ✖`,e.message),`Search failed. Use fwh-read-content-mvp to read the current page instead.`}},"fwh-list-nav-links-mvp":async()=>{if($(`fwh-list-nav-links-mvp ▶`),typeof window>`u`)return`Cannot read navigation (no browser context).`;let e=new Map,t=[],n=[`nav a`,`[role='navigation'] a`,`header a`,`.navbar a`,`.nav a`,`.menu a`,`.navigation a`,`.header a`,`[class*='nav'] a`,`[class*='menu'] a`,`[class*='header'] a`];if(Qg(t=>{for(let r of n)try{t.querySelectorAll(r).forEach(t=>{if(Cn(t))return;let n=t.getAttribute(`href`),r=(t.textContent||``).replace(/\s+/g,` `).trim();if(!r||!n||n===`#`||n.startsWith(`javascript:`))return;try{let e=t.parentElement;if(e&&Zg(e).display===`none`&&!e.closest(`nav, [role='navigation'], header, [class*='nav'], [class*='menu']`))return}catch{}let i=r.toLowerCase()+`|`+n;e.has(i)||e.set(i,{text:r,href:n})})}catch{}t.querySelectorAll(`select`).forEach(t=>{if(Cn(t))return;let n=e_(t);/nav|menu|page|section|go to/i.test(n||t.name||t.id||``)&&Array.from(t.options).forEach(r=>{if(!r.value||r.disabled)return;let i=r.text.trim();if(!i)return;let a=i.toLowerCase()+`|`+r.value;e.has(a)||e.set(a,{text:i,href:r.value,isSelectOption:!0,selectLabel:n||t.name||`dropdown`})})})}),e.forEach(e=>t.push(e)),!t.length)return`NAV_LINKS: No navigation links found on this page. Use fwh-read-content-mvp to read page content or fwh-search-mvp to search the site.`;let r=t.slice(0,50).map((e,t)=>`${t+1}. "${e.text}"${e.isSelectOption?` (dropdown option in "${e.selectLabel}")`:``} → ${e.href}`).join(`
|
|
421
421
|
`);return $(`fwh-list-nav-links-mvp ✓`,`${t.length} links`),`NAV_LINKS (${t.length} total, showing up to 50):\n${r}\n\nIMPORTANT: Use the EXACT text shown above when reading link names to the visitor. Do NOT rename, abbreviate, or invent options not listed here. To navigate, call fwh-navigate-mvp with the href value.`},"fwh-end-session-mvp":async()=>($(`fwh-end-session-mvp ▶`),typeof e.requestEndSession==`function`&&e.requestEndSession(),`Voice session is ending.`),"fwh-request-email-mvp":async()=>($(`fwh-request-email-mvp ▶`),typeof e.requestEmailCollection==`function`&&e.requestEmailCollection(),`EMAIL_BOX_SHOWN. An email input text box is now visible to the visitor. Say EXACTLY: "Please enter your email in the text box to continue." Then stop and wait silently — do NOT ask them to say it aloud, do NOT repeat, and do NOT answer any other question until you receive the EMAIL_SAVED message.`),"fwh-get-current-page-mvp":async()=>{if($(`fwh-get-current-page-mvp ▶`),typeof window>`u`)return`Cannot determine current page (no browser context).`;let e=window.location.href,t=window.location.pathname,n=document.title||``;if(fr())return $(`fwh-get-current-page-mvp ✓ 404 detected`,{url:e}),`CURRENT_PAGE_NOT_FOUND. The visitor is currently on a 404 / page-not-found error page (${e}). There is no real content here. Do NOT describe this page, do NOT list sections, do NOT offer a walkthrough, and do NOT describe it from your own knowledge. Tell the visitor plainly that the page they are on doesn't exist, and offer to take them somewhere that does.`;let{headings:r,pageTitleHeading:i,introText:a}=d_();$(`fwh-get-current-page-mvp ✓`,{url:e,path:t,title:n}),$(`fwh-get-current-page-mvp → page headings collected:`,r,`titleHeading:`,i);let o=i?` NOTE: "${i}" is the PAGE TITLE, not a content section — do NOT fwh-highlight-mvp it or list it as a separate section in a walkthrough. Instead, open with a brief 1-2 sentence summary of this intro text before covering the real sections: "${a||`(no intro text captured)`}"`:a?` NOTE: no separate page-title heading was detected, but this intro text appears before the first section — open your walkthrough with a brief 1-2 sentence summary of it before covering sections: "${a}"`:``;if(!r.length)return`CURRENT_PAGE. URL: ${e} | Title: ${n} |${o} No structured section headings found on this page. If there is intro text above, describe it; otherwise describe the page from your knowledge. Do not call this tool again.`;let s=r.map(e=>`"${e}"`).join(` | `);return`CURRENT_PAGE. URL: ${e} | Title: ${n} | Page sections (${r.length} total): ${s}.${o} *** WALKTHROUGH: do NOT drive it yourself. Do not call fwh-highlight-mvp and do not decide the order — highlighting and section order are handled for you. You will be told which section to describe, one at a time. When told, describe ONLY that section in 3-4 sentences and then stop; never announce another section, never ask whether to continue. ***`},"fwh-list-forms-mvp":async()=>{let e=n_(),t=r_();$(`fwh-list-forms-mvp ✓`,`${e.length} field(s)`,`${t.length} button(s)`);let n=e.map(e=>({...e,inputCategory:e.tag===`SELECT`?`dropdown_select`:`text_input`}));return JSON.stringify({fields:n,buttons:t,hint:`field_key: use the label/placeholder/name shown above. CRITICAL: fields with inputCategory='dropdown_select' are SELECT elements — their field_key must match the SELECT's own label (e.g. 'Company Size'), NOT the option text (e.g. 'Small Business') and NOT a nearby text input label. Never fill a SELECT option value into a text input.`})},"fwh-fill-form-mvp":async()=>`FORM_FILLING_DISABLED: Form filling is not available at this time. Tell the visitor what fields are present (use fwh-list-forms-mvp) and ask them to fill the form themselves.`,"fwh-click-button-mvp":async e=>{let t=c_(e),n=(t.button_text??t.buttonText??t.text??``).trim(),r=(t.card_context??t.cardContext??``).trim();if($(`fwh-click-button-mvp ▶`,n,r?`(card_context: ${r})`:``),!n)return`Missing button_text: pass the visible label, e.g. Conduct Audit.`;let i=s_(n,a,r);if(!i)return $(`fwh-click-button-mvp ✖ no match`),`Could not find a button matching "${n}". Call fwh-list-forms-mvp to see button texts.`;try{xh(`Clicking ${n}...`,{spinner:!0});try{i.scrollIntoView({block:`center`,inline:`nearest`})}catch{i.scrollIntoView(!0)}await Gh(i),i.focus?.(),await qh(i),Jh(i),i.click();let e=(i.textContent||i.value||i.getAttribute(`aria-label`)||``).trim().slice(0,80)||`button`,t=`OK_CLICK ${e}`;return $(`fwh-click-button-mvp ✓`,t),xh(`Clicked ${e}`,{spinner:!1,icon:`✅`,duration:1200}),t}catch(e){let t=`Click failed: ${e?.message||String(e)}`;return $(`fwh-click-button-mvp ✖`,t),xh(`Click failed`,{spinner:!1,icon:`⚠️`,duration:1800}),t}finally{Kh(),Sh(700)}},"fwh-cart-get-mvp":async()=>{$(`fwh-cart-get-mvp ▶`);try{let e=await Ar();if(!e)return`CART_UNAVAILABLE: this page is not a Shopify storefront, so there is no cart to read.`;let t=e.items.map((e,t)=>{let n=e.variantTitle&&e.variantTitle!==`Default Title`?` (${e.variantTitle})`:``;return`${t+1}. ${e.title}${n} — qty ${e.quantity} — ${e.linePrice}${e.handle?` — handle: ${e.handle}`:``}`}),n=`CART. Items: ${e.itemCount} | Subtotal: ${e.subtotal}${e.currency?` ${e.currency}`:``}${t.length?` | ${t.join(` | `)}`:` | (cart is empty)`}`;return $(`fwh-cart-get-mvp ✓`,n),n}catch(e){return $(`fwh-cart-get-mvp ✖`,e),`CART_ERROR: ${e?.userMessage||e?.message||`could not read the cart`}`}},"fwh-shop-search-mvp":async e=>{let t=String((e||{}).query||``).trim();if($(`fwh-shop-search-mvp ▶`,t),!t)return`Missing query: pass the product name or keyword to search.`;try{let e=await Lr(t);if(!e.length)return`NO_PRODUCTS_FOUND for "${t}". Tell the visitor no matching product was found — do not invent products.`;let n=e.map(e=>`"${e.title}" — handle: ${e.handle}${e.price?` — price: ${e.price}`:``}`).join(` | `);return $(`fwh-shop-search-mvp ✓`,n),`PRODUCTS_FOUND: ${n}. Use the handle with fwh-cart-add-mvp or fwh-view-product-mvp.`}catch(e){return $(`fwh-shop-search-mvp ✖`,e),`SEARCH_ERROR: ${e?.message||`product search failed`}`}},"fwh-cart-add-mvp":async e=>{let t=e||{},n=String(t.product_handle||t.productHandle||``).trim().toLowerCase(),r=String(t.variant||``).trim()||null,i=Math.min(20,Math.max(1,parseInt(t.quantity,10)||1));if($(`fwh-cart-add-mvp ▶`,{handle:n,variantText:r,quantity:i}),!n)return`Missing product_handle: search the product first with fwh-shop-search-mvp.`;try{let{variant:e,product:t}=await jr(n,r);await Mr(e.id,i);let a=await Dr(),o=await s(a),c=e.title&&e.title!==`Default Title`?` (${e.title})`:``,l=`ADDED ${i} × ${t.title}${c}. Cart now has ${a.item_count} item(s), subtotal ${kr(a.items_subtotal_price)}${a.currency?` ${a.currency}`:``}.${o}`;return $(`fwh-cart-add-mvp ✓`,l),l}catch(e){return $(`fwh-cart-add-mvp ✖`,e),`ADD_FAILED: ${e?.userMessage||e?.message||`could not add the item`}. Relay this reason to the visitor.`}},"fwh-cart-update-mvp":async e=>{let t=e||{},n=parseInt(t.line,10),r=Math.min(20,Math.max(0,parseInt(t.quantity,10)));if($(`fwh-cart-update-mvp ▶`,{line:n,quantity:r}),!Number.isFinite(n)||n<1)return`Missing/invalid line: call fwh-cart-get-mvp first and use the item's 1-based number.`;if(!Number.isFinite(r))return`Missing/invalid quantity.`;try{let e=await Dr(),t=e.items?.[n-1];if(!t)return`NO_SUCH_LINE: the cart has ${e.items?.length||0} item(s). Call fwh-cart-get-mvp for the current list.`;let i=await Nr(n,r),a=await s(i),o=`UPDATED ${t.product_title||t.title} to qty ${r}. Cart now has ${i.item_count} item(s), subtotal ${kr(i.items_subtotal_price)}${i.currency?` ${i.currency}`:``}.${a}`;return $(`fwh-cart-update-mvp ✓`,o),o}catch(e){return $(`fwh-cart-update-mvp ✖`,e),`UPDATE_FAILED: ${e?.userMessage||e?.message||`could not update the item`}`}},"fwh-cart-remove-mvp":async e=>{let t=parseInt((e||{}).line,10);if($(`fwh-cart-remove-mvp ▶`,{line:t}),!Number.isFinite(t)||t<1)return`Missing/invalid line: call fwh-cart-get-mvp first and use the item's 1-based number.`;try{let e=await Dr(),n=e.items?.[t-1];if(!n)return`NO_SUCH_LINE: the cart has ${e.items?.length||0} item(s). Call fwh-cart-get-mvp for the current list.`;let r=await Nr(t,0),i=await s(r),a=`REMOVED ${n.product_title||n.title}. Cart now has ${r.item_count} item(s), subtotal ${kr(r.items_subtotal_price)}${r.currency?` ${r.currency}`:``}.${i}`;return $(`fwh-cart-remove-mvp ✓`,a),a}catch(e){return $(`fwh-cart-remove-mvp ✖`,e),`REMOVE_FAILED: ${e?.userMessage||e?.message||`could not remove the item`}`}},"fwh-cart-open-mvp":async()=>($(`fwh-cart-open-mvp ▶`),c(Pr(),`Cart`)),"fwh-checkout-mvp":async()=>{$(`fwh-checkout-mvp ▶`);try{if(!(await Dr()).item_count)return`CART_EMPTY: do not go to checkout. Tell the visitor their cart is empty and offer to help find products.`}catch{}return c(Fr(),`Checkout`)},"fwh-view-product-mvp":async e=>{let t=e||{},n=String(t.product_handle||t.productHandle||``).trim().toLowerCase();return $(`fwh-view-product-mvp ▶`,{handle:n}),n?c(Ir(n),n.replace(/[-_]/g,` `)):`Missing product_handle: search the product first with fwh-shop-search-mvp.`}};async function s(e){try{let{sectionsSwapped:t}=await Gr(e);if(!t&&Rr())return` NOTE: the cart list on this page could not be re-rendered — briefly tell the visitor the page may need a quick refresh to show the change.`}catch{}return``}function c(e,n){if(typeof window>`u`)return`NAV_FAILED: no browser context.`;let r=e;try{r=new URL(e,window.location.href).href}catch{}return t&&Te(t,window.location.href,r,n),lr(r),gr().then(()=>{window.location.assign(r)}),`OK_NAVIGATING to ${n}. The page is loading — say one short phrase like "Here you go." and nothing more.`}return{...o,highlightOnPage:o[`fwh-highlight-mvp`],getPageContent:o[`fwh-page-content-mvp`],endVoiceSession:o[`fwh-end-session-mvp`],listPageForms:o[`fwh-list-forms-mvp`],fillFormFields:o[`fwh-fill-form-mvp`],clickPageButton:o[`fwh-click-button-mvp`]}}var m_=/^(get in touch|contact us|let'?s (talk|connect)|subscribe|newsletter|follow us|quick links|company|solutions|what we do|resources|legal|sitemap|share this)/i;function h_(){try{let{headings:e,pageTitleHeading:t,containerHeadings:n}=d_(),r=new Set((n||[]).map(e=>e.trim())),i=(e||[]).map(e=>typeof e==`string`?e:e?.text||``).filter(Boolean).filter(e=>!t||e.trim()!==t.trim()).filter(e=>!m_.test(e.trim())),a=i.filter(e=>!r.has(e.trim()));return a.length?a:i}catch{return[]}}function g_(e){return!e||typeof e!=`object`?e:Object.fromEntries(Object.entries(e).map(([e,t])=>[e,async n=>{let r=_n();r?console.log(`[FlowEngage:voice-tool] ▶`,e,n):console.info(`[FlowEngage] voice tool » ${e}`);let i;try{i=await t(n)}catch(t){let n=`Tool error: ${t?.message||String(t)}`;return console.error(`[FlowEngage] voice tool ${e} threw:`,t),n}let a=typeof i==`string`?i.length>220?`${i.slice(0,220)}…`:i:String(i);return r?console.log(`[FlowEngage:voice-tool] ◀`,e,a):console.info(`[FlowEngage] voice tool « ${e}:`,a),i}]))}var __=`[PAGE_CONTEXT]`;function v_(e,t,n){return`${__} SESSION_CONTEXT (NOT a tool result — do NOT treat as CURRENT_PAGE): Visitor is on: ${e} | Approx headings for awareness only: ${t||``}. ${n?`Forms: ${n}. `:``}The visitor is ALREADY on this page — do NOT offer to navigate there.`}var y_={blue:`#135bd8`,red:`#ef334b`,yellow:`#ffbd2e`,dark:`#0d1b2a`,highlight:`rgba(255,255,255,0.9)`,green:`#10b981`};function b_(e){if(!Array.isArray(e)||e.length<2)return`linear-gradient(145deg, #dce8f022 0%, #e8dff522 45%, #f5e0e022 100%), #f7f8fa`;let[t,n,r]=e;return e.length>=3&&r?[`radial-gradient(ellipse 80% 70% at -10% 50%, ${t}1a 0%, transparent 70%)`,`radial-gradient(ellipse 70% 80% at 110% 30%, ${n}1a 0%, transparent 70%)`,`radial-gradient(ellipse 80% 60% at 50% 110%, ${r}1a 0%, transparent 70%)`,`#f7f8fa`].join(`, `):[`radial-gradient(ellipse 80% 100% at -10% 50%, ${t}1a 0%, transparent 70%)`,`radial-gradient(ellipse 80% 100% at 110% 50%, ${n}1a 0%, transparent 70%)`,`#f7f8fa`].join(`, `)}function x_(e){if(!e||typeof e!=`string`)return`#1a1a1a`;let t=e.replace(`#`,``),n=t.length===3?t.split(``).map(e=>e+e).join(``):t,r=parseInt(n.slice(0,2),16)/255,i=parseInt(n.slice(2,4),16)/255,a=parseInt(n.slice(4,6),16)/255,o=e=>e<=.04045?e/12.92:((e+.055)/1.055)**2.4;return .2126*o(r)+.7152*o(i)+.0722*o(a)>.179?`#1a1a1a`:`#ffffff`}function S_(e){let[t,n]=d.default.useState(null),r=typeof e==`string`?e.trim():``;return d.default.useEffect(()=>{if(!r){n(null);return}n(null);let e=!1,t=new window.Image;return t.onload=()=>{e||n(r)},t.onerror=()=>{e||n(null)},t.src=r,()=>{e=!0}},[r]),t}function C_({logo:e,style:t}){let n=S_(typeof e==`string`?e:null);if(!e)return null;if(typeof e==`string`)return n?(0,f.jsx)(`img`,{src:n,alt:``,style:{objectFit:`contain`,flexShrink:0,display:`block`,...t}}):null;let r=typeof e==`function`?d.default.createElement(e):d.default.isValidElement(e)?e:null;return r?(0,f.jsx)(`span`,{className:`fe-logo-slot`,style:{display:`inline-flex`,alignItems:`center`,flexShrink:0,overflow:`hidden`,...t},children:r}):null}function w_({title:e}){return(0,f.jsx)(`div`,{style:{width:`100%`,height:`100%`,borderRadius:`16px`,background:`linear-gradient(135deg, rgba(59,130,246,0.16) 0%, rgba(59,130,246,0.08) 100%)`,color:`#2563eb`,display:`flex`,alignItems:`center`,justifyContent:`center`,fontSize:`18px`,fontWeight:700,letterSpacing:`-0.03em`},children:String(e||`FE`).split(/\s+/).filter(Boolean).slice(0,2).map(e=>e[0]?.toUpperCase()||``).join(``)||`FE`})}function T_(e,t){let n=(t||``).trim();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)||e===`bot`&&(/\b(that'?s all|we'?re done)\b/i.test(n)||/\b(goodbye|bye)\b/i.test(n)||/\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))):!1}function E_({orbColors:e,...t}){return(0,f.jsx)(Rg,{colors:e,...t})}function D_({copy:e,orbColors:t,primaryColor:n,onYes:r,onLater:i,isMobile:a,isClosing:o}){let s=n||y_.green,c=x_(s),l=a?72:94,u=(0,f.jsxs)(`div`,{style:{display:`flex`,alignItems:a?`flex-end`:`center`,gap:a?`7px`:`9px`},children:[(0,f.jsxs)(`div`,{style:{flex:1,minWidth:0},children:[(0,f.jsx)(qm.p,{initial:{x:-16,opacity:0},animate:{x:0,opacity:1},transition:{type:`spring`,stiffness:260,damping:24,delay:.36},style:{margin:`0 0 14px`,fontSize:`14px`,fontWeight:700,color:`white`,lineHeight:1.4,textShadow:`0 1px 4px rgba(0,0,0,0.35)`,maxWidth:`210px`,wordBreak:`break-word`,overflow:`hidden`,display:`-webkit-box`,WebkitLineClamp:4,WebkitBoxOrient:`vertical`},children:e.entryPrompt}),(0,f.jsxs)(qm.div,{initial:{y:10,opacity:0},animate:{y:0,opacity:1},transition:{type:`spring`,stiffness:260,damping:24,delay:.54},style:{display:`flex`,gap:`16px`,flexWrap:`nowrap`},children:[(0,f.jsx)(`button`,{onClick:r,style:{height:`31px`,boxSizing:`border-box`,padding:`0 16px`,borderRadius:`999px`,background:s,color:c,fontWeight:400,fontSize:`14px`,border:`none`,cursor:`pointer`,whiteSpace:`nowrap`,boxShadow:`0 4px 16px ${s}66`,transition:`transform 0.15s ease`},onMouseEnter:e=>{e.currentTarget.style.transform=`scale(1.04)`},onMouseLeave:e=>{e.currentTarget.style.transform=`scale(1)`},children:e.yesLabel}),(0,f.jsx)(`button`,{onClick:i,style:{height:`31px`,boxSizing:`border-box`,padding:`0 16px`,borderRadius:`999px`,background:`transparent`,color:`white`,fontWeight:400,fontSize:`14px`,border:`2px solid rgba(255,255,255,0.55)`,cursor:`pointer`,whiteSpace:`nowrap`,transition:`border-color 0.15s ease`},onMouseEnter:e=>{e.currentTarget.style.borderColor=`rgba(255,255,255,0.9)`},onMouseLeave:e=>{e.currentTarget.style.borderColor=`rgba(255,255,255,0.55)`},children:e.laterLabel})]})]}),(0,f.jsx)(qm.div,{initial:{x:120,opacity:0},animate:{x:0,opacity:1},transition:{type:`spring`,stiffness:220,damping:22,delay:0},style:{flexShrink:0},children:(0,f.jsx)(E_,{size:l,orbColors:t})})]});return(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(qm.div,{initial:{opacity:0},animate:{opacity:o?0:1},transition:{duration:.28},style:{position:`fixed`,inset:0,background:`rgba(0,0,0,0.72)`,zIndex:-1}}),a?(0,f.jsx)(`div`,{style:{position:`absolute`,bottom:0,left:0,right:0,padding:`24px 20px 40px`,overflow:`hidden`,animation:o?`fe-orb-slide-down 0.28s cubic-bezier(0.16,1,0.3,1) forwards`:``},children:u}):(0,f.jsx)(`div`,{style:{animation:o?`fe-orb-slide-down 0.28s cubic-bezier(0.16,1,0.3,1) forwards`:``},children:u})]})}function O_({copy:e,orbColors:t,primaryColor:n,onVoice:r,onText:i,onClose:a,isMobile:o,isClosing:s}){let c=n||y_.green,l=x_(c);return(0,f.jsxs)(qm.div,{initial:{scale:50/350,borderRadius:`50%`},animate:s?{scale:50/350,opacity:0,borderRadius:`50%`}:{scale:1,borderRadius:`16px`,opacity:1},transition:{type:`spring`,stiffness:260,damping:26},style:{transformOrigin:o?`bottom center`:`bottom right`,background:`white`,width:o?`100%`:`350px`,height:`245px`,padding:`24px 40px 32px`,boxSizing:`border-box`,display:`flex`,flexDirection:`column`,alignItems:`center`,justifyContent:`space-between`,boxShadow:`0 20px 60px rgba(0,0,0,0.2)`,position:`relative`,overflow:`hidden`},children:[(0,f.jsx)(`button`,{onClick:a,style:{position:`absolute`,top:`14px`,right:`14px`,background:`none`,border:`none`,cursor:`pointer`,color:`#9ca3af`,padding:`4px`,display:`flex`,alignItems:`center`,justifyContent:`center`,borderRadius:`50%`,transition:`color 0.15s ease`},onMouseEnter:e=>{e.currentTarget.style.color=`#374151`},onMouseLeave:e=>{e.currentTarget.style.color=`#9ca3af`},children:(0,f.jsx)(`svg`,{width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2.2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:(0,f.jsx)(`polyline`,{points:`6 9 12 15 18 9`})})}),(0,f.jsx)(E_,{size:72,orbColors:t}),(0,f.jsxs)(`div`,{style:{textAlign:`center`},children:[(0,f.jsx)(`p`,{style:{margin:0,fontSize:`16px`,fontWeight:400,color:`#111827`,lineHeight:1.4},children:e.connectionTitle}),e.connectionSubtitle&&(0,f.jsx)(`p`,{style:{margin:`6px 0 0`,fontSize:`13px`,fontWeight:400,color:`#6b7280`,lineHeight:1.4},children:e.connectionSubtitle})]}),(0,f.jsxs)(`div`,{style:{display:`flex`,gap:`16px`,width:`100%`},children:[(0,f.jsx)(`button`,{onClick:r,style:{width:`127px`,height:`43px`,borderRadius:`999px`,background:c,color:l,fontWeight:400,fontSize:`16px`,border:`none`,cursor:`pointer`,transition:`transform 0.15s ease`,boxShadow:`0 4px 12px ${c}55`},onMouseEnter:e=>{e.currentTarget.style.transform=`scale(1.03)`},onMouseLeave:e=>{e.currentTarget.style.transform=`scale(1)`},children:e.voiceLabel}),(0,f.jsx)(`button`,{onClick:i,style:{width:`127px`,height:`43px`,borderRadius:`999px`,background:`transparent`,color:`#111827`,fontWeight:400,fontSize:`16px`,border:`1.5px solid #d1d5db`,cursor:`pointer`,transition:`border-color 0.15s ease`},onMouseEnter:e=>{e.currentTarget.style.borderColor=`#6b7280`},onMouseLeave:e=>{e.currentTarget.style.borderColor=`#d1d5db`},children:e.textLabel})]})]})}function k_({orbColors:e,onEnterChat:t,onEndChat:n,agentId:r,siteId:i,apiBaseUrl:a,chatId:o,historyRef:s,onMessageSync:c,beaconSyncVoiceTranscript:l,widgetPosition:u=`bottom-right`,voiceEndCounter:p,websiteName:m,voiceFirstMessage:h,captureVisitorEmail:g,emailRequired:_,emailCaptureEnabled:v=!0,listeningLabel:y=`Listening...`,thinkingLabel:b=`Thinking...`,enterChatModeLabel:x=`Enter Chat Mode`,endConversationLabel:S=`End Conversation`}){(0,d.useEffect)(()=>(console.log(`[FlowEngage:VoiceDebug] 🔵 VoiceMode MOUNTED`),()=>console.log(`[FlowEngage:VoiceDebug] 🔴 VoiceMode UNMOUNTED`)),[]);let[C,w]=(0,d.useState)(`idle`),[T,E]=(0,d.useState)(!1),[ee,D]=(0,d.useState)(!1),[O,k]=(0,d.useState)(()=>te(i)===`auto`),re=()=>{let e=O?`ask`:`auto`;ne(i,e),k(e===`auto`)},[ie,A]=(0,d.useState)(!1),[ae,oe]=(0,d.useState)(``),[se,ce]=(0,d.useState)(``),le=(0,d.useRef)(!1),j=(0,d.useRef)(!1),ue=(0,d.useRef)(null),de=(0,d.useRef)(0),M=(0,d.useRef)(``),N=(0,d.useRef)(!1),fe=(0,d.useRef)(0),pe=(0,d.useRef)(!1),me=(0,d.useRef)(!1),he=(0,d.useRef)(!1),ge=(0,d.useRef)([]),P=(0,d.useRef)(null),be=(0,d.useRef)(``),xe=(0,d.useRef)(null),Se=e=>{pe.current=e,e||(me.current=!1),I.current?.setWalkthroughActive?.(e)};xe.current=async()=>{if(me.current)return;let e=typeof window<`u`?window.location.pathname:null;if(e&&P.current&&e!==P.current){console.log(`[FlowEngage] walkthrough ended — visitor left `+P.current),P.current=null,ge.current=[],Se(!1);return}let t=ge.current;Ce.current+=1;let n=Ce.current,r=t[n];if(!r){if(Se(!1),t.length)I.current?.sendUserMessage?.(`That was the last section. Say so in one short sentence and ask if they want to explore any section further.`);else if(n===0){let e=``;try{let t=await Te.current?.[`fwh-read-content-mvp`]?.({});typeof t==`string`&&t&&!/^Could not/i.test(t)&&(e=t.slice(0,2800))}catch{}I.current?.sendUserMessage?.(e?`Give the visitor a complete spoken overview of this page in 4-6 sentences, based ONLY on this page content:\n"""${e}"""\nThen ask which part they'd like to hear more about. Do not call any tool.`:`Give the visitor a complete spoken overview of this page in 4-6 sentences based on what you know about it, then ask which part they'd like to hear more about. Do not call any tool.`,{suppressQuestion:!0})}return}try{await Te.current?.highlightOnPage?.({section_text:r})}catch(e){console.warn(`[FlowEngage] walkthrough highlight failed:`,e?.message||e)}console.debug(`[FlowEngage] walkthrough step ${n+1}/${t.length}: ${r}`);let i=``;try{let e=await Te.current?.[`fwh-read-content-mvp`]?.({section_text:r});typeof e==`string`&&e&&!/^Could not/i.test(e)&&(i=e.slice(0,2500))}catch{}let a=i?`Here is this section's full content — describe it from THIS text only, and do not call any tool:\n"""${i}"""\n`:`read the section's content silently first (fwh-read-content-mvp) if you don't already have it. `;I.current?.sendUserMessage?.(`Describe the section titled "${r}" in 3-4 sentences, starting with ${n===0?`Starting with`:`Next —`} ${r}. Describe ONLY this section. If this section contains named sub-items (steps, checklist entries, cards, plans, FAQs, team members), name EVERY one of them in order with a short phrase each — never skip or collapse them into a summary; `+a+`This section is ON THE PAGE THE VISITOR IS ALREADY ON — do NOT call fwh-navigate-mvp, do not go to a product/detail page, and do not offer to, even if this section's name also matches a page name. Do not mention any other section, do not ask whether to continue, then stop.`,{suppressQuestion:!0})};let Ce=(0,d.useRef)(0),I=(0,d.useRef)(null),Te=(0,d.useRef)(null),De=(0,d.useRef)(``),L=(0,d.useRef)(async()=>{}),Oe=(0,d.useRef)(!1),ke=(0,d.useRef)(!1),Ae=(0,d.useRef)(!1),je=(0,d.useRef)(!1),Me=(0,d.useRef)(`minimized`),Ne=(0,d.useRef)(null),Pe=(0,d.useRef)(null),Fe=(0,d.useRef)(null),Ie=(0,d.useRef)(0),Le=(0,d.useRef)(!1),R=(0,d.useRef)(`idle`),z=(0,d.useRef)(null),Re=(0,d.useRef)(!1),ze=(0,d.useRef)(null),Be=(0,d.useCallback)((e=`minimized`)=>{console.warn(`[FlowEngage:VoiceDebug] 🛑 requestSessionShutdown called`,{destination:e,stack:Error().stack?.split(`
|
|
422
|
-
`).slice(1,4).join(` | `)}),Me.current=e,je.current=!0,queueMicrotask(()=>{Oe.current||(Oe.current=!0,L.current?.())})},[s]),B=(0,d.useCallback)(()=>{ue.current&&=(clearTimeout(ue.current),null)},[]);(0,d.useEffect)(()=>{if(_){le.current=!1;return}if(le.current=!0,j.current=!1,A(!1),B(),N.current){N.current=!1,I.current?.setMicMuted?.(!1);let e=M.current;M.current=``,I.current?.injectAgentSpeech?.(`Thanks — I already have your email on file.`,`interrupt`),e&&I.current?.sendUserMessage?.(`The visitor's email is already on file — do not ask for it. Answer their question now: "${e}"`)}I.current?.sendContextualUpdate?.(`EMAIL REQUIREMENT SATISFIED — the visitor's email is already on file. All earlier instructions about requiring or asking for an email are VOID. Never ask for it again.`)},[_,B]);let Ve=/(walk\s*(me\s*)?through|walkthrough|take me through|(complete|full|detailed) (overview|understanding|walkthrough|picture|breakdown|explanation)|give me (a|an|the) (overview|tour|walk)|tell me about (each|all)|go through (it|them|each|all)|overview of|one by one|tell me everything|explain (me )?(this|the) (whole |entire |full )?page|everything (about|on) this page|show me (each|all|around))/i,He=/(that covers everything|that was all|covered everything|that(?:'|’)?s everything)/i,Ue=/^((uh+|um+|so|well|okay|ok|yeah)[,.\s]+)*(yes|yeah|yep|yup|sure|ok(ay)?|please|continue|go on|next|keep going|carry on|go ahead|resume|back to the tour)[\s.!,]*$/i,We=/(would you like (me )?to take you|shall i take you|want me to take you|would you like to (go|explore|visit|see)|take you there)/i,Ge=/^((uh+|um+|oh|ah|so|well)[,.\s]+)*(cool|nice|great|awesome|wow|lovely|perfect|got it|gotcha|i see|understood|makes sense|interesting|right|alright|sounds good|mm+-?hmm+|hmm+|k)[\s.!,]*$/i,Ke=e=>String(e||``).replace(/[\s.,!?]*((uh+|um+|er+|hmm+|mm+)[\s.,!?]*)+$/i,``).trim(),qe=(0,d.useCallback)(()=>{B(),ue.current=setTimeout(()=>{if(!(!j.current||le.current))if(de.current<3){de.current+=1;try{I.current?.injectAgentSpeech?.(`I still need your email in the box below to continue — could you enter it, please?`,`queue`)}catch{}qe()}else{try{I.current?.injectAgentSpeech?.(`Since I haven't received your email, I'll close this session for now. Feel free to come back anytime — take care!`,`queue`)}catch{}j.current=!1,A(!1),B(),Le.current=!0}},1e4)},[B]),Je=(0,d.useCallback)(async e=>{je.current=!0;try{xn()}catch{}try{Fh()}catch{}if(i)try{Ee(i)}catch{}try{await I.current?.endSession?.()}catch{}let t=Ne.current,n=Pe.current;if(t&&o&&a){let e=n?Math.round((Date.now()-n)/1e3):0;fetch(`${a}/api/voice/save-chat-recording`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({chatId:o,voiceSessionId:t,duration:e,siteId:i})}).catch(e=>console.warn(`[FlowEngage] save-chat-recording failed:`,e.message))}if(Ne.current=null,Pe.current=null,I.current=null,w(`idle`),E(!1),Oe.current=!1,xr(),i)try{ye(i)}catch{}typeof c==`function`&&await c(s.current),e?.()},[s,c,i,o,a]);L.current=()=>Je(Me.current===`chat`?t:n),(0,d.useEffect)(()=>{ke.current=T},[T]),(0,d.useEffect)(()=>{p&&Be(`chat`)},[p]),(0,d.useEffect)(()=>{let e=!1,d=null,f=null,p=null;return(async()=>{if(!i){w(`error`);return}try{if((await navigator.mediaDevices.getUserMedia({audio:!0})).getTracks().forEach(e=>e.stop()),e)return;s.current=[],De.current=``,Oe.current=!1,Ae.current=!1,je.current=!1,Me.current=`minimized`,br({onBeforeNavigate:()=>{if(i&&s.current.length>0)try{let e=qg(s.current);e&&_e(i,e)}catch{}if(!Bn()){let e=Ne.current,t=Pe.current;if(e&&o&&a)try{let n=t?Math.round((Date.now()-t)/1e3):0,r=JSON.stringify({chatId:o,voiceSessionId:e,duration:n,siteId:i}),s=`${a}/api/voice/save-chat-recording`;fetch(s,{method:`POST`,headers:{"Content-Type":`application/json`},body:r,keepalive:!0}).catch(()=>{}),Ne.current=null,Pe.current=null}catch{}}F()}});let t=g_(p_({siteId:i||null,apiBaseUrl:a||null,chatId:o||null,getLastUserVoiceText:()=>De.current,beaconSyncFullVoiceHistory:()=>l?.(s.current),isWalkthroughActive:()=>pe.current&&!me.current,requestEndSession:()=>{Le.current=!0},requestEmailCollection:()=>{v&&(le.current||j.current||(j.current=!0,de.current=0,A(!0),qe()))}})),n=i?ve(i):null,r=!!n?.transcript,c=i?Ee(i):null,g=!!c,y=typeof window<`u`?window.location.pathname:`/`,b=typeof document<`u`?document.title:``,x=y.replace(/^\/[a-z]{2}(?:-[a-z]{2})?(?=\/|$)/i,``)||`/`,S=x===`/`||x===``,C={apiBaseUrl:a,siteId:i,chatId:o,clientTools:t};Te.current=t;let T=(y.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()),ee=``;b&&b.includes(`|`)&&(ee=b.split(`|`).map(e=>e.trim()).filter(Boolean).reduce((e,t)=>e.length<=t.length?e:t));let D=ee||T||``,O=D?`the ${D} page`:`this page`,k=(()=>{if(!c?.targetUrl)return!0;try{return new URL(c.targetUrl,window.location.href).pathname.replace(/\/$/,``)===window.location.pathname.replace(/\/$/,``)}catch{return!0}})(),ne=(()=>{if(!c?.targetUrl)return null;try{return new URL(c.targetUrl,window.location.href).pathname}catch{return null}})();if(g&&!k)C.greeting=`You're on ${O}. What would you like to know?`,console.warn(`[FlowEngage] nav target "${c?.pageName}" not reached — on "${D}" instead; not claiming navigation`);else if(g){let e=c?.pageName?`the ${c.pageName} page`:O;C.greeting=`I have navigated you to ${e}. Do you want to know about any specific section or want a complete overview?`,console.log(`[FlowEngage] 🔄 Agent-nav resume: greeting set for "${e}" (${y})`)}else if(r)C.greeting=`Welcome back! I see you're now on ${O}. Would you like me to continue what I was explaining before, or would you like to know about this page instead?`;else{let e=m||``;C.greeting=h||(S?e?`Hi, welcome to ${e}! How may I help you?`:`Hi, welcome! How may I help you?`:`Welcome to ${O}! How may I help you?`),S||console.log(`[FlowEngage] 🏠 Page-context greeting: "${D}" for ${y}`)}let re=!1,ie=g?`NAVIGATION COMPLETE — The exact phrase "I have navigated you to the [page] page. Do you want to know about any specific section or want a complete overview?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript?`Prior transcript:\n${n.transcript}\n`:`(no prior transcript)`}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until the visitor responds. When they reply: if they name a specific section → call fwh-get-current-page-mvp then answer immediately; if they say "overview" or anything non-specific → call fwh-get-current-page-mvp (zero speech) then do a full highlighted walkthrough.`:null,ae=!1,oe=!g&&!r?`Your greeting has been spoken. Wait for the visitor to ask a question or tell you what they need. Do NOT auto-start a walkthrough or highlight any sections — let the visitor lead.`:null,se=r&&!g,ce=!1,ue=se?`The question "Would you like me to continue what I was explaining before, or would you like to know about this page instead?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript?`Prior transcript:\n${n.transcript}\n`:`(no prior transcript, or the visitor navigated before saying much)`}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until they respond. When they reply: if they want you to CONTINUE the previous topic → resume verbally from the prior transcript above, do NOT call fwh-get-current-page-mvp or describe the new page unless they ask. If they want to know about THIS page → call fwh-get-current-page-mvp (zero speech) then proceed as a normal fresh-page walkthrough/description. If their reply is ambiguous, ask them to clarify which they'd prefer rather than guessing.`:null,ye=!1,L=!1,Fe=v&&!!_&&!he.current,Je=await gh.startSession({...C,onConnect:({conversationId:e})=>{Ne.current=e,Pe.current=Date.now(),console.log(`[FlowEngage] 🎙️ AI chat session connected:`,e)},onUnhandledClientToolCall:e=>console.error(`[FlowEngage] Unhandled tool:`,e?.tool_name),onSessionInfo:({emailOnFile:e})=>{he.current=!!e,e&&(le.current=!0,j.current=!1,A(!1),B(),console.log(`[FlowEngage] email already on file per server — gate disabled`))},onAgentTurnEnd:()=>{e||pe.current&&(me.current||N.current||setTimeout(()=>xe.current?.(),250))},onSessionEnding:()=>{e||Be(`minimized`)},onPanel:({title:t,items:n})=>{e||Nh(n,{title:t||`Products`,side:u===`bottom-left`?`left`:`right`,productUrl:e=>e.handle?`${window.location.origin}/products/${encodeURIComponent(e.handle)}`:null})},onModeChange:({mode:t,tentative:n})=>{if(!e){if(t===`speaking`){z.current&&=(clearTimeout(z.current),null),ze.current&&=(clearTimeout(ze.current),null),w(`speaking`),R.current=`speaking`,Re.current=!1;return}if(t===`user-speaking`){if(j.current&&!le.current&&qe(),ze.current&&=(clearTimeout(ze.current),null),R.current===`speaking`)return;try{xn()}catch{}z.current&&=(clearTimeout(z.current),null),R.current!==`listening`&&(w(`listening`),R.current=`listening`);return}if(t===`thinking`){if(z.current&&=(clearTimeout(z.current),null),ze.current&&=(clearTimeout(ze.current),null),R.current===`speaking`)return;w(`thinking`),R.current=`thinking`,n&&(ze.current=setTimeout(()=>{ze.current=null,!(e||R.current!==`thinking`)&&(R.current=`listening`,w(`listening`))},4e3));return}if(R.current=`listening`,Le.current){Le.current=!1,Be();return}if(Fe&&!L&&!le.current&&!he.current){L=!0,re=!0,ae=!0;try{I.current?.sendContextualUpdate?.(`CRITICAL — EMAIL REQUIRED BEFORE ANYTHING ELSE. The visitor has not provided their email. The app itself speaks the email request out loud — you must NOT say it, and you must NOT answer. Stay completely silent from now until you receive an EMAIL_SAVED message: produce no speech and call no tools, no matter what the visitor says. Remember their question. On EMAIL_SAVED, thank them briefly and answer that remembered question in full.`)}catch{}}if(ie&&!re){re=!0;try{I.current?.sendContextualUpdate?.(ie)}catch{}}if(ue&&!ce){ce=!0;try{I.current?.sendContextualUpdate?.(ue)}catch{}}if(oe&&!ae){ae=!0;try{I.current?.sendContextualUpdate?.(oe)}catch{}}z.current&&clearTimeout(z.current),z.current=setTimeout(()=>{e||R.current!==`speaking`&&(Re.current||(z.current=null,R.current=`listening`,w(`listening`)))},1200)}},onMessage:t=>{if(e||N.current&&t.role!==`user`&&!/email/i.test(String(t.message||``)))return;let n=t.role===`user`&&/^(Describe the section titled "|\[SYSTEM:|Give the visitor a complete spoken overview|That was the last section)/.test(String(t.message||``).trim()),r=/\b(?:this|that|the)\s+(?:case\s*stud(?:y|ies)|product|article|blog|post|item|service)\b/i.test(String(t.message||``));if(!n&&!r&&t.role===`user`&&Ve.test(String(t.message||``)))typeof window<`u`&&(window.__fe_user_nav_during_speech=null),Se(!0),me.current=!1,fe.current=0,Ce.current=-1,ge.current=h_(),P.current=typeof window<`u`?window.location.pathname:null,setTimeout(()=>xe.current?.(),400),console.log(`[FlowEngage] walkthrough armed — ${ge.current.length} sections on this page`);else if(!n&&t.role===`user`&&pe.current){let e=String(t.message||``).trim(),n=Ke(e),r=e=>!!e&&(Ue.test(e)||Ge.test(e));We.test(be.current||``)?(console.log(`[FlowEngage] walkthrough ended — visitor accepted a navigation offer`),P.current=null,ge.current=[],Se(!1)):!n||r(e)||r(n)?me.current&&(me.current=!1,console.debug(`[FlowEngage] walkthrough resumed by visitor`),setTimeout(()=>xe.current?.(),300)):(console.log(`[FlowEngage] walkthrough ended — visitor moved to a new topic`),P.current=null,ge.current=[],Se(!1))}if(t.role!==`user`&&!n&&(be.current=String(t.message||``)),t.role!==`user`&&He.test(String(t.message||``))){let e=ge.current.length;!e||Ce.current>=e-1?Se(!1):console.debug(`[FlowEngage] agent claimed completion at step ${Ce.current+1}/${e} — ignoring, tour continues`)}let a=t.role===`user`?`user`:`bot`,o=t.message||t.text||t.content||``;if(o){if(a===`user`&&(De.current=o,typeof window<`u`&&(window.__fe_user_nav_during_speech=null),Re.current=!0,z.current&&=(clearTimeout(z.current),null),R.current!==`speaking`&&(w(`thinking`),R.current=`thinking`),Fe&&!ye&&!le.current&&!he.current&&(ye=!0,M.current=o,N.current=!0,I.current?.injectAgentSpeech?.(`Before I answer that, could you please enter your email in the text box below?`,`interrupt`),I.current?.setMicMuted?.(!0),j.current||(j.current=!0,de.current=0,A(!0),qe()))),s.current=[...s.current,{sender:a,message:o,timestamp:new Date().toISOString(),debug:t.debug}],i)try{let e=qg(s.current);e&&_e(i,e)}catch{}if(T_(a,o)){Le.current=!0;return}if(ke.current&&a===`bot`&&/are you still there|still there\??|hello\?|anyone there|checking in|you there|are you available|is anyone|did you step away/i.test(o))if(Ie.current+=1,Ie.current===2)try{I.current?.sendContextualUpdate?.(`IMPORTANT: The visitor is muted and has not responded to your last 2 checks. Make exactly ONE more check. If there is still no response, say: 'I feel like you are not active, so I'm ending this session. You can connect with me anytime.' then immediately call the fwh-end-session-mvp tool to end the voice session. Do NOT ask again after that.`)}catch{}else Ie.current>=5&&Be(`minimized`)}},onError:t=>{if(!e&&!je.current&&!Ae.current&&!we()){Ae.current=!0,console.error(`[FlowEngageVoiceCall] session:error`,{message:t?.message,name:t?.name,raw:t});try{I.current?.setMicMuted?.(!0)}catch{}E(!0),w(`error`),Be(`chat`)}},onDisconnect:t=>{if(e||je.current||Ae.current||we())return;let n=s.current?.length||0;console.warn(`[FlowEngageVoiceCall] session:disconnected`,{historyCount:n,reason:t?.reason,closeCode:t?.context?.code,closeReason:t?.context?.reason||t?.message,lastUserText:De.current||``,wasMuted:ke.current}),Ae.current=!0;try{I.current?.setMicMuted?.(!0)}catch{}E(!0),Be(`chat`)}});if(e){console.warn(`[FlowEngage:VoiceDebug] ❌ cancelled=true after startSession resolved — killing session immediately`),Je.endSession().catch(()=>{});return}d=Je,I.current=Je,w(`listening`);try{sessionStorage.setItem(`el_conv_${i}`,Je.getId())}catch{}parent.postMessage({type:`AGENT_EXPAND`},`*`);try{let e=typeof window<`u`?window.location.href:``,a=`[]`;try{a=await t[`fwh-list-forms-mvp`]()}catch{}let o=`none`;if(typeof window<`u`&&typeof document<`u`)try{let e=`#flowengage-root, .flowengage-shell, .flowengage-widget-root`,t=document.querySelector(`main, [role='main']`)||document.body,n=Array.from(t.querySelectorAll(`h1, h2, h3`)).filter(t=>!t.closest(e)).map(e=>e.textContent.replace(/\s+/g,` `).trim()).filter(e=>e.length>2&&e.length<200);n.length||(n=Array.from(document.body.querySelectorAll(`h1, h2, h3`)).filter(t=>!t.closest(e)).map(e=>e.textContent.replace(/\s+/g,` `).trim()).filter(e=>e.length>2&&e.length<200)),n.length&&(o=[...new Set(n)].slice(0,15).join(` | `))}catch{}let s=r&&!g?`RESUMING PRIOR SESSION — DO NOT GREET. The visitor is continuing the same conversation after a page reload. Do not introduce yourself or re-state your capabilities. Wait silently until the visitor speaks; if you must speak first, briefly acknowledge where you left off in one short sentence.\nPrior transcript:\n${n?.transcript||``}\n\n`:``;Je.sendContextualUpdate(`${s}SiteId: ${i||`unknown`}. ${_?``:`Visitor's email is already on file from a prior session — do NOT ask for it again. `}WALKTHROUGH RULE: When the visitor asks for a walkthrough or overview of this page, you MUST IMMEDIATELY call fwh-get-current-page-mvp (zero speech — call it silently with NO words before it) to get the exact highlightable headings. Do NOT skip fwh-get-current-page-mvp. Voice: call end_voice_session or say you are ending the session to stop.`),Je.sendContextualUpdate(v_(e,o,a))}catch{}if(typeof window<`u`){let t=window.location.pathname,n=Date.now(),r=e=>{try{return(decodeURIComponent(e||`/`).replace(/\/+$/,``)||`/`).toLowerCase()}catch{return((e||`/`).replace(/\/+$/,``)||`/`).toLowerCase()}},a=()=>{if(e)return;let a=window.location.pathname;if(r(a)===r(t)){t=a;return}if(Date.now()-n<8e3){t=a;return}let o=typeof window<`u`&&window.__fe_agent_nav||null,s=ne&&r(a)===r(ne)||Mn()||o&&Date.now()-o.ts<15e3&&r(o.path)===r(a),c=t;t=a;let l=window.location.href,u=typeof document<`u`?document.title:``,d=typeof document<`u`?Array.from(document.querySelectorAll(`h1, h2, h3, h4`)).filter(e=>!e.closest(`#flowengage-root, .flowengage-shell`)).map(e=>({tag:e.tagName,text:e.textContent.trim()})).filter(e=>e.text.length>0&&e.text.length<200).slice(0,20):[];try{xn()}catch{}let f=d.map(e=>e.text).join(` | `),p=e=>(e.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase())||`Home`,m=p(c),h=p(a);if(s){if(typeof window<`u`&&(window.__fe_agent_nav=null),console.log(`[FlowEngage] agent-driven navigation settled: "${c}" → "${a}"`),te(i)!==`auto`)try{I.current?.sendUserMessage?.(`[SYSTEM: YOU just navigated the visitor to the "${h}" page (${l}) at their request — this was YOUR action, not theirs. Page title: "${u}". Sections: ${JSON.stringify(d)}. Say: "Here we are on the ${h} page." Then introduce this page in 2-3 sentences, and finish by asking: "Would you like me to walk you through it section by section?" NEVER say "I see you've navigated" — the visitor did not navigate, you did. Do NOT call fwh-navigate-mvp again. Wait for their answer before describing sections.]`,{suppressQuestion:!0})}catch{}else console.log(`[FlowEngage] auto-nav — skipping the arrival acknowledgment turn`);try{I.current?.sendContextualUpdate?.(v_(l,f))}catch{}return}typeof window<`u`&&(window.__fe_user_nav_during_speech={fromPath:c,toPath:a,fromPageName:m,toPageName:h,ts:Date.now()}),console.log(`[FlowEngage] visitor navigation detected: "${c}" → "${a}" (agent target: ${ne||`none`})`);try{I.current?.sendUserMessage?.(`[SYSTEM: I navigated away mid-walkthrough — I am now on the "${h}" page (${l}). Page title: "${u}". Sections: ${JSON.stringify(d)}. STOP everything. Do NOT describe the new page. Say EXACTLY: "I see you've navigated to the ${h} page — would you like me to continue what I was explaining, or shall I walk you through this page?" Then wait for my answer. IF I say continue/yes/carry on: resume speaking verbally from where you left off — do NOT call fwh-navigate-mvp, do NOT call fwh-highlight-mvp, the visitor will navigate themselves if needed. IF I want to hear about this page: call fwh-highlight-mvp for sections of "${h}" only.]`)}catch{}try{I.current?.sendContextualUpdate?.(v_(l,f))}catch{}};f=window.history.pushState.bind(window.history),window.history.pushState=(...e)=>{f(...e),setTimeout(a,50)},p=()=>setTimeout(a,50),window.addEventListener(`popstate`,p)}}catch(r){e||(console.error(`[FlowEngage] startSession failed`,r),r.message&&r.message.includes(`Too many voice sessions`)?(alert(r.message),n&&n(`minimized`)):(w(`error`),Me.current=`chat`,window.setTimeout(()=>{e||t?.()},0)))}})(),()=>{console.warn(`[FlowEngage:VoiceDebug] ⚠️ useEffect CLEANUP fired — session will be killed`,{agentId:r,siteId:i,apiBaseUrl:a}),e=!0,je.current=!0;try{xn()}catch{}if(i)try{Ee(i)}catch{}typeof window<`u`&&(window.__fe_user_nav_during_speech=null),xr(),z.current&&=(clearTimeout(z.current),null),ze.current&&=(clearTimeout(ze.current),null),ue.current&&=(clearTimeout(ue.current),null),Re.current=!1,R.current=`idle`,f&&typeof window<`u`&&(window.history.pushState=f),p&&typeof window<`u`&&window.removeEventListener(`popstate`,p);let t=d||I.current,n=Ne.current,l=Pe.current;if(t?.endSession?.().catch(()=>{}),n&&o&&a){let e=l?Math.round((Date.now()-l)/1e3):0,t=JSON.stringify({chatId:o,voiceSessionId:n,duration:e,siteId:i}),r=`${a}/api/voice/save-chat-recording`;typeof navigator<`u`&&navigator.sendBeacon&&navigator.sendBeacon(r,new Blob([t],{type:`application/json`}))||fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`},body:t,keepalive:!0}).catch(()=>{}),Ne.current=null,Pe.current=null}typeof c==`function`&&s.current.length>0&&c(s.current).catch?.(()=>{})}},[r,i,a,Be]),(0,d.useEffect)(()=>{if(!ee)return;let e=e=>{let t=e.composedPath?.()?.[0]||e.target;Fe.current&&!Fe.current.contains(t)&&D(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[ee]);let V=async()=>{if(I.current)try{if(T){Ie.current=0,await I.current.setMicMuted?.(!1),E(!1);try{I.current.sendContextualUpdate?.(`The visitor has unmuted. Resume normal interaction and wait for them to speak.`)}catch{}}else{Ie.current=0,await I.current.setMicMuted?.(!0),E(!0);try{I.current.sendContextualUpdate?.(`The visitor has muted their microphone. Do NOT speak, ask if they are there, or prompt them in any way. Stay completely silent until they unmute.`)}catch{}}}catch{}},H=(0,d.useCallback)(async e=>{e?.preventDefault?.();let t=ae.trim().toLowerCase();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)){ce(`Please enter a valid email address.`);return}ce(``),le.current=!0,j.current=!1,B(),A(!1),oe(``),N.current=!1;try{typeof g==`function`&&await g(t)}catch{}I.current?.setMicMuted?.(!1);let n=M.current;M.current=``;try{I.current?.injectAgentSpeech?.(`Thank you for submitting your email.`),I.current?.sendUserMessage?.(n||`The visitor's email is on file. Carry on helping them and never ask for it again.`)}catch{}},[ae,g,B]),U=Array.isArray(e)&&e[0]||`#3B82F6`;return(0,f.jsxs)(`div`,{ref:Fe,style:{position:`relative`,display:`flex`,flexDirection:`column`,alignItems:`center`,gap:`14px`,animation:`fe-orb-slide-up 0.42s cubic-bezier(0.16,1,0.3,1)`},children:[ie&&(0,f.jsxs)(`form`,{onSubmit:H,style:{width:`270px`,maxWidth:`calc(100vw - 40px)`,boxSizing:`border-box`,background:`rgba(15,22,36,0.97)`,backdropFilter:`blur(14px)`,WebkitBackdropFilter:`blur(14px)`,borderRadius:`16px`,boxShadow:`0 10px 40px rgba(0,0,0,0.45)`,padding:`16px`,display:`flex`,flexDirection:`column`,gap:`10px`,animation:`fe-orb-fade-in 0.18s ease`},children:[(0,f.jsx)(`span`,{style:{color:`white`,fontSize:`13px`,fontWeight:600,lineHeight:1.4},children:`Please enter your email to continue`}),(0,f.jsx)(`input`,{type:`email`,inputMode:`email`,autoComplete:`email`,autoFocus:!0,value:ae,onChange:e=>{oe(e.target.value),se&&ce(``)},placeholder:`you@example.com`,style:{width:`100%`,boxSizing:`border-box`,padding:`10px 12px`,borderRadius:`10px`,border:se?`1.5px solid #ef4444`:`1.5px solid #4b5563`,background:`#1f2937`,color:`white`,fontSize:`14px`,outline:`none`}}),se&&(0,f.jsx)(`span`,{style:{color:`#f87171`,fontSize:`12px`},children:se}),(0,f.jsx)(`button`,{type:`submit`,disabled:!ae.trim(),style:{width:`100%`,padding:`10px 12px`,borderRadius:`10px`,border:`none`,background:ae.trim()?U:`#374151`,color:`white`,fontSize:`14px`,fontWeight:600,cursor:ae.trim()?`pointer`:`default`,transition:`background 0.2s ease`},children:`Submit`})]}),(0,f.jsxs)(`div`,{style:{position:`relative`},children:[(C===`listening`||C===`thinking`)&&!T&&(0,f.jsx)(`span`,{style:{position:`absolute`,right:`100%`,marginRight:`18px`,top:`50%`,transform:`translateY(-50%)`,color:`white`,fontSize:`18px`,fontWeight:600,letterSpacing:`0.01em`,textShadow:`0 2px 4px rgba(0,0,0,0.4)`,whiteSpace:`nowrap`},children:C===`thinking`?b:y}),T&&(0,f.jsx)(`span`,{style:{position:`absolute`,right:`100%`,marginRight:`18px`,top:`50%`,transform:`translateY(-50%)`,color:`#f87171`,fontSize:`16px`,fontWeight:600,letterSpacing:`0.01em`,textShadow:`0 2px 4px rgba(0,0,0,0.4)`,whiteSpace:`nowrap`},children:`Muted`}),(0,f.jsx)(E_,{size:72,voiceState:C,isMuted:T,orbColors:e})]}),C===`error`&&(0,f.jsx)(`span`,{style:{color:`#f87171`,fontSize:`13px`,fontWeight:500},children:`Could not start voice session.`}),(0,f.jsxs)(`div`,{style:{display:`flex`,gap:`12px`,alignItems:`center`},children:[(0,f.jsx)(`button`,{onClick:V,title:T?`Unmute`:`Mute`,style:{width:`24px`,height:`24px`,borderRadius:`50%`,background:T?`#7f1d1d`:`#1f2937`,border:T?`1.5px solid #ef4444`:`1.5px solid #4b5563`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:T?`#f87171`:`white`,transition:`all 0.2s ease`,boxShadow:`0 2px 8px rgba(0,0,0,0.4)`,padding:0,flexShrink:0},children:T?(0,f.jsxs)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,f.jsx)(`line`,{x1:`1`,y1:`1`,x2:`23`,y2:`23`}),(0,f.jsx)(`path`,{d:`M9 9v3a3 3 0 005.12 2.12M15 9.34V4a3 3 0 00-5.94-.6`}),(0,f.jsx)(`path`,{d:`M17 16.95A7 7 0 015 12v-2m14 0v2a7 7 0 01-.11 1.23`}),(0,f.jsx)(`line`,{x1:`12`,y1:`19`,x2:`12`,y2:`23`}),(0,f.jsx)(`line`,{x1:`8`,y1:`23`,x2:`16`,y2:`23`})]}):(0,f.jsx)(Xh,{size:16,color:`currentColor`})}),(0,f.jsx)(`button`,{onClick:()=>D(e=>!e),style:{width:`24px`,height:`24px`,borderRadius:`50%`,background:`#1f2937`,border:`1.5px solid #4b5563`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:`white`,transition:`background 0.2s ease`,boxShadow:`0 2px 8px rgba(0,0,0,0.4)`,padding:0,flexShrink:0},children:(0,f.jsx)(ig,{size:16,color:`currentColor`})})]}),ee&&(0,f.jsxs)(`div`,{style:{position:`absolute`,bottom:0,right:0,width:`230px`,zIndex:9999,background:`rgba(15,22,36,0.96)`,backdropFilter:`blur(14px)`,WebkitBackdropFilter:`blur(14px)`,borderRadius:`16px`,boxShadow:`0 10px 40px rgba(0,0,0,0.45)`,animation:`fe-orb-fade-in 0.18s ease`,padding:`12px 12px 40px`},children:[(0,f.jsxs)(`button`,{onClick:()=>{D(!1),Me.current=`chat`,Je(t)},style:{display:`flex`,alignItems:`center`,gap:`12px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`white`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`,whiteSpace:`nowrap`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.08)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,f.jsx)(ag,{}),x]}),i&&(0,f.jsxs)(`button`,{onClick:()=>re(),style:{display:`flex`,alignItems:`center`,justifyContent:`space-between`,gap:`8px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`white`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.08)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,f.jsxs)(`span`,{style:{display:`flex`,alignItems:`center`,gap:`12px`,minWidth:0,overflow:`hidden`},children:[(0,f.jsx)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,style:{flexShrink:0},children:(0,f.jsx)(`polygon`,{points:`3 11 22 2 13 21 11 13 3 11`})}),(0,f.jsx)(`span`,{style:{overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:`Auto-navigate`})]}),(0,f.jsx)(`span`,{"aria-hidden":!0,style:{width:`34px`,height:`19px`,borderRadius:`999px`,background:O?`#22c55e`:`rgba(255,255,255,0.2)`,position:`relative`,flexShrink:0,transition:`background 0.15s ease`},children:(0,f.jsx)(`span`,{style:{position:`absolute`,top:`2px`,left:O?`17px`:`2px`,width:`15px`,height:`15px`,borderRadius:`50%`,background:`white`,transition:`left 0.15s ease`}})})]}),(0,f.jsxs)(`button`,{onClick:()=>{D(!1),Me.current=`minimized`,Je(n)},style:{display:`flex`,alignItems:`center`,gap:`12px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`#E30C12`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`,whiteSpace:`nowrap`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(227,12,18,0.1)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,f.jsx)(og,{}),S]}),(0,f.jsx)(`button`,{onClick:()=>D(!1),style:{position:`absolute`,bottom:`8px`,right:`8px`,width:`24px`,height:`24px`,borderRadius:`50%`,background:`rgba(255,255,255,0.15)`,border:`none`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:`white`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.25)`},onMouseLeave:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.15)`},children:(0,f.jsxs)(`svg`,{width:`12`,height:`12`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,f.jsx)(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),(0,f.jsx)(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})]})})]})]})}function A_({orbColors:e}){let t=Array.isArray(e)&&e.length>0?e:[`#135bd8`,`#ef334b`,`#ffbd2e`],n=t[0],r=t[1]||t[0],i=t[2]||t[0];return(0,f.jsx)(`div`,{style:{width:`32px`,height:`32px`,borderRadius:`50%`,flexShrink:0,background:`
|
|
422
|
+
`).slice(1,4).join(` | `)}),Me.current=e,je.current=!0,queueMicrotask(()=>{Oe.current||(Oe.current=!0,L.current?.())})},[s]),B=(0,d.useCallback)(()=>{ue.current&&=(clearTimeout(ue.current),null)},[]);(0,d.useEffect)(()=>{if(_){le.current=!1;return}if(le.current=!0,j.current=!1,A(!1),B(),N.current){N.current=!1,I.current?.setMicMuted?.(!1);let e=M.current;M.current=``,I.current?.injectAgentSpeech?.(`Thanks — I already have your email on file.`,`interrupt`),e&&I.current?.sendUserMessage?.(`The visitor's email is already on file — do not ask for it. Answer their question now: "${e}"`)}I.current?.sendContextualUpdate?.(`EMAIL REQUIREMENT SATISFIED — the visitor's email is already on file. All earlier instructions about requiring or asking for an email are VOID. Never ask for it again.`)},[_,B]);let Ve=/(walk\s*(me\s*)?through|walkthrough|take me through|(complete|full|detailed) (overview|understanding|walkthrough|picture|breakdown|explanation)|give me (a|an|the) (overview|tour|walk)|tell me about (each|all)|go through (it|them|each|all)|overview of|one by one|tell me everything|explain (me )?(this|the) (whole |entire |full )?page|everything (about|on) this page|show me (each|all|around))/i,He=/(that covers everything|that was all|covered everything|that(?:'|’)?s everything)/i,Ue=/^((uh+|um+|so|well|okay|ok|yeah)[,.\s]+)*(yes|yeah|yep|yup|sure|ok(ay)?|please|continue|go on|next|keep going|carry on|go ahead|resume|back to the tour)[\s.!,]*$/i,We=/(would you like (me )?to take you|shall i take you|want me to take you|would you like to (go|explore|visit|see)|take you there)/i,Ge=/^((uh+|um+|oh|ah|so|well)[,.\s]+)*(cool|nice|great|awesome|wow|lovely|perfect|got it|gotcha|i see|understood|makes sense|interesting|right|alright|sounds good|mm+-?hmm+|hmm+|k)[\s.!,]*$/i,Ke=e=>String(e||``).replace(/[\s.,!?]*((uh+|um+|er+|hmm+|mm+)[\s.,!?]*)+$/i,``).trim(),qe=(0,d.useCallback)(()=>{B(),ue.current=setTimeout(()=>{if(!(!j.current||le.current))if(de.current<3){de.current+=1;try{I.current?.injectAgentSpeech?.(`I still need your email in the box below to continue — could you enter it, please?`,`queue`)}catch{}qe()}else{try{I.current?.injectAgentSpeech?.(`Since I haven't received your email, I'll close this session for now. Feel free to come back anytime — take care!`,`queue`)}catch{}j.current=!1,A(!1),B(),Le.current=!0}},1e4)},[B]),Je=(0,d.useCallback)(async e=>{je.current=!0;try{xn()}catch{}try{Fh()}catch{}if(i)try{Ee(i)}catch{}try{await I.current?.endSession?.()}catch{}let t=Ne.current,n=Pe.current;if(t&&o&&a){let e=n?Math.round((Date.now()-n)/1e3):0;fetch(`${a}/api/voice/save-chat-recording`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({chatId:o,voiceSessionId:t,duration:e,siteId:i})}).catch(e=>console.warn(`[FlowEngage] save-chat-recording failed:`,e.message))}if(Ne.current=null,Pe.current=null,I.current=null,w(`idle`),E(!1),Oe.current=!1,xr(),i)try{ye(i)}catch{}typeof c==`function`&&await c(s.current),e?.()},[s,c,i,o,a]);L.current=()=>Je(Me.current===`chat`?t:n),(0,d.useEffect)(()=>{ke.current=T},[T]),(0,d.useEffect)(()=>{p&&Be(`chat`)},[p]),(0,d.useEffect)(()=>{let e=!1,d=null,f=null,p=null;return(async()=>{if(!i){w(`error`);return}try{if((await navigator.mediaDevices.getUserMedia({audio:!0})).getTracks().forEach(e=>e.stop()),e)return;s.current=[],De.current=``,Oe.current=!1,Ae.current=!1,je.current=!1,Me.current=`minimized`,br({onBeforeNavigate:()=>{if(i&&s.current.length>0)try{let e=qg(s.current);e&&_e(i,e)}catch{}if(!Bn()){let e=Ne.current,t=Pe.current;if(e&&o&&a)try{let n=t?Math.round((Date.now()-t)/1e3):0,r=JSON.stringify({chatId:o,voiceSessionId:e,duration:n,siteId:i}),s=`${a}/api/voice/save-chat-recording`;fetch(s,{method:`POST`,headers:{"Content-Type":`application/json`},body:r,keepalive:!0}).catch(()=>{}),Ne.current=null,Pe.current=null}catch{}}F()}});let t=g_(p_({siteId:i||null,apiBaseUrl:a||null,chatId:o||null,getLastUserVoiceText:()=>De.current,beaconSyncFullVoiceHistory:()=>l?.(s.current),isWalkthroughActive:()=>pe.current&&!me.current,requestEndSession:()=>{Le.current=!0},requestEmailCollection:()=>{v&&(le.current||j.current||(j.current=!0,de.current=0,A(!0),qe()))}})),n=i?ve(i):null,r=!!n?.transcript,c=i?Ee(i):null,g=!!c,y=typeof window<`u`?window.location.pathname:`/`,b=typeof document<`u`?document.title:``,x=y.replace(/^\/[a-z]{2}(?:-[a-z]{2})?(?=\/|$)/i,``)||`/`,S=x===`/`||x===``,C={apiBaseUrl:a,siteId:i,chatId:o,clientTools:t};Te.current=t;let T=(y.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase()),ee=``;b&&b.includes(`|`)&&(ee=b.split(`|`).map(e=>e.trim()).filter(Boolean).reduce((e,t)=>e.length<=t.length?e:t));let D=ee||T||``,O=D?`the ${D} page`:`this page`,k=(()=>{if(!c?.targetUrl)return!0;try{return new URL(c.targetUrl,window.location.href).pathname.replace(/\/$/,``)===window.location.pathname.replace(/\/$/,``)}catch{return!0}})(),ne=(()=>{if(!c?.targetUrl)return null;try{return new URL(c.targetUrl,window.location.href).pathname}catch{return null}})();if(g&&!k)C.greeting=`You're on ${O}. What would you like to know?`,console.warn(`[FlowEngage] nav target "${c?.pageName}" not reached — on "${D}" instead; not claiming navigation`);else if(g){let e=c?.pageName?`the ${c.pageName} page`:O;C.greeting=`I have navigated you to ${e}. Do you want to know about any specific section or want a complete overview?`,console.log(`[FlowEngage] 🔄 Agent-nav resume: greeting set for "${e}" (${y})`)}else if(r)C.greeting=`Welcome back! I see you're now on ${O}. Would you like me to continue what I was explaining before, or would you like to know about this page instead?`;else{let e=m||``;C.greeting=h||(S?e?`Hi, welcome to ${e}! How may I help you?`:`Hi, welcome! How may I help you?`:`Welcome to ${O}! How may I help you?`),S||console.log(`[FlowEngage] 🏠 Page-context greeting: "${D}" for ${y}`)}let re=!1,ie=g?`NAVIGATION COMPLETE — The exact phrase "I have navigated you to the [page] page. Do you want to know about any specific section or want a complete overview?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript?`Prior transcript:\n${n.transcript}\n`:`(no prior transcript)`}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until the visitor responds. When they reply: if they name a specific section → call fwh-get-current-page-mvp then answer immediately; if they say "overview" or anything non-specific → call fwh-get-current-page-mvp (zero speech) then do a full highlighted walkthrough.`:null,ae=!1,oe=!g&&!r?`Your greeting has been spoken. Wait for the visitor to ask a question or tell you what they need. Do NOT auto-start a walkthrough or highlight any sections — let the visitor lead.`:null,se=r&&!g,ce=!1,ue=se?`The question "Would you like me to continue what I was explaining before, or would you like to know about this page instead?" has already been spoken as your firstMessage. DO NOT repeat it. PRIOR CONVERSATION CONTEXT:\n${n?.transcript?`Prior transcript:\n${n.transcript}\n`:`(no prior transcript, or the visitor navigated before saying much)`}\nNow STOP — wait silently for the visitor's reply. Do NOT speak, do NOT call any tool until they respond. When they reply: if they want you to CONTINUE the previous topic → resume verbally from the prior transcript above, do NOT call fwh-get-current-page-mvp or describe the new page unless they ask. If they want to know about THIS page → call fwh-get-current-page-mvp (zero speech) then proceed as a normal fresh-page walkthrough/description. If their reply is ambiguous, ask them to clarify which they'd prefer rather than guessing.`:null,ye=!1,L=!1,Fe=v&&!!_&&!he.current,Je=await gh.startSession({...C,onConnect:({conversationId:e})=>{Ne.current=e,Pe.current=Date.now(),console.log(`[FlowEngage] 🎙️ AI chat session connected:`,e)},onUnhandledClientToolCall:e=>console.error(`[FlowEngage] Unhandled tool:`,e?.tool_name),onSessionInfo:({emailOnFile:e})=>{he.current=!!e,e&&(le.current=!0,j.current=!1,A(!1),B(),console.log(`[FlowEngage] email already on file per server — gate disabled`))},onAgentTurnEnd:()=>{e||pe.current&&(me.current||N.current||setTimeout(()=>xe.current?.(),250))},onSessionEnding:()=>{e||Be(`minimized`)},onPanel:({title:t,items:n})=>{e||Nh(n,{title:t||`Products`,side:u===`bottom-left`?`left`:`right`,productUrl:e=>e.handle?`${window.location.origin}/products/${encodeURIComponent(e.handle)}`:null})},onModeChange:({mode:t,tentative:n})=>{if(!e){if(t===`speaking`){z.current&&=(clearTimeout(z.current),null),ze.current&&=(clearTimeout(ze.current),null),w(`speaking`),R.current=`speaking`,Re.current=!1;return}if(t===`user-speaking`){if(j.current&&!le.current&&qe(),ze.current&&=(clearTimeout(ze.current),null),R.current===`speaking`)return;try{xn()}catch{}z.current&&=(clearTimeout(z.current),null),R.current!==`listening`&&(w(`listening`),R.current=`listening`);return}if(t===`thinking`){if(z.current&&=(clearTimeout(z.current),null),ze.current&&=(clearTimeout(ze.current),null),R.current===`speaking`)return;w(`thinking`),R.current=`thinking`,n&&(ze.current=setTimeout(()=>{ze.current=null,!(e||R.current!==`thinking`)&&(R.current=`listening`,w(`listening`))},4e3));return}if(R.current=`listening`,Le.current){Le.current=!1,Be();return}if(Fe&&!L&&!le.current&&!he.current){L=!0,re=!0,ae=!0;try{I.current?.sendContextualUpdate?.(`CRITICAL — EMAIL REQUIRED BEFORE ANYTHING ELSE. The visitor has not provided their email. The app itself speaks the email request out loud — you must NOT say it, and you must NOT answer. Stay completely silent from now until you receive an EMAIL_SAVED message: produce no speech and call no tools, no matter what the visitor says. Remember their question. On EMAIL_SAVED, thank them briefly and answer that remembered question in full.`)}catch{}}if(ie&&!re){re=!0;try{I.current?.sendContextualUpdate?.(ie)}catch{}}if(ue&&!ce){ce=!0;try{I.current?.sendContextualUpdate?.(ue)}catch{}}if(oe&&!ae){ae=!0;try{I.current?.sendContextualUpdate?.(oe)}catch{}}z.current&&clearTimeout(z.current),z.current=setTimeout(()=>{e||R.current!==`speaking`&&(Re.current||(z.current=null,R.current=`listening`,w(`listening`)))},1200)}},onMessage:t=>{if(e||N.current&&t.role!==`user`&&!/email/i.test(String(t.message||``)))return;let n=t.role===`user`&&/^(Describe the section titled "|\[SYSTEM:|Give the visitor a complete spoken overview|That was the last section)/.test(String(t.message||``).trim());if(t.role===`user`&&!n)try{Fh()}catch{}let r=/\b(?:this|that|the)\s+(?:case\s*stud(?:y|ies)|product|article|blog|post|item|service)\b/i.test(String(t.message||``));if(!n&&!r&&t.role===`user`&&Ve.test(String(t.message||``)))typeof window<`u`&&(window.__fe_user_nav_during_speech=null),Se(!0),me.current=!1,fe.current=0,Ce.current=-1,ge.current=h_(),P.current=typeof window<`u`?window.location.pathname:null,setTimeout(()=>xe.current?.(),400),console.log(`[FlowEngage] walkthrough armed — ${ge.current.length} sections on this page`);else if(!n&&t.role===`user`&&pe.current){let e=String(t.message||``).trim(),n=Ke(e),r=e=>!!e&&(Ue.test(e)||Ge.test(e));We.test(be.current||``)?(console.log(`[FlowEngage] walkthrough ended — visitor accepted a navigation offer`),P.current=null,ge.current=[],Se(!1)):!n||r(e)||r(n)?me.current&&(me.current=!1,console.debug(`[FlowEngage] walkthrough resumed by visitor`),setTimeout(()=>xe.current?.(),300)):(console.log(`[FlowEngage] walkthrough ended — visitor moved to a new topic`),P.current=null,ge.current=[],Se(!1))}if(t.role!==`user`&&!n&&(be.current=String(t.message||``)),t.role!==`user`&&He.test(String(t.message||``))){let e=ge.current.length;!e||Ce.current>=e-1?Se(!1):console.debug(`[FlowEngage] agent claimed completion at step ${Ce.current+1}/${e} — ignoring, tour continues`)}let a=t.role===`user`?`user`:`bot`,o=t.message||t.text||t.content||``;if(o){if(a===`user`&&(De.current=o,typeof window<`u`&&(window.__fe_user_nav_during_speech=null),Re.current=!0,z.current&&=(clearTimeout(z.current),null),R.current!==`speaking`&&(w(`thinking`),R.current=`thinking`),Fe&&!ye&&!le.current&&!he.current&&(ye=!0,M.current=o,N.current=!0,I.current?.injectAgentSpeech?.(`Before I answer that, could you please enter your email in the text box below?`,`interrupt`),I.current?.setMicMuted?.(!0),j.current||(j.current=!0,de.current=0,A(!0),qe()))),s.current=[...s.current,{sender:a,message:o,timestamp:new Date().toISOString(),debug:t.debug}],i)try{let e=qg(s.current);e&&_e(i,e)}catch{}if(T_(a,o)){Le.current=!0;return}if(ke.current&&a===`bot`&&/are you still there|still there\??|hello\?|anyone there|checking in|you there|are you available|is anyone|did you step away/i.test(o))if(Ie.current+=1,Ie.current===2)try{I.current?.sendContextualUpdate?.(`IMPORTANT: The visitor is muted and has not responded to your last 2 checks. Make exactly ONE more check. If there is still no response, say: 'I feel like you are not active, so I'm ending this session. You can connect with me anytime.' then immediately call the fwh-end-session-mvp tool to end the voice session. Do NOT ask again after that.`)}catch{}else Ie.current>=5&&Be(`minimized`)}},onError:t=>{if(!e&&!je.current&&!Ae.current&&!we()){Ae.current=!0,console.error(`[FlowEngageVoiceCall] session:error`,{message:t?.message,name:t?.name,raw:t});try{I.current?.setMicMuted?.(!0)}catch{}E(!0),w(`error`),Be(`chat`)}},onDisconnect:t=>{if(e||je.current||Ae.current||we())return;let n=s.current?.length||0;console.warn(`[FlowEngageVoiceCall] session:disconnected`,{historyCount:n,reason:t?.reason,closeCode:t?.context?.code,closeReason:t?.context?.reason||t?.message,lastUserText:De.current||``,wasMuted:ke.current}),Ae.current=!0;try{I.current?.setMicMuted?.(!0)}catch{}E(!0),Be(`chat`)}});if(e){console.warn(`[FlowEngage:VoiceDebug] ❌ cancelled=true after startSession resolved — killing session immediately`),Je.endSession().catch(()=>{});return}d=Je,I.current=Je,w(`listening`);try{sessionStorage.setItem(`el_conv_${i}`,Je.getId())}catch{}parent.postMessage({type:`AGENT_EXPAND`},`*`);try{let e=typeof window<`u`?window.location.href:``,a=`[]`;try{a=await t[`fwh-list-forms-mvp`]()}catch{}let o=`none`;if(typeof window<`u`&&typeof document<`u`)try{let e=`#flowengage-root, .flowengage-shell, .flowengage-widget-root`,t=document.querySelector(`main, [role='main']`)||document.body,n=Array.from(t.querySelectorAll(`h1, h2, h3`)).filter(t=>!t.closest(e)).map(e=>e.textContent.replace(/\s+/g,` `).trim()).filter(e=>e.length>2&&e.length<200);n.length||(n=Array.from(document.body.querySelectorAll(`h1, h2, h3`)).filter(t=>!t.closest(e)).map(e=>e.textContent.replace(/\s+/g,` `).trim()).filter(e=>e.length>2&&e.length<200)),n.length&&(o=[...new Set(n)].slice(0,15).join(` | `))}catch{}let s=r&&!g?`RESUMING PRIOR SESSION — DO NOT GREET. The visitor is continuing the same conversation after a page reload. Do not introduce yourself or re-state your capabilities. Wait silently until the visitor speaks; if you must speak first, briefly acknowledge where you left off in one short sentence.\nPrior transcript:\n${n?.transcript||``}\n\n`:``;Je.sendContextualUpdate(`${s}SiteId: ${i||`unknown`}. ${_?``:`Visitor's email is already on file from a prior session — do NOT ask for it again. `}WALKTHROUGH RULE: When the visitor asks for a walkthrough or overview of this page, you MUST IMMEDIATELY call fwh-get-current-page-mvp (zero speech — call it silently with NO words before it) to get the exact highlightable headings. Do NOT skip fwh-get-current-page-mvp. Voice: call end_voice_session or say you are ending the session to stop.`),Je.sendContextualUpdate(v_(e,o,a))}catch{}if(typeof window<`u`){let t=window.location.pathname,n=Date.now(),r=e=>{try{return(decodeURIComponent(e||`/`).replace(/\/+$/,``)||`/`).toLowerCase()}catch{return((e||`/`).replace(/\/+$/,``)||`/`).toLowerCase()}},a=()=>{if(e)return;let a=window.location.pathname;if(r(a)===r(t)){t=a;return}if(Date.now()-n<8e3){t=a;return}let o=typeof window<`u`&&window.__fe_agent_nav||null,s=ne&&r(a)===r(ne)||Mn()||o&&Date.now()-o.ts<15e3&&r(o.path)===r(a),c=t;t=a;let l=window.location.href,u=typeof document<`u`?document.title:``,d=typeof document<`u`?Array.from(document.querySelectorAll(`h1, h2, h3, h4`)).filter(e=>!e.closest(`#flowengage-root, .flowengage-shell`)).map(e=>({tag:e.tagName,text:e.textContent.trim()})).filter(e=>e.text.length>0&&e.text.length<200).slice(0,20):[];try{xn()}catch{}let f=d.map(e=>e.text).join(` | `),p=e=>(e.split(`/`).filter(Boolean).pop()||``).replace(/[-_]/g,` `).replace(/\b\w/g,e=>e.toUpperCase())||`Home`,m=p(c),h=p(a);if(s){if(typeof window<`u`&&(window.__fe_agent_nav=null),console.log(`[FlowEngage] agent-driven navigation settled: "${c}" → "${a}"`),te(i)!==`auto`)try{I.current?.sendUserMessage?.(`[SYSTEM: YOU just navigated the visitor to the "${h}" page (${l}) at their request — this was YOUR action, not theirs. Page title: "${u}". Sections: ${JSON.stringify(d)}. Say: "Here we are on the ${h} page." Then introduce this page in 2-3 sentences, and finish by asking: "Would you like me to walk you through it section by section?" NEVER say "I see you've navigated" — the visitor did not navigate, you did. Do NOT call fwh-navigate-mvp again. Wait for their answer before describing sections.]`,{suppressQuestion:!0})}catch{}else console.log(`[FlowEngage] auto-nav — skipping the arrival acknowledgment turn`);try{I.current?.sendContextualUpdate?.(v_(l,f))}catch{}return}typeof window<`u`&&(window.__fe_user_nav_during_speech={fromPath:c,toPath:a,fromPageName:m,toPageName:h,ts:Date.now()}),console.log(`[FlowEngage] visitor navigation detected: "${c}" → "${a}" (agent target: ${ne||`none`})`);try{I.current?.sendUserMessage?.(`[SYSTEM: I navigated away mid-walkthrough — I am now on the "${h}" page (${l}). Page title: "${u}". Sections: ${JSON.stringify(d)}. STOP everything. Do NOT describe the new page. Say EXACTLY: "I see you've navigated to the ${h} page — would you like me to continue what I was explaining, or shall I walk you through this page?" Then wait for my answer. IF I say continue/yes/carry on: resume speaking verbally from where you left off — do NOT call fwh-navigate-mvp, do NOT call fwh-highlight-mvp, the visitor will navigate themselves if needed. IF I want to hear about this page: call fwh-highlight-mvp for sections of "${h}" only.]`)}catch{}try{I.current?.sendContextualUpdate?.(v_(l,f))}catch{}};f=window.history.pushState.bind(window.history),window.history.pushState=(...e)=>{f(...e),setTimeout(a,50)},p=()=>setTimeout(a,50),window.addEventListener(`popstate`,p)}}catch(r){e||(console.error(`[FlowEngage] startSession failed`,r),r.message&&r.message.includes(`Too many voice sessions`)?(alert(r.message),n&&n(`minimized`)):(w(`error`),Me.current=`chat`,window.setTimeout(()=>{e||t?.()},0)))}})(),()=>{console.warn(`[FlowEngage:VoiceDebug] ⚠️ useEffect CLEANUP fired — session will be killed`,{agentId:r,siteId:i,apiBaseUrl:a}),e=!0,je.current=!0;try{xn()}catch{}if(i)try{Ee(i)}catch{}typeof window<`u`&&(window.__fe_user_nav_during_speech=null),xr(),z.current&&=(clearTimeout(z.current),null),ze.current&&=(clearTimeout(ze.current),null),ue.current&&=(clearTimeout(ue.current),null),Re.current=!1,R.current=`idle`,f&&typeof window<`u`&&(window.history.pushState=f),p&&typeof window<`u`&&window.removeEventListener(`popstate`,p);let t=d||I.current,n=Ne.current,l=Pe.current;if(t?.endSession?.().catch(()=>{}),n&&o&&a){let e=l?Math.round((Date.now()-l)/1e3):0,t=JSON.stringify({chatId:o,voiceSessionId:n,duration:e,siteId:i}),r=`${a}/api/voice/save-chat-recording`;typeof navigator<`u`&&navigator.sendBeacon&&navigator.sendBeacon(r,new Blob([t],{type:`application/json`}))||fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`},body:t,keepalive:!0}).catch(()=>{}),Ne.current=null,Pe.current=null}typeof c==`function`&&s.current.length>0&&c(s.current).catch?.(()=>{})}},[r,i,a,Be]),(0,d.useEffect)(()=>{if(!ee)return;let e=e=>{let t=e.composedPath?.()?.[0]||e.target;Fe.current&&!Fe.current.contains(t)&&D(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[ee]);let V=async()=>{if(I.current)try{if(T){Ie.current=0,await I.current.setMicMuted?.(!1),E(!1);try{I.current.sendContextualUpdate?.(`The visitor has unmuted. Resume normal interaction and wait for them to speak.`)}catch{}}else{Ie.current=0,await I.current.setMicMuted?.(!0),E(!0);try{I.current.sendContextualUpdate?.(`The visitor has muted their microphone. Do NOT speak, ask if they are there, or prompt them in any way. Stay completely silent until they unmute.`)}catch{}}}catch{}},H=(0,d.useCallback)(async e=>{e?.preventDefault?.();let t=ae.trim().toLowerCase();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)){ce(`Please enter a valid email address.`);return}ce(``),le.current=!0,j.current=!1,B(),A(!1),oe(``),N.current=!1;try{typeof g==`function`&&await g(t)}catch{}I.current?.setMicMuted?.(!1);let n=M.current;M.current=``;try{I.current?.injectAgentSpeech?.(`Thank you for submitting your email.`),I.current?.sendUserMessage?.(n||`The visitor's email is on file. Carry on helping them and never ask for it again.`)}catch{}},[ae,g,B]),U=Array.isArray(e)&&e[0]||`#3B82F6`;return(0,f.jsxs)(`div`,{ref:Fe,style:{position:`relative`,display:`flex`,flexDirection:`column`,alignItems:`center`,gap:`14px`,animation:`fe-orb-slide-up 0.42s cubic-bezier(0.16,1,0.3,1)`},children:[ie&&(0,f.jsxs)(`form`,{onSubmit:H,style:{width:`270px`,maxWidth:`calc(100vw - 40px)`,boxSizing:`border-box`,background:`rgba(15,22,36,0.97)`,backdropFilter:`blur(14px)`,WebkitBackdropFilter:`blur(14px)`,borderRadius:`16px`,boxShadow:`0 10px 40px rgba(0,0,0,0.45)`,padding:`16px`,display:`flex`,flexDirection:`column`,gap:`10px`,animation:`fe-orb-fade-in 0.18s ease`},children:[(0,f.jsx)(`span`,{style:{color:`white`,fontSize:`13px`,fontWeight:600,lineHeight:1.4},children:`Please enter your email to continue`}),(0,f.jsx)(`input`,{type:`email`,inputMode:`email`,autoComplete:`email`,autoFocus:!0,value:ae,onChange:e=>{oe(e.target.value),se&&ce(``)},placeholder:`you@example.com`,style:{width:`100%`,boxSizing:`border-box`,padding:`10px 12px`,borderRadius:`10px`,border:se?`1.5px solid #ef4444`:`1.5px solid #4b5563`,background:`#1f2937`,color:`white`,fontSize:`14px`,outline:`none`}}),se&&(0,f.jsx)(`span`,{style:{color:`#f87171`,fontSize:`12px`},children:se}),(0,f.jsx)(`button`,{type:`submit`,disabled:!ae.trim(),style:{width:`100%`,padding:`10px 12px`,borderRadius:`10px`,border:`none`,background:ae.trim()?U:`#374151`,color:`white`,fontSize:`14px`,fontWeight:600,cursor:ae.trim()?`pointer`:`default`,transition:`background 0.2s ease`},children:`Submit`})]}),(0,f.jsxs)(`div`,{style:{position:`relative`},children:[(C===`listening`||C===`thinking`)&&!T&&(0,f.jsx)(`span`,{style:{position:`absolute`,right:`100%`,marginRight:`18px`,top:`50%`,transform:`translateY(-50%)`,color:`white`,fontSize:`18px`,fontWeight:600,letterSpacing:`0.01em`,textShadow:`0 2px 4px rgba(0,0,0,0.4)`,whiteSpace:`nowrap`},children:C===`thinking`?b:y}),T&&(0,f.jsx)(`span`,{style:{position:`absolute`,right:`100%`,marginRight:`18px`,top:`50%`,transform:`translateY(-50%)`,color:`#f87171`,fontSize:`16px`,fontWeight:600,letterSpacing:`0.01em`,textShadow:`0 2px 4px rgba(0,0,0,0.4)`,whiteSpace:`nowrap`},children:`Muted`}),(0,f.jsx)(E_,{size:72,voiceState:C,isMuted:T,orbColors:e})]}),C===`error`&&(0,f.jsx)(`span`,{style:{color:`#f87171`,fontSize:`13px`,fontWeight:500},children:`Could not start voice session.`}),(0,f.jsxs)(`div`,{style:{display:`flex`,gap:`12px`,alignItems:`center`},children:[(0,f.jsx)(`button`,{onClick:V,title:T?`Unmute`:`Mute`,style:{width:`24px`,height:`24px`,borderRadius:`50%`,background:T?`#7f1d1d`:`#1f2937`,border:T?`1.5px solid #ef4444`:`1.5px solid #4b5563`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:T?`#f87171`:`white`,transition:`all 0.2s ease`,boxShadow:`0 2px 8px rgba(0,0,0,0.4)`,padding:0,flexShrink:0},children:T?(0,f.jsxs)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,f.jsx)(`line`,{x1:`1`,y1:`1`,x2:`23`,y2:`23`}),(0,f.jsx)(`path`,{d:`M9 9v3a3 3 0 005.12 2.12M15 9.34V4a3 3 0 00-5.94-.6`}),(0,f.jsx)(`path`,{d:`M17 16.95A7 7 0 015 12v-2m14 0v2a7 7 0 01-.11 1.23`}),(0,f.jsx)(`line`,{x1:`12`,y1:`19`,x2:`12`,y2:`23`}),(0,f.jsx)(`line`,{x1:`8`,y1:`23`,x2:`16`,y2:`23`})]}):(0,f.jsx)(Xh,{size:16,color:`currentColor`})}),(0,f.jsx)(`button`,{onClick:()=>D(e=>!e),style:{width:`24px`,height:`24px`,borderRadius:`50%`,background:`#1f2937`,border:`1.5px solid #4b5563`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:`white`,transition:`background 0.2s ease`,boxShadow:`0 2px 8px rgba(0,0,0,0.4)`,padding:0,flexShrink:0},children:(0,f.jsx)(ig,{size:16,color:`currentColor`})})]}),ee&&(0,f.jsxs)(`div`,{style:{position:`absolute`,bottom:0,right:0,width:`230px`,zIndex:9999,background:`rgba(15,22,36,0.96)`,backdropFilter:`blur(14px)`,WebkitBackdropFilter:`blur(14px)`,borderRadius:`16px`,boxShadow:`0 10px 40px rgba(0,0,0,0.45)`,animation:`fe-orb-fade-in 0.18s ease`,padding:`12px 12px 40px`},children:[(0,f.jsxs)(`button`,{onClick:()=>{D(!1),Me.current=`chat`,Je(t)},style:{display:`flex`,alignItems:`center`,gap:`12px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`white`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`,whiteSpace:`nowrap`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.08)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,f.jsx)(ag,{}),x]}),i&&(0,f.jsxs)(`button`,{onClick:()=>re(),style:{display:`flex`,alignItems:`center`,justifyContent:`space-between`,gap:`8px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`white`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.08)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,f.jsxs)(`span`,{style:{display:`flex`,alignItems:`center`,gap:`12px`,minWidth:0,overflow:`hidden`},children:[(0,f.jsx)(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,style:{flexShrink:0},children:(0,f.jsx)(`polygon`,{points:`3 11 22 2 13 21 11 13 3 11`})}),(0,f.jsx)(`span`,{style:{overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:`Auto-navigate`})]}),(0,f.jsx)(`span`,{"aria-hidden":!0,style:{width:`34px`,height:`19px`,borderRadius:`999px`,background:O?`#22c55e`:`rgba(255,255,255,0.2)`,position:`relative`,flexShrink:0,transition:`background 0.15s ease`},children:(0,f.jsx)(`span`,{style:{position:`absolute`,top:`2px`,left:O?`17px`:`2px`,width:`15px`,height:`15px`,borderRadius:`50%`,background:`white`,transition:`left 0.15s ease`}})})]}),(0,f.jsxs)(`button`,{onClick:()=>{D(!1),Me.current=`minimized`,Je(n)},style:{display:`flex`,alignItems:`center`,gap:`12px`,width:`100%`,padding:`12px 16px`,background:`none`,border:`none`,color:`#E30C12`,fontSize:`14px`,fontWeight:500,cursor:`pointer`,textAlign:`left`,transition:`background 0.15s ease`,borderRadius:`8px`,whiteSpace:`nowrap`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(227,12,18,0.1)`},onMouseLeave:e=>{e.currentTarget.style.background=`none`},children:[(0,f.jsx)(og,{}),S]}),(0,f.jsx)(`button`,{onClick:()=>D(!1),style:{position:`absolute`,bottom:`8px`,right:`8px`,width:`24px`,height:`24px`,borderRadius:`50%`,background:`rgba(255,255,255,0.15)`,border:`none`,display:`flex`,alignItems:`center`,justifyContent:`center`,cursor:`pointer`,color:`white`},onMouseEnter:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.25)`},onMouseLeave:e=>{e.currentTarget.style.background=`rgba(255,255,255,0.15)`},children:(0,f.jsxs)(`svg`,{width:`12`,height:`12`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,f.jsx)(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),(0,f.jsx)(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})]})})]})]})}function A_({orbColors:e}){let t=Array.isArray(e)&&e.length>0?e:[`#135bd8`,`#ef334b`,`#ffbd2e`],n=t[0],r=t[1]||t[0],i=t[2]||t[0];return(0,f.jsx)(`div`,{style:{width:`32px`,height:`32px`,borderRadius:`50%`,flexShrink:0,background:`
|
|
423
423
|
radial-gradient(circle at 18% 50%, ${n} 0%, ${n}99 35%, transparent 65%),
|
|
424
424
|
radial-gradient(circle at 82% 50%, ${r} 0%, ${r}99 35%, transparent 65%),
|
|
425
425
|
radial-gradient(circle at 50% 82%, ${i} 0%, ${i}99 30%, transparent 58%),
|
|
@@ -13976,7 +13976,11 @@ function H_({ orbColors: e, onEnterChat: t, onEndChat: n, agentId: r, siteId: i,
|
|
|
13976
13976
|
},
|
|
13977
13977
|
onMessage: (t) => {
|
|
13978
13978
|
if (e || pe.current && t.role !== "user" && !/email/i.test(String(t.message || ""))) return;
|
|
13979
|
-
let n = t.role === "user" && /^(Describe the section titled "|\[SYSTEM:|Give the visitor a complete spoken overview|That was the last section)/.test(String(t.message || "").trim())
|
|
13979
|
+
let n = t.role === "user" && /^(Describe the section titled "|\[SYSTEM:|Give the visitor a complete spoken overview|That was the last section)/.test(String(t.message || "").trim());
|
|
13980
|
+
if (t.role === "user" && !n) try {
|
|
13981
|
+
Jh();
|
|
13982
|
+
} catch {}
|
|
13983
|
+
let r = /\b(?:this|that|the)\s+(?:case\s*stud(?:y|ies)|product|article|blog|post|item|service)\b/i.test(String(t.message || ""));
|
|
13980
13984
|
if (!n && !r && t.role === "user" && We.test(String(t.message || ""))) typeof window < "u" && (window.__fe_user_nav_during_speech = null), I(!0), ge.current = !1, me.current = 0, L.current = -1, ve.current = O_(), ye.current = typeof window < "u" ? window.location.pathname : null, setTimeout(() => xe.current?.(), 400), console.log(`[FlowEngage] walkthrough armed — ${ve.current.length} sections on this page`);
|
|
13981
13985
|
else if (!n && t.role === "user" && he.current) {
|
|
13982
13986
|
let e = String(t.message || "").trim(), n = Ye(e), r = (e) => !!e && (Ke.test(e) || Je.test(e));
|