@anker-in/campaign-ui 0.4.5-beta.16 → 0.4.5-beta.18

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.
Files changed (86) hide show
  1. package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js +1 -1
  2. package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
  3. package/dist/cjs/components/LiveChatWidget/api/chat.d.ts +1 -1
  4. package/dist/cjs/components/LiveChatWidget/api/chat.js +2 -2
  5. package/dist/cjs/components/LiveChatWidget/api/chat.js.map +3 -3
  6. package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
  7. package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js.map +3 -3
  8. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -1
  9. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +2 -2
  10. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
  11. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +2 -2
  12. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
  13. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js.map +2 -2
  14. package/dist/cjs/components/LiveChatWidget/components/MessageList.js +3 -3
  15. package/dist/cjs/components/LiveChatWidget/components/MessageList.js.map +3 -3
  16. package/dist/cjs/components/LiveChatWidget/constants.d.ts +1 -0
  17. package/dist/cjs/components/LiveChatWidget/constants.js +1 -1
  18. package/dist/cjs/components/LiveChatWidget/constants.js.map +3 -3
  19. package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.js +1 -1
  20. package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.js.map +3 -3
  21. package/dist/cjs/components/LiveChatWidget/hooks/useChatState.d.ts +5 -0
  22. package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js +1 -1
  23. package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
  24. package/dist/cjs/components/LiveChatWidget/utils/fetcher.d.ts +3 -1
  25. package/dist/cjs/components/LiveChatWidget/utils/fetcher.js +1 -1
  26. package/dist/cjs/components/LiveChatWidget/utils/fetcher.js.map +3 -3
  27. package/dist/cjs/components/credits/context/hooks/useSubscribed.js.map +2 -2
  28. package/dist/cjs/components/credits/modal/ActivitiesModal.js +1 -1
  29. package/dist/cjs/components/credits/modal/ActivitiesModal.js.map +2 -2
  30. package/dist/cjs/components/index.d.ts +2 -2
  31. package/dist/cjs/components/index.js +1 -1
  32. package/dist/cjs/components/index.js.map +2 -2
  33. package/dist/cjs/stories/CartCard.stories.d.ts +33 -0
  34. package/dist/cjs/stories/CartCard.stories.js +21 -0
  35. package/dist/cjs/stories/CartCard.stories.js.map +7 -0
  36. package/dist/esm/components/LiveChatWidget/LiveChatWidget.js +1 -1
  37. package/dist/esm/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
  38. package/dist/esm/components/LiveChatWidget/api/chat.d.ts +1 -1
  39. package/dist/esm/components/LiveChatWidget/api/chat.js +2 -2
  40. package/dist/esm/components/LiveChatWidget/api/chat.js.map +3 -3
  41. package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
  42. package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js.map +3 -3
  43. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -1
  44. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
  45. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
  46. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
  47. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
  48. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js.map +3 -3
  49. package/dist/esm/components/LiveChatWidget/components/MessageList.js +3 -3
  50. package/dist/esm/components/LiveChatWidget/components/MessageList.js.map +3 -3
  51. package/dist/esm/components/LiveChatWidget/constants.d.ts +1 -0
  52. package/dist/esm/components/LiveChatWidget/constants.js +1 -1
  53. package/dist/esm/components/LiveChatWidget/constants.js.map +3 -3
  54. package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.js +1 -1
  55. package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.js.map +3 -3
  56. package/dist/esm/components/LiveChatWidget/hooks/useChatState.d.ts +5 -0
  57. package/dist/esm/components/LiveChatWidget/hooks/useChatState.js +1 -1
  58. package/dist/esm/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
  59. package/dist/esm/components/LiveChatWidget/utils/fetcher.d.ts +3 -1
  60. package/dist/esm/components/LiveChatWidget/utils/fetcher.js +1 -1
  61. package/dist/esm/components/LiveChatWidget/utils/fetcher.js.map +3 -3
  62. package/dist/esm/components/credits/context/hooks/useSubscribed.js.map +2 -2
  63. package/dist/esm/components/credits/modal/ActivitiesModal.js +1 -1
  64. package/dist/esm/components/credits/modal/ActivitiesModal.js.map +2 -2
  65. package/dist/esm/components/index.d.ts +2 -2
  66. package/dist/esm/components/index.js +1 -1
  67. package/dist/esm/components/index.js.map +2 -2
  68. package/dist/esm/stories/CartCard.stories.d.ts +33 -0
  69. package/dist/esm/stories/CartCard.stories.js +21 -0
  70. package/dist/esm/stories/CartCard.stories.js.map +7 -0
  71. package/package.json +2 -1
  72. package/src/components/LiveChatWidget/LiveChatWidget.tsx +6 -4
  73. package/src/components/LiveChatWidget/api/chat.ts +3 -2
  74. package/src/components/LiveChatWidget/components/MessageContent/CartCard.tsx +2 -6
  75. package/src/components/LiveChatWidget/components/MessageContent/ProductCard.tsx +3 -6
  76. package/src/components/LiveChatWidget/components/MessageContent/ProductComparison.tsx +5 -5
  77. package/src/components/LiveChatWidget/components/MessageContent/ProductList.tsx +3 -6
  78. package/src/components/LiveChatWidget/components/MessageList.tsx +10 -4
  79. package/src/components/LiveChatWidget/constants.ts +9 -0
  80. package/src/components/LiveChatWidget/hooks/useChatAPI.ts +2 -3
  81. package/src/components/LiveChatWidget/hooks/useChatState.ts +27 -7
  82. package/src/components/LiveChatWidget/utils/fetcher.ts +12 -2
  83. package/src/components/credits/context/hooks/useSubscribed.ts +1 -0
  84. package/src/components/credits/modal/ActivitiesModal.tsx +2 -2
  85. package/src/components/index.ts +2 -2
  86. package/src/stories/CartCard.stories.tsx +459 -0
@@ -1,2 +1,2 @@
1
- "use strict";var Xe=Object.create;var $=Object.defineProperty;var Ke=Object.getOwnPropertyDescriptor;var Je=Object.getOwnPropertyNames;var Ze=Object.getPrototypeOf,je=Object.prototype.hasOwnProperty;var Ue=(o,c)=>{for(var p in c)$(o,p,{get:c[p],enumerable:!0})},fe=(o,c,p,A)=>{if(c&&typeof c=="object"||typeof c=="function")for(let i of Je(c))!je.call(o,i)&&i!==p&&$(o,i,{get:()=>c[i],enumerable:!(A=Ke(c,i))||A.enumerable});return o};var Z=(o,c,p)=>(p=o!=null?Xe(Ze(o)):{},fe(c||!o||!o.__esModule?$(p,"default",{value:o,enumerable:!0}):p,o)),be=o=>fe($({},"__esModule",{value:!0}),o);var tt={};Ue(tt,{LiveChatWidget:()=>et});module.exports=be(tt);var g=require("react/jsx-runtime"),r=Z(require("react")),j=Z(require("@radix-ui/react-dialog")),me=require("./constants"),ge=require("./components/ChatBubble"),he=require("./components/ChatWindow"),ye=require("./components/ComplianceDialog"),_e=require("./hooks/useChatState"),we=require("./hooks/useChatAPI"),Ce=require("./utils/messageRenderers"),Re=require("./utils/validation"),U=require("./utils/userId"),b=require("./utils/productTransformers.js"),xe=require("./utils/cartTransformers.js"),ee=Z(require("js-cookie")),n=require("./components/MessageContent/index.js");const et=({apiBaseUrl:o,headers:c,recaptchaSitekey:p,recaptchaAction:A,site:i,channelCode:w,loginUserId:y,cartId:te,accessToken:se,position:Se,welcomeMessage:C,quickReplies:_,customRenderers:k,logoUrl:De,title:Ee,chatBubbleIcon:Pe,open:Ie,onOpenChange:Me,onOpen:Te,onClose:ve,onMessageSend:z,onError:R,onTextMessage:Ae,onProductList:qe,onPromotionList:Oe,onAddToCart:q,onCart:Q,showNewSessionButton:Fe,commonText:ae,productCardRender:Be,productComparisonRender:ne,bottomTips:Le,complianceConfig:D})=>{const H=D?.cookieName||"livechat_compliance_agreed",[re,Y]=r.default.useState(!1),[ie,We]=r.default.useState(()=>D?ee.default.get(H)!==void 0:!0),P=r.default.useMemo(()=>({...me.DEFAULT_COMMON_TEXT,...ae}),[ae]),Ne=(0,_e.useChatState)({welcomeMessage:C,site:i,open:Ie,onOpenChange:Me,onOpen:Te,onClose:ve,onMessageSend:z,onError:R,onTextMessage:Ae,onProductList:qe,onPromotionList:Oe,onAddToCart:q,onCart:Q,productCardRender:Be,productComparisonRender:ne}),{messages:O,isOpen:F,userId:u,sessionId:G,inputValue:V,isStreaming:$e,openChat:I,closeChat:oe,setInputValue:X,setUserId:de,addMessage:h,setMessages:E,clearMessages:B,handleSSEEvent:ce,saveSession:M,clearSession:T}=Ne,[ke,L]=r.default.useState(!1),{sendMessageStream:le,createSession:x}=(0,we.useChatAPI)({apiBaseUrl:o,headers:c,recaptchaConfig:{needRecaptcha:!!p,recaptchaSitekey:p,recaptchaAction:A},onError:R}),pe=r.default.useRef(async e=>{}),ze=r.default.useMemo(()=>{const e=new Ce.MessageRendererRegistry;e.register("text",n.TextBlock),e.register("product_card",n.ProductCard),e.register("product_list",n.ProductList),e.register("product_comparison",n.ProductComparisonRenderer),e.register("policy",n.PolicyBlock),e.register("thinking",n.ThinkingBlock),e.register("error",n.ErrorBlock),e.register("faq_list",n.FAQListRenderer),e.register("promotion_list",n.PromotionListRenderer),e.register("cart",n.CartCard);const s=(0,n.createQuickRepliesRenderer)(d=>{pe.current(d.value)});return e.register("quick_replies",s),k&&e.registerMany(k),e},[k]),K=r.default.useRef(!1);(0,r.useEffect)(()=>{if(!F){K.current=!1;return}if(u===void 0||K.current)return;K.current=!0;const e=G;e?Qe(e):v()},[F,u]);const ue=(0,r.useCallback)(e=>{if(Array.isArray(e.content))return e;const s=[];typeof e.content=="string"&&e.content.trim()&&s.push({type:"text",text:e.content});const d=e.structuredContent||e.structured_content;return Array.isArray(d)&&d.forEach(t=>{if(t.type==="product_list"&&Array.isArray(t.data))s.push({type:"product_list",data:{products:(0,b.transformProducts)(t.data,i),title:void 0,commonText:P}});else if(t.type==="quick_replies"&&t.data?.replies)s.push({type:"quick_replies",data:{replies:t.data.replies}});else if(t.type==="policy"&&t.data?.title&&t.data?.content)s.push({type:"policy",data:{title:t.data.title,content:t.data.content}});else if(t.type==="product_comparison"&&t.data?.products&&t.data?.dimensions)s.push({type:"product_comparison",data:{products:(0,b.transformProducts)(t.data.products,i),dimensions:t.data.dimensions,onAddToCart:q,commonText:P,productComparisonRender:ne}});else if(t.type==="faq_list"&&t.data?.found!==void 0)s.push({type:"faq_list",data:t.data});else if(t.type==="promotion_list"&&t.data?.found!==void 0)s.push({type:"promotion_list",data:{...t.data,commonText:P}});else if(t.type==="cart"&&t.data?.id!==void 0){const f=(0,xe.transformCartData)(t.data);s.push({type:"cart",data:{...f,onCart:Q,commonText:P}})}else s.push(t)}),s.length===0&&s.push({type:"text",text:""}),{...e,content:s}},[i,Q,q,P]),v=(0,r.useCallback)(async()=>{if(u!==void 0){C||L(!0);try{const e=await x({user_id:u??"",site:i,channel_code:w,real_user_id:y,page_url:typeof window<"u"?window.location.href:void 0});if(e.success){M(e.sessionId),e.userId&&((0,U.saveUserId)(e.userId),de(e.userId)),B();const s=e.welcomeMessage||C;if(s){const d=[{type:"text",text:s}],t=e.quickQuestions;if(t&&t.length>0){const f=t.map((m,a)=>({id:`quick-${a}`,label:m,value:m}));d.push({type:"quick_replies",data:{replies:f}})}else _&&_.length>0&&d.push({type:"quick_replies",data:{replies:_}});h({id:`welcome-${Date.now()}`,role:"assistant",content:d,timestamp:Date.now()})}}}catch(e){console.error("[LiveChatWidget] Failed to create new session:",e),R?.(e);const s=e?.type==="GoogleRecaptchaError";h({id:`error-${Date.now()}`,role:"system",content:[{type:"error",data:{message:s?"Your session has expired. Please refresh the page and try again.":"Failed to create session. Please refresh the page and try again.",code:s?"RECAPTCHA_ERROR":"SESSION_CREATE_ERROR"}}],timestamp:Date.now()})}finally{L(!1)}}},[u,i,w,y,x,M,B,C,_,h,R]),Qe=(0,r.useCallback)(async e=>{C||L(!0);try{const s=await x({user_id:u??"",session_id:e,site:i,channel_code:w,real_user_id:y,page_url:typeof window<"u"?window.location.href:void 0});if(s.success&&s.resumed){s.userId&&((0,U.saveUserId)(s.userId),de(s.userId));const d=s.welcomeMessage||C,t=d?[{type:"text",text:d}]:[],f=s.quickQuestions;if(f&&f.length>0){const m=f.map((a,N)=>({id:`quick-${N}`,label:a,value:a}));t.push({type:"quick_replies",data:{replies:m}})}else _&&_.length>0&&t.push({type:"quick_replies",data:{replies:_}});if(s.messages&&s.messages.length>0){const m=s.messages.filter(a=>a!=null).map(ue).filter(a=>a.content&&a.content.length>0&&!(a.content.length===1&&a.content[0].type==="text"&&!a.content[0].text));if(t.length>0){const a={id:`welcome-${Date.now()}`,role:"assistant",content:t,timestamp:Date.now()};E([a,...m])}else E(m)}else B(),t.length>0&&h({id:`welcome-${Date.now()}`,role:"assistant",content:t,timestamp:Date.now()})}else s.resumed||(T(),v())}catch(s){console.error("[LiveChatWidget] Failed to resume session:",s),s?.type==="GoogleRecaptchaError"?h({id:`error-${Date.now()}`,role:"system",content:[{type:"error",data:{message:"Your session has expired. Please refresh the page and try again.",code:"RECAPTCHA_ERROR"}}],timestamp:Date.now()}):(h({id:`error-${Date.now()}`,role:"system",content:[{type:"error",data:{message:"Failed to resume session. Creating a new session...",code:"SESSION_RESUME_ERROR"}}],timestamp:Date.now()}),T(),v())}finally{L(!1)}},[u,i,w,y,x,E,T,B,ue,v,C,_,h]),W=(0,r.useCallback)(async(e,s=!1)=>{const d=e||V.trim();if(!d)return;!e&&!s&&X("");const t=(0,Re.sanitizeInput)(d);if(!t){R?.(new Error("Invalid message"));return}if(!s){const a={id:`user-${Date.now()}`,role:"user",content:[{type:"text",text:t}],timestamp:Date.now()};h(a)}const f={id:`thinking-${Date.now()}`,role:"assistant",content:[{type:"thinking",data:{status:"thinking"}}],timestamp:Date.now()};h(f),s||z?.(t);let m=!1;try{let a=G;if(!a){const l=await x({user_id:u??"",site:i,channel_code:w,real_user_id:y,page_url:typeof window<"u"?window.location.href:void 0});if(l.success)a=l.sessionId,M(a);else throw new Error("Failed to create session")}if(await le({message:t,user_id:u??"",session_id:a,context:{cartId:te,accessToken:se,real_user_id:y}},l=>{ce(l),l.event==="error"&&l.data.type==="validation_error"&&(m=!0,T())}),m&&!s){console.log("[LiveChatWidget] Session expired (validation_error), creating new session and retrying...");const l=O.filter(J=>J.id!==f.id);E(l);const S=await x({user_id:u??"",site:i,channel_code:w,real_user_id:y,page_url:typeof window<"u"?window.location.href:void 0});if(S.success)M(S.sessionId),await W(t,!0);else throw new Error("Failed to recreate session after expiration")}}catch(a){console.error("[LiveChatWidget] Failed to send message:",a),R?.(a);const N=a?.type==="GoogleRecaptchaError";let l,S;N?(l="Your session has expired. Please refresh the page and try again.",S="RECAPTCHA_ERROR"):m?(l="Your session has expired. We tried to reconnect but failed. Please try again.",S="SESSION_EXPIRED"):(l="Failed to send message. Please check your network connection and try again.",S="NETWORK_ERROR");const J=O.filter(Ve=>Ve.id!==f.id);E([...J,{id:`error-${Date.now()}`,role:"system",content:[{type:"error",data:{message:l,code:S}}],timestamp:Date.now()}])}},[V,u,G,i,w,y,te,se,O,X,h,E,x,le,ce,M,T,z,R]);r.default.useEffect(()=>{pe.current=W},[W]);const He=(0,r.useCallback)(()=>{D&&!ie?Y(!0):I()},[D,ie,I]),Ye=(0,r.useCallback)(()=>{We(!0),Y(!1),ee.default.set(H,"true",{expires:365}),I()},[I,H]),Ge=(0,r.useCallback)(()=>{Y(!1)},[]);return(0,g.jsxs)(g.Fragment,{children:[D&&(0,g.jsx)(ye.ComplianceDialog,{open:re,config:D,onAgree:Ye,onClose:Ge}),(0,g.jsx)(ge.ChatBubble,{position:Se,onClick:He,visible:!F&&!re,iconImageUrl:Pe}),(0,g.jsx)(j.Root,{open:F,onOpenChange:e=>e?I():oe(),children:(0,g.jsx)(j.Portal,{children:(0,g.jsx)(j.Content,{className:"livechat-window-enter",style:{position:"fixed",zIndex:9998},children:(0,g.jsx)(he.ChatWindow,{messages:O,inputValue:V,onInputChange:X,onSend:()=>W(),onClose:oe,onNewSession:v,title:Ee,logoUrl:De,isSending:$e,isLoadingHistory:ke,rendererRegistry:ze,inputPlaceholder:"",onAddToCart:q,showNewSessionButton:Fe,bottomTips:Le})})})})]})};
1
+ "use strict";var Xe=Object.create;var k=Object.defineProperty;var Ke=Object.getOwnPropertyDescriptor;var Je=Object.getOwnPropertyNames;var Ze=Object.getPrototypeOf,je=Object.prototype.hasOwnProperty;var Ue=(o,c)=>{for(var p in c)k(o,p,{get:c[p],enumerable:!0})},me=(o,c,p,q)=>{if(c&&typeof c=="object"||typeof c=="function")for(let i of Je(c))!je.call(o,i)&&i!==p&&k(o,i,{get:()=>c[i],enumerable:!(q=Ke(c,i))||q.enumerable});return o};var j=(o,c,p)=>(p=o!=null?Xe(Ze(o)):{},me(c||!o||!o.__esModule?k(p,"default",{value:o,enumerable:!0}):p,o)),be=o=>me(k({},"__esModule",{value:!0}),o);var tt={};Ue(tt,{LiveChatWidget:()=>et});module.exports=be(tt);var g=require("react/jsx-runtime"),n=j(require("react")),U=j(require("@radix-ui/react-dialog")),ge=require("./constants"),he=require("./components/ChatBubble"),ye=require("./components/ChatWindow"),_e=require("./components/ComplianceDialog"),we=require("./hooks/useChatState"),Ce=require("./hooks/useChatAPI"),Re=require("./utils/messageRenderers"),xe=require("./utils/validation"),b=require("./utils/userId"),ee=require("./utils/productTransformers.js"),De=require("./utils/cartTransformers.js"),te=j(require("js-cookie")),r=require("./components/MessageContent/index.js");const et=({apiBaseUrl:o,headers:c,recaptchaSitekey:p,recaptchaAction:q,site:i,channelCode:w,loginUserId:y,cartId:se,accessToken:ae,position:Se,welcomeMessage:C,quickReplies:_,customRenderers:z,logoUrl:Ee,title:Pe,chatBubbleIcon:Ie,open:Me,onOpenChange:Te,onOpen:ve,onClose:Ae,onMessageSend:H,onError:R,onTextMessage:qe,onProductList:Oe,onPromotionList:Fe,onAddToCart:O,onCart:Q,showNewSessionButton:Be,commonText:re,productCardRender:Le,productComparisonRender:ne,bottomTips:We,complianceConfig:D})=>{const Y=D?.cookieName||"livechat_compliance_agreed",[ie,G]=n.default.useState(!1),[oe,Ne]=n.default.useState(()=>D?te.default.get(Y)!==void 0:!0),P=n.default.useMemo(()=>({...ge.DEFAULT_COMMON_TEXT,...re}),[re]),$e=(0,we.useChatState)({welcomeMessage:C,site:i,open:Me,onOpenChange:Te,onOpen:ve,onClose:Ae,onMessageSend:H,onError:R,onTextMessage:qe,onProductList:Oe,onPromotionList:Fe,onAddToCart:O,onCart:Q,productCardRender:Le,productComparisonRender:ne}),{messages:F,isOpen:B,userId:u,sessionId:V,inputValue:X,isStreaming:ke,openChat:I,closeChat:de,setInputValue:K,setUserId:ce,addMessage:h,setMessages:S,clearMessages:L,startStream:le,saveSession:M,clearSession:T}=$e,[ze,W]=n.default.useState(!1),{sendMessageStream:pe,createSession:x}=(0,Ce.useChatAPI)({apiBaseUrl:o,headers:c,recaptchaConfig:{needRecaptcha:!!p,recaptchaSitekey:p,recaptchaAction:q},onError:R}),ue=n.default.useRef(async e=>{}),He=n.default.useMemo(()=>{const e=new Re.MessageRendererRegistry;e.register("text",r.TextBlock),e.register("product_card",r.ProductCard),e.register("product_list",r.ProductList),e.register("product_comparison",r.ProductComparisonRenderer),e.register("policy",r.PolicyBlock),e.register("thinking",r.ThinkingBlock),e.register("error",r.ErrorBlock),e.register("faq_list",r.FAQListRenderer),e.register("promotion_list",r.PromotionListRenderer),e.register("cart",r.CartCard);const s=(0,r.createQuickRepliesRenderer)(d=>{ue.current(d.value)});return e.register("quick_replies",s),z&&e.registerMany(z),e},[z]),J=n.default.useRef(!1);(0,n.useEffect)(()=>{if(!B){J.current=!1;return}if(u===void 0||J.current)return;J.current=!0;const e=V;e?Qe(e):v()},[B,u]);const fe=(0,n.useCallback)(e=>{if(Array.isArray(e.content))return e;const s=[];typeof e.content=="string"&&e.content.trim()&&s.push({type:"text",text:e.content});const d=e.structuredContent||e.structured_content;return Array.isArray(d)&&d.forEach(t=>{if(t.type==="product_list"&&Array.isArray(t.data))s.push({type:"product_list",data:{products:(0,ee.transformProducts)(t.data,i),title:void 0,commonText:P}});else if(t.type==="quick_replies"&&t.data?.replies)s.push({type:"quick_replies",data:{replies:t.data.replies}});else if(t.type==="policy"&&t.data?.title&&t.data?.content)s.push({type:"policy",data:{title:t.data.title,content:t.data.content}});else if(t.type==="product_comparison"&&t.data?.products&&t.data?.dimensions)s.push({type:"product_comparison",data:{products:(0,ee.transformProducts)(t.data.products,i),dimensions:t.data.dimensions,onAddToCart:O,commonText:P,productComparisonRender:ne}});else if(t.type==="faq_list"&&t.data?.found!==void 0)s.push({type:"faq_list",data:t.data});else if(t.type==="promotion_list"&&t.data?.found!==void 0)s.push({type:"promotion_list",data:{...t.data,commonText:P}});else if(t.type==="cart"&&t.data?.id!==void 0){const f=(0,De.transformCartData)(t.data);s.push({type:"cart",data:{...f,onCart:Q,commonText:P}})}else s.push(t)}),s.length===0&&s.push({type:"text",text:""}),{...e,content:s}},[i,Q,O,P]),v=(0,n.useCallback)(async()=>{if(u!==void 0){C||W(!0);try{const e=await x({user_id:u??"",site:i,channel_code:w,real_user_id:y,page_url:typeof window<"u"?window.location.href:void 0});if(e.success){M(e.sessionId),e.userId&&((0,b.saveUserId)(e.userId),ce(e.userId)),L();const s=e.welcomeMessage||C;if(s){const d=[{type:"text",text:s}],t=e.quickQuestions;if(t&&t.length>0){const f=t.map((m,a)=>({id:`quick-${a}`,label:m,value:m}));d.push({type:"quick_replies",data:{replies:f}})}else _&&_.length>0&&d.push({type:"quick_replies",data:{replies:_}});h({id:`welcome-${Date.now()}`,role:"assistant",content:d,timestamp:Date.now()})}}}catch(e){console.error("[LiveChatWidget] Failed to create new session:",e),R?.(e);const s=e?.type==="GoogleRecaptchaError";h({id:`error-${Date.now()}`,role:"system",content:[{type:"error",data:{message:s?"Your session has expired. Please refresh the page and try again.":"Failed to create session. Please refresh the page and try again.",code:s?"RECAPTCHA_ERROR":"SESSION_CREATE_ERROR"}}],timestamp:Date.now()})}finally{W(!1)}}},[u,i,w,y,x,M,L,C,_,h,R]),Qe=(0,n.useCallback)(async e=>{C||W(!0);try{const s=await x({user_id:u??"",session_id:e,site:i,channel_code:w,real_user_id:y,page_url:typeof window<"u"?window.location.href:void 0});if(s.success&&s.resumed){s.userId&&((0,b.saveUserId)(s.userId),ce(s.userId));const d=s.welcomeMessage||C,t=d?[{type:"text",text:d}]:[],f=s.quickQuestions;if(f&&f.length>0){const m=f.map((a,A)=>({id:`quick-${A}`,label:a,value:a}));t.push({type:"quick_replies",data:{replies:m}})}else _&&_.length>0&&t.push({type:"quick_replies",data:{replies:_}});if(s.messages&&s.messages.length>0){const m=s.messages.filter(a=>a!=null).map(fe).filter(a=>a.content&&a.content.length>0&&!(a.content.length===1&&a.content[0].type==="text"&&!a.content[0].text));if(t.length>0){const a={id:`welcome-${Date.now()}`,role:"assistant",content:t,timestamp:Date.now()};S([a,...m])}else S(m)}else L(),t.length>0&&h({id:`welcome-${Date.now()}`,role:"assistant",content:t,timestamp:Date.now()})}else s.resumed||(T(),v())}catch(s){console.error("[LiveChatWidget] Failed to resume session:",s),s?.type==="GoogleRecaptchaError"?h({id:`error-${Date.now()}`,role:"system",content:[{type:"error",data:{message:"Your session has expired. Please refresh the page and try again.",code:"RECAPTCHA_ERROR"}}],timestamp:Date.now()}):(h({id:`error-${Date.now()}`,role:"system",content:[{type:"error",data:{message:"Failed to resume session. Creating a new session...",code:"SESSION_RESUME_ERROR"}}],timestamp:Date.now()}),T(),v())}finally{W(!1)}},[u,i,w,y,x,S,T,L,fe,v,C,_,h]),N=(0,n.useCallback)(async(e,s=!1)=>{const d=e||X.trim();if(!d)return;!e&&!s&&K("");const t=(0,xe.sanitizeInput)(d);if(!t){R?.(new Error("Invalid message"));return}if(!s){const a={id:`user-${Date.now()}`,role:"user",content:[{type:"text",text:t}],timestamp:Date.now()};h(a)}const f={id:`thinking-${Date.now()}`,role:"assistant",content:[{type:"thinking",data:{status:"thinking"}}],timestamp:Date.now()};h(f),s||H?.(t);let m=!1;try{let a=V;if(!a){const l=await x({user_id:u??"",site:i,channel_code:w,real_user_id:y,page_url:typeof window<"u"?window.location.href:void 0});if(l.success)a=l.sessionId,M(a);else throw new Error("Failed to create session")}const A={message:t,user_id:u??"",session_id:a,context:{cartId:se,accessToken:ae,real_user_id:y}},E=le();if(await pe(A,l=>{E(l),l.event==="error"&&l.data.type==="validation_error"&&(m=!0,T())}),m&&!s){console.log("[LiveChatWidget] Session expired (validation_error), creating new session and retrying...");const l=F.filter(Z=>Z.id!==f.id);S(l);const $=await x({user_id:u??"",site:i,channel_code:w,real_user_id:y,page_url:typeof window<"u"?window.location.href:void 0});if($.success)M($.sessionId),await N(t,!0);else throw new Error("Failed to recreate session after expiration")}}catch(a){console.error("[LiveChatWidget] Failed to send message:",a),R?.(a);const A=a?.type==="GoogleRecaptchaError";let E,l;A?(E="Your session has expired. Please refresh the page and try again.",l="RECAPTCHA_ERROR"):m?(E="Your session has expired. We tried to reconnect but failed. Please try again.",l="SESSION_EXPIRED"):(E="Failed to send message. Please check your network connection and try again.",l="NETWORK_ERROR");const $=F.filter(Z=>Z.id!==f.id);S([...$,{id:`error-${Date.now()}`,role:"system",content:[{type:"error",data:{message:E,code:l}}],timestamp:Date.now()}])}},[X,u,V,i,w,y,se,ae,F,K,h,S,x,pe,le,M,T,H,R]);n.default.useEffect(()=>{ue.current=N},[N]);const Ye=(0,n.useCallback)(()=>{D&&!oe?G(!0):I()},[D,oe,I]),Ge=(0,n.useCallback)(()=>{Ne(!0),G(!1),te.default.set(Y,"true",{expires:365}),I()},[I,Y]),Ve=(0,n.useCallback)(()=>{G(!1)},[]);return(0,g.jsxs)(g.Fragment,{children:[D&&(0,g.jsx)(_e.ComplianceDialog,{open:ie,config:D,onAgree:Ge,onClose:Ve}),(0,g.jsx)(he.ChatBubble,{position:Se,onClick:Ye,visible:!B&&!ie,iconImageUrl:Ie}),(0,g.jsx)(U.Root,{open:B,onOpenChange:e=>e?I():de(),children:(0,g.jsx)(U.Portal,{children:(0,g.jsx)(U.Content,{className:"livechat-window-enter",style:{position:"fixed",zIndex:9998},children:(0,g.jsx)(ye.ChatWindow,{messages:F,inputValue:X,onInputChange:K,onSend:()=>N(),onClose:de,onNewSession:v,title:Pe,logoUrl:Ee,isSending:ke,isLoadingHistory:ze,rendererRegistry:He,inputPlaceholder:"",onAddToCart:O,showNewSessionButton:Be,bottomTips:We})})})})]})};
2
2
  //# sourceMappingURL=LiveChatWidget.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/components/LiveChatWidget/LiveChatWidget.tsx"],
