diff --git a/frontend/public/webclient/replypier.min.js b/frontend/public/webclient/replypier.min.js index a791ae7..506a638 100644 --- a/frontend/public/webclient/replypier.min.js +++ b/frontend/public/webclient/replypier.min.js @@ -1,5 +1,5 @@ -(()=>{var we=(ie,ne,t)=>new Promise((E,p)=>{var D=y=>{try{z(t.next(y))}catch(B){p(B)}},G=y=>{try{z(t.throw(y))}catch(B){p(B)}},z=y=>y.done?E(y.value):Promise.resolve(y.value).then(D,G);z((t=t.apply(ie,ne)).next())});var _e=` -`;window.replypierId;window.replypierBaseUrl;window.ReplyPier;window.replypierConfig;(function(){function ie(e){return/^#[0-9A-F]{6}$/i.test(e)}function ne(){return typeof window!="undefined"&&window.replypierBaseUrl?window.replypierBaseUrl:typeof window!="undefined"&&window.APP_CONFIG&&window.APP_CONFIG.API_URL?window.APP_CONFIG.API_URL:"https://api.replypier.site/api/v1"}let t={baseUrl:ne(),containerId:"replypier-container",backdropId:"replypier-backdrop",buttonId:"replypier-button",chatBubbleColor:"#f34611",loadingContainerId:"replypier-loading",tokenKey:"rpid",tokenProvider:null,containerBottom:100,containerRight:20,containerWidth:400,containerHeight:560,launcherBottom:20,launcherRight:20,side:"right",displayMode:"floating",launcher:!0,sidebarWidth:420,searchBarWidth:320,searchPlaceholder:"\u6709\u95EE\u9898\u968F\u65F6\u95EE\u6211\u2026",zIndex:999999,trigger:null,hotkey:!0,surfaceIsPalette:null,chatStyle:null,chatInitiationMessages:[],initiationMessageId:"replypier-initiation",initiationShownKey:"rpim_shown",unreadCount:0,hasOpened:!1},E={};function p(e,i){t[e]=i,E[e]=!0}let D=["floating","sidebar-left","sidebar-right","search-bar"],G=["ASK_ANYTHING","AURORA"],z=680,y=620,B=140,A={width:!1,height:!1},P=0;function j(){return t.surfaceIsPalette===!1?!1:(G.indexOf(t.chatStyle)!==-1||t.displayMode==="search-bar")&&t.displayMode!=="sidebar-left"&&t.displayMode!=="sidebar-right"}function q(){let e=document.getElementById(t.containerId);if(!e)return;if(!j()||!P){e.style.height="";return}let i=Math.min(oe().height,Math.round(window.innerHeight*.76));e.style.height=Math.max(B,Math.min(i,P))+"px"}function oe(){return{width:A.width?t.containerWidth:z,height:A.height?t.containerHeight:y}}function Te(e){if(!e||typeof e!="object")return;let i=function(n,r){E[n]||(t[n]=r)};D.indexOf(e.mode)!==-1&&i("displayMode",e.mode),(e.side==="left"||e.side==="right")&&i("side",e.side),typeof e.launcher=="boolean"&&i("launcher",e.launcher),m(e.width)&&(i("containerWidth",Math.max(280,e.width)),E.containerWidth||(A.width=!0)),m(e.height)&&(i("containerHeight",Math.max(400,e.height)),E.containerHeight||(A.height=!0)),m(e.sidebar_width)&&i("sidebarWidth",Math.max(280,e.sidebar_width)),typeof e.search_placeholder=="string"&&e.search_placeholder&&i("searchPlaceholder",e.search_placeholder.slice(0,80)),m(e.offset_bottom)&&i("launcherBottom",Math.max(0,e.offset_bottom)),m(e.offset_side)&&i("launcherRight",Math.max(0,e.offset_side)),m(e.z_index)&&i("zIndex",Math.max(1,Math.floor(e.z_index)))}function Ie(){let e=j(),i=e?oe():{width:t.containerWidth,height:t.containerHeight};return{type:"WIDGET_DISPLAY",mode:e?"ask-ai":t.displayMode,width:i.width,height:i.height,hotkey:t.hotkey!==!1}}let l=null,R=[],V=!1,g=null,L=null,x=!1;function Z(e){l?e():x||R.push(e)}let X={},re=!1;function _(e,i){let n=X[e];!n||!n.length||n.slice().forEach(function(r){try{r(i)}catch(c){}})}function ze(e){let i=/^#?([0-9a-f]{6})$/i.exec((e||"").trim());if(!i)return"#0B0C10";let n=parseInt(i[1],16);return(.299*(n>>16&255)+.587*(n>>8&255)+.114*(n&255))/255>.6?"#0B0C10":"#FFFFFF"}let ae=!1,M=null,se=[];function N(){if(ae)return;ae=!0,M&&(clearTimeout(M),M=null);let e=document.getElementById(t.buttonId);e&&e.classList.remove("replypier-pending"),se.splice(0).forEach(i=>{try{i()}catch(n){}})}function F(){let e=document.getElementById(t.buttonId);if(!e)return;let i=e.querySelector(".rp-badge");if(!i)return;let n=t.hasOpened?t.unreadCount:(t.chatInitiationMessages||[]).length;i.textContent=n>99?"99+":String(n),i.style.display=n>0&&!e.classList.contains("active")?"flex":"none"}function O(){let e=t.displayMode,i=e==="sidebar-left"||e==="sidebar-right",n=e==="sidebar-left"?"left":e==="sidebar-right"?"right":t.side,r=e==="search-bar"?48:64,c=G.indexOf(t.chatStyle)===-1,a=j(),d=oe(),w=document.createElement("style");w.id="replypier-styles",w.textContent=` +(()=>{var ve=(re,oe,t)=>new Promise(($,p)=>{var G=y=>{try{P(t.next(y))}catch(R){p(R)}},q=y=>{try{P(t.throw(y))}catch(R){p(R)}},P=y=>y.done?$(y.value):Promise.resolve(y.value).then(G,q);P((t=t.apply(re,oe)).next())});var ze=` +`;window.replypierId;window.replypierBaseUrl;window.ReplyPier;window.replypierConfig;(function(){function re(e){return/^#[0-9A-F]{6}$/i.test(e)}function oe(){return typeof window!="undefined"&&window.replypierBaseUrl?window.replypierBaseUrl:typeof window!="undefined"&&window.APP_CONFIG&&window.APP_CONFIG.API_URL?window.APP_CONFIG.API_URL:"https://api.replypier.site/api/v1"}let t={baseUrl:oe(),containerId:"replypier-container",backdropId:"replypier-backdrop",buttonId:"replypier-button",chatBubbleColor:"#f34611",loadingContainerId:"replypier-loading",tokenKey:"rpid",tokenProvider:null,containerBottom:100,containerRight:20,containerWidth:400,containerHeight:560,launcherBottom:20,launcherRight:20,side:"right",displayMode:"floating",launcher:!0,sidebarWidth:420,searchBarWidth:320,searchPlaceholder:"\u6709\u95EE\u9898\u968F\u65F6\u95EE\u6211\u2026",zIndex:999999,trigger:null,hotkey:!0,surfaceIsPalette:null,chatStyle:null,chatInitiationMessages:[],initiationMessageId:"replypier-initiation",initiationShownKey:"rpim_shown",unreadCount:0,hasOpened:!1},$={};function p(e,i){t[e]=i,$[e]=!0}let G=["floating","sidebar-left","sidebar-right","search-bar"],q=["ASK_ANYTHING","AURORA"],P=680,y=620,R=140,A={width:!1,height:!1},N=0;function V(){return t.surfaceIsPalette===!1?!1:(q.indexOf(t.chatStyle)!==-1||t.displayMode==="search-bar")&&t.displayMode!=="sidebar-left"&&t.displayMode!=="sidebar-right"}function Z(){let e=document.getElementById(t.containerId);if(!e)return;if(!V()||!N){e.style.height="";return}let i=Math.min(ae().height,Math.round(window.innerHeight*.76));e.style.height=Math.max(R,Math.min(i,N))+"px"}function ae(){return{width:A.width?t.containerWidth:P,height:A.height?t.containerHeight:y}}function Be(e){if(!e||typeof e!="object")return;let i=function(n,o){$[n]||(t[n]=o)};G.indexOf(e.mode)!==-1&&i("displayMode",e.mode),(e.side==="left"||e.side==="right")&&i("side",e.side),typeof e.launcher=="boolean"&&i("launcher",e.launcher),h(e.width)&&(i("containerWidth",Math.max(280,e.width)),$.containerWidth||(A.width=!0)),h(e.height)&&(i("containerHeight",Math.max(400,e.height)),$.containerHeight||(A.height=!0)),h(e.sidebar_width)&&i("sidebarWidth",Math.max(280,e.sidebar_width)),typeof e.search_placeholder=="string"&&e.search_placeholder&&i("searchPlaceholder",e.search_placeholder.slice(0,80)),h(e.offset_bottom)&&i("launcherBottom",Math.max(0,e.offset_bottom)),h(e.offset_side)&&i("launcherRight",Math.max(0,e.offset_side)),h(e.z_index)&&i("zIndex",Math.max(1,Math.floor(e.z_index)))}function ke(){let e=V(),i=e?ae():{width:t.containerWidth,height:t.containerHeight};return{type:"WIDGET_DISPLAY",mode:e?"ask-ai":t.displayMode,width:i.width,height:i.height,hotkey:t.hotkey!==!1}}let l=null,F=[],X=!1,g=null,L=null,x=!1;function J(e){l?e():x||F.push(e)}let Q={},se=!1;function _(e,i){let n=Q[e];!n||!n.length||n.slice().forEach(function(o){try{o(i)}catch(c){}})}function Pe(e){let i=/^#?([0-9a-f]{6})$/i.exec((e||"").trim());if(!i)return"#0B0C10";let n=parseInt(i[1],16);return(.299*(n>>16&255)+.587*(n>>8&255)+.114*(n&255))/255>.6?"#0B0C10":"#FFFFFF"}let le=!1,E=null,de=[];function ee(){if(le)return;le=!0,E&&(clearTimeout(E),E=null);let e=document.getElementById(t.buttonId);e&&e.classList.remove("replypier-pending"),de.splice(0).forEach(i=>{try{i()}catch(n){}})}function O(){let e=document.getElementById(t.buttonId);if(!e)return;let i=e.querySelector(".rp-badge");if(!i)return;let n=t.hasOpened?t.unreadCount:(t.chatInitiationMessages||[]).length;i.textContent=n>99?"99+":String(n),i.style.display=n>0&&!e.classList.contains("active")?"flex":"none"}function H(){let e=t.displayMode,i=e==="sidebar-left"||e==="sidebar-right",n=e==="sidebar-left"?"left":e==="sidebar-right"?"right":t.side,o=e==="search-bar"?48:64,c=q.indexOf(t.chatStyle)===-1,a=V(),d=ae(),v=document.createElement("style");v.id="replypier-styles",v.textContent=` /* ===== Rounded-square launcher (design comp) ===== */ #${t.buttonId} { position: fixed; @@ -9,7 +9,7 @@ height: 64px; border-radius: 20px 20px 20px 6px; background: ${t.chatBubbleColor}; - color: ${ze(t.chatBubbleColor)}; + color: ${Pe(t.chatBubbleColor)}; box-shadow: 0 16px 40px -8px ${t.chatBubbleColor}cc, inset 0 0 0 1px rgba(255,255,255,0.08); cursor: pointer; z-index: ${t.zIndex}; @@ -482,7 +482,7 @@ position: fixed !important; /* Sit above the launcher (+12px gap) and align to its edge on the active side, so it never overlaps the icon regardless of the configured position/mode. */ - bottom: ${(t.launcherBottom||20)+r+12}px !important; + bottom: ${(t.launcherBottom||20)+o+12}px !important; ${n}: ${t.launcherRight||20}px !important; max-width: 260px !important; background: white !important; @@ -805,11 +805,35 @@ font-size: 14px; line-height: 1.6; color: #64748b; - margin: 0 0 24px 0; + margin: 0 0 20px 0; max-width: 280px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; } + .replypier-error-retry { + min-width: 104px; + min-height: 44px; + padding: 10px 24px; + margin: 0 0 22px; + border: 0; + border-radius: 12px; + background: #4f46e5; + color: #fff; + font: 600 14px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + cursor: pointer; + } + .replypier-error-retry:hover:not(:disabled) { + background: #4338ca; + } + .replypier-error-retry:focus-visible { + outline: 3px solid #818cf8; + outline-offset: 3px; + } + .replypier-error-retry:disabled { + opacity: 0.7; + cursor: wait; + } + .replypier-error-footer { display: flex; align-items: center; @@ -854,7 +878,6 @@ } .replypier-error-message { - font-size: 13px; max-width: 260px; } } @@ -865,11 +888,11 @@ re-show (display: flex !important at equal specificity). */ #${t.buttonId}, #${t.buttonId}.mobile-closed { display: none !important; } `} - `;let f=document.getElementById("replypier-styles");f&&f.remove(),document.head.appendChild(w),ve()}function ve(){let e=document.getElementById(t.buttonId);if(!e)return;let i=e.querySelector(".rp-search-placeholder");i&&i.textContent!==t.searchPlaceholder&&(i.textContent=t.searchPlaceholder)}function C(e){if(typeof e!="string")return null;let i=e.trim();return!i||i==="undefined"||i==="null"?null:i}function le(){return C(window.replypierToken)||C(localStorage.getItem(t.tokenKey))}function de(e){let i=C(e);if(!i)return null;let n=i.split(".")[1];if(!n)return null;try{let r=JSON.parse(atob(n.replace(/-/g,"+").replace(/_/g,"/")));return r.sub||r.customer_id||null}catch(r){return null}}function J(e){e&&localStorage.setItem(t.tokenKey,e)}function W(){localStorage.removeItem(t.tokenKey)}function H(){return window.innerWidth<=768}function Be(e){return e?e.replace(/\uFFFD/g,"").replace(/[��]/g,"").replace(/[\x00-\x1F\x7F-\x9F]/g,"").trim():""}function Pe(){if(!t.chatInitiationMessages||t.chatInitiationMessages.length===0)return null;let e=Math.floor(Math.random()*t.chatInitiationMessages.length),i=t.chatInitiationMessages[e];return i?Be(i):null}function Re(){return H()||!t.launcher||!t.chatInitiationMessages||t.chatInitiationMessages.length===0?!1:!sessionStorage.getItem(t.initiationShownKey)}function pe(){sessionStorage.setItem(t.initiationShownKey,"true")}function Ne(){let e=Pe();if(!e)return null;let i=document.createElement("div");return i.id=t.initiationMessageId,i.innerHTML=` + `;let m=document.getElementById("replypier-styles");m&&m.remove(),document.head.appendChild(v),$e()}function $e(){let e=document.getElementById(t.buttonId);if(!e)return;let i=e.querySelector(".rp-search-placeholder");i&&i.textContent!==t.searchPlaceholder&&(i.textContent=t.searchPlaceholder)}function M(e){if(typeof e!="string")return null;let i=e.trim();return!i||i==="undefined"||i==="null"?null:i}function pe(){return M(window.replypierToken)||M(localStorage.getItem(t.tokenKey))}function ce(e){let i=M(e);if(!i)return null;let n=i.split(".")[1];if(!n)return null;try{let o=JSON.parse(atob(n.replace(/-/g,"+").replace(/_/g,"/")));return o.sub||o.customer_id||null}catch(o){return null}}function te(e){e&&localStorage.setItem(t.tokenKey,e)}function W(){localStorage.removeItem(t.tokenKey)}function U(){return window.innerWidth<=768}function Re(e){return e?e.replace(/\uFFFD/g,"").replace(/[��]/g,"").replace(/[\x00-\x1F\x7F-\x9F]/g,"").trim():""}function Ne(){if(!t.chatInitiationMessages||t.chatInitiationMessages.length===0)return null;let e=Math.floor(Math.random()*t.chatInitiationMessages.length),i=t.chatInitiationMessages[e];return i?Re(i):null}function Fe(){return U()||!t.launcher||!t.chatInitiationMessages||t.chatInitiationMessages.length===0?!1:!sessionStorage.getItem(t.initiationShownKey)}function he(){sessionStorage.setItem(t.initiationShownKey,"true")}function Oe(){let e=Ne();if(!e)return null;let i=document.createElement("div");return i.id=t.initiationMessageId,i.innerHTML=`

- `,i.dataset.fullMessage=e,i}function Fe(e,i,n=50){let r=e.querySelector(".initiation-message-text");if(!r)return;let c=0,a=setInterval(()=>{c{e.classList.add("show"),setTimeout(()=>{let r=e.dataset.fullMessage;r&&Fe(e,r,40)},300)},1500),e.addEventListener("click",r=>{r.target.classList.contains("initiation-close")||(e.typeInterval&&clearInterval(e.typeInterval),ce(e),i(),pe())});let n=e.querySelector(".initiation-close");n&&n.addEventListener("click",r=>{r.stopPropagation(),e.typeInterval&&clearInterval(e.typeInterval),ce(e),pe()})}function ce(e){e&&(e.classList.remove("show"),setTimeout(()=>{e.parentNode&&e.parentNode.removeChild(e)},400))}function he(){x=!1,O();let e=document.createElement("div");e.id=t.buttonId,e.classList.add("replypier-pending"),e.innerHTML=` + `,i.dataset.fullMessage=e,i}function He(e,i,n=50){let o=e.querySelector(".initiation-message-text");if(!o)return;let c=0,a=setInterval(()=>{c{e.classList.add("show"),setTimeout(()=>{let o=e.dataset.fullMessage;o&&He(e,o,40)},300)},1500),e.addEventListener("click",o=>{o.target.classList.contains("initiation-close")||(e.typeInterval&&clearInterval(e.typeInterval),ue(e),i(),he())});let n=e.querySelector(".initiation-close");n&&n.addEventListener("click",o=>{o.stopPropagation(),e.typeInterval&&clearInterval(e.typeInterval),ue(e),he()})}function ue(e){e&&(e.classList.remove("show"),setTimeout(()=>{e.parentNode&&e.parentNode.removeChild(e)},400))}function me(){x=!1,H();let e=document.createElement("div");e.id=t.buttonId,e.classList.add("replypier-pending"),e.innerHTML=`
@@ -879,11 +902,11 @@ - `,M=setTimeout(N,8e3);let i=document.createElement("div");i.id=t.containerId;let n=document.createElement("div");n.id="replypier-mobile-close",n.innerHTML=` + `,E=setTimeout(ee,8e3);let i=document.createElement("div");i.id=t.containerId;let n=document.createElement("div");n.id="replypier-mobile-close",n.innerHTML=` - `;let r=document.createElement("div");r.id="replypier-mobile-topbar",r.innerHTML=` + `;let o=document.createElement("div");o.id="replypier-mobile-topbar",o.innerHTML=`

\u804A\u5929

- `;let c=document.createElement("div");c.id=t.backdropId,document.body.appendChild(e),document.body.appendChild(c),document.body.appendChild(i),document.body.appendChild(n),document.body.appendChild(r),F(),ve();let a=!1,d=null,w=!1,f=null;function b(){a=!a,i.classList.toggle("active"),c.classList.toggle("active",a),e.classList.toggle("active"),n.classList.toggle("active"),a&&(t.hasOpened=!0,t.unreadCount=0),d&&d.contentWindow&&d.contentWindow.postMessage({type:"WIDGET_VISIBILITY",open:a},"*"),F(),a&&f&&(ce(f),f=null,pe()),H()?a?(e.classList.remove("mobile-closed"),e.style.animation=""):(e.classList.add("mobile-closed"),e.style.animation="replypier-float 4s ease-in-out infinite",r.classList.remove("active"),document.body.classList.remove("ask-anything-mobile")):a&&n.classList.remove("active"),a&&d&&d.contentWindow.postMessage({type:"SCROLL_TO_BOTTOM"},"*"),_(a?"open":"close")}let U=null,fe=!1;function Ge(o){let s=String(o||"").slice(0,2e3);s&&(fe&&d&&d.contentWindow?d.contentWindow.postMessage({type:"PREFILL_MESSAGE",text:s},"*"):U=s)}let K=0;function Ce(){for(K++;i.firstChild;)i.removeChild(i.firstChild);d=null,g=null,L=null,fe=!1,re=!1,U=null,w=!1,e.classList.remove("loading"),P=0,q()}function je(){a&&b(),Ce(),t.unreadCount=0,t.hasOpened=!1,F(),Q()}function qe(){a&&b(),Ce(),window.removeEventListener("resize",Le),window.removeEventListener("message",Ae),f&&(f.typeInterval&&clearInterval(f.typeInterval),me(f),f=null),[e,c,i,n,r].forEach(me),me(document.getElementById("replypier-styles")),document.body.classList.remove("ask-anything-mobile"),ae=!1,M&&(clearTimeout(M),M=null),se.length=0,t.unreadCount=0,t.hasOpened=!1,l=null,R.length=0,V=!1,x=!0}function Se(o){let s=document.createElement("div");s.className="replypier-error-ui";let I=document.createElement("div");I.className="replypier-error-card";let v=document.createElement("button");v.type="button",v.className="replypier-error-close",v.setAttribute("aria-label","\u5173\u95ED\u804A\u5929"),v.textContent="\xD7",v.addEventListener("click",function(){a&&b()}),s.appendChild(v);let u=document.createElement("div");u.className="replypier-error-icon-wrapper";let h=document.createElementNS("http://www.w3.org/2000/svg","svg");h.setAttribute("class","replypier-error-icon"),h.setAttribute("viewBox","0 0 24 24"),h.setAttribute("fill","none"),h.setAttribute("stroke","currentColor"),h.setAttribute("stroke-width","1.5");let k=document.createElementNS("http://www.w3.org/2000/svg","path");k.setAttribute("d","M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z");let $=document.createElementNS("http://www.w3.org/2000/svg","path");$.setAttribute("d","M9 12l2 2 4-4"),h.appendChild(k),h.appendChild($),u.appendChild(h);let S=document.createElement("h2");S.className="replypier-error-title",S.textContent="\u5BA2\u670D\u6682\u4E0D\u53EF\u7528";let be=document.createElement("p");be.className="replypier-error-message",be.textContent=o;let te=document.createElement("div");te.className="replypier-error-footer";let ye=document.createElement("span");ye.innerHTML=_e;let xe=ye.querySelector("svg");xe.setAttribute("width","20"),xe.setAttribute("height","16"),xe.setAttribute("aria-hidden","true");let T=document.createElement("a");return T.href="https://replypier.site",T.target="_blank",T.rel="noopener noreferrer",T.className="replypier-error-footer-link",T.textContent="\u7531 ReplyPier \u63D0\u4F9B\u652F\u6301",te.appendChild(ye),te.appendChild(T),I.appendChild(u),I.appendChild(S),I.appendChild(be),I.appendChild(te),s.appendChild(I),s}function Q(){return we(this,null,function*(){if(w||d)return;let o=K;try{w=!0,e.classList.add("loading");let s=le(),I=`${t.baseUrl}/widgets/${window.replypierId}/data?widget_id=${window.replypierId}`,v={method:"GET",mode:"cors",headers:s?{Authorization:`Bearer ${s}`}:{}};fetch(I,v).then(u=>we(null,null,function*(){if(o!==K)return null;if(u.status===401)try{let k=(yield u.json()).detail;if(k&&k.code==="identity_expired")return e.classList.remove("loading"),_("identityExpired"),Ve(),null;let $=typeof k=="string"?k:"";if($.includes("generate-token")||$.includes("API key")||$.includes("Token must be obtained")){e.classList.remove("loading"),W();let S=Se("\u6B64\u804A\u5929\u7EC4\u4EF6\u5C1A\u672A\u914D\u7F6E\u5B8C\u6210\uFF0C\u8BF7\u8054\u7CFB\u7F51\u7AD9\u7BA1\u7406\u5458\u5F00\u901A\u5728\u7EBF\u5BA2\u670D\u3002");return i.appendChild(S),N(),null}}catch(h){}if(!u.ok)throw new Error("HTTP error! status: "+u.status);return u.text()})).then(u=>{u!==null&&o===K&&(d=document.createElement("iframe"),d.className="replypier-iframe",d.title="\u804A\u5929",d.srcdoc=u,d.addEventListener("load",function(){fe=!0,d.contentWindow.postMessage(Ie(),"*"),d.contentWindow.postMessage({type:"WIDGET_VISIBILITY",open:a},"*"),U&&(d.contentWindow.postMessage({type:"PREFILL_MESSAGE",text:U},"*"),U=null),re=!0,Y=!1,_("ready")}),i.appendChild(d),g=d.contentWindow,e.classList.remove("loading"),d.style.opacity="1")}).catch(u=>{if(o!==K)return;console.error("Failed to load widget:",u),e.classList.remove("loading");let h=window.location.hostname||"\u5F53\u524D\u57DF\u540D",$=/^(localhost|127\.0\.0\.1|0\.0\.0\.0|\[?::1\]?)$/i.test(h)||/\.local$/i.test(h)?"\u804A\u5929\u7A97\u53E3\u65E0\u6CD5\u5728\u201C"+h+"\u201D\u4E0A\u52A0\u8F7D\u3002\u672C\u5730\u6D4B\u8BD5\u65F6\uFF0C\u8BF7\u5728 ReplyPier \u540E\u53F0\uFF08\u7EC4\u7EC7\u8BBE\u7F6E\uFF09\u4E2D\u628A\u7EC4\u7EC7\u57DF\u540D\u8BBE\u4E3A\u201C"+h+"\u201D\u3002":"\u804A\u5929\u7A97\u53E3\u65E0\u6CD5\u5728\u201C"+h+"\u201D\u4E0A\u52A0\u8F7D\u3002\u8BF7\u5728 ReplyPier \u540E\u53F0\uFF08\u7EC4\u7EC7\u8BBE\u7F6E\uFF09\u4E2D\u5C06\u6B64\u57DF\u540D\u52A0\u5165\u7EC4\u7EC7\uFF0C\u5373\u53EF\u5728\u6B64\u542F\u7528\u804A\u5929\u3002",S=Se($);i.appendChild(S),N()})}catch(s){console.error("Failed to load widget:",s),e.classList.remove("loading"),N()}finally{w=!1}})}function Ae(o){if(!(!o.data||typeof o.data.type!="string")&&!(!g||o.source!==g)){if(o.data.type==="UNREAD_COUNT"){t.unreadCount=Math.max(0,parseInt(o.data.count,10)||0),F(),_("unread",t.unreadCount);return}if(o.data.type==="TOKEN_UPDATE"){let s=C(o.data.token);if(!s)return;J(s),o.source.postMessage({type:"TOKEN_RECEIVED",token:s},"*");return}if(o.data.type==="CHAT_SESSION"){L=String(o.data.sessionId||"")||null,o.data.created&&_("chatCreated",{sessionId:L,authenticated:!!o.data.authenticated});return}o.data.type==="IDENTITY_EXPIRED"&&(_("identityExpired"),Ze())}}function Ve(){if(Y||typeof t.tokenProvider!="function"){W(),window.replypierToken=null,Q();return}Y=!0,Promise.resolve().then(()=>t.tokenProvider()).then(o=>{let s=C(o);s?(window.replypierToken=s,J(s)):(W(),window.replypierToken=null)}).catch(o=>{console.error("ReplyPier: tokenProvider failed",o),W(),window.replypierToken=null}).finally(()=>{Q()})}let ge=!1,Y=!1;function Ze(){if(!ge){if(Y||typeof t.tokenProvider!="function"){ee({type:"IDENTITY_UNAVAILABLE"});return}ge=!0,Y=!0,Promise.resolve().then(()=>t.tokenProvider()).then(o=>{let s=C(o);if(!s){ee({type:"IDENTITY_UNAVAILABLE"});return}window.replypierToken=s,J(s),ee({type:"TOKEN_REFRESH",token:s})}).catch(o=>{console.error("ReplyPier: tokenProvider failed",o),ee({type:"IDENTITY_UNAVAILABLE"})}).finally(()=>{ge=!1})}}function ee(o){g&&g.postMessage(o,"*")}window.addEventListener("message",Ae),Q(),se.push(()=>{setTimeout(()=>{x||!a&&t.chatInitiationMessages.length>0&&(f=Ne(),Oe(f,b))},2e3)}),e.addEventListener("click",b),n.addEventListener("click",b),c.addEventListener("click",function(){a&&b()}),H()&&!a&&(e.classList.add("mobile-closed"),e.style.animation="replypier-float 4s ease-in-out infinite");function Le(){let o=H();o&&!a?(e.classList.add("mobile-closed"),e.style.animation="replypier-float 4s ease-in-out infinite",n.classList.remove("active")):o||(e.classList.remove("mobile-closed"),e.style.animation="",a&&n.classList.remove("active")),O(),q()}window.addEventListener("resize",Le),l={isOpen:function(){return a},toggle:b,open:function(o){a||b(),o&&o.message&&Ge(o.message)},close:function(){a&&b()},reload:je,destroy:qe},R.splice(0).forEach(function(o){try{o()}catch(s){}})}$e(window.replypierConfig),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){x||he()}):he();let We=typeof navigator!="undefined"&&/Mac|iPod|iPhone|iPad/.test(navigator.platform||"");document.addEventListener("keydown",function(e){if(!l||!j())return;let i=We?e.metaKey&&!e.ctrlKey:e.ctrlKey&&!e.metaKey;if(t.hotkey&&i&&!e.altKey&&!e.repeat&&(e.key==="k"||e.key==="K")){e.preventDefault(),l.toggle();return}e.key==="Escape"&&l.isOpen()&&l.close()}),document.addEventListener("click",function(e){let i=e.target;if(!i||!i.closest)return;let n=i.closest("[data-replypier-open]");if(!n&&t.trigger)try{n=i.closest(t.trigger)}catch(r){}n&&(n.tagName==="A"&&e.preventDefault(),l?l.toggle():!x&&!V&&(V=!0,R.push(function(){l.open()})))}),window.addEventListener("message",function(e){if(!(!e.data||typeof e.data.type!="string")&&!(!g||e.source!==g)){if(e.data.type==="WIDGET_RESIZE"){if(typeof e.data.height!="number"||!isFinite(e.data.height))return;P=Math.round(e.data.height),q();return}if(e.data.type==="WIDGET_SURFACE"){let i=!!e.data.palette;if(t.surfaceIsPalette===i)return;t.surfaceIsPalette=i,i||(P=0),O(),q();return}if(e.data.type==="WIDGET_MINIMIZE"){Z(function(){l.close()});return}if(e.data.type==="CUSTOMIZATION_UPDATE"){let i=e.data.data,n=i.chat_bubble_color;if(t.chatBubbleColor=ie(n)?n:t.chatBubbleColor,typeof i.chat_style=="string"&&(t.chatStyle=i.chat_style),i.chat_initiation_messages&&Array.isArray(i.chat_initiation_messages)&&(t.chatInitiationMessages=i.chat_initiation_messages),Te(i.widget_display),!H())E.containerBottom||(t.containerBottom=i.chat_style==="ASK_ANYTHING"?90:100);else{let r=l?l.isOpen():!1,c=document.getElementById("replypier-mobile-topbar");i.chat_style==="ASK_ANYTHING"&&r?(document.body.classList.add("ask-anything-mobile"),c&&c.classList.add("active")):(document.body.classList.remove("ask-anything-mobile"),c&&c.classList.remove("active"))}O(),F(),N(),e.source&&typeof e.source.postMessage=="function"&&e.source.postMessage(Ie(),"*")}}});function m(e){return typeof e=="number"&&isFinite(e)}function me(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function ue(){document.getElementById("replypier-styles")&&O()}function ke(e,i){if(!e||typeof e!="object")return;(e.side==="left"||e.side==="right")&&p("side",e.side),m(e.bottom)&&p("launcherBottom",Math.max(0,e.bottom));let n=m(e.offset)?e.offset:e.right;m(n)&&p("launcherRight",Math.max(0,n)),i||ue()}function $e(e){!e||typeof e!="object"||(e.baseUrl&&(t.baseUrl=e.baseUrl),e.id&&(window.replypierId=e.id),typeof e.tokenProvider=="function"&&(t.tokenProvider=e.tokenProvider),typeof e.launcher=="boolean"&&p("launcher",e.launcher),D.indexOf(e.displayMode)!==-1&&p("displayMode",e.displayMode),m(e.width)&&(p("containerWidth",Math.max(280,e.width)),A.width=!0),m(e.height)&&(p("containerHeight",Math.max(400,e.height)),A.height=!0),typeof e.hotkey=="boolean"&&p("hotkey",e.hotkey),m(e.sidebarWidth)&&p("sidebarWidth",Math.max(280,e.sidebarWidth)),m(e.zIndex)&&p("zIndex",Math.max(1,Math.floor(e.zIndex))),typeof e.trigger=="string"&&e.trigger&&p("trigger",e.trigger),e.searchBar&&typeof e.searchBar=="object"&&(typeof e.searchBar.placeholder=="string"&&e.searchBar.placeholder&&p("searchPlaceholder",e.searchBar.placeholder.slice(0,80)),m(e.searchBar.width)&&p("searchBarWidth",Math.max(160,e.searchBar.width))),e.position&&ke(e.position,!0),ue())}function He(){l&&l.reload()}function Ee(e,i){if(e&&i){let n=`${t.baseUrl}/widgets/${window.replypierId}/end-chat?session_id=${encodeURIComponent(i)}&reason=CUSTOMER_REQUEST`;try{fetch(n,{method:"POST",mode:"cors",keepalive:!0,headers:{Authorization:`Bearer ${e}`}}).catch(()=>{})}catch(r){}}L=null,He()}function Ue(){if(l){l.reload();return}x&&(x=!1,document.readyState!=="loading"&&he())}function Ke(){if(l){l.destroy();return}x=!0,R.length=0,V=!1}function Ye(e){let i=C(e);if(!i)return;let n=le();if(i===n)return;let r=n&&de(i)&&de(i)===de(n);if(window.replypierToken=i,J(i),r&&g){g.postMessage({type:"TOKEN_REFRESH",token:i},"*");return}Ee(n,L)}function De(){let e=le(),i=L;window.replypierToken=null,W(),Ee(e,i)}function Me(e){p("launcher",!!e),ue()}window.ReplyPier={init:$e,setPosition:function(e){ke(e)},open:function(e){Z(function(){l.open(e)})},close:function(){Z(function(){l.close()})},toggle:function(){Z(function(){l.toggle()})},isOpen:function(){return l?l.isOpen():!1},showLauncher:function(){Me(!0)},hideLauncher:function(){Me(!1)},identify:function(e){Ye(e)},logout:function(){De()},reload:function(){Ue()},destroy:function(){Ke()},on:function(e,i){if(!(typeof e!="string"||typeof i!="function")){if(e==="ready"&&re){try{i()}catch(n){}return}(X[e]=X[e]||[]).push(i)}},off:function(e,i){let n=X[e];if(!n)return;let r=n.indexOf(i);r!==-1&&n.splice(r,1)}}})();})(); + `;let c=document.createElement("div");c.id=t.backdropId,document.body.appendChild(e),document.body.appendChild(c),document.body.appendChild(i),document.body.appendChild(n),document.body.appendChild(o),O(),$e();let a=!1,d=null,v=!1,m=null;function b(){a=!a,i.classList.toggle("active"),c.classList.toggle("active",a),e.classList.toggle("active"),n.classList.toggle("active"),a&&(t.hasOpened=!0,t.unreadCount=0),d&&d.contentWindow&&d.contentWindow.postMessage({type:"WIDGET_VISIBILITY",open:a},"*"),O(),a&&m&&(ue(m),m=null,he()),U()?a?(e.classList.remove("mobile-closed"),e.style.animation=""):(e.classList.add("mobile-closed"),e.style.animation="replypier-float 4s ease-in-out infinite",o.classList.remove("active"),document.body.classList.remove("ask-anything-mobile")):a&&n.classList.remove("active"),a&&d&&d.contentWindow.postMessage({type:"SCROLL_TO_BOTTOM"},"*"),_(a?"open":"close")}let K=null,ge=!1;function qe(r){let s=String(r||"").slice(0,2e3);s&&(ge&&d&&d.contentWindow?d.contentWindow.postMessage({type:"PREFILL_MESSAGE",text:s},"*"):K=s)}let Y=0;function Ae(){for(Y++;i.firstChild;)i.removeChild(i.firstChild);d=null,g=null,L=null,ge=!1,se=!1,K=null,v=!1,e.classList.remove("loading"),N=0,Z()}function Ve(){a&&b(),Ae(),t.unreadCount=0,t.hasOpened=!1,O(),D()}function Ze(){a&&b(),Ae(),window.removeEventListener("resize",Te),window.removeEventListener("message",_e),m&&(m.typeInterval&&clearInterval(m.typeInterval),T(m),m=null),[e,c,i,n,o].forEach(T),T(document.getElementById("replypier-styles")),document.body.classList.remove("ask-anything-mobile"),le=!1,E&&(clearTimeout(E),E=null),de.length=0,t.unreadCount=0,t.hasOpened=!1,l=null,F.length=0,X=!1,x=!0}function Xe(r,s){let w=document.createElement("div");w.className="replypier-error-ui";let I=document.createElement("div");I.className="replypier-error-card";let k=document.createElement("button");k.type="button",k.className="replypier-error-close",k.setAttribute("aria-label","\u5173\u95ED\u804A\u5929"),k.textContent="\xD7",k.addEventListener("click",function(){a&&b()}),w.appendChild(k);let u=document.createElement("div");u.className="replypier-error-icon-wrapper";let f=document.createElementNS("http://www.w3.org/2000/svg","svg");f.setAttribute("class","replypier-error-icon"),f.setAttribute("viewBox","0 0 24 24"),f.setAttribute("aria-hidden","true"),f.setAttribute("fill","none"),f.setAttribute("stroke","currentColor"),f.setAttribute("stroke-width","1.5"),f.setAttribute("stroke-linecap","round"),f.setAttribute("stroke-linejoin","round");let C=document.createElementNS("http://www.w3.org/2000/svg","path");C.setAttribute("d","M5 4h14a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-8l-4 3v-3H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z");let z=document.createElementNS("http://www.w3.org/2000/svg","path");z.setAttribute("d","M8 10h8M8 13h5"),f.appendChild(C),f.appendChild(z),u.appendChild(f);let ye=document.createElement("h2");ye.className="replypier-error-title",ye.textContent="\u5BA2\u670D\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5";let xe=document.createElement("p");xe.className="replypier-error-message",xe.textContent=r;let S=document.createElement("button");S.type="button",S.className="replypier-error-retry",S.textContent="\u91CD\u8BD5",S.addEventListener("click",function(){S.disabled=!0,S.textContent="\u6B63\u5728\u91CD\u8BD5\u2026",s()});let ne=document.createElement("div");ne.className="replypier-error-footer";let we=document.createElement("span");we.innerHTML=ze;let Ie=we.querySelector("svg");Ie.setAttribute("width","20"),Ie.setAttribute("height","16"),Ie.setAttribute("aria-hidden","true");let B=document.createElement("a");return B.href="https://replypier.site",B.target="_blank",B.rel="noopener noreferrer",B.className="replypier-error-footer-link",B.textContent="\u7531 ReplyPier \u63D0\u4F9B\u652F\u6301",ne.appendChild(we),ne.appendChild(B),I.appendChild(u),I.appendChild(ye),I.appendChild(xe),I.appendChild(S),I.appendChild(ne),w.appendChild(I),w}function Le(r){T(r);let s=Xe("\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5\uFF0C\u6216\u7A0D\u540E\u518D\u6765\u3002",function(){D(s)});i.appendChild(s),ee()}function D(r){return ve(this,null,function*(){if(v||d)return;let s=Y;try{v=!0,e.classList.add("loading");let w=pe(),I=`${t.baseUrl}/widgets/${window.replypierId}/data?widget_id=${window.replypierId}`,k={method:"GET",mode:"cors",headers:w?{Authorization:`Bearer ${w}`}:{}};fetch(I,k).then(u=>ve(null,null,function*(){if(s!==Y)return null;if(u.status===401)try{let C=(yield u.json()).detail;if(C&&C.code==="identity_expired")return e.classList.remove("loading"),T(r),_("identityExpired"),Je(),null;let z=typeof C=="string"?C:"";if(z.includes("generate-token")||z.includes("API key")||z.includes("Token must be obtained"))return e.classList.remove("loading"),W(),console.error("Failed to load widget: visitor authentication required (HTTP 401); check the widget token configuration."),Le(r),null}catch(f){}if(!u.ok)throw new Error("HTTP error! status: "+u.status);return u.text()})).then(u=>{u!==null&&s===Y&&(T(r),d=document.createElement("iframe"),d.className="replypier-iframe",d.title="\u804A\u5929",d.srcdoc=u,d.addEventListener("load",function(){ge=!0,d.contentWindow.postMessage(ke(),"*"),d.contentWindow.postMessage({type:"WIDGET_VISIBILITY",open:a},"*"),K&&(d.contentWindow.postMessage({type:"PREFILL_MESSAGE",text:K},"*"),K=null),se=!0,j=!1,_("ready")}),i.appendChild(d),g=d.contentWindow,e.classList.remove("loading"),d.style.opacity="1")}).catch(u=>{s===Y&&(console.error("Failed to load widget:",u),e.classList.remove("loading"),Le(r))})}catch(w){console.error("Failed to load widget:",w),e.classList.remove("loading"),ee()}finally{v=!1}})}function _e(r){if(!(!r.data||typeof r.data.type!="string")&&!(!g||r.source!==g)){if(r.data.type==="UNREAD_COUNT"){t.unreadCount=Math.max(0,parseInt(r.data.count,10)||0),O(),_("unread",t.unreadCount);return}if(r.data.type==="TOKEN_UPDATE"){let s=M(r.data.token);if(!s)return;te(s),r.source.postMessage({type:"TOKEN_RECEIVED",token:s},"*");return}if(r.data.type==="CHAT_SESSION"){L=String(r.data.sessionId||"")||null,r.data.created&&_("chatCreated",{sessionId:L,authenticated:!!r.data.authenticated});return}r.data.type==="IDENTITY_EXPIRED"&&(_("identityExpired"),Qe())}}function Je(){if(j||typeof t.tokenProvider!="function"){W(),window.replypierToken=null,D();return}j=!0,Promise.resolve().then(()=>t.tokenProvider()).then(r=>{let s=M(r);s?(window.replypierToken=s,te(s)):(W(),window.replypierToken=null)}).catch(r=>{console.error("ReplyPier: tokenProvider failed",r),W(),window.replypierToken=null}).finally(()=>{D()})}let be=!1,j=!1;function Qe(){if(!be){if(j||typeof t.tokenProvider!="function"){ie({type:"IDENTITY_UNAVAILABLE"});return}be=!0,j=!0,Promise.resolve().then(()=>t.tokenProvider()).then(r=>{let s=M(r);if(!s){ie({type:"IDENTITY_UNAVAILABLE"});return}window.replypierToken=s,te(s),ie({type:"TOKEN_REFRESH",token:s})}).catch(r=>{console.error("ReplyPier: tokenProvider failed",r),ie({type:"IDENTITY_UNAVAILABLE"})}).finally(()=>{be=!1})}}function ie(r){g&&g.postMessage(r,"*")}window.addEventListener("message",_e),D(),de.push(()=>{setTimeout(()=>{x||!a&&t.chatInitiationMessages.length>0&&(m=Oe(),We(m,b))},2e3)}),e.addEventListener("click",b),n.addEventListener("click",b),c.addEventListener("click",function(){a&&b()}),U()&&!a&&(e.classList.add("mobile-closed"),e.style.animation="replypier-float 4s ease-in-out infinite");function Te(){let r=U();r&&!a?(e.classList.add("mobile-closed"),e.style.animation="replypier-float 4s ease-in-out infinite",n.classList.remove("active")):r||(e.classList.remove("mobile-closed"),e.style.animation="",a&&n.classList.remove("active")),H(),Z()}window.addEventListener("resize",Te),l={isOpen:function(){return a},toggle:b,open:function(r){a||b(),r&&r.message&&qe(r.message)},close:function(){a&&b()},reload:Ve,destroy:Ze},F.splice(0).forEach(function(r){try{r()}catch(s){}})}Me(window.replypierConfig),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){x||me()}):me();let Ue=typeof navigator!="undefined"&&/Mac|iPod|iPhone|iPad/.test(navigator.platform||"");document.addEventListener("keydown",function(e){if(!l||!V())return;let i=Ue?e.metaKey&&!e.ctrlKey:e.ctrlKey&&!e.metaKey;if(t.hotkey&&i&&!e.altKey&&!e.repeat&&(e.key==="k"||e.key==="K")){e.preventDefault(),l.toggle();return}e.key==="Escape"&&l.isOpen()&&l.close()}),document.addEventListener("click",function(e){let i=e.target;if(!i||!i.closest)return;let n=i.closest("[data-replypier-open]");if(!n&&t.trigger)try{n=i.closest(t.trigger)}catch(o){}n&&(n.tagName==="A"&&e.preventDefault(),l?l.toggle():!x&&!X&&(X=!0,F.push(function(){l.open()})))}),window.addEventListener("message",function(e){if(!(!e.data||typeof e.data.type!="string")&&!(!g||e.source!==g)){if(e.data.type==="WIDGET_RESIZE"){if(typeof e.data.height!="number"||!isFinite(e.data.height))return;N=Math.round(e.data.height),Z();return}if(e.data.type==="WIDGET_SURFACE"){let i=!!e.data.palette;if(t.surfaceIsPalette===i)return;t.surfaceIsPalette=i,i||(N=0),H(),Z();return}if(e.data.type==="WIDGET_MINIMIZE"){J(function(){l.close()});return}if(e.data.type==="CUSTOMIZATION_UPDATE"){let i=e.data.data,n=i.chat_bubble_color;if(t.chatBubbleColor=re(n)?n:t.chatBubbleColor,typeof i.chat_style=="string"&&(t.chatStyle=i.chat_style),i.chat_initiation_messages&&Array.isArray(i.chat_initiation_messages)&&(t.chatInitiationMessages=i.chat_initiation_messages),Be(i.widget_display),!U())$.containerBottom||(t.containerBottom=i.chat_style==="ASK_ANYTHING"?90:100);else{let o=l?l.isOpen():!1,c=document.getElementById("replypier-mobile-topbar");i.chat_style==="ASK_ANYTHING"&&o?(document.body.classList.add("ask-anything-mobile"),c&&c.classList.add("active")):(document.body.classList.remove("ask-anything-mobile"),c&&c.classList.remove("active"))}H(),O(),ee(),e.source&&typeof e.source.postMessage=="function"&&e.source.postMessage(ke(),"*")}}});function h(e){return typeof e=="number"&&isFinite(e)}function T(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function fe(){document.getElementById("replypier-styles")&&H()}function Ee(e,i){if(!e||typeof e!="object")return;(e.side==="left"||e.side==="right")&&p("side",e.side),h(e.bottom)&&p("launcherBottom",Math.max(0,e.bottom));let n=h(e.offset)?e.offset:e.right;h(n)&&p("launcherRight",Math.max(0,n)),i||fe()}function Me(e){!e||typeof e!="object"||(e.baseUrl&&(t.baseUrl=e.baseUrl),e.id&&(window.replypierId=e.id),typeof e.tokenProvider=="function"&&(t.tokenProvider=e.tokenProvider),typeof e.launcher=="boolean"&&p("launcher",e.launcher),G.indexOf(e.displayMode)!==-1&&p("displayMode",e.displayMode),h(e.width)&&(p("containerWidth",Math.max(280,e.width)),A.width=!0),h(e.height)&&(p("containerHeight",Math.max(400,e.height)),A.height=!0),typeof e.hotkey=="boolean"&&p("hotkey",e.hotkey),h(e.sidebarWidth)&&p("sidebarWidth",Math.max(280,e.sidebarWidth)),h(e.zIndex)&&p("zIndex",Math.max(1,Math.floor(e.zIndex))),typeof e.trigger=="string"&&e.trigger&&p("trigger",e.trigger),e.searchBar&&typeof e.searchBar=="object"&&(typeof e.searchBar.placeholder=="string"&&e.searchBar.placeholder&&p("searchPlaceholder",e.searchBar.placeholder.slice(0,80)),h(e.searchBar.width)&&p("searchBarWidth",Math.max(160,e.searchBar.width))),e.position&&Ee(e.position,!0),fe())}function Ke(){l&&l.reload()}function Ce(e,i){if(e&&i){let n=`${t.baseUrl}/widgets/${window.replypierId}/end-chat?session_id=${encodeURIComponent(i)}&reason=CUSTOMER_REQUEST`;try{fetch(n,{method:"POST",mode:"cors",keepalive:!0,headers:{Authorization:`Bearer ${e}`}}).catch(()=>{})}catch(o){}}L=null,Ke()}function Ye(){if(l){l.reload();return}x&&(x=!1,document.readyState!=="loading"&&me())}function De(){if(l){l.destroy();return}x=!0,F.length=0,X=!1}function je(e){let i=M(e);if(!i)return;let n=pe();if(i===n)return;let o=n&&ce(i)&&ce(i)===ce(n);if(window.replypierToken=i,te(i),o&&g){g.postMessage({type:"TOKEN_REFRESH",token:i},"*");return}Ce(n,L)}function Ge(){let e=pe(),i=L;window.replypierToken=null,W(),Ce(e,i)}function Se(e){p("launcher",!!e),fe()}window.ReplyPier={init:Me,setPosition:function(e){Ee(e)},open:function(e){J(function(){l.open(e)})},close:function(){J(function(){l.close()})},toggle:function(){J(function(){l.toggle()})},isOpen:function(){return l?l.isOpen():!1},showLauncher:function(){Se(!0)},hideLauncher:function(){Se(!1)},identify:function(e){je(e)},logout:function(){Ge()},reload:function(){Ye()},destroy:function(){De()},on:function(e,i){if(!(typeof e!="string"||typeof i!="function")){if(e==="ready"&&se){try{i()}catch(n){}return}(Q[e]=Q[e]||[]).push(i)}},off:function(e,i){let n=Q[e];if(!n)return;let o=n.indexOf(i);o!==-1&&n.splice(o,1)}}})();})(); diff --git a/frontend/src/__tests__/webclient/replypierLoadError.spec.ts b/frontend/src/__tests__/webclient/replypierLoadError.spec.ts new file mode 100644 index 0000000..f0d2647 --- /dev/null +++ b/frontend/src/__tests__/webclient/replypierLoadError.spec.ts @@ -0,0 +1,155 @@ +/* +Copyright 2024-2026 ReplyPier + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +describe('ReplyPier widget load errors', () => { + const html = 'widget' + const okResponse = () => ({ ok: true, status: 200, text: async () => html }) + const flush = () => new Promise((resolve) => setTimeout(resolve, 0)) + let fetchMock: ReturnType + + async function loadLoader() { + vi.resetModules() + await import('@/webclient/replypier.js') + await flush() + } + + function retryButton() { + return document.querySelector('.replypier-error-retry') as HTMLButtonElement | null + } + + beforeEach(() => { + document.body.innerHTML = '' + document.head.innerHTML = '' + localStorage.clear() + ;(window as any).replypierId = 'w-test' + ;(window as any).replypierBaseUrl = 'https://api.example.com/api/v1' + ;(window as any).replypierToken = undefined + ;(window as any).replypierConfig = undefined + fetchMock = vi.fn().mockResolvedValue(okResponse()) + vi.stubGlobal('fetch', fetchMock) + vi.spyOn(console, 'error').mockImplementation(() => {}) + }) + + afterEach(() => { + ;(window as any).ReplyPier?.destroy() + delete (window as any).ReplyPier + vi.restoreAllMocks() + vi.unstubAllGlobals() + }) + + it.each([ + ['network failure', () => Promise.reject(new TypeError('Failed to fetch'))], + ['server error', () => Promise.resolve({ ok: false, status: 503 })], + ])('shows actionable visitor-facing copy for a %s', async (_label, fail) => { + fetchMock.mockImplementationOnce(fail) + await loadLoader() + + const error = document.querySelector('.replypier-error-ui') + expect(error?.textContent).toContain('客服暂时无法连接') + expect(error?.textContent).toContain('请检查网络后重试') + expect(error?.textContent).not.toMatch(/域名|后台|组织设置/) + expect(retryButton()?.textContent).toBe('重试') + expect(console.error).toHaveBeenCalledWith('Failed to load widget:', expect.any(Error)) + }) + + it('retries in the open window, preserves the visitor token and avoids duplicate requests', async () => { + ;(window as any).replypierToken = 'visitor-token' + let resolveRetry: (response: ReturnType) => void = () => {} + fetchMock + .mockRejectedValueOnce(new TypeError('Failed to fetch')) + .mockImplementationOnce(() => new Promise((resolve) => { resolveRetry = resolve })) + await loadLoader() + const api = (window as any).ReplyPier + api.open() + + const retry = retryButton()! + retry.click() + retry.click() + expect(retry.disabled).toBe(true) + expect(retry.textContent).toBe('正在重试…') + expect(fetchMock).toHaveBeenCalledTimes(2) + expect((fetchMock.mock.calls[1][1] as RequestInit).headers).toEqual({ Authorization: 'Bearer visitor-token' }) + + resolveRetry(okResponse()) + await flush() + expect(api.isOpen()).toBe(true) + expect(document.querySelector('.replypier-error-ui')).toBeNull() + expect(document.querySelectorAll('.replypier-iframe')).toHaveLength(1) + }) + + it('lets the visitor retry again after another failure', async () => { + fetchMock.mockRejectedValueOnce(new TypeError('offline')).mockRejectedValueOnce(new TypeError('still offline')) + await loadLoader() + + retryButton()!.click() + await flush() + expect(document.querySelectorAll('.replypier-error-ui')).toHaveLength(1) + expect(retryButton()?.disabled).toBe(false) + + retryButton()!.click() + await flush() + expect(fetchMock).toHaveBeenCalledTimes(3) + expect(document.querySelector('.replypier-error-ui')).toBeNull() + expect(document.querySelector('.replypier-iframe')).not.toBeNull() + }) + + it('recovers an expired identity during a retry without leaving the error card over the chat', async () => { + ;(window as any).replypierToken = 'expired-token' + fetchMock + .mockRejectedValueOnce(new TypeError('offline')) + .mockResolvedValueOnce({ + ok: false, + status: 401, + json: async () => ({ detail: { code: 'identity_expired' } }), + }) + await loadLoader() + + retryButton()!.click() + await flush() + await flush() + expect(fetchMock).toHaveBeenCalledTimes(3) + expect((fetchMock.mock.calls[2][1] as RequestInit).headers).toEqual({}) + expect(document.querySelector('.replypier-error-ui')).toBeNull() + expect(document.querySelector('.replypier-iframe')).not.toBeNull() + }) + + it('keeps the close control usable on the error screen', async () => { + fetchMock.mockRejectedValueOnce(new TypeError('offline')) + await loadLoader() + const api = (window as any).ReplyPier + api.open() + + ;(document.querySelector('.replypier-error-close') as HTMLButtonElement).click() + expect(api.isOpen()).toBe(false) + }) + + it('does not expose configuration details for a token-required widget', async () => { + fetchMock.mockResolvedValueOnce({ + ok: false, + status: 401, + json: async () => ({ detail: 'Token must be obtained from /generate-token' }), + }) + await loadLoader() + + const error = document.querySelector('.replypier-error-ui') + expect(error?.textContent).toContain('客服暂时无法连接') + expect(error?.textContent).not.toMatch(/管理员|配置|token|API/i) + expect(retryButton()).not.toBeNull() + expect(console.error).toHaveBeenCalledWith(expect.stringContaining('HTTP 401')) + }) +}) diff --git a/frontend/src/webclient/replypier.js b/frontend/src/webclient/replypier.js index 5391aec..eaf6331 100644 --- a/frontend/src/webclient/replypier.js +++ b/frontend/src/webclient/replypier.js @@ -1092,11 +1092,35 @@ window.replypierConfig; font-size: 14px; line-height: 1.6; color: #64748b; - margin: 0 0 24px 0; + margin: 0 0 20px 0; max-width: 280px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; } + .replypier-error-retry { + min-width: 104px; + min-height: 44px; + padding: 10px 24px; + margin: 0 0 22px; + border: 0; + border-radius: 12px; + background: #4f46e5; + color: #fff; + font: 600 14px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + cursor: pointer; + } + .replypier-error-retry:hover:not(:disabled) { + background: #4338ca; + } + .replypier-error-retry:focus-visible { + outline: 3px solid #818cf8; + outline-offset: 3px; + } + .replypier-error-retry:disabled { + opacity: 0.7; + cursor: wait; + } + .replypier-error-footer { display: flex; align-items: center; @@ -1141,7 +1165,6 @@ window.replypierConfig; } .replypier-error-message { - font-size: 13px; max-width: 260px; } } @@ -1575,8 +1598,8 @@ window.replypierConfig; isDestroyed = true } - // Create error UI for authentication failures using safe DOM methods - function createErrorUI(message) { + // Keep technical failure details in the console, not in the visitor's UI. + function createErrorUI(message, onRetry) { const errorDiv = document.createElement('div'); errorDiv.className = 'replypier-error-ui'; @@ -1603,13 +1626,16 @@ window.replypierConfig; const iconSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); iconSvg.setAttribute('class', 'replypier-error-icon'); iconSvg.setAttribute('viewBox', '0 0 24 24'); + iconSvg.setAttribute('aria-hidden', 'true'); iconSvg.setAttribute('fill', 'none'); iconSvg.setAttribute('stroke', 'currentColor'); iconSvg.setAttribute('stroke-width', '1.5'); + iconSvg.setAttribute('stroke-linecap', 'round'); + iconSvg.setAttribute('stroke-linejoin', 'round'); const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - path1.setAttribute('d', 'M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z'); + path1.setAttribute('d', 'M5 4h14a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-8l-4 3v-3H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z'); const path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - path2.setAttribute('d', 'M9 12l2 2 4-4'); + path2.setAttribute('d', 'M8 10h8M8 13h5'); iconSvg.appendChild(path1); iconSvg.appendChild(path2); iconWrapper.appendChild(iconSvg); @@ -1617,13 +1643,23 @@ window.replypierConfig; // Title const title = document.createElement('h2'); title.className = 'replypier-error-title'; - title.textContent = '客服暂不可用'; + title.textContent = '客服暂时无法连接'; // Message const messageEl = document.createElement('p'); messageEl.className = 'replypier-error-message'; messageEl.textContent = message; + const retryButton = document.createElement('button'); + retryButton.type = 'button'; + retryButton.className = 'replypier-error-retry'; + retryButton.textContent = '重试'; + retryButton.addEventListener('click', function () { + retryButton.disabled = true; + retryButton.textContent = '正在重试…'; + onRetry(); + }); + // Footer const footer = document.createElement('div'); footer.className = 'replypier-error-footer'; @@ -1647,14 +1683,24 @@ window.replypierConfig; card.appendChild(iconWrapper); card.appendChild(title); card.appendChild(messageEl); + card.appendChild(retryButton); card.appendChild(footer); errorDiv.appendChild(card); return errorDiv; } + function showLoadError(previousError) { + removeNode(previousError) + const errorUI = createErrorUI('请检查网络后重试,或稍后再来。', function () { + prefetchWidget(errorUI) + }) + container.appendChild(errorUI) + revealButton() + } + // Start prefetching the widget data - async function prefetchWidget() { + async function prefetchWidget(previousError) { if (isLoading || iframe) return // A reload for a new identity bumps loadGeneration, so a response that was @@ -1690,6 +1736,7 @@ window.replypierConfig; // error or silently starting over as a stranger. if (detail && detail.code === 'identity_expired') { button.classList.remove('loading') + removeNode(previousError) emitEvent('identityExpired') recoverIdentityAndLoad() return null; @@ -1699,11 +1746,8 @@ window.replypierConfig; if (errorDetail.includes('generate-token') || errorDetail.includes('API key') || errorDetail.includes('Token must be obtained')) { button.classList.remove('loading') removeToken(); - const errorUI = createErrorUI('此聊天组件尚未配置完成,请联系网站管理员开通在线客服。'); - container.appendChild(errorUI); - // Reveal (with the default color) so this error is reachable — - // it'll never get a real color since the widget never loaded. - revealButton(); + console.error('Failed to load widget: visitor authentication required (HTTP 401); check the widget token configuration.') + showLoadError(previousError) return null; } } catch { @@ -1720,6 +1764,7 @@ window.replypierConfig; if (html === null) return; // Error was already handled if (generation !== loadGeneration) return; // superseded by a reload + removeNode(previousError) iframe = document.createElement('iframe') iframe.className = 'replypier-iframe' // Screen readers announce a frame by its title. @@ -1753,19 +1798,7 @@ window.replypierConfig; if (generation !== loadGeneration) return; // superseded by a reload console.error('Failed to load widget:', error) button.classList.remove('loading') - // A failed fetch here is almost always the browser blocking the request - // because this domain isn't in the organization's allowed domains (CORS). - // Point the site owner at the fix instead of a dead-end "try again later". - const host = window.location.hostname || '当前域名'; - const isLocal = /^(localhost|127\.0\.0\.1|0\.0\.0\.0|\[?::1\]?)$/i.test(host) || /\.local$/i.test(host); - const message = isLocal - ? '聊天窗口无法在“' + host + '”上加载。本地测试时,请在 ReplyPier 后台(组织设置)中把组织域名设为“' + host + '”。' - : '聊天窗口无法在“' + host + '”上加载。请在 ReplyPier 后台(组织设置)中将此域名加入组织,即可在此启用聊天。'; - const errorUI = createErrorUI(message); - container.appendChild(errorUI); - // Reveal (with the default color) so this error is reachable — it'll - // never get a real color since the widget never loaded. - revealButton(); + showLoadError(previousError) }); } catch (error) { diff --git a/replypier-test/README.md b/replypier-test/README.md index 297e2d4..71bbc16 100644 --- a/replypier-test/README.md +++ b/replypier-test/README.md @@ -60,9 +60,9 @@ npm start ## Troubleshooting -- **"Chat can't load on 'localhost'"** — the origin isn't in your organization's allowed - domains (Organization settings). The port matters: `localhost:3000` allowed does not - permit `localhost:3010`. +- **“客服暂时无法连接”** — check the browser console and the widget-data request in + Network for network, HTTP, or CORS failures. If the request was rejected by CORS, + check the page origin against the organization's allowed domain, including its port. - **Dashboard placement settings not applying** — almost always a stale loader. Check the served file has the newer features: `curl -s | grep -c sidebar-left`. - **Buttons do nothing on the gallery pages** — the loader failed to load; the page shows