4
- "sourcesContent": ["/**\n * LiveChat \u4E3B\u7EC4\u4EF6\n * \u96C6\u6210\u6240\u6709\u5B50\u7EC4\u4EF6\uFF0C\u63D0\u4F9B\u5B8C\u6574\u7684\u804A\u5929\u529F\u80FD\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u4E09\u5C42\u67B6\u6784\u8BBE\u8BA1\n */\n\nimport React, { useEffect, useCallback } from 'react'\nimport * as Dialog from '@radix-ui/react-dialog'\nimport type {\n LiveChatWidgetProps,\n QuickReply,\n Message,\n MessageContent,\n ChatStreamRequest,\n BackendCartData,\n CommonText,\n} from './types'\nimport { DEFAULT_COMMON_TEXT } from './constants'\nimport { ChatBubble } from './components/ChatBubble'\nimport { ChatWindow } from './components/ChatWindow'\nimport { ComplianceDialog } from './components/ComplianceDialog'\nimport { useChatState } from './hooks/useChatState'\nimport { useChatAPI } from './hooks/useChatAPI'\nimport { MessageRendererRegistry } from './utils/messageRenderers'\nimport { sanitizeInput } from './utils/validation'\nimport { saveUserId } from './utils/userId'\nimport { transformProducts } from './utils/productTransformers.js'\nimport { transformCartData } from './utils/cartTransformers.js'\nimport Cookies from 'js-cookie'\nimport {\n TextBlock,\n ProductCard,\n ProductList,\n ProductComparisonRenderer,\n PolicyBlock,\n createQuickRepliesRenderer,\n ThinkingBlock,\n ErrorBlock,\n FAQListRenderer,\n PromotionListRenderer,\n CartCard,\n} from './components/MessageContent/index.js'\n\n/**\n * LiveChat \u804A\u5929\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u6C14\u6CE1\u5F39\u7A97\u804A\u5929\u754C\u9762\n * - SSE \u6D41\u5F0F\u6D88\u606F\u63A5\u6536\n * - \u4F1A\u8BDD\u7BA1\u7406\uFF08userId, sessionId\uFF09\n * - \u5386\u53F2\u6D88\u606F\u52A0\u8F7D\n * - \u591A\u79CD\u6D88\u606F\u7C7B\u578B\u6E32\u67D3\n * - \u81EA\u5B9A\u4E49\u6269\u5C55\u673A\u5236\n * - reCAPTCHA v3 \u5B89\u5168\u9632\u62A4\n *\n * \u67B6\u6784\uFF1A\n * - UI Layer: ChatBubble, ChatWindow, MessageList, etc.\n * - Logic Layer: useChatState, useChatAPI, useSession\n * - Core Layer: MessageRendererRegistry, \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n *\n * @example\n * ```tsx\n * // \u57FA\u7840\u4F7F\u7528\uFF08\u4F7F\u7528\u9ED8\u8BA4\u4F4D\u7F6E\uFF09\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * welcomeMessage=\"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\"\n * onMessageSend={(msg) => console.log('Sent:', msg)}\n * />\n *\n * // \u4F7F\u7528\u81EA\u5B9A\u4E49\u4F4D\u7F6E\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * position={{ bottom: \"20px\", right: \"30px\" }}\n * onMessageSend={(msg) => console.log('Sent:', msg)}\n * />\n *\n * // \u542F\u7528 reCAPTCHA v3 \u5B89\u5168\u9632\u62A4\uFF08\u63D0\u4F9B sitekey \u5373\u81EA\u52A8\u542F\u7528\uFF09\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * recaptchaSitekey=\"6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14\"\n * recaptchaAction=\"livechat\"\n * />\n *\n * // \u4F7F\u7528\u81EA\u5B9A\u4E49 headers \u548C reCAPTCHA\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * headers={{\n * \"Authorization\": \"Bearer your-token\",\n * \"X-Custom-Header\": \"value\"\n * }}\n * recaptchaSitekey=\"your-site-key\"\n * />\n * ```\n */\nexport const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({\n apiBaseUrl,\n headers,\n recaptchaSitekey,\n recaptchaAction,\n site,\n channelCode,\n loginUserId,\n cartId,\n accessToken,\n position,\n welcomeMessage,\n quickReplies,\n customRenderers,\n logoUrl,\n title,\n chatBubbleIcon,\n open,\n onOpenChange,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n showNewSessionButton,\n commonText,\n productCardRender,\n productComparisonRender,\n bottomTips,\n complianceConfig,\n}) => {\n // \u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\u72B6\u6001\n // \u4ECE Cookie \u8BFB\u53D6\u7528\u6237\u662F\u5426\u5DF2\u540C\u610F\uFF08\u6709\u6548\u671F 365 \u5929\uFF09\n const cookieName = complianceConfig?.cookieName || 'livechat_compliance_agreed'\n const [showComplianceDialog, setShowComplianceDialog] = React.useState(false)\n const [hasAgreedCompliance, setHasAgreedCompliance] = React.useState(() => {\n // \u521D\u59CB\u5316\u65F6\u68C0\u67E5 Cookie\n return complianceConfig ? Cookies.get(cookieName) !== undefined : true\n })\n\n // \u5408\u5E76\u9ED8\u8BA4\u6587\u6848\u548C\u81EA\u5B9A\u4E49\u6587\u6848\n const mergedText: Required<CommonText> = React.useMemo(\n () => ({\n ...DEFAULT_COMMON_TEXT,\n ...commonText,\n }),\n [commonText]\n )\n\n // \u72B6\u6001\u7BA1\u7406\n const chatState = useChatState({\n welcomeMessage,\n site,\n open,\n onOpenChange,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n productCardRender,\n productComparisonRender,\n })\n\n const {\n messages,\n isOpen,\n userId,\n sessionId,\n inputValue,\n isStreaming,\n openChat,\n closeChat,\n setInputValue,\n setUserId,\n addMessage,\n setMessages,\n clearMessages,\n handleSSEEvent,\n saveSession,\n clearSession,\n } = chatState\n\n // \u521D\u59CB\u5316\u52A0\u8F7D\u72B6\u6001\uFF08\u7528\u6237\u672A\u914D\u7F6E\u6B22\u8FCE\u8BED\u65F6\uFF0C\u663E\u793A loading \u76F4\u5230\u63A5\u53E3\u8FD4\u56DE\uFF09\n const [isInitializing, setIsInitializing] = React.useState(false)\n\n // API \u8C03\u7528\n const { sendMessageStream, createSession } = useChatAPI({\n apiBaseUrl,\n headers,\n recaptchaConfig: {\n needRecaptcha: !!recaptchaSitekey, // \u6839\u636E sitekey \u81EA\u52A8\u5224\u65AD\u662F\u5426\u542F\u7528\n recaptchaSitekey,\n recaptchaAction,\n },\n onError,\n })\n\n // \u4F7F\u7528 ref \u5B58\u50A8\u6700\u65B0\u7684 handleSendMessage\uFF0C\u907F\u514D\u5FAA\u73AF\u4F9D\u8D56\n const handleSendMessageRef = React.useRef<(_message?: string) => Promise<void>>(async (_message?: string) => {})\n\n // \u6D88\u606F\u6E32\u67D3\u5668\u6CE8\u518C\u8868\n const rendererRegistry = React.useMemo(() => {\n const registry = new MessageRendererRegistry()\n\n // \u6CE8\u518C\u9ED8\u8BA4\u6E32\u67D3\u5668\n registry.register('text', TextBlock)\n registry.register('product_card', ProductCard)\n registry.register('product_list', ProductList)\n registry.register('product_comparison', ProductComparisonRenderer)\n registry.register('policy', PolicyBlock)\n registry.register('thinking', ThinkingBlock)\n registry.register('error', ErrorBlock)\n registry.register('faq_list', FAQListRenderer)\n registry.register('promotion_list', PromotionListRenderer)\n registry.register('cart', CartCard)\n\n // \u6CE8\u518C\u5FEB\u6377\u56DE\u590D\u6E32\u67D3\u5668\uFF08\u5E26\u56DE\u8C03\uFF09\n const quickRepliesRenderer = createQuickRepliesRenderer((reply: QuickReply) => {\n // \u4F7F\u7528 ref \u8C03\u7528\u6700\u65B0\u7684 handleSendMessage\n handleSendMessageRef.current(reply.value)\n })\n registry.register('quick_replies', quickRepliesRenderer)\n\n // \u6CE8\u518C\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n if (customRenderers) {\n registry.registerMany(customRenderers)\n }\n\n return registry\n }, [customRenderers])\n\n // \u6807\u8BB0\u662F\u5426\u5DF2\u7ECF\u521D\u59CB\u5316\u8FC7\u4F1A\u8BDD\uFF0C\u9632\u6B62\u91CD\u590D\u8C03\u7528\n const sessionInitializedRef = React.useRef(false)\n\n /**\n * T043: \u6253\u5F00\u804A\u5929\u7A97\u53E3\u65F6\u521D\u59CB\u5316\u4F1A\u8BDD\n * \u4F7F\u7528 API v2.0.0 \u7684\u7EDF\u4E00\u63A5\u53E3\uFF1A\n * - \u5982\u679C\u6CA1\u6709 sessionId\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n * - \u5982\u679C\u6709 sessionId\uFF0C\u6062\u590D\u4F1A\u8BDD\u5E76\u52A0\u8F7D\u5386\u53F2\u6D88\u606F\n */\n useEffect(() => {\n // \u7A97\u53E3\u5173\u95ED\u65F6\u91CD\u7F6E\u521D\u59CB\u5316\u6807\u8BB0\n if (!isOpen) {\n sessionInitializedRef.current = false\n return\n }\n\n // userId \u4E3A undefined \u8868\u793A\u5C1A\u672A\u521D\u59CB\u5316\uFF0C\u7B49\u5F85\u521D\u59CB\u5316\u5B8C\u6210\n if (userId === undefined) return\n\n // \u5982\u679C\u5DF2\u7ECF\u521D\u59CB\u5316\u8FC7\uFF0C\u4E0D\u518D\u91CD\u590D\u8C03\u7528\n if (sessionInitializedRef.current) return\n sessionInitializedRef.current = true\n\n const currentSessionId = sessionId\n\n if (!currentSessionId) {\n // \u6CA1\u6709\u4F1A\u8BDD\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n handleCreateNewSession()\n } else {\n // \u6709\u4F1A\u8BDD\uFF0C\u5C1D\u8BD5\u6062\u590D\u4F1A\u8BDD\u548C\u5386\u53F2\u6D88\u606F\n handleResumeSession(currentSessionId)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen, userId])\n\n /**\n * \u89C4\u8303\u5316\u6D88\u606F\u683C\u5F0F\uFF08\u786E\u4FDD content \u662F\u6570\u7EC4\uFF09\n * \u540E\u7AEF\u8FD4\u56DE\u683C\u5F0F\uFF1A\n * - content: \u5B57\u7B26\u4E32\uFF08\u6587\u672C\u5185\u5BB9\uFF09\n * - structuredContent: \u6570\u7EC4\uFF08\u7ED3\u6784\u5316\u5185\u5BB9\uFF0C\u5982\u4EA7\u54C1\u5217\u8868\u3001\u653F\u7B56\u7B49\uFF09\n * \u9700\u8981\u5408\u5E76\u4E3A\u7EDF\u4E00\u7684 content \u6570\u7EC4\u683C\u5F0F\n */\n const normalizeMessage = useCallback(\n (message: any): Message => {\n // \u5982\u679C content \u5DF2\u7ECF\u662F\u6570\u7EC4\uFF0C\u76F4\u63A5\u8FD4\u56DE\n if (Array.isArray(message.content)) {\n return message as Message\n }\n\n const contentBlocks: MessageContent[] = []\n\n // \u5904\u7406\u6587\u672C\u5185\u5BB9\n if (typeof message.content === 'string' && message.content.trim()) {\n contentBlocks.push({\n type: 'text',\n text: message.content,\n })\n }\n\n // \u5904\u7406\u7ED3\u6784\u5316\u5185\u5BB9\n // \u5386\u53F2\u6D88\u606F\u683C\u5F0F: structured_content: [{type, data}]\n const structuredData = message.structuredContent || message.structured_content\n\n if (Array.isArray(structuredData)) {\n structuredData.forEach((block: any) => {\n if (block.type === 'product_list' && Array.isArray(block.data)) {\n // \u8F6C\u6362\u4EA7\u54C1\u5217\u8868\u6570\u636E\u7ED3\u6784 - data \u76F4\u63A5\u662F\u4EA7\u54C1\u6570\u7EC4\n contentBlocks.push({\n type: 'product_list',\n data: {\n products: transformProducts(block.data, site),\n title: undefined, // \u5386\u53F2\u6D88\u606F\u4E0D\u5305\u542B title\n commonText: mergedText,\n },\n })\n } else if (block.type === 'quick_replies' && block.data?.replies) {\n contentBlocks.push({\n type: 'quick_replies',\n data: {\n replies: block.data.replies,\n },\n })\n } else if (block.type === 'policy' && block.data?.title && block.data?.content) {\n contentBlocks.push({\n type: 'policy',\n data: {\n title: block.data.title,\n content: block.data.content,\n },\n })\n } else if (block.type === 'product_comparison' && block.data?.products && block.data?.dimensions) {\n // \u8F6C\u6362\u4EA7\u54C1\u5BF9\u6BD4\u6570\u636E\u7ED3\u6784\u5E76\u6CE8\u5165 onAddToCart \u56DE\u8C03\u548C\u81EA\u5B9A\u4E49\u6E32\u67D3\u51FD\u6570\n contentBlocks.push({\n type: 'product_comparison',\n data: {\n products: transformProducts(block.data.products, site),\n dimensions: block.data.dimensions,\n onAddToCart: onAddToCart,\n commonText: mergedText,\n productComparisonRender: productComparisonRender,\n },\n })\n } else if (block.type === 'faq_list' && block.data?.found !== undefined) {\n // FAQ \u5217\u8868\u5361\u7247 - \u76F4\u63A5\u4F7F\u7528\u540E\u7AEF\u6570\u636E\n contentBlocks.push({\n type: 'faq_list',\n data: block.data,\n })\n } else if (block.type === 'promotion_list' && block.data?.found !== undefined) {\n // \u4FC3\u9500\u6D3B\u52A8\u5217\u8868\u5361\u7247 - \u76F4\u63A5\u4F7F\u7528\u540E\u7AEF\u6570\u636E\n contentBlocks.push({\n type: 'promotion_list',\n data: {\n ...block.data,\n commonText: mergedText,\n },\n })\n } else if (block.type === 'cart' && block.data?.id !== undefined) {\n // \u8D2D\u7269\u8F66\u5361\u7247 - \u8F6C\u6362\u540E\u7AEF\u6570\u636E\u683C\u5F0F\u5E76\u6CE8\u5165 onCart \u56DE\u8C03\n const transformedData = transformCartData(block.data as BackendCartData)\n contentBlocks.push({\n type: 'cart',\n data: {\n ...transformedData,\n onCart: onCart,\n commonText: mergedText,\n },\n })\n } else {\n // \u5176\u4ED6\u7C7B\u578B\u76F4\u63A5\u6DFB\u52A0\n contentBlocks.push(block)\n }\n })\n }\n\n // \u5982\u679C\u6CA1\u6709\u4EFB\u4F55\u5185\u5BB9\u5757\uFF0C\u8FD4\u56DE\u7A7A\u6587\u672C\u5757\n if (contentBlocks.length === 0) {\n contentBlocks.push({\n type: 'text',\n text: '',\n })\n }\n\n return {\n ...message,\n content: contentBlocks,\n } as Message\n },\n [site, onCart, onAddToCart, mergedText]\n )\n\n /**\n * \u521B\u5EFA\u65B0\u4F1A\u8BDD\n */\n const handleCreateNewSession = useCallback(async () => {\n // userId \u4E3A undefined \u8868\u793A\u5C1A\u672A\u521D\u59CB\u5316\uFF0C\u7B49\u5F85\u521D\u59CB\u5316\u5B8C\u6210\n if (userId === undefined) return\n\n // \u5982\u679C\u7528\u6237\u6CA1\u6709\u914D\u7F6E\u6B22\u8FCE\u8BED\uFF0C\u663E\u793A loading \u72B6\u6001\n if (!welcomeMessage) {\n setIsInitializing(true)\n }\n\n try {\n const response = await createSession({\n user_id: userId ?? '',\n site: site,\n channel_code: channelCode,\n real_user_id: loginUserId,\n page_url: typeof window !== 'undefined' ? window.location.href : undefined,\n })\n\n if (response.success) {\n // \u4FDD\u5B58\u65B0\u4F1A\u8BDD ID\n saveSession(response.sessionId)\n\n // \u4FDD\u5B58\u540E\u7AEF\u8FD4\u56DE\u7684 userId\uFF08\u5982\u679C\u6709\uFF09\n if (response.userId) {\n saveUserId(response.userId)\n setUserId(response.userId)\n }\n\n // \u6E05\u7A7A\u6D88\u606F\u5217\u8868\n clearMessages()\n\n // \u4F7F\u7528\u540E\u7AEF\u8FD4\u56DE\u7684\u6B22\u8FCE\u6D88\u606F\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u4F7F\u7528 props \u4E2D\u7684\u9ED8\u8BA4\u503C\n const messageText = response.welcomeMessage || welcomeMessage\n\n if (messageText) {\n const welcomeContent: MessageContent[] = [{ type: 'text', text: messageText }]\n\n // \u4F7F\u7528\u540E\u7AEF\u8FD4\u56DE\u7684\u5FEB\u6377\u95EE\u9898\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u4F7F\u7528 props \u4E2D\u7684\u5FEB\u6377\u56DE\u590D\n const questions = response.quickQuestions\n if (questions && questions.length > 0) {\n // \u5C06\u540E\u7AEF\u7684 quickQuestions (\u5B57\u7B26\u4E32\u6570\u7EC4) \u8F6C\u6362\u4E3A QuickReply \u683C\u5F0F\n const quickRepliesFromBackend = questions.map((question, index) => ({\n id: `quick-${index}`,\n label: question,\n value: question,\n }))\n\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickRepliesFromBackend,\n },\n })\n } else if (quickReplies && quickReplies.length > 0) {\n // \u5982\u679C\u540E\u7AEF\u6CA1\u6709\u8FD4\u56DE\uFF0C\u4F7F\u7528 props \u4E2D\u7684\u5FEB\u6377\u56DE\u590D\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickReplies,\n },\n })\n }\n\n addMessage({\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: welcomeContent,\n timestamp: Date.now(),\n })\n }\n }\n } catch (error) {\n console.error('[LiveChatWidget] Failed to create new session:', error)\n onError?.(error as Error)\n\n // Check if it's a reCAPTCHA error\n const isRecaptcha = (error as any)?.type === 'GoogleRecaptchaError'\n\n // \u6DFB\u52A0\u9519\u8BEF\u6D88\u606F\u5230\u754C\u9762\n addMessage({\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: isRecaptcha\n ? 'Your session has expired. Please refresh the page and try again.'\n : 'Failed to create session. Please refresh the page and try again.',\n code: isRecaptcha ? 'RECAPTCHA_ERROR' : 'SESSION_CREATE_ERROR',\n },\n },\n ],\n timestamp: Date.now(),\n })\n } finally {\n // \u63A5\u53E3\u8FD4\u56DE\u540E\u5173\u95ED loading \u72B6\u6001\n setIsInitializing(false)\n }\n }, [\n userId,\n site,\n channelCode,\n loginUserId,\n createSession,\n saveSession,\n clearMessages,\n welcomeMessage,\n quickReplies,\n addMessage,\n onError,\n ])\n\n /**\n * \u6062\u590D\u4F1A\u8BDD\u548C\u5386\u53F2\u6D88\u606F\uFF08\u4F7F\u7528\u65B0\u7684 API v2.0.0\uFF09\n */\n const handleResumeSession = useCallback(\n async (existingSessionId: string) => {\n // \u5982\u679C\u7528\u6237\u6CA1\u6709\u914D\u7F6E\u6B22\u8FCE\u8BED\uFF0C\u663E\u793A loading \u72B6\u6001\n if (!welcomeMessage) {\n setIsInitializing(true)\n }\n\n try {\n const response = await createSession({\n user_id: userId ?? '',\n session_id: existingSessionId,\n site: site,\n channel_code: channelCode,\n real_user_id: loginUserId,\n page_url: typeof window !== 'undefined' ? window.location.href : undefined,\n })\n\n if (response.success && response.resumed) {\n // \u4F1A\u8BDD\u6062\u590D\u6210\u529F\n\n // \u4FDD\u5B58\u540E\u7AEF\u8FD4\u56DE\u7684 userId\uFF08\u5982\u679C\u6709\uFF09\n if (response.userId) {\n saveUserId(response.userId)\n setUserId(response.userId)\n }\n\n // \u51C6\u5907\u6B22\u8FCE\u6D88\u606F\uFF08\u65E0\u8BBA\u662F\u5426\u6709\u5386\u53F2\u6D88\u606F\u90FD\u9700\u8981\uFF09\n const messageText = response.welcomeMessage || welcomeMessage\n const welcomeContent: MessageContent[] = messageText ? [{ type: 'text', text: messageText }] : []\n\n // \u6DFB\u52A0\u5FEB\u6377\u56DE\u590D\u5230\u6B22\u8FCE\u6D88\u606F\n const questions = response.quickQuestions\n if (questions && questions.length > 0) {\n const quickRepliesFromBackend = questions.map((question, index) => ({\n id: `quick-${index}`,\n label: question,\n value: question,\n }))\n\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickRepliesFromBackend,\n },\n })\n } else if (quickReplies && quickReplies.length > 0) {\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickReplies,\n },\n })\n }\n\n if (response.messages && response.messages.length > 0) {\n // \u6709\u5386\u53F2\u6D88\u606F\uFF0C\u89C4\u8303\u5316\u5E76\u52A0\u8F7D\uFF08\u8FC7\u6EE4\u6389 null/undefined \u503C\u548C\u7A7A\u5185\u5BB9\u6D88\u606F\uFF09\n const normalizedMessages = response.messages\n .filter((msg: any) => msg != null)\n .map(normalizeMessage)\n .filter((msg: Message) => msg.content && msg.content.length > 0 && !(msg.content.length === 1 && msg.content[0].type === 'text' && !msg.content[0].text))\n\n // \u5982\u679C\u6709\u6B22\u8FCE\u6D88\u606F\uFF0C\u5C06\u5176\u6DFB\u52A0\u5230\u5386\u53F2\u6D88\u606F\u7684\u5F00\u5934\n if (welcomeContent.length > 0) {\n const welcomeMsg: Message = {\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: welcomeContent,\n timestamp: Date.now(),\n }\n setMessages([welcomeMsg, ...normalizedMessages])\n } else {\n setMessages(normalizedMessages)\n }\n } else {\n // \u6CA1\u6709\u5386\u53F2\u6D88\u606F\uFF0C\u4EC5\u663E\u793A\u6B22\u8FCE\u6D88\u606F\n clearMessages()\n\n if (welcomeContent.length > 0) {\n addMessage({\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: welcomeContent,\n timestamp: Date.now(),\n })\n }\n }\n } else if (!response.resumed) {\n // \u4F1A\u8BDD\u65E0\u6548\u6216\u8FC7\u671F\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n clearSession()\n handleCreateNewSession()\n }\n } catch (error) {\n console.error('[LiveChatWidget] Failed to resume session:', error)\n\n // Check if it's a reCAPTCHA error\n const isRecaptcha = (error as any)?.type === 'GoogleRecaptchaError'\n\n if (isRecaptcha) {\n // reCAPTCHA error - show refresh page message, don't retry\n addMessage({\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: 'Your session has expired. Please refresh the page and try again.',\n code: 'RECAPTCHA_ERROR',\n },\n },\n ],\n timestamp: Date.now(),\n })\n } else {\n // Other errors - show message and try to create new session\n addMessage({\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: 'Failed to resume session. Creating a new session...',\n code: 'SESSION_RESUME_ERROR',\n },\n },\n ],\n timestamp: Date.now(),\n })\n\n // \u6062\u590D\u5931\u8D25\uFF0C\u6E05\u7A7A\u4F1A\u8BDD\u5E76\u521B\u5EFA\u65B0\u7684\n clearSession()\n handleCreateNewSession()\n }\n } finally {\n // \u63A5\u53E3\u8FD4\u56DE\u540E\u5173\u95ED loading \u72B6\u6001\n setIsInitializing(false)\n }\n },\n [\n userId,\n site,\n channelCode,\n loginUserId,\n createSession,\n setMessages,\n clearSession,\n clearMessages,\n normalizeMessage,\n handleCreateNewSession,\n welcomeMessage,\n quickReplies,\n addMessage,\n ]\n )\n\n /**\n * \u53D1\u9001\u6D88\u606F\n */\n const handleSendMessage = useCallback(\n async (message?: string, isRetry: boolean = false) => {\n const textToSend = message || inputValue.trim()\n\n if (!textToSend) return\n\n // \u6E05\u7A7A\u8F93\u5165\u6846\uFF08\u4EC5\u5728\u975E\u91CD\u8BD5\u65F6\uFF09\n if (!message && !isRetry) {\n setInputValue('')\n }\n\n // \u8F93\u5165\u9A8C\u8BC1\n const sanitized = sanitizeInput(textToSend)\n if (!sanitized) {\n onError?.(new Error('Invalid message'))\n return\n }\n\n // \u6DFB\u52A0\u7528\u6237\u6D88\u606F\u5230\u754C\u9762\uFF08\u4EC5\u5728\u975E\u91CD\u8BD5\u65F6\uFF09\n if (!isRetry) {\n const userMessage = {\n id: `user-${Date.now()}`,\n role: 'user' as const,\n content: [{ type: 'text' as const, text: sanitized }],\n timestamp: Date.now(),\n }\n addMessage(userMessage)\n }\n\n // \u7ACB\u5373\u6DFB\u52A0\u601D\u8003\u72B6\u6001\u6D88\u606F\uFF0C\u63D0\u5347\u7528\u6237\u4F53\u9A8C\n const thinkingMessage = {\n id: `thinking-${Date.now()}`,\n role: 'assistant' as const,\n content: [{ type: 'thinking' as const, data: { status: 'thinking' } }],\n timestamp: Date.now(),\n }\n addMessage(thinkingMessage)\n\n // \u89E6\u53D1\u6D88\u606F\u53D1\u9001\u56DE\u8C03\uFF08\u4EC5\u5728\u975E\u91CD\u8BD5\u65F6\uFF09\n if (!isRetry) {\n onMessageSend?.(sanitized)\n }\n\n // \u6807\u8BB0\u662F\u5426\u68C0\u6D4B\u5230\u4F1A\u8BDD\u8FC7\u671F\n let sessionExpiredDetected = false\n\n try {\n // \u786E\u4FDD\u6709 sessionId\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u5148\u521B\u5EFA\u4F1A\u8BDD\n let currentSessionId = sessionId\n if (!currentSessionId) {\n // \u6CA1\u6709\u4F1A\u8BDD\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n const response = await createSession({\n user_id: userId ?? '',\n site: site,\n channel_code: channelCode,\n real_user_id: loginUserId,\n page_url: typeof window !== 'undefined' ? window.location.href : undefined,\n })\n if (response.success) {\n currentSessionId = response.sessionId\n saveSession(currentSessionId)\n } else {\n throw new Error('Failed to create session')\n }\n }\n\n // \u6784\u5EFA\u8BF7\u6C42\u53C2\u6570\uFF08session_id \u73B0\u5728\u662F\u5FC5\u586B\u7684\uFF09\n const requestPayload: ChatStreamRequest = {\n message: sanitized,\n user_id: userId ?? '',\n session_id: currentSessionId,\n context: {\n cartId: cartId,\n accessToken: accessToken,\n real_user_id: loginUserId,\n },\n }\n\n // \u53D1\u9001\u6D88\u606F\u5230\u540E\u7AEF\n await sendMessageStream(requestPayload, event => {\n // \u5904\u7406 SSE \u4E8B\u4EF6\n handleSSEEvent(event)\n\n // \u7279\u6B8A\u5904\u7406\uFF1A\u4F1A\u8BDD\u8FC7\u671F\uFF08error \u4E8B\u4EF6\u4E14 type \u4E3A validation_error\uFF09\n if (event.event === 'error' && event.data.type === 'validation_error') {\n sessionExpiredDetected = true\n clearSession()\n }\n })\n\n // \u5982\u679C\u68C0\u6D4B\u5230\u4F1A\u8BDD\u8FC7\u671F\u4E14\u4E0D\u662F\u91CD\u8BD5\uFF0C\u81EA\u52A8\u521B\u5EFA\u65B0\u4F1A\u8BDD\u5E76\u91CD\u8BD5\n if (sessionExpiredDetected && !isRetry) {\n console.log('[LiveChatWidget] Session expired (validation_error), creating new session and retrying...')\n\n // \u79FB\u9664 thinking \u6D88\u606F\n const messagesWithoutThinking = messages.filter(msg => msg.id !== thinkingMessage.id)\n setMessages(messagesWithoutThinking)\n\n // \u521B\u5EFA\u65B0\u4F1A\u8BDD\n const response = await createSession({\n user_id: userId ?? '',\n site: site,\n channel_code: channelCode,\n real_user_id: loginUserId,\n page_url: typeof window !== 'undefined' ? window.location.href : undefined,\n })\n\n if (response.success) {\n saveSession(response.sessionId)\n // \u91CD\u8BD5\u53D1\u9001\u6D88\u606F\n await handleSendMessage(sanitized, true)\n } else {\n throw new Error('Failed to recreate session after expiration')\n }\n }\n } catch (error) {\n console.error('[LiveChatWidget] Failed to send message:', error)\n onError?.(error as Error)\n\n // Check if it's a reCAPTCHA error\n const isRecaptcha = (error as any)?.type === 'GoogleRecaptchaError'\n\n // Determine error message based on error type\n let errorMessage: string\n let errorCode: string\n\n if (isRecaptcha) {\n errorMessage = 'Your session has expired. Please refresh the page and try again.'\n errorCode = 'RECAPTCHA_ERROR'\n } else if (sessionExpiredDetected) {\n errorMessage = 'Your session has expired. We tried to reconnect but failed. Please try again.'\n errorCode = 'SESSION_EXPIRED'\n } else {\n errorMessage = 'Failed to send message. Please check your network connection and try again.'\n errorCode = 'NETWORK_ERROR'\n }\n\n // \u79FB\u9664\u521A\u624D\u6DFB\u52A0\u7684 thinking \u6D88\u606F\uFF0C\u5E76\u6DFB\u52A0\u9519\u8BEF\u6D88\u606F\n const messagesWithoutThinking = messages.filter(msg => msg.id !== thinkingMessage.id)\n setMessages([\n ...messagesWithoutThinking,\n {\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: errorMessage,\n code: errorCode,\n },\n },\n ],\n timestamp: Date.now(),\n },\n ])\n }\n },\n [\n inputValue,\n userId,\n sessionId,\n site,\n channelCode,\n loginUserId,\n cartId,\n accessToken,\n messages,\n setInputValue,\n addMessage,\n setMessages,\n createSession,\n sendMessageStream,\n handleSSEEvent,\n saveSession,\n clearSession,\n onMessageSend,\n onError,\n ]\n )\n\n // \u66F4\u65B0 ref \u4EE5\u4FDD\u6301\u6700\u65B0\u7684 handleSendMessage\n React.useEffect(() => {\n handleSendMessageRef.current = handleSendMessage\n }, [handleSendMessage])\n\n /**\n * \u5904\u7406\u6C14\u6CE1\u6309\u94AE\u70B9\u51FB\n * \u5982\u679C\u914D\u7F6E\u4E86\u6CD5\u89C4\u534F\u8BAE\u4E14\u7528\u6237\u672A\u540C\u610F\uFF0C\u5148\u663E\u793A\u6CD5\u89C4\u5F39\u7A97\n * \u5426\u5219\u76F4\u63A5\u6253\u5F00\u804A\u5929\u7A97\u53E3\n */\n const handleBubbleClick = useCallback(() => {\n if (complianceConfig && !hasAgreedCompliance) {\n // \u663E\u793A\u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\n setShowComplianceDialog(true)\n } else {\n // \u76F4\u63A5\u6253\u5F00\u804A\u5929\u7A97\u53E3\n openChat()\n }\n }, [complianceConfig, hasAgreedCompliance, openChat])\n\n /**\n * \u5904\u7406\u7528\u6237\u540C\u610F\u6CD5\u89C4\u534F\u8BAE\n */\n const handleComplianceAgree = useCallback(() => {\n // \u8BBE\u7F6E\u540C\u610F\u72B6\u6001\n setHasAgreedCompliance(true)\n setShowComplianceDialog(false)\n\n // \u4FDD\u5B58\u5230 Cookie\uFF08\u6709\u6548\u671F 365 \u5929\uFF09\n Cookies.set(cookieName, 'true', { expires: 365 })\n\n // \u540C\u610F\u540E\u7ACB\u5373\u6253\u5F00\u804A\u5929\u7A97\u53E3\n openChat()\n }, [openChat, cookieName])\n\n /**\n * \u5904\u7406\u6CD5\u89C4\u5F39\u7A97\u5173\u95ED\n */\n const handleComplianceClose = useCallback(() => {\n setShowComplianceDialog(false)\n }, [])\n\n return (\n <>\n {/* \u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97 */}\n {complianceConfig && (\n <ComplianceDialog\n open={showComplianceDialog}\n config={complianceConfig}\n onAgree={handleComplianceAgree}\n onClose={handleComplianceClose}\n />\n )}\n\n {/* \u6C14\u6CE1\u6309\u94AE */}\n <ChatBubble\n position={position}\n onClick={handleBubbleClick}\n visible={!isOpen && !showComplianceDialog}\n iconImageUrl={chatBubbleIcon}\n />\n\n {/* \u804A\u5929\u7A97\u53E3\uFF08\u4F7F\u7528 Radix UI Dialog\uFF09 */}\n <Dialog.Root open={isOpen} onOpenChange={open => (open ? openChat() : closeChat())}>\n <Dialog.Portal>\n <Dialog.Content\n className=\"livechat-window-enter\"\n style={{\n position: 'fixed',\n zIndex: 9998,\n }}\n >\n <ChatWindow\n messages={messages}\n inputValue={inputValue}\n onInputChange={setInputValue}\n onSend={() => handleSendMessage()}\n onClose={closeChat}\n onNewSession={handleCreateNewSession}\n title={title}\n logoUrl={logoUrl}\n isSending={isStreaming}\n isLoadingHistory={isInitializing}\n rendererRegistry={rendererRegistry}\n inputPlaceholder=\"\"\n onAddToCart={onAddToCart}\n showNewSessionButton={showNewSessionButton}\n bottomTips={bottomTips}\n />\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n </>\n )\n}\n"],
5
- "mappings": "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",
6
- "names": ["LiveChatWidget_exports", "__export", "LiveChatWidget", "__toCommonJS", "import_jsx_runtime", "import_react", "Dialog", "import_constants", "import_ChatBubble", "import_ChatWindow", "import_ComplianceDialog", "import_useChatState", "import_useChatAPI", "import_messageRenderers", "import_validation", "import_userId", "import_productTransformers", "import_cartTransformers", "import_js_cookie", "import_MessageContent", "apiBaseUrl", "headers", "recaptchaSitekey", "recaptchaAction", "site", "channelCode", "loginUserId", "cartId", "accessToken", "position", "welcomeMessage", "quickReplies", "customRenderers", "logoUrl", "title", "chatBubbleIcon", "open", "onOpenChange", "onOpen", "onClose", "onMessageSend", "onError", "onTextMessage", "onProductList", "onPromotionList", "onAddToCart", "onCart", "showNewSessionButton", "commonText", "productCardRender", "productComparisonRender", "bottomTips", "complianceConfig", "cookieName", "showComplianceDialog", "setShowComplianceDialog", "React", "hasAgreedCompliance", "setHasAgreedCompliance", "Cookies", "mergedText", "chatState", "messages", "isOpen", "userId", "sessionId", "inputValue", "isStreaming", "openChat", "closeChat", "setInputValue", "setUserId", "addMessage", "setMessages", "clearMessages", "handleSSEEvent", "saveSession", "clearSession", "isInitializing", "setIsInitializing", "sendMessageStream", "createSession", "handleSendMessageRef", "_message", "rendererRegistry", "registry", "quickRepliesRenderer", "reply", "sessionInitializedRef", "currentSessionId", "handleResumeSession", "handleCreateNewSession", "normalizeMessage", "message", "contentBlocks", "structuredData", "block", "transformedData", "response", "messageText", "welcomeContent", "questions", "quickRepliesFromBackend", "question", "index", "error", "isRecaptcha", "existingSessionId", "normalizedMessages", "msg", "welcomeMsg", "handleSendMessage", "isRetry", "textToSend", "sanitized", "userMessage", "thinkingMessage", "sessionExpiredDetected", "event", "messagesWithoutThinking", "errorMessage", "errorCode", "handleBubbleClick", "handleComplianceAgree", "handleComplianceClose"]
4
+ "sourcesContent": ["/**\n * LiveChat \u4E3B\u7EC4\u4EF6\n * \u96C6\u6210\u6240\u6709\u5B50\u7EC4\u4EF6\uFF0C\u63D0\u4F9B\u5B8C\u6574\u7684\u804A\u5929\u529F\u80FD\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u4E09\u5C42\u67B6\u6784\u8BBE\u8BA1\n */\n\nimport React, { useEffect, useCallback } from 'react'\nimport * as Dialog from '@radix-ui/react-dialog'\nimport type {\n LiveChatWidgetProps,\n QuickReply,\n Message,\n MessageContent,\n ChatStreamRequest,\n BackendCartData,\n CommonText,\n} from './types'\nimport { DEFAULT_COMMON_TEXT } from './constants'\nimport { ChatBubble } from './components/ChatBubble'\nimport { ChatWindow } from './components/ChatWindow'\nimport { ComplianceDialog } from './components/ComplianceDialog'\nimport { useChatState } from './hooks/useChatState'\nimport { useChatAPI } from './hooks/useChatAPI'\nimport { MessageRendererRegistry } from './utils/messageRenderers'\nimport { sanitizeInput } from './utils/validation'\nimport { saveUserId } from './utils/userId'\nimport { transformProducts } from './utils/productTransformers.js'\nimport { transformCartData } from './utils/cartTransformers.js'\nimport Cookies from 'js-cookie'\nimport {\n TextBlock,\n ProductCard,\n ProductList,\n ProductComparisonRenderer,\n PolicyBlock,\n createQuickRepliesRenderer,\n ThinkingBlock,\n ErrorBlock,\n FAQListRenderer,\n PromotionListRenderer,\n CartCard,\n} from './components/MessageContent/index.js'\n\n/**\n * LiveChat \u804A\u5929\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u6C14\u6CE1\u5F39\u7A97\u804A\u5929\u754C\u9762\n * - SSE \u6D41\u5F0F\u6D88\u606F\u63A5\u6536\n * - \u4F1A\u8BDD\u7BA1\u7406\uFF08userId, sessionId\uFF09\n * - \u5386\u53F2\u6D88\u606F\u52A0\u8F7D\n * - \u591A\u79CD\u6D88\u606F\u7C7B\u578B\u6E32\u67D3\n * - \u81EA\u5B9A\u4E49\u6269\u5C55\u673A\u5236\n * - reCAPTCHA v3 \u5B89\u5168\u9632\u62A4\n *\n * \u67B6\u6784\uFF1A\n * - UI Layer: ChatBubble, ChatWindow, MessageList, etc.\n * - Logic Layer: useChatState, useChatAPI, useSession\n * - Core Layer: MessageRendererRegistry, \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n *\n * @example\n * ```tsx\n * // \u57FA\u7840\u4F7F\u7528\uFF08\u4F7F\u7528\u9ED8\u8BA4\u4F4D\u7F6E\uFF09\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * welcomeMessage=\"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\"\n * onMessageSend={(msg) => console.log('Sent:', msg)}\n * />\n *\n * // \u4F7F\u7528\u81EA\u5B9A\u4E49\u4F4D\u7F6E\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * position={{ bottom: \"20px\", right: \"30px\" }}\n * onMessageSend={(msg) => console.log('Sent:', msg)}\n * />\n *\n * // \u542F\u7528 reCAPTCHA v3 \u5B89\u5168\u9632\u62A4\uFF08\u63D0\u4F9B sitekey \u5373\u81EA\u52A8\u542F\u7528\uFF09\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * recaptchaSitekey=\"6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14\"\n * recaptchaAction=\"livechat\"\n * />\n *\n * // \u4F7F\u7528\u81EA\u5B9A\u4E49 headers \u548C reCAPTCHA\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * headers={{\n * \"Authorization\": \"Bearer your-token\",\n * \"X-Custom-Header\": \"value\"\n * }}\n * recaptchaSitekey=\"your-site-key\"\n * />\n * ```\n */\nexport const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({\n apiBaseUrl,\n headers,\n recaptchaSitekey,\n recaptchaAction,\n site,\n channelCode,\n loginUserId,\n cartId,\n accessToken,\n position,\n welcomeMessage,\n quickReplies,\n customRenderers,\n logoUrl,\n title,\n chatBubbleIcon,\n open,\n onOpenChange,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n showNewSessionButton,\n commonText,\n productCardRender,\n productComparisonRender,\n bottomTips,\n complianceConfig,\n}) => {\n // \u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\u72B6\u6001\n // \u4ECE Cookie \u8BFB\u53D6\u7528\u6237\u662F\u5426\u5DF2\u540C\u610F\uFF08\u6709\u6548\u671F 365 \u5929\uFF09\n const cookieName = complianceConfig?.cookieName || 'livechat_compliance_agreed'\n const [showComplianceDialog, setShowComplianceDialog] = React.useState(false)\n const [hasAgreedCompliance, setHasAgreedCompliance] = React.useState(() => {\n // \u521D\u59CB\u5316\u65F6\u68C0\u67E5 Cookie\n return complianceConfig ? Cookies.get(cookieName) !== undefined : true\n })\n\n // \u5408\u5E76\u9ED8\u8BA4\u6587\u6848\u548C\u81EA\u5B9A\u4E49\u6587\u6848\n const mergedText: Required<CommonText> = React.useMemo(\n () => ({\n ...DEFAULT_COMMON_TEXT,\n ...commonText,\n }),\n [commonText]\n )\n\n // \u72B6\u6001\u7BA1\u7406\n const chatState = useChatState({\n welcomeMessage,\n site,\n open,\n onOpenChange,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n productCardRender,\n productComparisonRender,\n })\n\n const {\n messages,\n isOpen,\n userId,\n sessionId,\n inputValue,\n isStreaming,\n openChat,\n closeChat,\n setInputValue,\n setUserId,\n addMessage,\n setMessages,\n clearMessages,\n startStream,\n saveSession,\n clearSession,\n } = chatState\n\n // \u521D\u59CB\u5316\u52A0\u8F7D\u72B6\u6001\uFF08\u7528\u6237\u672A\u914D\u7F6E\u6B22\u8FCE\u8BED\u65F6\uFF0C\u663E\u793A loading \u76F4\u5230\u63A5\u53E3\u8FD4\u56DE\uFF09\n const [isInitializing, setIsInitializing] = React.useState(false)\n\n // API \u8C03\u7528\n const { sendMessageStream, createSession } = useChatAPI({\n apiBaseUrl,\n headers,\n recaptchaConfig: {\n needRecaptcha: !!recaptchaSitekey, // \u6839\u636E sitekey \u81EA\u52A8\u5224\u65AD\u662F\u5426\u542F\u7528\n recaptchaSitekey,\n recaptchaAction,\n },\n onError,\n })\n\n // \u4F7F\u7528 ref \u5B58\u50A8\u6700\u65B0\u7684 handleSendMessage\uFF0C\u907F\u514D\u5FAA\u73AF\u4F9D\u8D56\n const handleSendMessageRef = React.useRef<(_message?: string) => Promise<void>>(async (_message?: string) => {})\n\n // \u6D88\u606F\u6E32\u67D3\u5668\u6CE8\u518C\u8868\n const rendererRegistry = React.useMemo(() => {\n const registry = new MessageRendererRegistry()\n\n // \u6CE8\u518C\u9ED8\u8BA4\u6E32\u67D3\u5668\n registry.register('text', TextBlock)\n registry.register('product_card', ProductCard)\n registry.register('product_list', ProductList)\n registry.register('product_comparison', ProductComparisonRenderer)\n registry.register('policy', PolicyBlock)\n registry.register('thinking', ThinkingBlock)\n registry.register('error', ErrorBlock)\n registry.register('faq_list', FAQListRenderer)\n registry.register('promotion_list', PromotionListRenderer)\n registry.register('cart', CartCard)\n\n // \u6CE8\u518C\u5FEB\u6377\u56DE\u590D\u6E32\u67D3\u5668\uFF08\u5E26\u56DE\u8C03\uFF09\n const quickRepliesRenderer = createQuickRepliesRenderer((reply: QuickReply) => {\n // \u4F7F\u7528 ref \u8C03\u7528\u6700\u65B0\u7684 handleSendMessage\n handleSendMessageRef.current(reply.value)\n })\n registry.register('quick_replies', quickRepliesRenderer)\n\n // \u6CE8\u518C\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n if (customRenderers) {\n registry.registerMany(customRenderers)\n }\n\n return registry\n }, [customRenderers])\n\n // \u6807\u8BB0\u662F\u5426\u5DF2\u7ECF\u521D\u59CB\u5316\u8FC7\u4F1A\u8BDD\uFF0C\u9632\u6B62\u91CD\u590D\u8C03\u7528\n const sessionInitializedRef = React.useRef(false)\n\n /**\n * T043: \u6253\u5F00\u804A\u5929\u7A97\u53E3\u65F6\u521D\u59CB\u5316\u4F1A\u8BDD\n * \u4F7F\u7528 API v2.0.0 \u7684\u7EDF\u4E00\u63A5\u53E3\uFF1A\n * - \u5982\u679C\u6CA1\u6709 sessionId\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n * - \u5982\u679C\u6709 sessionId\uFF0C\u6062\u590D\u4F1A\u8BDD\u5E76\u52A0\u8F7D\u5386\u53F2\u6D88\u606F\n */\n useEffect(() => {\n // \u7A97\u53E3\u5173\u95ED\u65F6\u91CD\u7F6E\u521D\u59CB\u5316\u6807\u8BB0\n if (!isOpen) {\n sessionInitializedRef.current = false\n return\n }\n\n // userId \u4E3A undefined \u8868\u793A\u5C1A\u672A\u521D\u59CB\u5316\uFF0C\u7B49\u5F85\u521D\u59CB\u5316\u5B8C\u6210\n if (userId === undefined) return\n\n // \u5982\u679C\u5DF2\u7ECF\u521D\u59CB\u5316\u8FC7\uFF0C\u4E0D\u518D\u91CD\u590D\u8C03\u7528\n if (sessionInitializedRef.current) return\n sessionInitializedRef.current = true\n\n const currentSessionId = sessionId\n\n if (!currentSessionId) {\n // \u6CA1\u6709\u4F1A\u8BDD\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n handleCreateNewSession()\n } else {\n // \u6709\u4F1A\u8BDD\uFF0C\u5C1D\u8BD5\u6062\u590D\u4F1A\u8BDD\u548C\u5386\u53F2\u6D88\u606F\n handleResumeSession(currentSessionId)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen, userId])\n\n /**\n * \u89C4\u8303\u5316\u6D88\u606F\u683C\u5F0F\uFF08\u786E\u4FDD content \u662F\u6570\u7EC4\uFF09\n * \u540E\u7AEF\u8FD4\u56DE\u683C\u5F0F\uFF1A\n * - content: \u5B57\u7B26\u4E32\uFF08\u6587\u672C\u5185\u5BB9\uFF09\n * - structuredContent: \u6570\u7EC4\uFF08\u7ED3\u6784\u5316\u5185\u5BB9\uFF0C\u5982\u4EA7\u54C1\u5217\u8868\u3001\u653F\u7B56\u7B49\uFF09\n * \u9700\u8981\u5408\u5E76\u4E3A\u7EDF\u4E00\u7684 content \u6570\u7EC4\u683C\u5F0F\n */\n const normalizeMessage = useCallback(\n (message: any): Message => {\n // \u5982\u679C content \u5DF2\u7ECF\u662F\u6570\u7EC4\uFF0C\u76F4\u63A5\u8FD4\u56DE\n if (Array.isArray(message.content)) {\n return message as Message\n }\n\n const contentBlocks: MessageContent[] = []\n\n // \u5904\u7406\u6587\u672C\u5185\u5BB9\n if (typeof message.content === 'string' && message.content.trim()) {\n contentBlocks.push({\n type: 'text',\n text: message.content,\n })\n }\n\n // \u5904\u7406\u7ED3\u6784\u5316\u5185\u5BB9\n // \u5386\u53F2\u6D88\u606F\u683C\u5F0F: structured_content: [{type, data}]\n const structuredData = message.structuredContent || message.structured_content\n\n if (Array.isArray(structuredData)) {\n structuredData.forEach((block: any) => {\n if (block.type === 'product_list' && Array.isArray(block.data)) {\n // \u8F6C\u6362\u4EA7\u54C1\u5217\u8868\u6570\u636E\u7ED3\u6784 - data \u76F4\u63A5\u662F\u4EA7\u54C1\u6570\u7EC4\n contentBlocks.push({\n type: 'product_list',\n data: {\n products: transformProducts(block.data, site),\n title: undefined, // \u5386\u53F2\u6D88\u606F\u4E0D\u5305\u542B title\n commonText: mergedText,\n },\n })\n } else if (block.type === 'quick_replies' && block.data?.replies) {\n contentBlocks.push({\n type: 'quick_replies',\n data: {\n replies: block.data.replies,\n },\n })\n } else if (block.type === 'policy' && block.data?.title && block.data?.content) {\n contentBlocks.push({\n type: 'policy',\n data: {\n title: block.data.title,\n content: block.data.content,\n },\n })\n } else if (block.type === 'product_comparison' && block.data?.products && block.data?.dimensions) {\n // \u8F6C\u6362\u4EA7\u54C1\u5BF9\u6BD4\u6570\u636E\u7ED3\u6784\u5E76\u6CE8\u5165 onAddToCart \u56DE\u8C03\u548C\u81EA\u5B9A\u4E49\u6E32\u67D3\u51FD\u6570\n contentBlocks.push({\n type: 'product_comparison',\n data: {\n products: transformProducts(block.data.products, site),\n dimensions: block.data.dimensions,\n onAddToCart: onAddToCart,\n commonText: mergedText,\n productComparisonRender: productComparisonRender,\n },\n })\n } else if (block.type === 'faq_list' && block.data?.found !== undefined) {\n // FAQ \u5217\u8868\u5361\u7247 - \u76F4\u63A5\u4F7F\u7528\u540E\u7AEF\u6570\u636E\n contentBlocks.push({\n type: 'faq_list',\n data: block.data,\n })\n } else if (block.type === 'promotion_list' && block.data?.found !== undefined) {\n // \u4FC3\u9500\u6D3B\u52A8\u5217\u8868\u5361\u7247 - \u76F4\u63A5\u4F7F\u7528\u540E\u7AEF\u6570\u636E\n contentBlocks.push({\n type: 'promotion_list',\n data: {\n ...block.data,\n commonText: mergedText,\n },\n })\n } else if (block.type === 'cart' && block.data?.id !== undefined) {\n // \u8D2D\u7269\u8F66\u5361\u7247 - \u8F6C\u6362\u540E\u7AEF\u6570\u636E\u683C\u5F0F\u5E76\u6CE8\u5165 onCart \u56DE\u8C03\n const transformedData = transformCartData(block.data as BackendCartData)\n contentBlocks.push({\n type: 'cart',\n data: {\n ...transformedData,\n onCart: onCart,\n commonText: mergedText,\n },\n })\n } else {\n // \u5176\u4ED6\u7C7B\u578B\u76F4\u63A5\u6DFB\u52A0\n contentBlocks.push(block)\n }\n })\n }\n\n // \u5982\u679C\u6CA1\u6709\u4EFB\u4F55\u5185\u5BB9\u5757\uFF0C\u8FD4\u56DE\u7A7A\u6587\u672C\u5757\n if (contentBlocks.length === 0) {\n contentBlocks.push({\n type: 'text',\n text: '',\n })\n }\n\n return {\n ...message,\n content: contentBlocks,\n } as Message\n },\n [site, onCart, onAddToCart, mergedText]\n )\n\n /**\n * \u521B\u5EFA\u65B0\u4F1A\u8BDD\n */\n const handleCreateNewSession = useCallback(async () => {\n // userId \u4E3A undefined \u8868\u793A\u5C1A\u672A\u521D\u59CB\u5316\uFF0C\u7B49\u5F85\u521D\u59CB\u5316\u5B8C\u6210\n if (userId === undefined) return\n\n // \u5982\u679C\u7528\u6237\u6CA1\u6709\u914D\u7F6E\u6B22\u8FCE\u8BED\uFF0C\u663E\u793A loading \u72B6\u6001\n if (!welcomeMessage) {\n setIsInitializing(true)\n }\n\n try {\n const response = await createSession({\n user_id: userId ?? '',\n site: site,\n channel_code: channelCode,\n real_user_id: loginUserId,\n page_url: typeof window !== 'undefined' ? window.location.href : undefined,\n })\n\n if (response.success) {\n // \u4FDD\u5B58\u65B0\u4F1A\u8BDD ID\n saveSession(response.sessionId)\n\n // \u4FDD\u5B58\u540E\u7AEF\u8FD4\u56DE\u7684 userId\uFF08\u5982\u679C\u6709\uFF09\n if (response.userId) {\n saveUserId(response.userId)\n setUserId(response.userId)\n }\n\n // \u6E05\u7A7A\u6D88\u606F\u5217\u8868\n clearMessages()\n\n // \u4F7F\u7528\u540E\u7AEF\u8FD4\u56DE\u7684\u6B22\u8FCE\u6D88\u606F\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u4F7F\u7528 props \u4E2D\u7684\u9ED8\u8BA4\u503C\n const messageText = response.welcomeMessage || welcomeMessage\n\n if (messageText) {\n const welcomeContent: MessageContent[] = [{ type: 'text', text: messageText }]\n\n // \u4F7F\u7528\u540E\u7AEF\u8FD4\u56DE\u7684\u5FEB\u6377\u95EE\u9898\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u4F7F\u7528 props \u4E2D\u7684\u5FEB\u6377\u56DE\u590D\n const questions = response.quickQuestions\n if (questions && questions.length > 0) {\n // \u5C06\u540E\u7AEF\u7684 quickQuestions (\u5B57\u7B26\u4E32\u6570\u7EC4) \u8F6C\u6362\u4E3A QuickReply \u683C\u5F0F\n const quickRepliesFromBackend = questions.map((question, index) => ({\n id: `quick-${index}`,\n label: question,\n value: question,\n }))\n\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickRepliesFromBackend,\n },\n })\n } else if (quickReplies && quickReplies.length > 0) {\n // \u5982\u679C\u540E\u7AEF\u6CA1\u6709\u8FD4\u56DE\uFF0C\u4F7F\u7528 props \u4E2D\u7684\u5FEB\u6377\u56DE\u590D\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickReplies,\n },\n })\n }\n\n addMessage({\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: welcomeContent,\n timestamp: Date.now(),\n })\n }\n }\n } catch (error) {\n console.error('[LiveChatWidget] Failed to create new session:', error)\n onError?.(error as Error)\n\n // Check if it's a reCAPTCHA error\n const isRecaptcha = (error as any)?.type === 'GoogleRecaptchaError'\n\n // \u6DFB\u52A0\u9519\u8BEF\u6D88\u606F\u5230\u754C\u9762\n addMessage({\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: isRecaptcha\n ? 'Your session has expired. Please refresh the page and try again.'\n : 'Failed to create session. Please refresh the page and try again.',\n code: isRecaptcha ? 'RECAPTCHA_ERROR' : 'SESSION_CREATE_ERROR',\n },\n },\n ],\n timestamp: Date.now(),\n })\n } finally {\n // \u63A5\u53E3\u8FD4\u56DE\u540E\u5173\u95ED loading \u72B6\u6001\n setIsInitializing(false)\n }\n }, [\n userId,\n site,\n channelCode,\n loginUserId,\n createSession,\n saveSession,\n clearMessages,\n welcomeMessage,\n quickReplies,\n addMessage,\n onError,\n ])\n\n /**\n * \u6062\u590D\u4F1A\u8BDD\u548C\u5386\u53F2\u6D88\u606F\uFF08\u4F7F\u7528\u65B0\u7684 API v2.0.0\uFF09\n */\n const handleResumeSession = useCallback(\n async (existingSessionId: string) => {\n // \u5982\u679C\u7528\u6237\u6CA1\u6709\u914D\u7F6E\u6B22\u8FCE\u8BED\uFF0C\u663E\u793A loading \u72B6\u6001\n if (!welcomeMessage) {\n setIsInitializing(true)\n }\n\n try {\n const response = await createSession({\n user_id: userId ?? '',\n session_id: existingSessionId,\n site: site,\n channel_code: channelCode,\n real_user_id: loginUserId,\n page_url: typeof window !== 'undefined' ? window.location.href : undefined,\n })\n\n if (response.success && response.resumed) {\n // \u4F1A\u8BDD\u6062\u590D\u6210\u529F\n\n // \u4FDD\u5B58\u540E\u7AEF\u8FD4\u56DE\u7684 userId\uFF08\u5982\u679C\u6709\uFF09\n if (response.userId) {\n saveUserId(response.userId)\n setUserId(response.userId)\n }\n\n // \u51C6\u5907\u6B22\u8FCE\u6D88\u606F\uFF08\u65E0\u8BBA\u662F\u5426\u6709\u5386\u53F2\u6D88\u606F\u90FD\u9700\u8981\uFF09\n const messageText = response.welcomeMessage || welcomeMessage\n const welcomeContent: MessageContent[] = messageText ? [{ type: 'text', text: messageText }] : []\n\n // \u6DFB\u52A0\u5FEB\u6377\u56DE\u590D\u5230\u6B22\u8FCE\u6D88\u606F\n const questions = response.quickQuestions\n if (questions && questions.length > 0) {\n const quickRepliesFromBackend = questions.map((question, index) => ({\n id: `quick-${index}`,\n label: question,\n value: question,\n }))\n\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickRepliesFromBackend,\n },\n })\n } else if (quickReplies && quickReplies.length > 0) {\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickReplies,\n },\n })\n }\n\n if (response.messages && response.messages.length > 0) {\n // \u6709\u5386\u53F2\u6D88\u606F\uFF0C\u89C4\u8303\u5316\u5E76\u52A0\u8F7D\uFF08\u8FC7\u6EE4\u6389 null/undefined \u503C\u548C\u7A7A\u5185\u5BB9\u6D88\u606F\uFF09\n const normalizedMessages = response.messages\n .filter((msg: any) => msg != null)\n .map(normalizeMessage)\n .filter((msg: Message) => msg.content && msg.content.length > 0 && !(msg.content.length === 1 && msg.content[0].type === 'text' && !msg.content[0].text))\n\n // \u5982\u679C\u6709\u6B22\u8FCE\u6D88\u606F\uFF0C\u5C06\u5176\u6DFB\u52A0\u5230\u5386\u53F2\u6D88\u606F\u7684\u5F00\u5934\n if (welcomeContent.length > 0) {\n const welcomeMsg: Message = {\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: welcomeContent,\n timestamp: Date.now(),\n }\n setMessages([welcomeMsg, ...normalizedMessages])\n } else {\n setMessages(normalizedMessages)\n }\n } else {\n // \u6CA1\u6709\u5386\u53F2\u6D88\u606F\uFF0C\u4EC5\u663E\u793A\u6B22\u8FCE\u6D88\u606F\n clearMessages()\n\n if (welcomeContent.length > 0) {\n addMessage({\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: welcomeContent,\n timestamp: Date.now(),\n })\n }\n }\n } else if (!response.resumed) {\n // \u4F1A\u8BDD\u65E0\u6548\u6216\u8FC7\u671F\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n clearSession()\n handleCreateNewSession()\n }\n } catch (error) {\n console.error('[LiveChatWidget] Failed to resume session:', error)\n\n // Check if it's a reCAPTCHA error\n const isRecaptcha = (error as any)?.type === 'GoogleRecaptchaError'\n\n if (isRecaptcha) {\n // reCAPTCHA error - show refresh page message, don't retry\n addMessage({\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: 'Your session has expired. Please refresh the page and try again.',\n code: 'RECAPTCHA_ERROR',\n },\n },\n ],\n timestamp: Date.now(),\n })\n } else {\n // Other errors - show message and try to create new session\n addMessage({\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: 'Failed to resume session. Creating a new session...',\n code: 'SESSION_RESUME_ERROR',\n },\n },\n ],\n timestamp: Date.now(),\n })\n\n // \u6062\u590D\u5931\u8D25\uFF0C\u6E05\u7A7A\u4F1A\u8BDD\u5E76\u521B\u5EFA\u65B0\u7684\n clearSession()\n handleCreateNewSession()\n }\n } finally {\n // \u63A5\u53E3\u8FD4\u56DE\u540E\u5173\u95ED loading \u72B6\u6001\n setIsInitializing(false)\n }\n },\n [\n userId,\n site,\n channelCode,\n loginUserId,\n createSession,\n setMessages,\n clearSession,\n clearMessages,\n normalizeMessage,\n handleCreateNewSession,\n welcomeMessage,\n quickReplies,\n addMessage,\n ]\n )\n\n /**\n * \u53D1\u9001\u6D88\u606F\n */\n const handleSendMessage = useCallback(\n async (message?: string, isRetry: boolean = false) => {\n const textToSend = message || inputValue.trim()\n\n if (!textToSend) return\n\n // \u6E05\u7A7A\u8F93\u5165\u6846\uFF08\u4EC5\u5728\u975E\u91CD\u8BD5\u65F6\uFF09\n if (!message && !isRetry) {\n setInputValue('')\n }\n\n // \u8F93\u5165\u9A8C\u8BC1\n const sanitized = sanitizeInput(textToSend)\n if (!sanitized) {\n onError?.(new Error('Invalid message'))\n return\n }\n\n // \u6DFB\u52A0\u7528\u6237\u6D88\u606F\u5230\u754C\u9762\uFF08\u4EC5\u5728\u975E\u91CD\u8BD5\u65F6\uFF09\n if (!isRetry) {\n const userMessage = {\n id: `user-${Date.now()}`,\n role: 'user' as const,\n content: [{ type: 'text' as const, text: sanitized }],\n timestamp: Date.now(),\n }\n addMessage(userMessage)\n }\n\n // \u7ACB\u5373\u6DFB\u52A0\u601D\u8003\u72B6\u6001\u6D88\u606F\uFF0C\u63D0\u5347\u7528\u6237\u4F53\u9A8C\n const thinkingMessage = {\n id: `thinking-${Date.now()}`,\n role: 'assistant' as const,\n content: [{ type: 'thinking' as const, data: { status: 'thinking' } }],\n timestamp: Date.now(),\n }\n addMessage(thinkingMessage)\n\n // \u89E6\u53D1\u6D88\u606F\u53D1\u9001\u56DE\u8C03\uFF08\u4EC5\u5728\u975E\u91CD\u8BD5\u65F6\uFF09\n if (!isRetry) {\n onMessageSend?.(sanitized)\n }\n\n // \u6807\u8BB0\u662F\u5426\u68C0\u6D4B\u5230\u4F1A\u8BDD\u8FC7\u671F\n let sessionExpiredDetected = false\n\n try {\n // \u786E\u4FDD\u6709 sessionId\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u5148\u521B\u5EFA\u4F1A\u8BDD\n let currentSessionId = sessionId\n if (!currentSessionId) {\n // \u6CA1\u6709\u4F1A\u8BDD\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n const response = await createSession({\n user_id: userId ?? '',\n site: site,\n channel_code: channelCode,\n real_user_id: loginUserId,\n page_url: typeof window !== 'undefined' ? window.location.href : undefined,\n })\n if (response.success) {\n currentSessionId = response.sessionId\n saveSession(currentSessionId)\n } else {\n throw new Error('Failed to create session')\n }\n }\n\n // \u6784\u5EFA\u8BF7\u6C42\u53C2\u6570\uFF08session_id \u73B0\u5728\u662F\u5FC5\u586B\u7684\uFF09\n const requestPayload: ChatStreamRequest = {\n message: sanitized,\n user_id: userId ?? '',\n session_id: currentSessionId,\n context: {\n cartId: cartId,\n accessToken: accessToken,\n real_user_id: loginUserId,\n },\n }\n\n // \u521B\u5EFA\u7ED1\u5B9A\u4E86\u5F53\u524D streamId \u7684\u4E8B\u4EF6\u5904\u7406\u5668\uFF0C\u9632\u6B62\u5E76\u53D1 stream \u6C61\u67D3\u5171\u4EAB buffer\n const streamEventHandler = startStream()\n\n // \u53D1\u9001\u6D88\u606F\u5230\u540E\u7AEF\n await sendMessageStream(requestPayload, event => {\n streamEventHandler(event)\n\n // \u7279\u6B8A\u5904\u7406\uFF1A\u4F1A\u8BDD\u8FC7\u671F\uFF08error \u4E8B\u4EF6\u4E14 type \u4E3A validation_error\uFF09\n if (event.event === 'error' && event.data.type === 'validation_error') {\n sessionExpiredDetected = true\n clearSession()\n }\n })\n\n // \u5982\u679C\u68C0\u6D4B\u5230\u4F1A\u8BDD\u8FC7\u671F\u4E14\u4E0D\u662F\u91CD\u8BD5\uFF0C\u81EA\u52A8\u521B\u5EFA\u65B0\u4F1A\u8BDD\u5E76\u91CD\u8BD5\n if (sessionExpiredDetected && !isRetry) {\n console.log('[LiveChatWidget] Session expired (validation_error), creating new session and retrying...')\n\n // \u79FB\u9664 thinking \u6D88\u606F\n const messagesWithoutThinking = messages.filter(msg => msg.id !== thinkingMessage.id)\n setMessages(messagesWithoutThinking)\n\n // \u521B\u5EFA\u65B0\u4F1A\u8BDD\n const response = await createSession({\n user_id: userId ?? '',\n site: site,\n channel_code: channelCode,\n real_user_id: loginUserId,\n page_url: typeof window !== 'undefined' ? window.location.href : undefined,\n })\n\n if (response.success) {\n saveSession(response.sessionId)\n // \u91CD\u8BD5\u53D1\u9001\u6D88\u606F\n await handleSendMessage(sanitized, true)\n } else {\n throw new Error('Failed to recreate session after expiration')\n }\n }\n } catch (error) {\n console.error('[LiveChatWidget] Failed to send message:', error)\n onError?.(error as Error)\n\n // Check if it's a reCAPTCHA error\n const isRecaptcha = (error as any)?.type === 'GoogleRecaptchaError'\n\n // Determine error message based on error type\n let errorMessage: string\n let errorCode: string\n\n if (isRecaptcha) {\n errorMessage = 'Your session has expired. Please refresh the page and try again.'\n errorCode = 'RECAPTCHA_ERROR'\n } else if (sessionExpiredDetected) {\n errorMessage = 'Your session has expired. We tried to reconnect but failed. Please try again.'\n errorCode = 'SESSION_EXPIRED'\n } else {\n errorMessage = 'Failed to send message. Please check your network connection and try again.'\n errorCode = 'NETWORK_ERROR'\n }\n\n // \u79FB\u9664\u521A\u624D\u6DFB\u52A0\u7684 thinking \u6D88\u606F\uFF0C\u5E76\u6DFB\u52A0\u9519\u8BEF\u6D88\u606F\n const messagesWithoutThinking = messages.filter(msg => msg.id !== thinkingMessage.id)\n setMessages([\n ...messagesWithoutThinking,\n {\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: errorMessage,\n code: errorCode,\n },\n },\n ],\n timestamp: Date.now(),\n },\n ])\n }\n },\n [\n inputValue,\n userId,\n sessionId,\n site,\n channelCode,\n loginUserId,\n cartId,\n accessToken,\n messages,\n setInputValue,\n addMessage,\n setMessages,\n createSession,\n sendMessageStream,\n startStream,\n saveSession,\n clearSession,\n onMessageSend,\n onError,\n ]\n )\n\n // \u66F4\u65B0 ref \u4EE5\u4FDD\u6301\u6700\u65B0\u7684 handleSendMessage\n React.useEffect(() => {\n handleSendMessageRef.current = handleSendMessage\n }, [handleSendMessage])\n\n /**\n * \u5904\u7406\u6C14\u6CE1\u6309\u94AE\u70B9\u51FB\n * \u5982\u679C\u914D\u7F6E\u4E86\u6CD5\u89C4\u534F\u8BAE\u4E14\u7528\u6237\u672A\u540C\u610F\uFF0C\u5148\u663E\u793A\u6CD5\u89C4\u5F39\u7A97\n * \u5426\u5219\u76F4\u63A5\u6253\u5F00\u804A\u5929\u7A97\u53E3\n */\n const handleBubbleClick = useCallback(() => {\n if (complianceConfig && !hasAgreedCompliance) {\n // \u663E\u793A\u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97\n setShowComplianceDialog(true)\n } else {\n // \u76F4\u63A5\u6253\u5F00\u804A\u5929\u7A97\u53E3\n openChat()\n }\n }, [complianceConfig, hasAgreedCompliance, openChat])\n\n /**\n * \u5904\u7406\u7528\u6237\u540C\u610F\u6CD5\u89C4\u534F\u8BAE\n */\n const handleComplianceAgree = useCallback(() => {\n // \u8BBE\u7F6E\u540C\u610F\u72B6\u6001\n setHasAgreedCompliance(true)\n setShowComplianceDialog(false)\n\n // \u4FDD\u5B58\u5230 Cookie\uFF08\u6709\u6548\u671F 365 \u5929\uFF09\n Cookies.set(cookieName, 'true', { expires: 365 })\n\n // \u540C\u610F\u540E\u7ACB\u5373\u6253\u5F00\u804A\u5929\u7A97\u53E3\n openChat()\n }, [openChat, cookieName])\n\n /**\n * \u5904\u7406\u6CD5\u89C4\u5F39\u7A97\u5173\u95ED\n */\n const handleComplianceClose = useCallback(() => {\n setShowComplianceDialog(false)\n }, [])\n\n return (\n <>\n {/* \u6CD5\u89C4\u534F\u8BAE\u5F39\u7A97 */}\n {complianceConfig && (\n <ComplianceDialog\n open={showComplianceDialog}\n config={complianceConfig}\n onAgree={handleComplianceAgree}\n onClose={handleComplianceClose}\n />\n )}\n\n {/* \u6C14\u6CE1\u6309\u94AE */}\n <ChatBubble\n position={position}\n onClick={handleBubbleClick}\n visible={!isOpen && !showComplianceDialog}\n iconImageUrl={chatBubbleIcon}\n />\n\n {/* \u804A\u5929\u7A97\u53E3\uFF08\u4F7F\u7528 Radix UI Dialog\uFF09 */}\n <Dialog.Root open={isOpen} onOpenChange={open => (open ? openChat() : closeChat())}>\n <Dialog.Portal>\n <Dialog.Content\n className=\"livechat-window-enter\"\n style={{\n position: 'fixed',\n zIndex: 9998,\n }}\n >\n <ChatWindow\n messages={messages}\n inputValue={inputValue}\n onInputChange={setInputValue}\n onSend={() => handleSendMessage()}\n onClose={closeChat}\n onNewSession={handleCreateNewSession}\n title={title}\n logoUrl={logoUrl}\n isSending={isStreaming}\n isLoadingHistory={isInitializing}\n rendererRegistry={rendererRegistry}\n inputPlaceholder=\"\"\n onAddToCart={onAddToCart}\n showNewSessionButton={showNewSessionButton}\n bottomTips={bottomTips}\n />\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n </>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["LiveChatWidget_exports", "__export", "LiveChatWidget", "__toCommonJS", "import_jsx_runtime", "import_react", "Dialog", "import_constants", "import_ChatBubble", "import_ChatWindow", "import_ComplianceDialog", "import_useChatState", "import_useChatAPI", "import_messageRenderers", "import_validation", "import_userId", "import_productTransformers", "import_cartTransformers", "import_js_cookie", "import_MessageContent", "apiBaseUrl", "headers", "recaptchaSitekey", "recaptchaAction", "site", "channelCode", "loginUserId", "cartId", "accessToken", "position", "welcomeMessage", "quickReplies", "customRenderers", "logoUrl", "title", "chatBubbleIcon", "open", "onOpenChange", "onOpen", "onClose", "onMessageSend", "onError", "onTextMessage", "onProductList", "onPromotionList", "onAddToCart", "onCart", "showNewSessionButton", "commonText", "productCardRender", "productComparisonRender", "bottomTips", "complianceConfig", "cookieName", "showComplianceDialog", "setShowComplianceDialog", "React", "hasAgreedCompliance", "setHasAgreedCompliance", "Cookies", "mergedText", "chatState", "messages", "isOpen", "userId", "sessionId", "inputValue", "isStreaming", "openChat", "closeChat", "setInputValue", "setUserId", "addMessage", "setMessages", "clearMessages", "startStream", "saveSession", "clearSession", "isInitializing", "setIsInitializing", "sendMessageStream", "createSession", "handleSendMessageRef", "_message", "rendererRegistry", "registry", "quickRepliesRenderer", "reply", "sessionInitializedRef", "currentSessionId", "handleResumeSession", "handleCreateNewSession", "normalizeMessage", "message", "contentBlocks", "structuredData", "block", "transformedData", "response", "messageText", "welcomeContent", "questions", "quickRepliesFromBackend", "question", "index", "error", "isRecaptcha", "existingSessionId", "normalizedMessages", "msg", "welcomeMsg", "handleSendMessage", "isRetry", "textToSend", "sanitized", "userMessage", "thinkingMessage", "sessionExpiredDetected", "requestPayload", "streamEventHandler", "event", "messagesWithoutThinking", "errorMessage", "errorCode", "handleBubbleClick", "handleComplianceAgree", "handleComplianceClose"]
7
7
  }
@@ -29,7 +29,7 @@ export interface RecaptchaConfig {
29
29
  * @param customHeaders 自定义请求头
30
30
  * @param recaptchaConfig reCAPTCHA 配置
31
31
  */
32
- export declare function sendMessage(apiBaseUrl: string, request: ChatStreamRequest, onEvent: (event: SSEEvent) => void, customHeaders?: Record<string, string>, recaptchaConfig?: RecaptchaConfig): Promise<void>;
32
+ export declare function sendMessage(apiBaseUrl: string, request: ChatStreamRequest, onEvent: (event: SSEEvent) => void, customHeaders?: Record<string, string>, recaptchaConfig?: RecaptchaConfig, signal?: AbortSignal): Promise<void>;
33
33
  /**
34
34
  * 创建新会话或恢复现有会话
35
35
  *
@@ -1,3 +1,3 @@
1
- "use strict";var S=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var v=(r,t)=>{for(var s in t)S(r,s,{get:t[s],enumerable:!0})},b=(r,t,s,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of f(t))!E.call(r,e)&&e!==s&&S(r,e,{get:()=>t[e],enumerable:!(a=g(t,e))||a.enumerable});return r};var k=r=>b(S({},"__esModule",{value:!0}),r);var T={};v(T,{createNewSession:()=>P,sendMessage:()=>A});module.exports=k(T);var m=require("../utils/fetcher");async function A(r,t,s,a,e){const o=await(0,m.fetcher)({url:`${r}/api/chat/stream`,method:"POST",headers:{Accept:"text/event-stream",...a},body:t,needRecaptcha:e?.needRecaptcha,recaptchaSitekey:e?.recaptchaSitekey,recaptchaAction:e?.recaptchaAction||"send_message"});if(!o.ok){const n=await o.json().catch(()=>null),p=new Error(n?.message||`HTTP ${o.status}`);throw p.type=n?.type,p}const c=o.body?.getReader();if(!c)throw new Error("Response body is not readable");const R=new TextDecoder;let d="",h=null;try{for(;;){const{done:n,value:p}=await c.read();if(n)break;d+=R.decode(p,{stream:!0});const u=d.split(`
2
- `);d=u.pop()||"";for(const w of u){const i=w.trim();if(i===""){h=null;continue}if(i.startsWith("event:"))h=i.substring(6).trim();else if(i.startsWith("data:")){const y=i.substring(5).trim();try{const l=JSON.parse(y);s({event:h||"message",data:l})}catch(l){console.error("[LiveChat API] Failed to parse SSE data:",y,l),s({event:"error",data:{message:"Failed to parse SSE data",code:"PARSE_ERROR"}})}}}}}catch(n){throw console.error("[LiveChat API] SSE stream error:",n),n}finally{c.releaseLock()}}async function P(r,t,s,a){const e=await(0,m.fetcher)({url:`${r}/api/chat/new-session`,method:"POST",headers:s,body:t,needRecaptcha:a?.needRecaptcha,recaptchaSitekey:a?.recaptchaSitekey,recaptchaAction:a?.recaptchaAction||"new_session"});if(!e.ok){const o=await e.json().catch(()=>null),c=new Error(o?.message||`HTTP ${e.status}`);throw c.type=o?.type,c}return e.json()}
1
+ "use strict";var m=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var b=(r,t)=>{for(var s in t)m(r,s,{get:t[s],enumerable:!0})},A=(r,t,s,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of E(t))!v.call(r,e)&&e!==s&&m(r,e,{get:()=>t[e],enumerable:!(a=f(t,e))||a.enumerable});return r};var k=r=>A(m({},"__esModule",{value:!0}),r);var N={};b(N,{createNewSession:()=>T,sendMessage:()=>P});module.exports=k(N);var u=require("../utils/fetcher");async function P(r,t,s,a,e,i){const o=await(0,u.fetcher)({url:`${r}/api/chat/stream`,method:"POST",headers:{Accept:"text/event-stream",...a},body:t,needRecaptcha:e?.needRecaptcha,recaptchaSitekey:e?.recaptchaSitekey,recaptchaAction:e?.recaptchaAction||"send_message",signal:i});if(!o.ok){const n=await o.json().catch(()=>null),p=new Error(n?.message||`HTTP ${o.status}`);throw p.type=n?.type,p}const d=o.body?.getReader();if(!d)throw new Error("Response body is not readable");const w=new TextDecoder;let h="",l=null;try{for(;;){const{done:n,value:p}=await d.read();if(n)break;h+=w.decode(p,{stream:!0});const y=h.split(`
2
+ `);h=y.pop()||"";for(const g of y){const c=g.trim();if(c===""){l=null;continue}if(c.startsWith("event:"))l=c.substring(6).trim();else if(c.startsWith("data:")){const R=c.substring(5).trim();try{const S=JSON.parse(R);s({event:l||"message",data:S})}catch(S){console.error("[LiveChat API] Failed to parse SSE data:",R,S),s({event:"error",data:{message:"Failed to parse SSE data",code:"PARSE_ERROR"}})}}}}}catch(n){throw console.error("[LiveChat API] SSE stream error:",n),n}finally{d.releaseLock()}}async function T(r,t,s,a){const e=await(0,u.fetcher)({url:`${r}/api/chat/new-session`,method:"POST",headers:s,body:t,needRecaptcha:a?.needRecaptcha,recaptchaSitekey:a?.recaptchaSitekey,recaptchaAction:a?.recaptchaAction||"new_session"});if(!e.ok){const i=await e.json().catch(()=>null),o=new Error(i?.message||`HTTP ${e.status}`);throw o.type=i?.type,o}return e.json()}
3
3
  //# sourceMappingURL=chat.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/components/LiveChatWidget/api/chat.ts"],
4
- "sourcesContent": ["/**\n * LiveChat API \u5C42\n * \u5904\u7406 SSE \u6D41\u5F0F\u901A\u4FE1\u548C HTTP \u8BF7\u6C42\n * \u57FA\u4E8E specs/livechat-widget/contracts/*.yaml\n */\n\nimport type { ChatStreamRequest, SSEEvent, NewSessionRequest, NewSessionResponse, ErrorResponse } from '../types'\nimport { fetcher } from '../utils/fetcher'\n\n/**\n * Recaptcha \u914D\u7F6E\n */\nexport interface RecaptchaConfig {\n /**\n * \u662F\u5426\u542F\u7528 reCAPTCHA\n */\n needRecaptcha?: boolean\n /**\n * reCAPTCHA site key\n */\n recaptchaSitekey?: string\n /**\n * reCAPTCHA action \u540D\u79F0\n */\n recaptchaAction?: string\n}\n\n// ============================================================================\n// SSE \u6D41\u5F0F\u6D88\u606F\u5904\u7406 (T006)\n// ============================================================================\n\n/**\n * \u53D1\u9001\u6D88\u606F\u5E76\u63A5\u6536 SSE \u6D41\u5F0F\u54CD\u5E94\n * @param apiBaseUrl API \u57FA\u7840 URL\n * @param request \u8BF7\u6C42\u53C2\u6570\n * @param onEvent SSE \u4E8B\u4EF6\u56DE\u8C03\n * @param customHeaders \u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\n * @param recaptchaConfig reCAPTCHA \u914D\u7F6E\n */\nexport async function sendMessage(\n apiBaseUrl: string,\n request: ChatStreamRequest,\n onEvent: (event: SSEEvent) => void,\n customHeaders?: Record<string, string>,\n recaptchaConfig?: RecaptchaConfig\n): Promise<void> {\n // \u4F7F\u7528 fetcher \u5904\u7406 reCAPTCHA \u548C\u81EA\u5B9A\u4E49 headers\n const response = await fetcher({\n url: `${apiBaseUrl}/api/chat/stream`,\n method: 'POST',\n headers: {\n Accept: 'text/event-stream',\n ...customHeaders,\n },\n body: request,\n needRecaptcha: recaptchaConfig?.needRecaptcha,\n recaptchaSitekey: recaptchaConfig?.recaptchaSitekey,\n recaptchaAction: recaptchaConfig?.recaptchaAction || 'send_message',\n })\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => null)\n const error = new Error(errorData?.message || `HTTP ${response.status}`) as Error & { type?: string }\n error.type = errorData?.type\n throw error\n }\n\n const reader = response.body?.getReader()\n if (!reader) {\n throw new Error('Response body is not readable')\n }\n\n const decoder = new TextDecoder()\n let buffer = ''\n let currentEvent: string | null = null\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n // \u89E3\u7801\u5E76\u8FFD\u52A0\u5230\u7F13\u51B2\u533A\n buffer += decoder.decode(value, { stream: true })\n\n // \u6309\u884C\u5206\u5272\n const lines = buffer.split('\\n')\n\n // \u4FDD\u7559\u6700\u540E\u4E00\u4E2A\u4E0D\u5B8C\u6574\u7684\u884C\n buffer = lines.pop() || ''\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n if (trimmed === '') {\n // \u7A7A\u884C\u8868\u793A\u4E8B\u4EF6\u8FB9\u754C\uFF0C\u91CD\u7F6E\u5F53\u524D\u4E8B\u4EF6\u7C7B\u578B\n currentEvent = null\n continue\n }\n\n if (trimmed.startsWith('event:')) {\n // \u63D0\u53D6\u4E8B\u4EF6\u7C7B\u578B\n currentEvent = trimmed.substring(6).trim()\n } else if (trimmed.startsWith('data:')) {\n // \u63D0\u53D6\u6570\u636E\u5E76\u89E3\u6790 JSON\n const dataStr = trimmed.substring(5).trim()\n try {\n const data = JSON.parse(dataStr)\n onEvent({\n event: (currentEvent as any) || 'message',\n data,\n })\n } catch (err) {\n console.error('[LiveChat API] Failed to parse SSE data:', dataStr, err)\n onEvent({\n event: 'error',\n data: {\n message: 'Failed to parse SSE data',\n code: 'PARSE_ERROR',\n },\n })\n }\n }\n }\n }\n } catch (error) {\n console.error('[LiveChat API] SSE stream error:', error)\n throw error\n } finally {\n reader.releaseLock()\n }\n}\n\n// ============================================================================\n// \u521B\u5EFA/\u6062\u590D\u4F1A\u8BDD (T009)\n// ============================================================================\n\n/**\n * \u521B\u5EFA\u65B0\u4F1A\u8BDD\u6216\u6062\u590D\u73B0\u6709\u4F1A\u8BDD\n *\n * \u4F7F\u7528\u573A\u666F\uFF1A\n * 1. \u521B\u5EFA\u65B0\u4F1A\u8BDD\uFF1A\u4E0D\u4F20 session_id\uFF0C\u8FD4\u56DE\u65B0\u7684 sessionId\n * 2. \u6062\u590D\u4F1A\u8BDD\uFF1A\u4F20\u5165 session_id\uFF0C\u9A8C\u8BC1\u4F1A\u8BDD\u6709\u6548\u6027\u5E76\u8FD4\u56DE\u5386\u53F2\u6D88\u606F\uFF08resumed: true, messages: [...]\uFF09\n *\n * @param apiBaseUrl API \u57FA\u7840 URL\n * @param request \u8BF7\u6C42\u53C2\u6570\n * @param customHeaders \u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\n * @param recaptchaConfig reCAPTCHA \u914D\u7F6E\n * @returns \u4F1A\u8BDD\u4FE1\u606F\uFF0C\u53EF\u80FD\u5305\u542B\u5386\u53F2\u6D88\u606F\n */\nexport async function createNewSession(\n apiBaseUrl: string,\n request: NewSessionRequest,\n customHeaders?: Record<string, string>,\n recaptchaConfig?: RecaptchaConfig\n): Promise<NewSessionResponse> {\n // \u4F7F\u7528 fetcher \u5904\u7406 reCAPTCHA \u548C\u81EA\u5B9A\u4E49 headers\n const response = await fetcher({\n url: `${apiBaseUrl}/api/chat/new-session`,\n method: 'POST',\n headers: customHeaders,\n body: request,\n needRecaptcha: recaptchaConfig?.needRecaptcha,\n recaptchaSitekey: recaptchaConfig?.recaptchaSitekey,\n recaptchaAction: recaptchaConfig?.recaptchaAction || 'new_session',\n })\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => null)\n const error = new Error(errorData?.message || `HTTP ${response.status}`) as Error & { type?: string }\n error.type = errorData?.type\n throw error\n }\n\n return response.json()\n}\n"],
5
- "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,sBAAAE,EAAA,gBAAAC,IAAA,eAAAC,EAAAJ,GAOA,IAAAK,EAAwB,4BAgCxB,eAAsBF,EACpBG,EACAC,EACAC,EACAC,EACAC,EACe,CAEf,MAAMC,EAAW,QAAM,WAAQ,CAC7B,IAAK,GAAGL,CAAU,mBAClB,OAAQ,OACR,QAAS,CACP,OAAQ,oBACR,GAAGG,CACL,EACA,KAAMF,EACN,cAAeG,GAAiB,cAChC,iBAAkBA,GAAiB,iBACnC,gBAAiBA,GAAiB,iBAAmB,cACvD,CAAC,EAED,GAAI,CAACC,EAAS,GAAI,CAChB,MAAMC,EAAY,MAAMD,EAAS,KAAK,EAAE,MAAM,IAAM,IAAI,EAClDE,EAAQ,IAAI,MAAMD,GAAW,SAAW,QAAQD,EAAS,MAAM,EAAE,EACvE,MAAAE,EAAM,KAAOD,GAAW,KAClBC,CACR,CAEA,MAAMC,EAASH,EAAS,MAAM,UAAU,EACxC,GAAI,CAACG,EACH,MAAM,IAAI,MAAM,+BAA+B,EAGjD,MAAMC,EAAU,IAAI,YACpB,IAAIC,EAAS,GACTC,EAA8B,KAElC,GAAI,CACF,OAAa,CACX,KAAM,CAAE,KAAAC,EAAM,MAAAC,CAAM,EAAI,MAAML,EAAO,KAAK,EAC1C,GAAII,EAAM,MAGVF,GAAUD,EAAQ,OAAOI,EAAO,CAAE,OAAQ,EAAK,CAAC,EAGhD,MAAMC,EAAQJ,EAAO,MAAM;AAAA,CAAI,EAG/BA,EAASI,EAAM,IAAI,GAAK,GAExB,UAAWC,KAAQD,EAAO,CACxB,MAAME,EAAUD,EAAK,KAAK,EAE1B,GAAIC,IAAY,GAAI,CAElBL,EAAe,KACf,QACF,CAEA,GAAIK,EAAQ,WAAW,QAAQ,EAE7BL,EAAeK,EAAQ,UAAU,CAAC,EAAE,KAAK,UAChCA,EAAQ,WAAW,OAAO,EAAG,CAEtC,MAAMC,EAAUD,EAAQ,UAAU,CAAC,EAAE,KAAK,EAC1C,GAAI,CACF,MAAME,EAAO,KAAK,MAAMD,CAAO,EAC/Bf,EAAQ,CACN,MAAQS,GAAwB,UAChC,KAAAO,CACF,CAAC,CACH,OAASC,EAAK,CACZ,QAAQ,MAAM,2CAA4CF,EAASE,CAAG,EACtEjB,EAAQ,CACN,MAAO,QACP,KAAM,CACJ,QAAS,2BACT,KAAM,aACR,CACF,CAAC,CACH,CACF,CACF,CACF,CACF,OAASK,EAAO,CACd,cAAQ,MAAM,mCAAoCA,CAAK,EACjDA,CACR,QAAE,CACAC,EAAO,YAAY,CACrB,CACF,CAmBA,eAAsBZ,EACpBI,EACAC,EACAE,EACAC,EAC6B,CAE7B,MAAMC,EAAW,QAAM,WAAQ,CAC7B,IAAK,GAAGL,CAAU,wBAClB,OAAQ,OACR,QAASG,EACT,KAAMF,EACN,cAAeG,GAAiB,cAChC,iBAAkBA,GAAiB,iBACnC,gBAAiBA,GAAiB,iBAAmB,aACvD,CAAC,EAED,GAAI,CAACC,EAAS,GAAI,CAChB,MAAMC,EAAY,MAAMD,EAAS,KAAK,EAAE,MAAM,IAAM,IAAI,EAClDE,EAAQ,IAAI,MAAMD,GAAW,SAAW,QAAQD,EAAS,MAAM,EAAE,EACvE,MAAAE,EAAM,KAAOD,GAAW,KAClBC,CACR,CAEA,OAAOF,EAAS,KAAK,CACvB",
6
- "names": ["chat_exports", "__export", "createNewSession", "sendMessage", "__toCommonJS", "import_fetcher", "apiBaseUrl", "request", "onEvent", "customHeaders", "recaptchaConfig", "response", "errorData", "error", "reader", "decoder", "buffer", "currentEvent", "done", "value", "lines", "line", "trimmed", "dataStr", "data", "err"]
4
+ "sourcesContent": ["/**\n * LiveChat API \u5C42\n * \u5904\u7406 SSE \u6D41\u5F0F\u901A\u4FE1\u548C HTTP \u8BF7\u6C42\n * \u57FA\u4E8E specs/livechat-widget/contracts/*.yaml\n */\n\nimport type { ChatStreamRequest, SSEEvent, NewSessionRequest, NewSessionResponse, ErrorResponse } from '../types'\nimport { fetcher } from '../utils/fetcher'\n\n/**\n * Recaptcha \u914D\u7F6E\n */\nexport interface RecaptchaConfig {\n /**\n * \u662F\u5426\u542F\u7528 reCAPTCHA\n */\n needRecaptcha?: boolean\n /**\n * reCAPTCHA site key\n */\n recaptchaSitekey?: string\n /**\n * reCAPTCHA action \u540D\u79F0\n */\n recaptchaAction?: string\n}\n\n// ============================================================================\n// SSE \u6D41\u5F0F\u6D88\u606F\u5904\u7406 (T006)\n// ============================================================================\n\n/**\n * \u53D1\u9001\u6D88\u606F\u5E76\u63A5\u6536 SSE \u6D41\u5F0F\u54CD\u5E94\n * @param apiBaseUrl API \u57FA\u7840 URL\n * @param request \u8BF7\u6C42\u53C2\u6570\n * @param onEvent SSE \u4E8B\u4EF6\u56DE\u8C03\n * @param customHeaders \u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\n * @param recaptchaConfig reCAPTCHA \u914D\u7F6E\n */\nexport async function sendMessage(\n apiBaseUrl: string,\n request: ChatStreamRequest,\n onEvent: (event: SSEEvent) => void,\n customHeaders?: Record<string, string>,\n recaptchaConfig?: RecaptchaConfig,\n signal?: AbortSignal\n): Promise<void> {\n const response = await fetcher({\n url: `${apiBaseUrl}/api/chat/stream`,\n method: 'POST',\n headers: {\n Accept: 'text/event-stream',\n ...customHeaders,\n },\n body: request,\n needRecaptcha: recaptchaConfig?.needRecaptcha,\n recaptchaSitekey: recaptchaConfig?.recaptchaSitekey,\n recaptchaAction: recaptchaConfig?.recaptchaAction || 'send_message',\n signal,\n })\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => null)\n const error = new Error(errorData?.message || `HTTP ${response.status}`) as Error & { type?: string }\n error.type = errorData?.type\n throw error\n }\n\n const reader = response.body?.getReader()\n if (!reader) {\n throw new Error('Response body is not readable')\n }\n\n const decoder = new TextDecoder()\n let buffer = ''\n let currentEvent: string | null = null\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n // \u89E3\u7801\u5E76\u8FFD\u52A0\u5230\u7F13\u51B2\u533A\n buffer += decoder.decode(value, { stream: true })\n\n // \u6309\u884C\u5206\u5272\n const lines = buffer.split('\\n')\n\n // \u4FDD\u7559\u6700\u540E\u4E00\u4E2A\u4E0D\u5B8C\u6574\u7684\u884C\n buffer = lines.pop() || ''\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n if (trimmed === '') {\n // \u7A7A\u884C\u8868\u793A\u4E8B\u4EF6\u8FB9\u754C\uFF0C\u91CD\u7F6E\u5F53\u524D\u4E8B\u4EF6\u7C7B\u578B\n currentEvent = null\n continue\n }\n\n if (trimmed.startsWith('event:')) {\n // \u63D0\u53D6\u4E8B\u4EF6\u7C7B\u578B\n currentEvent = trimmed.substring(6).trim()\n } else if (trimmed.startsWith('data:')) {\n // \u63D0\u53D6\u6570\u636E\u5E76\u89E3\u6790 JSON\n const dataStr = trimmed.substring(5).trim()\n try {\n const data = JSON.parse(dataStr)\n onEvent({\n event: (currentEvent as any) || 'message',\n data,\n })\n } catch (err) {\n console.error('[LiveChat API] Failed to parse SSE data:', dataStr, err)\n onEvent({\n event: 'error',\n data: {\n message: 'Failed to parse SSE data',\n code: 'PARSE_ERROR',\n },\n })\n }\n }\n }\n }\n } catch (error) {\n console.error('[LiveChat API] SSE stream error:', error)\n throw error\n } finally {\n reader.releaseLock()\n }\n}\n\n// ============================================================================\n// \u521B\u5EFA/\u6062\u590D\u4F1A\u8BDD (T009)\n// ============================================================================\n\n/**\n * \u521B\u5EFA\u65B0\u4F1A\u8BDD\u6216\u6062\u590D\u73B0\u6709\u4F1A\u8BDD\n *\n * \u4F7F\u7528\u573A\u666F\uFF1A\n * 1. \u521B\u5EFA\u65B0\u4F1A\u8BDD\uFF1A\u4E0D\u4F20 session_id\uFF0C\u8FD4\u56DE\u65B0\u7684 sessionId\n * 2. \u6062\u590D\u4F1A\u8BDD\uFF1A\u4F20\u5165 session_id\uFF0C\u9A8C\u8BC1\u4F1A\u8BDD\u6709\u6548\u6027\u5E76\u8FD4\u56DE\u5386\u53F2\u6D88\u606F\uFF08resumed: true, messages: [...]\uFF09\n *\n * @param apiBaseUrl API \u57FA\u7840 URL\n * @param request \u8BF7\u6C42\u53C2\u6570\n * @param customHeaders \u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\n * @param recaptchaConfig reCAPTCHA \u914D\u7F6E\n * @returns \u4F1A\u8BDD\u4FE1\u606F\uFF0C\u53EF\u80FD\u5305\u542B\u5386\u53F2\u6D88\u606F\n */\nexport async function createNewSession(\n apiBaseUrl: string,\n request: NewSessionRequest,\n customHeaders?: Record<string, string>,\n recaptchaConfig?: RecaptchaConfig\n): Promise<NewSessionResponse> {\n // \u4F7F\u7528 fetcher \u5904\u7406 reCAPTCHA \u548C\u81EA\u5B9A\u4E49 headers\n const response = await fetcher({\n url: `${apiBaseUrl}/api/chat/new-session`,\n method: 'POST',\n headers: customHeaders,\n body: request,\n needRecaptcha: recaptchaConfig?.needRecaptcha,\n recaptchaSitekey: recaptchaConfig?.recaptchaSitekey,\n recaptchaAction: recaptchaConfig?.recaptchaAction || 'new_session',\n })\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => null)\n const error = new Error(errorData?.message || `HTTP ${response.status}`) as Error & { type?: string }\n error.type = errorData?.type\n throw error\n }\n\n return response.json()\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,sBAAAE,EAAA,gBAAAC,IAAA,eAAAC,EAAAJ,GAOA,IAAAK,EAAwB,4BAgCxB,eAAsBF,EACpBG,EACAC,EACAC,EACAC,EACAC,EACAC,EACe,CACf,MAAMC,EAAW,QAAM,WAAQ,CAC7B,IAAK,GAAGN,CAAU,mBAClB,OAAQ,OACR,QAAS,CACP,OAAQ,oBACR,GAAGG,CACL,EACA,KAAMF,EACN,cAAeG,GAAiB,cAChC,iBAAkBA,GAAiB,iBACnC,gBAAiBA,GAAiB,iBAAmB,eACrD,OAAAC,CACF,CAAC,EAED,GAAI,CAACC,EAAS,GAAI,CAChB,MAAMC,EAAY,MAAMD,EAAS,KAAK,EAAE,MAAM,IAAM,IAAI,EAClDE,EAAQ,IAAI,MAAMD,GAAW,SAAW,QAAQD,EAAS,MAAM,EAAE,EACvE,MAAAE,EAAM,KAAOD,GAAW,KAClBC,CACR,CAEA,MAAMC,EAASH,EAAS,MAAM,UAAU,EACxC,GAAI,CAACG,EACH,MAAM,IAAI,MAAM,+BAA+B,EAGjD,MAAMC,EAAU,IAAI,YACpB,IAAIC,EAAS,GACTC,EAA8B,KAElC,GAAI,CACF,OAAa,CACX,KAAM,CAAE,KAAAC,EAAM,MAAAC,CAAM,EAAI,MAAML,EAAO,KAAK,EAC1C,GAAII,EAAM,MAGVF,GAAUD,EAAQ,OAAOI,EAAO,CAAE,OAAQ,EAAK,CAAC,EAGhD,MAAMC,EAAQJ,EAAO,MAAM;AAAA,CAAI,EAG/BA,EAASI,EAAM,IAAI,GAAK,GAExB,UAAWC,KAAQD,EAAO,CACxB,MAAME,EAAUD,EAAK,KAAK,EAE1B,GAAIC,IAAY,GAAI,CAElBL,EAAe,KACf,QACF,CAEA,GAAIK,EAAQ,WAAW,QAAQ,EAE7BL,EAAeK,EAAQ,UAAU,CAAC,EAAE,KAAK,UAChCA,EAAQ,WAAW,OAAO,EAAG,CAEtC,MAAMC,EAAUD,EAAQ,UAAU,CAAC,EAAE,KAAK,EAC1C,GAAI,CACF,MAAME,EAAO,KAAK,MAAMD,CAAO,EAC/BhB,EAAQ,CACN,MAAQU,GAAwB,UAChC,KAAAO,CACF,CAAC,CACH,OAASC,EAAK,CACZ,QAAQ,MAAM,2CAA4CF,EAASE,CAAG,EACtElB,EAAQ,CACN,MAAO,QACP,KAAM,CACJ,QAAS,2BACT,KAAM,aACR,CACF,CAAC,CACH,CACF,CACF,CACF,CACF,OAASM,EAAO,CACd,cAAQ,MAAM,mCAAoCA,CAAK,EACjDA,CACR,QAAE,CACAC,EAAO,YAAY,CACrB,CACF,CAmBA,eAAsBb,EACpBI,EACAC,EACAE,EACAC,EAC6B,CAE7B,MAAME,EAAW,QAAM,WAAQ,CAC7B,IAAK,GAAGN,CAAU,wBAClB,OAAQ,OACR,QAASG,EACT,KAAMF,EACN,cAAeG,GAAiB,cAChC,iBAAkBA,GAAiB,iBACnC,gBAAiBA,GAAiB,iBAAmB,aACvD,CAAC,EAED,GAAI,CAACE,EAAS,GAAI,CAChB,MAAMC,EAAY,MAAMD,EAAS,KAAK,EAAE,MAAM,IAAM,IAAI,EAClDE,EAAQ,IAAI,MAAMD,GAAW,SAAW,QAAQD,EAAS,MAAM,EAAE,EACvE,MAAAE,EAAM,KAAOD,GAAW,KAClBC,CACR,CAEA,OAAOF,EAAS,KAAK,CACvB",
6
+ "names": ["chat_exports", "__export", "createNewSession", "sendMessage", "__toCommonJS", "import_fetcher", "apiBaseUrl", "request", "onEvent", "customHeaders", "recaptchaConfig", "signal", "response", "errorData", "error", "reader", "decoder", "buffer", "currentEvent", "done", "value", "lines", "line", "trimmed", "dataStr", "data", "err"]
7
7
  }
@@ -1,2 +1,2 @@
1
- "use strict";var c=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var y=(a,e)=>{for(var l in e)c(a,l,{get:e[l],enumerable:!0})},N=(a,e,l,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of b(e))!C.call(a,n)&&n!==l&&c(a,n,{get:()=>e[n],enumerable:!(o=v(e,n))||o.enumerable});return a};var h=a=>N(c({},"__esModule",{value:!0}),a);var F={};y(F,{CartCard:()=>w});module.exports=h(F);var t=require("react/jsx-runtime"),d=require("../../constants.js");function u(a){const{amount:e,currencyCode:l}=a,o=d.CURRENCY_SYMBOLS[l]||l,n=parseFloat(e);return`${o}${n.toFixed(2)}`}const A=({line:a})=>{const{quantity:e,merchandise:l,cost:o}=a,{product:n,title:i,image:s}=l,r=s?.url||"",m=parseFloat(o.totalAmount.amount)<parseFloat(o.subtotalAmount.amount)&&o.totalAmount.currencyCode===o.subtotalAmount.currencyCode;return(0,t.jsxs)("div",{className:"flex gap-4",children:[(0,t.jsx)("div",{className:"shrink-0 overflow-hidden rounded-md",style:{width:"72px",height:"72px"},children:(0,t.jsx)("img",{src:r,alt:n.title,className:"size-full object-cover",loading:"lazy"})}),(0,t.jsxs)("div",{className:"flex flex-1 flex-col",children:[(0,t.jsx)("h4",{className:"line-clamp-2 text-sm tablet:text-[16px] font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]",children:n.title}),i&&(0,t.jsx)("p",{className:"mt-0.5 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]",children:i}),(0,t.jsxs)("div",{className:"flex items-end justify-between gap-2 mt-1",children:[(0,t.jsx)("div",{className:"flex-1",children:(0,t.jsxs)("p",{className:"text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]",children:["\xD7",e]})}),(0,t.jsxs)("div",{className:"flex gap-1 text-right",children:[(0,t.jsx)("div",{className:"tablet:text-[16px] text-sm font-bold leading-[1.4] tracking-[-0.02em] text-gray-900",children:u(o.totalAmount)}),m&&(0,t.jsx)("div",{className:"tablet:text-[16px] text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56] line-through",children:u(o.subtotalAmount)})]})]})]})]})},k=({total:a,totalText:e})=>(0,t.jsx)("div",{className:"border-t border-gray-200 p-4",children:(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsx)("span",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900",children:e}),(0,t.jsx)("span",{className:"text-base tablet:text-[18px] font-bold leading-[1.4] tracking-[-0.02em] text-gray-900",children:u(a)})]})}),w={render:a=>{const e=a,{data:l}=e;if(!l)return null;const{isEmpty:o,lines:n,cost:i,checkoutUrl:s,onCart:r,cartId:m,commonText:g}=l,x={...d.DEFAULT_COMMON_TEXT,...g},f=()=>{r?r(m,s):s&&window.open(s,"_blank","noopener,noreferrer")};return o||!n||n.length===0?null:(0,t.jsxs)("div",{className:"w-full max-w-md overflow-hidden rounded-2xl shadow-sm",style:{backgroundColor:"#F5F6F7"},children:[(0,t.jsx)("div",{className:"flex flex-col gap-6 overflow-y-auto p-4",children:n.map(p=>(0,t.jsx)(A,{line:p},p.id))}),(0,t.jsx)(k,{total:i.totalAmount,totalText:x.total}),(s||r)&&(0,t.jsx)("div",{className:"px-4 pb-4",children:(0,t.jsx)("button",{type:"button",onClick:f,className:"livechat-btn-primary w-full rounded-full py-[10px] text-center text-sm font-bold leading-[1.4] tracking-[-0.02em] text-white",style:{backgroundColor:"#1D1D1F"},children:x.viewMore})})]})}};
1
+ "use strict";var c=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var y=(a,e)=>{for(var o in e)c(a,o,{get:e[o],enumerable:!0})},N=(a,e,o,l)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of b(e))!C.call(a,n)&&n!==o&&c(a,n,{get:()=>e[n],enumerable:!(l=v(e,n))||l.enumerable});return a};var h=a=>N(c({},"__esModule",{value:!0}),a);var F={};y(F,{CartCard:()=>w});module.exports=h(F);var t=require("react/jsx-runtime"),d=require("../../constants.js");function u(a){const{amount:e,currencyCode:o}=a;return(0,d.formatCurrency)(e,o)}const A=({line:a})=>{const{quantity:e,merchandise:o,cost:l}=a,{product:n,title:i,image:r}=o,s=r?.url||"",m=parseFloat(l.totalAmount.amount)<parseFloat(l.subtotalAmount.amount)&&l.totalAmount.currencyCode===l.subtotalAmount.currencyCode;return(0,t.jsxs)("div",{className:"flex gap-4",children:[(0,t.jsx)("div",{className:"shrink-0 overflow-hidden rounded-md",style:{width:"72px",height:"72px"},children:(0,t.jsx)("img",{src:s,alt:n.title,className:"size-full object-cover",loading:"lazy"})}),(0,t.jsxs)("div",{className:"flex flex-1 flex-col",children:[(0,t.jsx)("h4",{className:"line-clamp-2 text-sm tablet:text-[16px] font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]",children:n.title}),i&&(0,t.jsx)("p",{className:"mt-0.5 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]",children:i}),(0,t.jsxs)("div",{className:"flex items-end justify-between gap-2 mt-1",children:[(0,t.jsx)("div",{className:"flex-1",children:(0,t.jsxs)("p",{className:"text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]",children:["\xD7",e]})}),(0,t.jsxs)("div",{className:"flex gap-1 text-right",children:[(0,t.jsx)("div",{className:"tablet:text-[16px] text-sm font-bold leading-[1.4] tracking-[-0.02em] text-gray-900",children:u(l.totalAmount)}),m&&(0,t.jsx)("div",{className:"tablet:text-[16px] text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56] line-through",children:u(l.subtotalAmount)})]})]})]})]})},k=({total:a,totalText:e})=>(0,t.jsx)("div",{className:"border-t border-gray-200 p-4",children:(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsx)("span",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900",children:e}),(0,t.jsx)("span",{className:"text-base tablet:text-[18px] font-bold leading-[1.4] tracking-[-0.02em] text-gray-900",children:u(a)})]})}),w={render:a=>{const e=a,{data:o}=e;if(!o)return null;const{isEmpty:l,lines:n,cost:i,checkoutUrl:r,onCart:s,cartId:m,commonText:g}=o,x={...d.DEFAULT_COMMON_TEXT,...g},f=()=>{s?s(m,r):r&&window.open(r,"_blank","noopener,noreferrer")};return l||!n||n.length===0?null:(0,t.jsxs)("div",{className:"w-full max-w-md overflow-hidden rounded-2xl shadow-sm",style:{backgroundColor:"#F5F6F7"},children:[(0,t.jsx)("div",{className:"flex flex-col gap-6 overflow-y-auto p-4",children:n.map(p=>(0,t.jsx)(A,{line:p},p.id))}),(0,t.jsx)(k,{total:i.totalAmount,totalText:x.total}),(r||s)&&(0,t.jsx)("div",{className:"px-4 pb-4",children:(0,t.jsx)("button",{type:"button",onClick:f,className:"livechat-btn-primary w-full rounded-full py-[10px] text-center text-sm font-bold leading-[1.4] tracking-[-0.02em] text-white",style:{backgroundColor:"#1D1D1F"},children:x.viewMore})})]})}};
2
2
  //# sourceMappingURL=CartCard.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/components/LiveChatWidget/components/MessageContent/CartCard.tsx"],
4
- "sourcesContent": ["/**\n * \u8D2D\u7269\u8F66\u5361\u7247\u6E32\u67D3\u5668\n * \u663E\u793A\u8D2D\u7269\u8F66\u5185\u5BB9\u3001\u4EF7\u683C\u6C47\u603B\u548C\u7ED3\u8D26\u6309\u94AE\n * \u57FA\u4E8E\u540E\u7AEF\u8FD4\u56DE\u7684\u8D2D\u7269\u8F66\u6570\u636E\u7ED3\u6784\n */\n\nimport React from 'react'\nimport type { MessageRenderer, CartContent, CartLine, CartAmount } from '../../types'\nimport { CURRENCY_SYMBOLS, DEFAULT_COMMON_TEXT } from '../../constants.js'\n\n/**\n * \u683C\u5F0F\u5316\u91D1\u989D\n * @param amount \u91D1\u989D\u5BF9\u8C61\n * @returns \u683C\u5F0F\u5316\u540E\u7684\u91D1\u989D\u5B57\u7B26\u4E32\uFF08\u5982 \"$99.99\"\uFF09\n */\nfunction formatAmount(amount: CartAmount): string {\n const { amount: value, currencyCode } = amount\n\n const symbol = CURRENCY_SYMBOLS[currencyCode] || currencyCode\n const numValue = parseFloat(value)\n\n return `${symbol}${numValue.toFixed(2)}`\n}\n\n/**\n * \u8D2D\u7269\u8F66\u5546\u54C1\u884C\u7EC4\u4EF6\n */\nconst CartLineItem: React.FC<{\n line: CartLine\n}> = ({ line }) => {\n const { quantity, merchandise, cost } = line\n const { product, title: variantTitle, image } = merchandise\n\n // \u5546\u54C1\u56FE\u7247 URL\n const imageUrl = image?.url || ''\n\n // \u5224\u65AD\u662F\u5426\u6709\u6298\u6263\uFF08\u603B\u4EF7 < \u539F\u4EF7\uFF09\n const hasDiscount =\n parseFloat(cost.totalAmount.amount) < parseFloat(cost.subtotalAmount.amount) &&\n cost.totalAmount.currencyCode === cost.subtotalAmount.currencyCode\n\n return (\n <div className=\"flex gap-4\">\n {/* \u5546\u54C1\u56FE\u7247 */}\n <div className=\"shrink-0 overflow-hidden rounded-md\" style={{ width: '72px', height: '72px' }}>\n <img src={imageUrl} alt={product.title} className=\"size-full object-cover\" loading=\"lazy\" />\n </div>\n\n {/* \u5546\u54C1\u4FE1\u606F */}\n <div className=\"flex flex-1 flex-col\">\n <h4 className=\"line-clamp-2 text-sm tablet:text-[16px] font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]\">\n {product.title}\n </h4>\n {variantTitle && (\n <p className=\"mt-0.5 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]\">{variantTitle}</p>\n )}\n <div className=\"flex items-end justify-between gap-2 mt-1\">\n {/* \u5DE6\u4FA7\uFF1A\u6570\u91CF */}\n <div className=\"flex-1\">\n <p className=\"text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]\">\u00D7{quantity}</p>\n </div>\n\n {/* \u53F3\u4FA7\uFF1A\u4EF7\u683C */}\n <div className=\"flex gap-1 text-right\">\n <div className=\"tablet:text-[16px] text-sm font-bold leading-[1.4] tracking-[-0.02em] text-gray-900\">\n {formatAmount(cost.totalAmount)}\n </div>\n {hasDiscount && (\n <div className=\"tablet:text-[16px] text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56] line-through\">\n {formatAmount(cost.subtotalAmount)}\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * \u4EF7\u683C\u6C47\u603B\u7EC4\u4EF6\uFF08\u7B80\u5316\u7248\uFF09\n */\nconst CartSummary: React.FC<{\n total: CartAmount\n totalText: string\n}> = ({ total, totalText }) => {\n return (\n <div className=\"border-t border-gray-200 p-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900\">{totalText}</span>\n <span className=\"text-base tablet:text-[18px] font-bold leading-[1.4] tracking-[-0.02em] text-gray-900\">\n {formatAmount(total)}\n </span>\n </div>\n </div>\n )\n}\n\n/**\n * \u8D2D\u7269\u8F66\u5361\u7247\u6E32\u67D3\u5668\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u8D2D\u7269\u8F66\u5546\u54C1\u5217\u8868\n * - \u663E\u793A\u4EF7\u683C\u6C47\u603B\uFF08\u5C0F\u8BA1\u3001\u6298\u6263\u3001\u603B\u8BA1\uFF09\n * - \u663E\u793A\u6298\u6263\u7801\n * - \u63D0\u4F9B Checkout \u6309\u94AE\n * - \u7A7A\u8D2D\u7269\u8F66\u72B6\u6001\n *\n * \u5E03\u5C40\uFF1A\n * ```\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 \u8D2D\u7269\u8F66 (3 \u4EF6\u5546\u54C1) \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 [\u56FE] \u5546\u54C11 \u2502\n * \u2502 \u53D8\u4F53: Black \u2502\n * \u2502 \u6570\u91CF: 2 $199.98 \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 [\u56FE] \u5546\u54C12 \u2502\n * \u2502 \u53D8\u4F53: White \u2502\n * \u2502 \u6570\u91CF: 1 $99.99 \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 \u5C0F\u8BA1 $299.97 \u2502\n * \u2502 \u6298\u6263 [SPRING20] -$30.00 \u2502\n * \u2502 \u603B\u8BA1 $269.97 \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 [Checkout \u6309\u94AE] \u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * ```\n *\n * @example\n * ```tsx\n * const content: CartContent = {\n * type: 'cart',\n * data: {\n * isEmpty: false,\n * cartId: \"gid://...\",\n * totalQuantity: 3,\n * lines: [...],\n * cost: {...},\n * checkoutUrl: \"https://...\"\n * }\n * }\n * <CartCard.render(content, false, false) />\n * ```\n */\nexport const CartCard: MessageRenderer = {\n render: content => {\n const cartContent = content as CartContent\n const { data } = cartContent\n\n if (!data) {\n return null\n }\n\n const { isEmpty, lines, cost, checkoutUrl, onCart, cartId, commonText } = data\n\n // \u5408\u5E76\u9ED8\u8BA4\u6587\u6848\u548C\u81EA\u5B9A\u4E49\u6587\u6848\n const mergedText = { ...DEFAULT_COMMON_TEXT, ...commonText }\n\n // \u5904\u7406\u8D2D\u7269\u8F66\u6309\u94AE\u70B9\u51FB\n const handleCart = () => {\n if (onCart) {\n onCart(cartId, checkoutUrl)\n } else if (checkoutUrl) {\n window.open(checkoutUrl, '_blank', 'noopener,noreferrer')\n }\n }\n\n // \u7A7A\u8D2D\u7269\u8F66\u72B6\u6001 - \u4E0D\u5C55\u793A\u7EC4\u4EF6\n if (isEmpty || !lines || lines.length === 0) {\n return null\n }\n\n return (\n <div className=\"w-full max-w-md overflow-hidden rounded-2xl shadow-sm\" style={{ backgroundColor: '#F5F6F7' }}>\n {/* \u5546\u54C1\u5217\u8868 */}\n <div className=\"flex flex-col gap-6 overflow-y-auto p-4\">\n {lines.map(line => (\n <CartLineItem key={line.id} line={line} />\n ))}\n </div>\n\n {/* \u4EF7\u683C\u6C47\u603B */}\n <CartSummary total={cost.totalAmount} totalText={mergedText.total} />\n\n {/* Checkout \u6309\u94AE */}\n {(checkoutUrl || onCart) && (\n <div className=\"px-4 pb-4\">\n <button\n type=\"button\"\n onClick={handleCart}\n className=\"livechat-btn-primary w-full rounded-full py-[10px] text-center text-sm font-bold leading-[1.4] tracking-[-0.02em] text-white\"\n style={{ backgroundColor: '#1D1D1F' }}\n >\n {mergedText.viewMore}\n </button>\n </div>\n )}\n </div>\n )\n },\n}\n"],
5
- "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,cAAAE,IAAA,eAAAC,EAAAH,GA6CQ,IAAAI,EAAA,6BArCRC,EAAsD,8BAOtD,SAASC,EAAaC,EAA4B,CAChD,KAAM,CAAE,OAAQC,EAAO,aAAAC,CAAa,EAAIF,EAElCG,EAAS,mBAAiBD,CAAY,GAAKA,EAC3CE,EAAW,WAAWH,CAAK,EAEjC,MAAO,GAAGE,CAAM,GAAGC,EAAS,QAAQ,CAAC,CAAC,EACxC,CAKA,MAAMC,EAED,CAAC,CAAE,KAAAC,CAAK,IAAM,CACjB,KAAM,CAAE,SAAAC,EAAU,YAAAC,EAAa,KAAAC,CAAK,EAAIH,EAClC,CAAE,QAAAI,EAAS,MAAOC,EAAc,MAAAC,CAAM,EAAIJ,EAG1CK,EAAWD,GAAO,KAAO,GAGzBE,EACJ,WAAWL,EAAK,YAAY,MAAM,EAAI,WAAWA,EAAK,eAAe,MAAM,GAC3EA,EAAK,YAAY,eAAiBA,EAAK,eAAe,aAExD,SACE,QAAC,OAAI,UAAU,aAEb,oBAAC,OAAI,UAAU,sCAAsC,MAAO,CAAE,MAAO,OAAQ,OAAQ,MAAO,EAC1F,mBAAC,OAAI,IAAKI,EAAU,IAAKH,EAAQ,MAAO,UAAU,yBAAyB,QAAQ,OAAO,EAC5F,KAGA,QAAC,OAAI,UAAU,uBACb,oBAAC,MAAG,UAAU,oGACX,SAAAA,EAAQ,MACX,EACCC,MACC,OAAC,KAAE,UAAU,2EAA4E,SAAAA,EAAa,KAExG,QAAC,OAAI,UAAU,4CAEb,oBAAC,OAAI,UAAU,SACb,oBAAC,KAAE,UAAU,oEAAoE,iBAAEJ,GAAS,EAC9F,KAGA,QAAC,OAAI,UAAU,wBACb,oBAAC,OAAI,UAAU,sFACZ,SAAAR,EAAaU,EAAK,WAAW,EAChC,EACCK,MACC,OAAC,OAAI,UAAU,oGACZ,SAAAf,EAAaU,EAAK,cAAc,EACnC,GAEJ,GACF,GACF,GACF,CAEJ,EAKMM,EAGD,CAAC,CAAE,MAAAC,EAAO,UAAAC,CAAU,OAErB,OAAC,OAAI,UAAU,+BACb,oBAAC,OAAI,UAAU,oCACb,oBAAC,QAAK,UAAU,qEAAsE,SAAAA,EAAU,KAChG,OAAC,QAAK,UAAU,wFACb,SAAAlB,EAAaiB,CAAK,EACrB,GACF,EACF,EAmDSrB,EAA4B,CACvC,OAAQuB,GAAW,CACjB,MAAMC,EAAcD,EACd,CAAE,KAAAE,CAAK,EAAID,EAEjB,GAAI,CAACC,EACH,OAAO,KAGT,KAAM,CAAE,QAAAC,EAAS,MAAAC,EAAO,KAAAb,EAAM,YAAAc,EAAa,OAAAC,EAAQ,OAAAC,EAAQ,WAAAC,CAAW,EAAIN,EAGpEO,EAAa,CAAE,GAAG,sBAAqB,GAAGD,CAAW,EAGrDE,EAAa,IAAM,CACnBJ,EACFA,EAAOC,EAAQF,CAAW,EACjBA,GACT,OAAO,KAAKA,EAAa,SAAU,qBAAqB,CAE5D,EAGA,OAAIF,GAAW,CAACC,GAASA,EAAM,SAAW,EACjC,QAIP,QAAC,OAAI,UAAU,wDAAwD,MAAO,CAAE,gBAAiB,SAAU,EAEzG,oBAAC,OAAI,UAAU,0CACZ,SAAAA,EAAM,IAAIhB,MACT,OAACD,EAAA,CAA2B,KAAMC,GAAfA,EAAK,EAAgB,CACzC,EACH,KAGA,OAACS,EAAA,CAAY,MAAON,EAAK,YAAa,UAAWkB,EAAW,MAAO,GAGjEJ,GAAeC,OACf,OAAC,OAAI,UAAU,YACb,mBAAC,UACC,KAAK,SACL,QAASI,EACT,UAAU,+HACV,MAAO,CAAE,gBAAiB,SAAU,EAEnC,SAAAD,EAAW,SACd,EACF,GAEJ,CAEJ,CACF",
6
- "names": ["CartCard_exports", "__export", "CartCard", "__toCommonJS", "import_jsx_runtime", "import_constants", "formatAmount", "amount", "value", "currencyCode", "symbol", "numValue", "CartLineItem", "line", "quantity", "merchandise", "cost", "product", "variantTitle", "image", "imageUrl", "hasDiscount", "CartSummary", "total", "totalText", "content", "cartContent", "data", "isEmpty", "lines", "checkoutUrl", "onCart", "cartId", "commonText", "mergedText", "handleCart"]
4
+ "sourcesContent": ["/**\n * \u8D2D\u7269\u8F66\u5361\u7247\u6E32\u67D3\u5668\n * \u663E\u793A\u8D2D\u7269\u8F66\u5185\u5BB9\u3001\u4EF7\u683C\u6C47\u603B\u548C\u7ED3\u8D26\u6309\u94AE\n * \u57FA\u4E8E\u540E\u7AEF\u8FD4\u56DE\u7684\u8D2D\u7269\u8F66\u6570\u636E\u7ED3\u6784\n */\n\nimport React from 'react'\nimport type { MessageRenderer, CartContent, CartLine, CartAmount } from '../../types'\nimport { formatCurrency, DEFAULT_COMMON_TEXT } from '../../constants.js'\n\n/**\n * \u683C\u5F0F\u5316\u91D1\u989D\n * @param amount \u91D1\u989D\u5BF9\u8C61\n * @returns \u683C\u5F0F\u5316\u540E\u7684\u91D1\u989D\u5B57\u7B26\u4E32\uFF08\u5982 \"$99.99\"\uFF09\n */\nfunction formatAmount(amount: CartAmount): string {\n const { amount: value, currencyCode } = amount\n return formatCurrency(value, currencyCode)\n}\n\n/**\n * \u8D2D\u7269\u8F66\u5546\u54C1\u884C\u7EC4\u4EF6\n */\nconst CartLineItem: React.FC<{\n line: CartLine\n}> = ({ line }) => {\n const { quantity, merchandise, cost } = line\n const { product, title: variantTitle, image } = merchandise\n\n // \u5546\u54C1\u56FE\u7247 URL\n const imageUrl = image?.url || ''\n\n // \u5224\u65AD\u662F\u5426\u6709\u6298\u6263\uFF08\u603B\u4EF7 < \u539F\u4EF7\uFF09\n const hasDiscount =\n parseFloat(cost.totalAmount.amount) < parseFloat(cost.subtotalAmount.amount) &&\n cost.totalAmount.currencyCode === cost.subtotalAmount.currencyCode\n\n return (\n <div className=\"flex gap-4\">\n {/* \u5546\u54C1\u56FE\u7247 */}\n <div className=\"shrink-0 overflow-hidden rounded-md\" style={{ width: '72px', height: '72px' }}>\n <img src={imageUrl} alt={product.title} className=\"size-full object-cover\" loading=\"lazy\" />\n </div>\n\n {/* \u5546\u54C1\u4FE1\u606F */}\n <div className=\"flex flex-1 flex-col\">\n <h4 className=\"line-clamp-2 text-sm tablet:text-[16px] font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]\">\n {product.title}\n </h4>\n {variantTitle && (\n <p className=\"mt-0.5 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]\">{variantTitle}</p>\n )}\n <div className=\"flex items-end justify-between gap-2 mt-1\">\n {/* \u5DE6\u4FA7\uFF1A\u6570\u91CF */}\n <div className=\"flex-1\">\n <p className=\"text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]\">\u00D7{quantity}</p>\n </div>\n\n {/* \u53F3\u4FA7\uFF1A\u4EF7\u683C */}\n <div className=\"flex gap-1 text-right\">\n <div className=\"tablet:text-[16px] text-sm font-bold leading-[1.4] tracking-[-0.02em] text-gray-900\">\n {formatAmount(cost.totalAmount)}\n </div>\n {hasDiscount && (\n <div className=\"tablet:text-[16px] text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56] line-through\">\n {formatAmount(cost.subtotalAmount)}\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * \u4EF7\u683C\u6C47\u603B\u7EC4\u4EF6\uFF08\u7B80\u5316\u7248\uFF09\n */\nconst CartSummary: React.FC<{\n total: CartAmount\n totalText: string\n}> = ({ total, totalText }) => {\n return (\n <div className=\"border-t border-gray-200 p-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900\">{totalText}</span>\n <span className=\"text-base tablet:text-[18px] font-bold leading-[1.4] tracking-[-0.02em] text-gray-900\">\n {formatAmount(total)}\n </span>\n </div>\n </div>\n )\n}\n\n/**\n * \u8D2D\u7269\u8F66\u5361\u7247\u6E32\u67D3\u5668\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u8D2D\u7269\u8F66\u5546\u54C1\u5217\u8868\n * - \u663E\u793A\u4EF7\u683C\u6C47\u603B\uFF08\u5C0F\u8BA1\u3001\u6298\u6263\u3001\u603B\u8BA1\uFF09\n * - \u663E\u793A\u6298\u6263\u7801\n * - \u63D0\u4F9B Checkout \u6309\u94AE\n * - \u7A7A\u8D2D\u7269\u8F66\u72B6\u6001\n *\n * \u5E03\u5C40\uFF1A\n * ```\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 \u8D2D\u7269\u8F66 (3 \u4EF6\u5546\u54C1) \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 [\u56FE] \u5546\u54C11 \u2502\n * \u2502 \u53D8\u4F53: Black \u2502\n * \u2502 \u6570\u91CF: 2 $199.98 \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 [\u56FE] \u5546\u54C12 \u2502\n * \u2502 \u53D8\u4F53: White \u2502\n * \u2502 \u6570\u91CF: 1 $99.99 \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 \u5C0F\u8BA1 $299.97 \u2502\n * \u2502 \u6298\u6263 [SPRING20] -$30.00 \u2502\n * \u2502 \u603B\u8BA1 $269.97 \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 [Checkout \u6309\u94AE] \u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * ```\n *\n * @example\n * ```tsx\n * const content: CartContent = {\n * type: 'cart',\n * data: {\n * isEmpty: false,\n * cartId: \"gid://...\",\n * totalQuantity: 3,\n * lines: [...],\n * cost: {...},\n * checkoutUrl: \"https://...\"\n * }\n * }\n * <CartCard.render(content, false, false) />\n * ```\n */\nexport const CartCard: MessageRenderer = {\n render: content => {\n const cartContent = content as CartContent\n const { data } = cartContent\n\n if (!data) {\n return null\n }\n\n const { isEmpty, lines, cost, checkoutUrl, onCart, cartId, commonText } = data\n\n // \u5408\u5E76\u9ED8\u8BA4\u6587\u6848\u548C\u81EA\u5B9A\u4E49\u6587\u6848\n const mergedText = { ...DEFAULT_COMMON_TEXT, ...commonText }\n\n // \u5904\u7406\u8D2D\u7269\u8F66\u6309\u94AE\u70B9\u51FB\n const handleCart = () => {\n if (onCart) {\n onCart(cartId, checkoutUrl)\n } else if (checkoutUrl) {\n window.open(checkoutUrl, '_blank', 'noopener,noreferrer')\n }\n }\n\n // \u7A7A\u8D2D\u7269\u8F66\u72B6\u6001 - \u4E0D\u5C55\u793A\u7EC4\u4EF6\n if (isEmpty || !lines || lines.length === 0) {\n return null\n }\n\n return (\n <div className=\"w-full max-w-md overflow-hidden rounded-2xl shadow-sm\" style={{ backgroundColor: '#F5F6F7' }}>\n {/* \u5546\u54C1\u5217\u8868 */}\n <div className=\"flex flex-col gap-6 overflow-y-auto p-4\">\n {lines.map(line => (\n <CartLineItem key={line.id} line={line} />\n ))}\n </div>\n\n {/* \u4EF7\u683C\u6C47\u603B */}\n <CartSummary total={cost.totalAmount} totalText={mergedText.total} />\n\n {/* Checkout \u6309\u94AE */}\n {(checkoutUrl || onCart) && (\n <div className=\"px-4 pb-4\">\n <button\n type=\"button\"\n onClick={handleCart}\n className=\"livechat-btn-primary w-full rounded-full py-[10px] text-center text-sm font-bold leading-[1.4] tracking-[-0.02em] text-white\"\n style={{ backgroundColor: '#1D1D1F' }}\n >\n {mergedText.viewMore}\n </button>\n </div>\n )}\n </div>\n )\n },\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,cAAAE,IAAA,eAAAC,EAAAH,GAyCQ,IAAAI,EAAA,6BAjCRC,EAAoD,8BAOpD,SAASC,EAAaC,EAA4B,CAChD,KAAM,CAAE,OAAQC,EAAO,aAAAC,CAAa,EAAIF,EACxC,SAAO,kBAAeC,EAAOC,CAAY,CAC3C,CAKA,MAAMC,EAED,CAAC,CAAE,KAAAC,CAAK,IAAM,CACjB,KAAM,CAAE,SAAAC,EAAU,YAAAC,EAAa,KAAAC,CAAK,EAAIH,EAClC,CAAE,QAAAI,EAAS,MAAOC,EAAc,MAAAC,CAAM,EAAIJ,EAG1CK,EAAWD,GAAO,KAAO,GAGzBE,EACJ,WAAWL,EAAK,YAAY,MAAM,EAAI,WAAWA,EAAK,eAAe,MAAM,GAC3EA,EAAK,YAAY,eAAiBA,EAAK,eAAe,aAExD,SACE,QAAC,OAAI,UAAU,aAEb,oBAAC,OAAI,UAAU,sCAAsC,MAAO,CAAE,MAAO,OAAQ,OAAQ,MAAO,EAC1F,mBAAC,OAAI,IAAKI,EAAU,IAAKH,EAAQ,MAAO,UAAU,yBAAyB,QAAQ,OAAO,EAC5F,KAGA,QAAC,OAAI,UAAU,uBACb,oBAAC,MAAG,UAAU,oGACX,SAAAA,EAAQ,MACX,EACCC,MACC,OAAC,KAAE,UAAU,2EAA4E,SAAAA,EAAa,KAExG,QAAC,OAAI,UAAU,4CAEb,oBAAC,OAAI,UAAU,SACb,oBAAC,KAAE,UAAU,oEAAoE,iBAAEJ,GAAS,EAC9F,KAGA,QAAC,OAAI,UAAU,wBACb,oBAAC,OAAI,UAAU,sFACZ,SAAAN,EAAaQ,EAAK,WAAW,EAChC,EACCK,MACC,OAAC,OAAI,UAAU,oGACZ,SAAAb,EAAaQ,EAAK,cAAc,EACnC,GAEJ,GACF,GACF,GACF,CAEJ,EAKMM,EAGD,CAAC,CAAE,MAAAC,EAAO,UAAAC,CAAU,OAErB,OAAC,OAAI,UAAU,+BACb,oBAAC,OAAI,UAAU,oCACb,oBAAC,QAAK,UAAU,qEAAsE,SAAAA,EAAU,KAChG,OAAC,QAAK,UAAU,wFACb,SAAAhB,EAAae,CAAK,EACrB,GACF,EACF,EAmDSnB,EAA4B,CACvC,OAAQqB,GAAW,CACjB,MAAMC,EAAcD,EACd,CAAE,KAAAE,CAAK,EAAID,EAEjB,GAAI,CAACC,EACH,OAAO,KAGT,KAAM,CAAE,QAAAC,EAAS,MAAAC,EAAO,KAAAb,EAAM,YAAAc,EAAa,OAAAC,EAAQ,OAAAC,EAAQ,WAAAC,CAAW,EAAIN,EAGpEO,EAAa,CAAE,GAAG,sBAAqB,GAAGD,CAAW,EAGrDE,EAAa,IAAM,CACnBJ,EACFA,EAAOC,EAAQF,CAAW,EACjBA,GACT,OAAO,KAAKA,EAAa,SAAU,qBAAqB,CAE5D,EAGA,OAAIF,GAAW,CAACC,GAASA,EAAM,SAAW,EACjC,QAIP,QAAC,OAAI,UAAU,wDAAwD,MAAO,CAAE,gBAAiB,SAAU,EAEzG,oBAAC,OAAI,UAAU,0CACZ,SAAAA,EAAM,IAAIhB,MACT,OAACD,EAAA,CAA2B,KAAMC,GAAfA,EAAK,EAAgB,CACzC,EACH,KAGA,OAACS,EAAA,CAAY,MAAON,EAAK,YAAa,UAAWkB,EAAW,MAAO,GAGjEJ,GAAeC,OACf,OAAC,OAAI,UAAU,YACb,mBAAC,UACC,KAAK,SACL,QAASI,EACT,UAAU,+HACV,MAAO,CAAE,gBAAiB,SAAU,EAEnC,SAAAD,EAAW,SACd,EACF,GAEJ,CAEJ,CACF",
6
+ "names": ["CartCard_exports", "__export", "CartCard", "__toCommonJS", "import_jsx_runtime", "import_constants", "formatAmount", "amount", "value", "currencyCode", "CartLineItem", "line", "quantity", "merchandise", "cost", "product", "variantTitle", "image", "imageUrl", "hasDiscount", "CartSummary", "total", "totalText", "content", "cartContent", "data", "isEmpty", "lines", "checkoutUrl", "onCart", "cartId", "commonText", "mergedText", "handleCart"]
7
7
  }
@@ -1,2 +1,2 @@
1
- "use strict";var m=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var F=(t,o)=>{for(var a in o)m(t,a,{get:o[a],enumerable:!0})},D=(t,o,a,r)=>{if(o&&typeof o=="object"||typeof o=="function")for(let n of k(o))!w.call(t,n)&&n!==a&&m(t,n,{get:()=>o[n],enumerable:!(r=_(o,n))||r.enumerable});return t};var R=t=>D(m({},"__esModule",{value:!0}),t);var A={};F(A,{ProductCard:()=>$});module.exports=R(A);var e=require("react/jsx-runtime"),d=require("../../constants.js");function y(t){const{amount:o,currency:a}=t;return`${d.CURRENCY_SYMBOLS[a]||a}${o.toFixed(2)}`}function T(t,o,a=d.DEFAULT_COMMON_TEXT.off){if(!t.discount_type||t.discount_value===void 0)return"";const r=typeof t.discount_value=="string"?parseFloat(t.discount_value):t.discount_value;return isNaN(r)?"":t.discount_type==="percentage"?`${Math.round(r)}% ${a}`:t.discount_type==="fixed_amount"?`${d.CURRENCY_SYMBOLS[o]||o}${Math.round(r)} ${a}`:""}const M=({product:t,onAddToCart:o,addToCartText:a=d.DEFAULT_COMMON_TEXT.addToCart,offText:r=d.DEFAULT_COMMON_TEXT.off})=>{const{title:n,description:i,price:s,imageUrl:l,stockStatus:c,averageRating:f,variants:C}=t,h=c==="out_of_stock",u=C?.[0],p=u?.discount?.has_discount,g=p?u?.discount?.discount_price:null,x=u?.discount,N=g?{amount:g,currency:s.currency}:s,v=x&&p?T(x,s.currency,r):"",P=b=>{b.preventDefault(),b.stopPropagation(),o&&o(t)};return(0,e.jsx)("div",{className:"block w-full overflow-hidden rounded-2xl bg-[#F5F6F7] transition-shadow mb-[32px]",children:(0,e.jsx)("div",{className:"block",children:(0,e.jsxs)("div",{className:"flex gap-2 p-4 bg-white",children:[(0,e.jsx)("div",{className:" flex shrink-0 items-center overflow-hidden rounded-md ",style:{width:"40%"},children:(0,e.jsx)("img",{src:l,alt:n,className:`h-auto w-full object-cover ${h?"opacity-50":""}`,loading:"lazy"})}),(0,e.jsxs)("div",{className:"flex flex-1 flex-col justify-center",children:[v&&(0,e.jsx)("div",{className:"livechat-tag-product mb-1 w-fit rounded-full px-2 text-sm font-bold leading-none tracking-[-0.04em] text-white",style:{backgroundColor:"#005D8E",paddingTop:"6px",paddingBottom:"4px"},children:v}),(0,e.jsx)("h4",{className:"line-clamp-2 text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]",children:n}),(0,e.jsxs)("div",{className:"mt-4 flex items-center gap-2",children:[(0,e.jsxs)("div",{className:"flex items-center gap-1",children:[(0,e.jsx)("span",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#1D1D1F]",children:y(N)}),p&&(0,e.jsx)("span",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#6D6D6F] line-through",children:y(s)})]}),f!==void 0&&(0,e.jsxs)("div",{className:"flex items-center gap-0.5 text-xs text-gray-600",children:[(0,e.jsx)("span",{className:"text-yellow-500",children:"\u2B50"}),(0,e.jsx)("span",{children:f.toFixed(1)})]})]}),(0,e.jsx)("button",{type:"button",onClick:P,className:"livechat-btn-primary mt-2 w-fit rounded-full px-[20px] py-[10px] text-center text-sm font-bold leading-[1.2] tracking-[-0.04em] text-white",style:{backgroundColor:"#1D1D1F"},children:a})]})]})})})},$={render:(t,o,a)=>{const r=t,{product:n,rawProduct:i,productHandle:s,onAddToCart:l,productCardRender:c}=r.data;return console.log("[ProductCard] \u6E32\u67D3\u4EA7\u54C1\u5361\u7247:",{productHandle:s,hasProduct:!!n,hasRawProduct:!!i,hasCustomRender:!!c}),c?(console.log("[ProductCard] \u4F7F\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3, productHandle:",s),(0,e.jsx)(e.Fragment,{children:c(i||n,s)})):n?(0,e.jsx)(M,{product:n,onAddToCart:l}):null}};
1
+ "use strict";var m=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var D=(t,n)=>{for(var r in n)m(t,r,{get:n[r],enumerable:!0})},F=(t,n,r,a)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of k(n))!w.call(t,o)&&o!==r&&m(t,o,{get:()=>n[o],enumerable:!(a=_(n,o))||a.enumerable});return t};var T=t=>F(m({},"__esModule",{value:!0}),t);var $={};D($,{ProductCard:()=>A});module.exports=T($);var e=require("react/jsx-runtime"),d=require("../../constants.js");function y(t){const{amount:n,currency:r}=t;return(0,d.formatCurrency)(n,r)}function R(t,n,r=d.DEFAULT_COMMON_TEXT.off){if(!t.discount_type||t.discount_value===void 0)return"";const a=typeof t.discount_value=="string"?parseFloat(t.discount_value):t.discount_value;return isNaN(a)?"":t.discount_type==="percentage"?`${Math.round(a)}% ${r}`:t.discount_type==="fixed_amount"?`${(0,d.formatCurrency)(Math.round(a),n)} ${r}`:""}const M=({product:t,onAddToCart:n,addToCartText:r=d.DEFAULT_COMMON_TEXT.addToCart,offText:a=d.DEFAULT_COMMON_TEXT.off})=>{const{title:o,description:i,price:s,imageUrl:u,stockStatus:c,averageRating:f,variants:C}=t,h=c==="out_of_stock",l=C?.[0],p=l?.discount?.has_discount,g=p?l?.discount?.discount_price:null,x=l?.discount,N=g?{amount:g,currency:s.currency}:s,v=x&&p?R(x,s.currency,a):"",P=b=>{b.preventDefault(),b.stopPropagation(),n&&n(t)};return(0,e.jsx)("div",{className:"block w-full overflow-hidden rounded-2xl bg-[#F5F6F7] transition-shadow mb-[32px]",children:(0,e.jsx)("div",{className:"block",children:(0,e.jsxs)("div",{className:"flex gap-2 p-4 bg-white",children:[(0,e.jsx)("div",{className:" flex shrink-0 items-center overflow-hidden rounded-md ",style:{width:"40%"},children:(0,e.jsx)("img",{src:u,alt:o,className:`h-auto w-full object-cover ${h?"opacity-50":""}`,loading:"lazy"})}),(0,e.jsxs)("div",{className:"flex flex-1 flex-col justify-center",children:[v&&(0,e.jsx)("div",{className:"livechat-tag-product mb-1 w-fit rounded-full px-2 text-sm font-bold leading-none tracking-[-0.04em] text-white",style:{backgroundColor:"#005D8E",paddingTop:"6px",paddingBottom:"4px"},children:v}),(0,e.jsx)("h4",{className:"line-clamp-2 text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]",children:o}),(0,e.jsxs)("div",{className:"mt-4 flex items-center gap-2",children:[(0,e.jsxs)("div",{className:"flex items-center gap-1",children:[(0,e.jsx)("span",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#1D1D1F]",children:y(N)}),p&&(0,e.jsx)("span",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#6D6D6F] line-through",children:y(s)})]}),f!==void 0&&(0,e.jsxs)("div",{className:"flex items-center gap-0.5 text-xs text-gray-600",children:[(0,e.jsx)("span",{className:"text-yellow-500",children:"\u2B50"}),(0,e.jsx)("span",{children:f.toFixed(1)})]})]}),(0,e.jsx)("button",{type:"button",onClick:P,className:"livechat-btn-primary mt-2 w-fit rounded-full px-[20px] py-[10px] text-center text-sm font-bold leading-[1.2] tracking-[-0.04em] text-white",style:{backgroundColor:"#1D1D1F"},children:r})]})]})})})},A={render:(t,n,r)=>{const a=t,{product:o,rawProduct:i,productHandle:s,onAddToCart:u,productCardRender:c}=a.data;return console.log("[ProductCard] \u6E32\u67D3\u4EA7\u54C1\u5361\u7247:",{productHandle:s,hasProduct:!!o,hasRawProduct:!!i,hasCustomRender:!!c}),c?(console.log("[ProductCard] \u4F7F\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3, productHandle:",s),(0,e.jsx)(e.Fragment,{children:c(i||o,s)})):o?(0,e.jsx)(M,{product:o,onAddToCart:u}):null}};
2
2
  //# sourceMappingURL=ProductCard.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/components/LiveChatWidget/components/MessageContent/ProductCard.tsx"],
4
- "sourcesContent": ["/**\n * \u5546\u54C1\u5361\u7247\u6E32\u67D3\u5668 - \u7D27\u51D1\u578B\n * \u663E\u793A\u5355\u4E2A\u5546\u54C1\u7684\u8BE6\u7EC6\u4FE1\u606F\uFF08\u6A2A\u5411\u5E03\u5C40\uFF09\n * \u57FA\u4E8E specs/livechat-widget/data-model.md \u7684\u5546\u54C1\u6570\u636E\u6A21\u578B\n */\n\nimport React from 'react'\nimport type { MessageRenderer, ProductCardContent, Product, CommonText } from '../../types'\nimport { CURRENCY_SYMBOLS, DEFAULT_COMMON_TEXT } from '../../constants.js'\n\n/**\n * \u683C\u5F0F\u5316\u4EF7\u683C\n */\nfunction formatPrice(price: Product['price']): string {\n const { amount, currency } = price\n\n const symbol = CURRENCY_SYMBOLS[currency] || currency\n return `${symbol}${amount.toFixed(2)}`\n}\n\n/**\n * \u683C\u5F0F\u5316\u6298\u6263\u6807\u7B7E\u6587\u672C\n * @param discount \u6298\u6263\u5BF9\u8C61\n * @param currency \u8D27\u5E01\u4EE3\u7801\n * @param offText \"OFF\" \u6587\u6848\n * @returns \u683C\u5F0F\u5316\u540E\u7684\u6298\u6263\u6587\u672C\uFF08\u5982 \"$10 OFF\" \u6216 \"20% OFF\"\uFF09\n */\nfunction formatDiscountLabel(\n discount: { discount_type?: string; discount_value?: string | number },\n currency: string,\n offText: string = DEFAULT_COMMON_TEXT.off\n): string {\n if (!discount.discount_type || discount.discount_value === undefined) {\n return ''\n }\n\n // \u5C06 discount_value \u8F6C\u6362\u4E3A\u6570\u5B57\n const value =\n typeof discount.discount_value === 'string' ? parseFloat(discount.discount_value) : discount.discount_value\n\n if (isNaN(value)) {\n return ''\n }\n\n if (discount.discount_type === 'percentage') {\n return `${Math.round(value)}% ${offText}`\n }\n\n if (discount.discount_type === 'fixed_amount') {\n const symbol = CURRENCY_SYMBOLS[currency] || currency\n return `${symbol}${Math.round(value)} ${offText}`\n }\n\n return ''\n}\n\n/**\n * \u7D27\u51D1\u578B\u5546\u54C1\u5361\u7247\u7EC4\u4EF6\uFF08\u6A2A\u5411\u5E03\u5C40\uFF09\n */\nconst CompactProductCard: React.FC<{\n product: Product\n onAddToCart?: (product: Product) => void\n addToCartText?: string\n offText?: string\n}> = ({ product, onAddToCart, addToCartText = DEFAULT_COMMON_TEXT.addToCart, offText = DEFAULT_COMMON_TEXT.off }) => {\n const { title, description, price, imageUrl, stockStatus, averageRating, variants } = product\n\n const isOutOfStock = stockStatus === 'out_of_stock'\n\n // \u83B7\u53D6\u7B2C\u4E00\u4E2A\u53D8\u4F53\u7684\u6298\u6263\u4FE1\u606F\n const firstVariant = variants?.[0]\n const hasDiscount = firstVariant?.discount?.has_discount\n const discountPrice = hasDiscount ? firstVariant?.discount?.discount_price : null\n const discount = firstVariant?.discount\n\n // \u5F53\u524D\u663E\u793A\u4EF7\u683C\uFF1A\u6709\u6298\u6263\u65F6\u663E\u793A\u6298\u6263\u4EF7\uFF0C\u5426\u5219\u663E\u793A\u539F\u4EF7\n const currentPrice = discountPrice ? { amount: discountPrice, currency: price.currency } : price\n\n // \u683C\u5F0F\u5316\u6298\u6263\u6807\u7B7E\n const discountLabel = discount && hasDiscount ? formatDiscountLabel(discount, price.currency, offText) : ''\n\n const handleAddToCart = (e: React.MouseEvent) => {\n e.preventDefault()\n e.stopPropagation()\n if (onAddToCart) {\n onAddToCart(product)\n }\n }\n\n return (\n <div className=\"block w-full overflow-hidden rounded-2xl bg-[#F5F6F7] transition-shadow mb-[32px]\">\n <div className=\"block\">\n <div className=\"flex gap-2 p-4 bg-white\">\n {/* \u5546\u54C1\u56FE\u7247 */}\n <div className=\" flex shrink-0 items-center overflow-hidden rounded-md \" style={{ width: '40%' }}>\n <img\n src={imageUrl}\n alt={title}\n className={`h-auto w-full object-cover ${isOutOfStock ? 'opacity-50' : ''}`}\n loading=\"lazy\"\n />\n </div>\n\n {/* \u5546\u54C1\u4FE1\u606F */}\n <div className=\"flex flex-1 flex-col justify-center\">\n {/* \u6298\u6263\u6807\u7B7E */}\n {discountLabel && (\n <div\n className=\"livechat-tag-product mb-1 w-fit rounded-full px-2 text-sm font-bold leading-none tracking-[-0.04em] text-white\"\n style={{ backgroundColor: '#005D8E', paddingTop: '6px', paddingBottom: '4px' }}\n >\n {discountLabel}\n </div>\n )}\n\n {/* \u6807\u9898 */}\n <h4 className=\"line-clamp-2 text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]\">\n {title}\n </h4>\n\n {/* \u63CF\u8FF0\uFF08\u53EF\u9009\uFF09 */}\n {/* {description && (\n <p className=\"line-clamp-2 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#1D1D1F]\">\n {description}\n </p>\n )} */}\n\n {/* \u4EF7\u683C\u548C\u8BC4\u5206 */}\n <div className=\"mt-4 flex items-center gap-2\">\n <div className=\"flex items-center gap-1\">\n {/* \u5F53\u524D\u4EF7\u683C\uFF08\u6709\u6298\u6263\u65F6\u663E\u793A\u6298\u6263\u4EF7\uFF0C\u5426\u5219\u663E\u793A\u539F\u4EF7\uFF09 */}\n <span className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#1D1D1F]\">\n {formatPrice(currentPrice)}\n </span>\n {/* \u539F\u4EF7\uFF08\u5212\u7EBF\u4EF7\uFF09- \u4EC5\u5728\u6709\u6298\u6263\u65F6\u663E\u793A */}\n {hasDiscount && (\n <span className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#6D6D6F] line-through\">\n {formatPrice(price)}\n </span>\n )}\n </div>\n {/* \u8BC4\u5206\uFF08\u53EF\u9009\uFF09 */}\n {averageRating !== undefined && (\n <div className=\"flex items-center gap-0.5 text-xs text-gray-600\">\n <span className=\"text-yellow-500\">\u2B50</span>\n <span>{averageRating.toFixed(1)}</span>\n </div>\n )}\n </div>\n\n {/* Add to Cart \u6309\u94AE - \u5728\u4EF7\u683C\u4E0B\u65B9 */}\n <button\n type=\"button\"\n onClick={handleAddToCart}\n className=\"livechat-btn-primary mt-2 w-fit rounded-full px-[20px] py-[10px] text-center text-sm font-bold leading-[1.2] tracking-[-0.04em] text-white\"\n style={{ backgroundColor: '#1D1D1F' }}\n >\n {addToCartText}\n </button>\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * \u5546\u54C1\u5361\u7247\u6E32\u67D3\u5668\n *\n * \u529F\u80FD\uFF1A\n * - \u7D27\u51D1\u578B\u6A2A\u5411\u5E03\u5C40\uFF08\u56FE\u7247\u5728\u5DE6\uFF0C\u4FE1\u606F\u5728\u53F3\uFF09\n * - \u663E\u793A\u6298\u6263\u6807\u7B7E\u3001\u4EF7\u683C\u3001\u8BC4\u5206\n * - \u652F\u6301 Add to Cart \u6309\u94AE\n * - \u72EC\u7ACB\u6210\u6BB5\u5C55\u793A\n * - \u652F\u6301\u81EA\u5B9A\u4E49\u6E32\u67D3\u51FD\u6570\n *\n * \u5E03\u5C40\uFF1A\n * ```\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 [\u56FE\u7247] \u6298\u6263\u6807\u7B7E \u2502\n * \u2502 \u5546\u54C1\u6807\u9898 \u2502\n * \u2502 $29.99 (\u539F\u4EF7) \u2502\n * \u2502 \u2B50 4.5 \u2502\n * \u2502 [Add to Cart] \u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * ```\n *\n * @example\n * ```tsx\n * const content: ProductCardContent = {\n * type: 'product_card',\n * data: {\n * product: { ... },\n * onAddToCart: (product) => { ... },\n * productCardRender: (product, productHandle) => <CustomCard product={product} handle={productHandle} />\n * }\n * }\n * <ProductCard.render(content, false, false) />\n * ```\n */\nexport const ProductCard: MessageRenderer = {\n render: (content, isUser, isSystem) => {\n const productContent = content as ProductCardContent\n const { product, rawProduct, productHandle, onAddToCart, productCardRender } = productContent.data\n\n console.log('[ProductCard] \u6E32\u67D3\u4EA7\u54C1\u5361\u7247:', {\n productHandle,\n hasProduct: !!product,\n hasRawProduct: !!rawProduct,\n hasCustomRender: !!productCardRender,\n })\n\n // \u5982\u679C\u63D0\u4F9B\u4E86\u81EA\u5B9A\u4E49\u6E32\u67D3\u51FD\u6570\uFF0C\u4F7F\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3\uFF08\u4F20\u5165\u539F\u59CB\u540E\u7AEF\u6570\u636E\u548C productHandle\uFF09\n // \u5373\u4F7F product \u4E3A\u7A7A\uFF0C\u4E5F\u8C03\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3\u51FD\u6570\uFF0C\u8BA9\u5E94\u7528\u5C42\u53EF\u4EE5\u7528 productHandle \u67E5\u8BE2\u4EA7\u54C1\n if (productCardRender) {\n console.log('[ProductCard] \u4F7F\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3, productHandle:', productHandle)\n return <>{productCardRender(rawProduct || product, productHandle)}</>\n }\n\n // \u9ED8\u8BA4\u6E32\u67D3\uFF1A\u5982\u679C\u6CA1\u6709\u4EA7\u54C1\u6570\u636E\u5219\u4E0D\u6E32\u67D3\n if (!product) {\n return null\n }\n\n return <CompactProductCard product={product} onAddToCart={onAddToCart} />\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * \u5546\u54C1\u5361\u7247\u6E32\u67D3\u5668 - \u7D27\u51D1\u578B\n * \u663E\u793A\u5355\u4E2A\u5546\u54C1\u7684\u8BE6\u7EC6\u4FE1\u606F\uFF08\u6A2A\u5411\u5E03\u5C40\uFF09\n * \u57FA\u4E8E specs/livechat-widget/data-model.md \u7684\u5546\u54C1\u6570\u636E\u6A21\u578B\n */\n\nimport React from 'react'\nimport type { MessageRenderer, ProductCardContent, Product, CommonText } from '../../types'\nimport { formatCurrency, DEFAULT_COMMON_TEXT } from '../../constants.js'\n\n/**\n * \u683C\u5F0F\u5316\u4EF7\u683C\n */\nfunction formatPrice(price: Product['price']): string {\n const { amount, currency } = price\n return formatCurrency(amount, currency)\n}\n\n/**\n * \u683C\u5F0F\u5316\u6298\u6263\u6807\u7B7E\u6587\u672C\n * @param discount \u6298\u6263\u5BF9\u8C61\n * @param currency \u8D27\u5E01\u4EE3\u7801\n * @param offText \"OFF\" \u6587\u6848\n * @returns \u683C\u5F0F\u5316\u540E\u7684\u6298\u6263\u6587\u672C\uFF08\u5982 \"$10 OFF\" \u6216 \"20% OFF\"\uFF09\n */\nfunction formatDiscountLabel(\n discount: { discount_type?: string; discount_value?: string | number },\n currency: string,\n offText: string = DEFAULT_COMMON_TEXT.off\n): string {\n if (!discount.discount_type || discount.discount_value === undefined) {\n return ''\n }\n\n // \u5C06 discount_value \u8F6C\u6362\u4E3A\u6570\u5B57\n const value =\n typeof discount.discount_value === 'string' ? parseFloat(discount.discount_value) : discount.discount_value\n\n if (isNaN(value)) {\n return ''\n }\n\n if (discount.discount_type === 'percentage') {\n return `${Math.round(value)}% ${offText}`\n }\n\n if (discount.discount_type === 'fixed_amount') {\n return `${formatCurrency(Math.round(value), currency)} ${offText}`\n }\n\n return ''\n}\n\n/**\n * \u7D27\u51D1\u578B\u5546\u54C1\u5361\u7247\u7EC4\u4EF6\uFF08\u6A2A\u5411\u5E03\u5C40\uFF09\n */\nconst CompactProductCard: React.FC<{\n product: Product\n onAddToCart?: (product: Product) => void\n addToCartText?: string\n offText?: string\n}> = ({ product, onAddToCart, addToCartText = DEFAULT_COMMON_TEXT.addToCart, offText = DEFAULT_COMMON_TEXT.off }) => {\n const { title, description, price, imageUrl, stockStatus, averageRating, variants } = product\n\n const isOutOfStock = stockStatus === 'out_of_stock'\n\n // \u83B7\u53D6\u7B2C\u4E00\u4E2A\u53D8\u4F53\u7684\u6298\u6263\u4FE1\u606F\n const firstVariant = variants?.[0]\n const hasDiscount = firstVariant?.discount?.has_discount\n const discountPrice = hasDiscount ? firstVariant?.discount?.discount_price : null\n const discount = firstVariant?.discount\n\n // \u5F53\u524D\u663E\u793A\u4EF7\u683C\uFF1A\u6709\u6298\u6263\u65F6\u663E\u793A\u6298\u6263\u4EF7\uFF0C\u5426\u5219\u663E\u793A\u539F\u4EF7\n const currentPrice = discountPrice ? { amount: discountPrice, currency: price.currency } : price\n\n // \u683C\u5F0F\u5316\u6298\u6263\u6807\u7B7E\n const discountLabel = discount && hasDiscount ? formatDiscountLabel(discount, price.currency, offText) : ''\n\n const handleAddToCart = (e: React.MouseEvent) => {\n e.preventDefault()\n e.stopPropagation()\n if (onAddToCart) {\n onAddToCart(product)\n }\n }\n\n return (\n <div className=\"block w-full overflow-hidden rounded-2xl bg-[#F5F6F7] transition-shadow mb-[32px]\">\n <div className=\"block\">\n <div className=\"flex gap-2 p-4 bg-white\">\n {/* \u5546\u54C1\u56FE\u7247 */}\n <div className=\" flex shrink-0 items-center overflow-hidden rounded-md \" style={{ width: '40%' }}>\n <img\n src={imageUrl}\n alt={title}\n className={`h-auto w-full object-cover ${isOutOfStock ? 'opacity-50' : ''}`}\n loading=\"lazy\"\n />\n </div>\n\n {/* \u5546\u54C1\u4FE1\u606F */}\n <div className=\"flex flex-1 flex-col justify-center\">\n {/* \u6298\u6263\u6807\u7B7E */}\n {discountLabel && (\n <div\n className=\"livechat-tag-product mb-1 w-fit rounded-full px-2 text-sm font-bold leading-none tracking-[-0.04em] text-white\"\n style={{ backgroundColor: '#005D8E', paddingTop: '6px', paddingBottom: '4px' }}\n >\n {discountLabel}\n </div>\n )}\n\n {/* \u6807\u9898 */}\n <h4 className=\"line-clamp-2 text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]\">\n {title}\n </h4>\n\n {/* \u63CF\u8FF0\uFF08\u53EF\u9009\uFF09 */}\n {/* {description && (\n <p className=\"line-clamp-2 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#1D1D1F]\">\n {description}\n </p>\n )} */}\n\n {/* \u4EF7\u683C\u548C\u8BC4\u5206 */}\n <div className=\"mt-4 flex items-center gap-2\">\n <div className=\"flex items-center gap-1\">\n {/* \u5F53\u524D\u4EF7\u683C\uFF08\u6709\u6298\u6263\u65F6\u663E\u793A\u6298\u6263\u4EF7\uFF0C\u5426\u5219\u663E\u793A\u539F\u4EF7\uFF09 */}\n <span className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#1D1D1F]\">\n {formatPrice(currentPrice)}\n </span>\n {/* \u539F\u4EF7\uFF08\u5212\u7EBF\u4EF7\uFF09- \u4EC5\u5728\u6709\u6298\u6263\u65F6\u663E\u793A */}\n {hasDiscount && (\n <span className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#6D6D6F] line-through\">\n {formatPrice(price)}\n </span>\n )}\n </div>\n {/* \u8BC4\u5206\uFF08\u53EF\u9009\uFF09 */}\n {averageRating !== undefined && (\n <div className=\"flex items-center gap-0.5 text-xs text-gray-600\">\n <span className=\"text-yellow-500\">\u2B50</span>\n <span>{averageRating.toFixed(1)}</span>\n </div>\n )}\n </div>\n\n {/* Add to Cart \u6309\u94AE - \u5728\u4EF7\u683C\u4E0B\u65B9 */}\n <button\n type=\"button\"\n onClick={handleAddToCart}\n className=\"livechat-btn-primary mt-2 w-fit rounded-full px-[20px] py-[10px] text-center text-sm font-bold leading-[1.2] tracking-[-0.04em] text-white\"\n style={{ backgroundColor: '#1D1D1F' }}\n >\n {addToCartText}\n </button>\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * \u5546\u54C1\u5361\u7247\u6E32\u67D3\u5668\n *\n * \u529F\u80FD\uFF1A\n * - \u7D27\u51D1\u578B\u6A2A\u5411\u5E03\u5C40\uFF08\u56FE\u7247\u5728\u5DE6\uFF0C\u4FE1\u606F\u5728\u53F3\uFF09\n * - \u663E\u793A\u6298\u6263\u6807\u7B7E\u3001\u4EF7\u683C\u3001\u8BC4\u5206\n * - \u652F\u6301 Add to Cart \u6309\u94AE\n * - \u72EC\u7ACB\u6210\u6BB5\u5C55\u793A\n * - \u652F\u6301\u81EA\u5B9A\u4E49\u6E32\u67D3\u51FD\u6570\n *\n * \u5E03\u5C40\uFF1A\n * ```\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 [\u56FE\u7247] \u6298\u6263\u6807\u7B7E \u2502\n * \u2502 \u5546\u54C1\u6807\u9898 \u2502\n * \u2502 $29.99 (\u539F\u4EF7) \u2502\n * \u2502 \u2B50 4.5 \u2502\n * \u2502 [Add to Cart] \u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * ```\n *\n * @example\n * ```tsx\n * const content: ProductCardContent = {\n * type: 'product_card',\n * data: {\n * product: { ... },\n * onAddToCart: (product) => { ... },\n * productCardRender: (product, productHandle) => <CustomCard product={product} handle={productHandle} />\n * }\n * }\n * <ProductCard.render(content, false, false) />\n * ```\n */\nexport const ProductCard: MessageRenderer = {\n render: (content, isUser, isSystem) => {\n const productContent = content as ProductCardContent\n const { product, rawProduct, productHandle, onAddToCart, productCardRender } = productContent.data\n\n console.log('[ProductCard] \u6E32\u67D3\u4EA7\u54C1\u5361\u7247:', {\n productHandle,\n hasProduct: !!product,\n hasRawProduct: !!rawProduct,\n hasCustomRender: !!productCardRender,\n })\n\n // \u5982\u679C\u63D0\u4F9B\u4E86\u81EA\u5B9A\u4E49\u6E32\u67D3\u51FD\u6570\uFF0C\u4F7F\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3\uFF08\u4F20\u5165\u539F\u59CB\u540E\u7AEF\u6570\u636E\u548C productHandle\uFF09\n // \u5373\u4F7F product \u4E3A\u7A7A\uFF0C\u4E5F\u8C03\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3\u51FD\u6570\uFF0C\u8BA9\u5E94\u7528\u5C42\u53EF\u4EE5\u7528 productHandle \u67E5\u8BE2\u4EA7\u54C1\n if (productCardRender) {\n console.log('[ProductCard] \u4F7F\u7528\u81EA\u5B9A\u4E49\u6E32\u67D3, productHandle:', productHandle)\n return <>{productCardRender(rawProduct || product, productHandle)}</>\n }\n\n // \u9ED8\u8BA4\u6E32\u67D3\uFF1A\u5982\u679C\u6CA1\u6709\u4EA7\u54C1\u6570\u636E\u5219\u4E0D\u6E32\u67D3\n if (!product) {\n return null\n }\n\n return <CompactProductCard product={product} onAddToCart={onAddToCart} />\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["ProductCard_exports", "__export", "ProductCard", "__toCommonJS", "import_jsx_runtime", "import_constants", "formatPrice", "price", "amount", "currency", "formatDiscountLabel", "discount", "offText", "value", "CompactProductCard", "product", "onAddToCart", "addToCartText", "title", "description", "imageUrl", "stockStatus", "averageRating", "variants", "isOutOfStock", "firstVariant", "hasDiscount", "discountPrice", "currentPrice", "discountLabel", "handleAddToCart", "e", "content", "isUser", "isSystem", "productContent", "rawProduct", "productHandle", "productCardRender"]
7
7
  }