@anker-in/campaign-ui 0.2.11-beta.26 → 0.2.11-beta.28
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.
- package/dist/cjs/components/LiveChatWidget/LiveChatWidget.d.ts +1 -1
- package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js.map +1 -1
- package/dist/cjs/components/LiveChatWidget/api/chat.js +1 -1
- package/dist/cjs/components/LiveChatWidget/api/chat.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/ChatHeader.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/ChatHeader.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/utils/fetcher.js +1 -1
- package/dist/cjs/components/LiveChatWidget/utils/fetcher.js.map +2 -2
- package/dist/cjs/stories/LiveChatWidget.stories.js +4 -4
- package/dist/cjs/stories/LiveChatWidget.stories.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/LiveChatWidget.d.ts +1 -1
- package/dist/esm/components/LiveChatWidget/LiveChatWidget.js.map +1 -1
- package/dist/esm/components/LiveChatWidget/api/chat.js +2 -2
- package/dist/esm/components/LiveChatWidget/api/chat.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/ChatHeader.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/ChatHeader.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/ChatMessage.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/ChatMessage.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/ChatWindow.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/ChatWindow.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/utils/fetcher.js +1 -1
- package/dist/esm/components/LiveChatWidget/utils/fetcher.js.map +2 -2
- package/dist/esm/stories/LiveChatWidget.stories.js +5 -5
- package/dist/esm/stories/LiveChatWidget.stories.js.map +2 -2
- package/dist/index.d.mts +1305 -0
- package/dist/index.d.ts +1305 -0
- package/dist/index.js +26656 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +26641 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +1 -1
- package/src/components/LiveChatWidget/LiveChatWidget.tsx +1 -1
- package/src/components/LiveChatWidget/api/chat.ts +1 -1
- package/src/components/LiveChatWidget/components/ChatHeader.tsx +2 -3
- package/src/components/LiveChatWidget/components/ChatMessage.tsx +1 -1
- package/src/components/LiveChatWidget/components/ChatWindow.tsx +69 -20
- package/src/components/LiveChatWidget/components/MessageContent/CartCard.tsx +5 -5
- package/src/components/LiveChatWidget/components/MessageContent/QuickReplies.tsx +2 -2
- package/src/components/LiveChatWidget/components/MessageContent/TextBlock.tsx +1 -1
- package/src/components/LiveChatWidget/utils/fetcher.ts +1 -1
- package/src/stories/LiveChatWidget.stories.tsx +4 -4
- package/src/styles/livechat.css +8 -7
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var c=Object.create;var m=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var i=Object.getPrototypeOf,g=Object.prototype.hasOwnProperty;var N=(t,a)=>{for(var l in a)m(t,l,{get:a[l],enumerable:!0})},d=(t,a,l,s)=>{if(a&&typeof a=="object"||typeof a=="function")for(let o of b(a))!g.call(t,o)&&o!==l&&m(t,o,{get:()=>a[o],enumerable:!(s=x(a,o))||s.enumerable});return t};var f=(t,a,l)=>(l=t!=null?c(i(t)):{},d(a||!t||!t.__esModule?m(l,"default",{value:t,enumerable:!0}):l,t)),u=t=>d(m({},"__esModule",{value:!0}),t);var p={};N(p,{TextBlock:()=>h});module.exports=u(p);var n=require("react/jsx-runtime"),r=f(require("react-markdown"));const h={render:(t,a,l)=>{const s=t;return s.text?(0,n.jsx)("div",{className:"livechat-markdown text-
|
|
1
|
+
"use strict";var c=Object.create;var m=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var i=Object.getPrototypeOf,g=Object.prototype.hasOwnProperty;var N=(t,a)=>{for(var l in a)m(t,l,{get:a[l],enumerable:!0})},d=(t,a,l,s)=>{if(a&&typeof a=="object"||typeof a=="function")for(let o of b(a))!g.call(t,o)&&o!==l&&m(t,o,{get:()=>a[o],enumerable:!(s=x(a,o))||s.enumerable});return t};var f=(t,a,l)=>(l=t!=null?c(i(t)):{},d(a||!t||!t.__esModule?m(l,"default",{value:t,enumerable:!0}):l,t)),u=t=>d(m({},"__esModule",{value:!0}),t);var p={};N(p,{TextBlock:()=>h});module.exports=u(p);var n=require("react/jsx-runtime"),r=f(require("react-markdown"));const h={render:(t,a,l)=>{const s=t;return s.text?(0,n.jsx)("div",{className:"livechat-markdown text-base md:text-sm",children:(0,n.jsx)(r.default,{components:{a:({node:o,...e})=>(0,n.jsx)("a",{...e,className:`underline ${a?"text-blue-200 hover:text-blue-100":"text-blue-600 hover:text-blue-700"}`,target:"_blank",rel:"noopener noreferrer"}),code:({node:o,...e})=>e.inline?(0,n.jsx)("code",{...e,className:`rounded px-1.5 py-0.5 font-mono text-xs ${a?"bg-[#004A6E] text-white":"bg-gray-200 text-gray-800"}`}):(0,n.jsx)("code",{...e,className:`block overflow-x-auto rounded px-3 py-2 font-mono text-xs ${a?"bg-[#004A6E] text-white":"bg-gray-200 text-gray-800"}`}),p:({node:o,...e})=>(0,n.jsx)("p",{...e,className:"last:mb-0"}),ul:({node:o,...e})=>(0,n.jsx)("ul",{...e,className:"mb-2 ml-4 list-disc"}),ol:({node:o,...e})=>(0,n.jsx)("ol",{...e,className:"mb-2 ml-4 list-decimal"}),li:({node:o,...e})=>(0,n.jsx)("li",{...e,className:"mb-1"}),h1:({node:o,...e})=>(0,n.jsx)("h1",{...e,className:"mb-2 text-lg font-bold"}),h2:({node:o,...e})=>(0,n.jsx)("h2",{...e,className:"mb-2 text-base font-bold"}),h3:({node:o,...e})=>(0,n.jsx)("h3",{...e,className:"mb-1 text-sm font-bold"}),strong:({node:o,...e})=>(0,n.jsx)("strong",{...e,className:"font-semibold"}),em:({node:o,...e})=>(0,n.jsx)("em",{...e,className:"italic"})},children:s.text})}):null}};
|
|
2
2
|
//# sourceMappingURL=TextBlock.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/components/LiveChatWidget/components/MessageContent/TextBlock.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * \u6587\u672C\u6D88\u606F\u5185\u5BB9\u6E32\u67D3\u5668\n * \u652F\u6301 Markdown \u683C\u5F0F\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u6587\u672C\u6D88\u606F\u8BBE\u8BA1\n */\n\nimport React from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport type { MessageRenderer, TextContent } from '../../types'\n\n/**\n * \u6587\u672C\u6D88\u606F\u6E32\u67D3\u5668\n *\n * \u529F\u80FD\uFF1A\n * - \u652F\u6301 Markdown \u8BED\u6CD5\uFF08\u7C97\u4F53\u3001\u659C\u4F53\u3001\u94FE\u63A5\u3001\u5217\u8868\u7B49\uFF09\n * - \u5B89\u5168\u6E32\u67D3\uFF08React Markdown \u81EA\u52A8\u9632\u62A4 XSS\uFF09\n * - \u54CD\u5E94\u5F0F\u6587\u672C\u6837\u5F0F\n *\n * Markdown \u652F\u6301\uFF1A\n * - \u7C97\u4F53\uFF1A**text** \u6216 __text__\n * - \u659C\u4F53\uFF1A*text* \u6216 _text_\n * - \u94FE\u63A5\uFF1A[text](url)\n * - \u5217\u8868\uFF1A- item \u6216 1. item\n * - \u4EE3\u7801\uFF1A`code` \u6216 ```code block```\n *\n * @example\n * ```tsx\n * const content: TextContent = {\n * type: 'text',\n * text: '\u60A8\u597D\uFF01**\u8FD9\u662F\u7C97\u4F53**\uFF0C*\u8FD9\u662F\u659C\u4F53*\u3002'\n * }\n * <TextBlock.render(content, false, false) />\n * ```\n */\nexport const TextBlock: MessageRenderer = {\n render: (content, isUser, isSystem) => {\n const textContent = content as TextContent\n\n if (!textContent.text) {\n return null\n }\n\n return (\n <div className=\"livechat-markdown text-
|
|
5
|
-
"mappings": "0jBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,eAAAE,IAAA,eAAAC,EAAAH,GAgDc,IAAAI,EAAA,6BAzCdC,EAA0B,6BA2BnB,MAAMH,EAA6B,CACxC,OAAQ,CAACI,EAASC,EAAQC,IAAa,CACrC,MAAMC,EAAcH,EAEpB,OAAKG,EAAY,QAKf,OAAC,OAAI,UAAU,
|
|
4
|
+
"sourcesContent": ["/**\n * \u6587\u672C\u6D88\u606F\u5185\u5BB9\u6E32\u67D3\u5668\n * \u652F\u6301 Markdown \u683C\u5F0F\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u6587\u672C\u6D88\u606F\u8BBE\u8BA1\n */\n\nimport React from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport type { MessageRenderer, TextContent } from '../../types'\n\n/**\n * \u6587\u672C\u6D88\u606F\u6E32\u67D3\u5668\n *\n * \u529F\u80FD\uFF1A\n * - \u652F\u6301 Markdown \u8BED\u6CD5\uFF08\u7C97\u4F53\u3001\u659C\u4F53\u3001\u94FE\u63A5\u3001\u5217\u8868\u7B49\uFF09\n * - \u5B89\u5168\u6E32\u67D3\uFF08React Markdown \u81EA\u52A8\u9632\u62A4 XSS\uFF09\n * - \u54CD\u5E94\u5F0F\u6587\u672C\u6837\u5F0F\n *\n * Markdown \u652F\u6301\uFF1A\n * - \u7C97\u4F53\uFF1A**text** \u6216 __text__\n * - \u659C\u4F53\uFF1A*text* \u6216 _text_\n * - \u94FE\u63A5\uFF1A[text](url)\n * - \u5217\u8868\uFF1A- item \u6216 1. item\n * - \u4EE3\u7801\uFF1A`code` \u6216 ```code block```\n *\n * @example\n * ```tsx\n * const content: TextContent = {\n * type: 'text',\n * text: '\u60A8\u597D\uFF01**\u8FD9\u662F\u7C97\u4F53**\uFF0C*\u8FD9\u662F\u659C\u4F53*\u3002'\n * }\n * <TextBlock.render(content, false, false) />\n * ```\n */\nexport const TextBlock: MessageRenderer = {\n render: (content, isUser, isSystem) => {\n const textContent = content as TextContent\n\n if (!textContent.text) {\n return null\n }\n\n return (\n <div className=\"livechat-markdown text-base md:text-sm\">\n <ReactMarkdown\n components={{\n // \u81EA\u5B9A\u4E49\u94FE\u63A5\u6837\u5F0F\n a: ({ node, ...props }) => (\n <a\n {...props}\n className={`underline ${isUser ? 'text-blue-200 hover:text-blue-100' : 'text-blue-600 hover:text-blue-700'}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n />\n ),\n // \u81EA\u5B9A\u4E49\u4EE3\u7801\u5757\u6837\u5F0F\n code: ({ node, ...props }: any) =>\n props.inline ? (\n <code\n {...props}\n className={`rounded px-1.5 py-0.5 font-mono text-xs ${isUser ? 'bg-[#004A6E] text-white' : 'bg-gray-200 text-gray-800'}`}\n />\n ) : (\n <code\n {...props}\n className={`block overflow-x-auto rounded px-3 py-2 font-mono text-xs ${isUser ? 'bg-[#004A6E] text-white' : 'bg-gray-200 text-gray-800'}`}\n />\n ),\n // \u81EA\u5B9A\u4E49\u6BB5\u843D\u6837\u5F0F\n p: ({ node, ...props }) => <p {...props} className=\"last:mb-0\" />,\n // \u81EA\u5B9A\u4E49\u5217\u8868\u6837\u5F0F\n ul: ({ node, ...props }) => <ul {...props} className=\"mb-2 ml-4 list-disc\" />,\n ol: ({ node, ...props }) => <ol {...props} className=\"mb-2 ml-4 list-decimal\" />,\n li: ({ node, ...props }) => <li {...props} className=\"mb-1\" />,\n // \u81EA\u5B9A\u4E49\u6807\u9898\u6837\u5F0F\n h1: ({ node, ...props }) => <h1 {...props} className=\"mb-2 text-lg font-bold\" />,\n h2: ({ node, ...props }) => <h2 {...props} className=\"mb-2 text-base font-bold\" />,\n h3: ({ node, ...props }) => <h3 {...props} className=\"mb-1 text-sm font-bold\" />,\n // \u81EA\u5B9A\u4E49\u5F3A\u8C03\u6837\u5F0F\n strong: ({ node, ...props }) => <strong {...props} className=\"font-semibold\" />,\n em: ({ node, ...props }) => <em {...props} className=\"italic\" />,\n }}\n >\n {textContent.text}\n </ReactMarkdown>\n </div>\n )\n },\n}\n"],
|
|
5
|
+
"mappings": "0jBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,eAAAE,IAAA,eAAAC,EAAAH,GAgDc,IAAAI,EAAA,6BAzCdC,EAA0B,6BA2BnB,MAAMH,EAA6B,CACxC,OAAQ,CAACI,EAASC,EAAQC,IAAa,CACrC,MAAMC,EAAcH,EAEpB,OAAKG,EAAY,QAKf,OAAC,OAAI,UAAU,yCACb,mBAAC,EAAAC,QAAA,CACC,WAAY,CAEV,EAAG,CAAC,CAAE,KAAAC,EAAM,GAAGC,CAAM,OACnB,OAAC,KACE,GAAGA,EACJ,UAAW,aAAaL,EAAS,oCAAsC,mCAAmC,GAC1G,OAAO,SACP,IAAI,sBACN,EAGF,KAAM,CAAC,CAAE,KAAAI,EAAM,GAAGC,CAAM,IACtBA,EAAM,UACJ,OAAC,QACE,GAAGA,EACJ,UAAW,2CAA2CL,EAAS,0BAA4B,2BAA2B,GACxH,KAEA,OAAC,QACE,GAAGK,EACJ,UAAW,6DAA6DL,EAAS,0BAA4B,2BAA2B,GAC1I,EAGJ,EAAG,CAAC,CAAE,KAAAI,EAAM,GAAGC,CAAM,OAAM,OAAC,KAAG,GAAGA,EAAO,UAAU,YAAY,EAE/D,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,OAAM,OAAC,MAAI,GAAGA,EAAO,UAAU,sBAAsB,EAC3E,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,OAAM,OAAC,MAAI,GAAGA,EAAO,UAAU,yBAAyB,EAC9E,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,OAAM,OAAC,MAAI,GAAGA,EAAO,UAAU,OAAO,EAE5D,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,OAAM,OAAC,MAAI,GAAGA,EAAO,UAAU,yBAAyB,EAC9E,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,OAAM,OAAC,MAAI,GAAGA,EAAO,UAAU,2BAA2B,EAChF,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,OAAM,OAAC,MAAI,GAAGA,EAAO,UAAU,yBAAyB,EAE9E,OAAQ,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,OAAM,OAAC,UAAQ,GAAGA,EAAO,UAAU,gBAAgB,EAC7E,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,OAAM,OAAC,MAAI,GAAGA,EAAO,UAAU,SAAS,CAChE,EAEC,SAAAH,EAAY,KACf,EACF,EA9CO,IAgDX,CACF",
|
|
6
6
|
"names": ["TextBlock_exports", "__export", "TextBlock", "__toCommonJS", "import_jsx_runtime", "import_react_markdown", "content", "isUser", "isSystem", "textContent", "ReactMarkdown", "node", "props"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var o=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var
|
|
1
|
+
"use strict";var o=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var y=(t,e)=>{for(var r in e)o(t,r,{get:e[r],enumerable:!0})},m=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of T(e))!w.call(t,a)&&a!==r&&o(t,a,{get:()=>e[a],enumerable:!(n=f(e,a))||n.enumerable});return t};var C=t=>m(o({},"__esModule",{value:!0}),t);var k={};y(k,{fetcher:()=>b});module.exports=C(k);const P=async(t,e)=>{if(typeof window>"u"||!window.grecaptcha?.execute)return console.warn("[LiveChat Fetcher] reCAPTCHA not loaded"),!1;try{return await window.grecaptcha.execute(e,{action:t})}catch(r){return console.error("[LiveChat Fetcher] reCAPTCHA execution failed:",r),!1}};async function R(t,e,r="X-Recaptcha-Token"){const n=await P(t,e);return n?{[r]:n}:{}}const b=async({url:t,method:e="POST",headers:r={},body:n,timeout:a=9e4,needRecaptcha:g=!1,recaptchaSitekey:s,recaptchaAction:p="",recaptchaHeaderKey:u="X-Recaptcha-Token"})=>{let d={};g&&(s?d=await R(p,s,u):console.warn("[LiveChat Fetcher] needRecaptcha=true but recaptchaSitekey is missing"));const h=n?JSON.stringify(n):void 0,l=new AbortController;let c;a&&(c=setTimeout(()=>l.abort(),a));try{const i=await fetch(t,{method:e,mode:"cors",headers:{"Content-Type":"application/json",...r,...d},signal:l.signal,...e!=="GET"&&h&&{body:h}});return c&&clearTimeout(c),i}catch(i){throw c&&clearTimeout(c),i}};
|
|
2
2
|
//# sourceMappingURL=fetcher.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/components/LiveChatWidget/utils/fetcher.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * LiveChat Fetcher\n * \u53C2\u7167 storefront \u7684\u5B9E\u73B0\uFF0C\u652F\u6301 reCAPTCHA\n */\n\n/**\n * \u6267\u884C Google reCAPTCHA \u9A8C\u8BC1\n */\nconst executeRecaptcha = async (action: string, sitekey: string): Promise<string | false> => {\n if (typeof window === 'undefined' || !window.grecaptcha?.execute) {\n console.warn('[LiveChat Fetcher] reCAPTCHA not loaded')\n return false\n }\n\n try {\n const token = await window.grecaptcha.execute(sitekey, { action })\n return token\n } catch (error) {\n console.error('[LiveChat Fetcher] reCAPTCHA execution failed:', error)\n return false\n }\n}\n\n/**\n * \u83B7\u53D6 reCAPTCHA headers\n */\nasync function getRecaptchaHeaders(\n action: string,\n sitekey: string,\n headerKey = 'X-Recaptcha-Token'\n): Promise<Record<string, string>> {\n const recaptchaToken = await executeRecaptcha(action, sitekey)\n if (!recaptchaToken) {\n return {}\n }\n return {\n [headerKey]: recaptchaToken,\n }\n}\n\n/**\n * Fetcher \u53C2\u6570\n */\nexport interface FetcherOptions {\n url: string\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'\n headers?: Record<string, string>\n body?: any\n timeout?: number\n needRecaptcha?: boolean\n recaptchaSitekey?: string\n recaptchaAction?: string\n recaptchaHeaderKey?: string\n}\n\n/**\n * Fetcher \u51FD\u6570\n * \u652F\u6301 reCAPTCHA \u548C\u81EA\u5B9A\u4E49 headers\n */\nexport const fetcher = async ({\n url,\n method = 'POST',\n headers = {},\n body,\n timeout = 90000,\n needRecaptcha = false,\n recaptchaSitekey,\n recaptchaAction = '
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,IAAA,eAAAC,EAAAH,GAQA,MAAMI,EAAmB,MAAOC,EAAgBC,IAA6C,CAC3F,GAAI,OAAO,OAAW,KAAe,CAAC,OAAO,YAAY,QACvD,eAAQ,KAAK,yCAAyC,EAC/C,GAGT,GAAI,CAEF,OADc,MAAM,OAAO,WAAW,QAAQA,EAAS,CAAE,OAAAD,CAAO,CAAC,CAEnE,OAASE,EAAO,CACd,eAAQ,MAAM,iDAAkDA,CAAK,EAC9D,EACT,CACF,EAKA,eAAeC,EACbH,EACAC,EACAG,EAAY,oBACqB,CACjC,MAAMC,EAAiB,MAAMN,EAAiBC,EAAQC,CAAO,EAC7D,OAAKI,EAGE,CACL,CAACD,CAAS,EAAGC,CACf,EAJS,CAAC,CAKZ,CAqBO,MAAMR,EAAU,MAAO,CAC5B,IAAAS,EACA,OAAAC,EAAS,OACT,QAAAC,EAAU,CAAC,EACX,KAAAC,EACA,QAAAC,EAAU,IACV,cAAAC,EAAgB,GAChB,iBAAAC,EACA,gBAAAC,EAAkB,
|
|
4
|
+
"sourcesContent": ["/**\n * LiveChat Fetcher\n * \u53C2\u7167 storefront \u7684\u5B9E\u73B0\uFF0C\u652F\u6301 reCAPTCHA\n */\n\n/**\n * \u6267\u884C Google reCAPTCHA \u9A8C\u8BC1\n */\nconst executeRecaptcha = async (action: string, sitekey: string): Promise<string | false> => {\n if (typeof window === 'undefined' || !window.grecaptcha?.execute) {\n console.warn('[LiveChat Fetcher] reCAPTCHA not loaded')\n return false\n }\n\n try {\n const token = await window.grecaptcha.execute(sitekey, { action })\n return token\n } catch (error) {\n console.error('[LiveChat Fetcher] reCAPTCHA execution failed:', error)\n return false\n }\n}\n\n/**\n * \u83B7\u53D6 reCAPTCHA headers\n */\nasync function getRecaptchaHeaders(\n action: string,\n sitekey: string,\n headerKey = 'X-Recaptcha-Token'\n): Promise<Record<string, string>> {\n const recaptchaToken = await executeRecaptcha(action, sitekey)\n if (!recaptchaToken) {\n return {}\n }\n return {\n [headerKey]: recaptchaToken,\n }\n}\n\n/**\n * Fetcher \u53C2\u6570\n */\nexport interface FetcherOptions {\n url: string\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'\n headers?: Record<string, string>\n body?: any\n timeout?: number\n needRecaptcha?: boolean\n recaptchaSitekey?: string\n recaptchaAction?: string\n recaptchaHeaderKey?: string\n}\n\n/**\n * Fetcher \u51FD\u6570\n * \u652F\u6301 reCAPTCHA \u548C\u81EA\u5B9A\u4E49 headers\n */\nexport const fetcher = async ({\n url,\n method = 'POST',\n headers = {},\n body,\n timeout = 90000,\n needRecaptcha = false,\n recaptchaSitekey,\n recaptchaAction = '',\n recaptchaHeaderKey = 'X-Recaptcha-Token',\n}: FetcherOptions): Promise<Response> => {\n // \u83B7\u53D6 reCAPTCHA headers\uFF08\u5982\u679C\u9700\u8981\uFF09\n let recaptchaHeaders: Record<string, string> = {}\n if (needRecaptcha) {\n if (!recaptchaSitekey) {\n console.warn('[LiveChat Fetcher] needRecaptcha=true but recaptchaSitekey is missing')\n } else {\n recaptchaHeaders = await getRecaptchaHeaders(recaptchaAction, recaptchaSitekey, recaptchaHeaderKey)\n }\n }\n\n // \u51C6\u5907\u8BF7\u6C42\u4F53\n const bodyData = body ? JSON.stringify(body) : undefined\n\n const controller = new AbortController()\n let timeoutTimer: NodeJS.Timeout | undefined\n if (timeout) {\n timeoutTimer = setTimeout(() => controller.abort(), timeout)\n }\n\n try {\n const response = await fetch(url, {\n method,\n mode: 'cors',\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n ...recaptchaHeaders,\n },\n signal: controller.signal,\n ...(method !== 'GET' && bodyData && { body: bodyData }),\n })\n\n if (timeoutTimer) {\n clearTimeout(timeoutTimer)\n }\n\n return response\n } catch (error) {\n if (timeoutTimer) {\n clearTimeout(timeoutTimer)\n }\n throw error\n }\n}\n\n/**\n * \u6269\u5C55 Window \u63A5\u53E3\u4EE5\u652F\u6301 grecaptcha\n */\ndeclare global {\n interface Window {\n grecaptcha?: {\n execute: (sitekey: string, options: { action: string }) => Promise<string>\n ready: (callback: () => void) => void\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,IAAA,eAAAC,EAAAH,GAQA,MAAMI,EAAmB,MAAOC,EAAgBC,IAA6C,CAC3F,GAAI,OAAO,OAAW,KAAe,CAAC,OAAO,YAAY,QACvD,eAAQ,KAAK,yCAAyC,EAC/C,GAGT,GAAI,CAEF,OADc,MAAM,OAAO,WAAW,QAAQA,EAAS,CAAE,OAAAD,CAAO,CAAC,CAEnE,OAASE,EAAO,CACd,eAAQ,MAAM,iDAAkDA,CAAK,EAC9D,EACT,CACF,EAKA,eAAeC,EACbH,EACAC,EACAG,EAAY,oBACqB,CACjC,MAAMC,EAAiB,MAAMN,EAAiBC,EAAQC,CAAO,EAC7D,OAAKI,EAGE,CACL,CAACD,CAAS,EAAGC,CACf,EAJS,CAAC,CAKZ,CAqBO,MAAMR,EAAU,MAAO,CAC5B,IAAAS,EACA,OAAAC,EAAS,OACT,QAAAC,EAAU,CAAC,EACX,KAAAC,EACA,QAAAC,EAAU,IACV,cAAAC,EAAgB,GAChB,iBAAAC,EACA,gBAAAC,EAAkB,GAClB,mBAAAC,EAAqB,mBACvB,IAAyC,CAEvC,IAAIC,EAA2C,CAAC,EAC5CJ,IACGC,EAGHG,EAAmB,MAAMZ,EAAoBU,EAAiBD,EAAkBE,CAAkB,EAFlG,QAAQ,KAAK,uEAAuE,GAOxF,MAAME,EAAWP,EAAO,KAAK,UAAUA,CAAI,EAAI,OAEzCQ,EAAa,IAAI,gBACvB,IAAIC,EACAR,IACFQ,EAAe,WAAW,IAAMD,EAAW,MAAM,EAAGP,CAAO,GAG7D,GAAI,CACF,MAAMS,EAAW,MAAM,MAAMb,EAAK,CAChC,OAAAC,EACA,KAAM,OACN,QAAS,CACP,eAAgB,mBAChB,GAAGC,EACH,GAAGO,CACL,EACA,OAAQE,EAAW,OACnB,GAAIV,IAAW,OAASS,GAAY,CAAE,KAAMA,CAAS,CACvD,CAAC,EAED,OAAIE,GACF,aAAaA,CAAY,EAGpBC,CACT,OAASjB,EAAO,CACd,MAAIgB,GACF,aAAaA,CAAY,EAErBhB,CACR,CACF",
|
|
6
6
|
"names": ["fetcher_exports", "__export", "fetcher", "__toCommonJS", "executeRecaptcha", "action", "sitekey", "error", "getRecaptchaHeaders", "headerKey", "recaptchaToken", "url", "method", "headers", "body", "timeout", "needRecaptcha", "recaptchaSitekey", "recaptchaAction", "recaptchaHeaderKey", "recaptchaHeaders", "bodyData", "controller", "timeoutTimer", "response"]
|
|
7
7
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var n=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var l=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var p=(t,e)=>{for(var r in e)n(t,r,{get:e[r],enumerable:!0})},m=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of l(e))!d.call(t,a)&&a!==r&&n(t,a,{get:()=>e[a],enumerable:!(s=c(e,a))||s.enumerable});return t};var g=t=>m(n({},"__esModule",{value:!0}),t);var R={};p(R,{CustomBranding:()=>A,CustomImageGalleryRenderer:()=>w,CustomPosition:()=>C,CustomVideoRenderer:()=>b,CustomWelcomeMessage:()=>f,Default:()=>h,DefaultPositionDemo:()=>I,FAQListMessage:()=>L,MobileView:()=>x,ProductComparisonMessage:()=>U,TabletView:()=>k,WithEventCallbacks:()=>S,WithQuickReplies:()=>v,WithRecaptcha:()=>M,default:()=>y});module.exports=g(R);var o=require("react/jsx-runtime"),i=require("../components/LiveChatWidget"),T=require("../styles/livechat.css");const u={title:"Campaign/LiveChatWidget",component:i.LiveChatWidget,parameters:{layout:"fullscreen",docs:{story:{inline:!1,iframeHeight:500},description:{component:`
|
|
2
2
|
# LiveChat \u804A\u5929\u7EC4\u4EF6
|
|
3
3
|
|
|
4
4
|
\u53EF\u590D\u7528\u7684\u6C14\u6CE1\u5F39\u7A97\u804A\u5929\u7EC4\u4EF6\uFF0C\u652F\u6301 SSE \u6D41\u5F0F\u6D88\u606F\u3001\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\u548C\u591A\u79CD\u6D88\u606F\u7C7B\u578B\u3002
|
|
@@ -47,7 +47,7 @@ const customRenderers = {
|
|
|
47
47
|
customRenderers={customRenderers}
|
|
48
48
|
/>
|
|
49
49
|
\`\`\`
|
|
50
|
-
`}}},tags:["autodocs"],argTypes:{apiBaseUrl:{control:"text",description:"API \u57FA\u7840 URL"},headers:{control:"object",description:"\u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\uFF0C\u5C06\u5728\u6240\u6709 API \u8BF7\u6C42\u4E2D\u6DFB\u52A0",table:{defaultValue:{summary:"undefined"}}},needRecaptcha:{control:"boolean",description:"\u662F\u5426\u542F\u7528 Google reCAPTCHA v3 \u9A8C\u8BC1",table:{defaultValue:{summary:"false"}}},recaptchaSitekey:{control:"text",description:"Google reCAPTCHA v3 site key",table:{defaultValue:{summary:"undefined"}}},recaptchaAction:{control:"text",description:"reCAPTCHA action \u540D\u79F0\uFF0C\u7528\u4E8E\u533A\u5206\u4E0D\u540C\u7684\u9A8C\u8BC1\u573A\u666F",table:{defaultValue:{summary:'"activity"'}}},site:{control:"text",description:"Shopify \u5E97\u94FA URL"},welcomeMessage:{control:"text",description:"\u6B22\u8FCE\u6D88\u606F",table:{defaultValue:{summary:"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F"}}},logoUrl:{control:"text",description:"Logo URL"},position:{control:"object",description:"\u6C14\u6CE1\u6309\u94AE\u4F4D\u7F6E\u5BF9\u8C61",table:{defaultValue:{summary:'{ bottom: "1.5rem", right: "1.5rem" }'}}}},args:{apiBaseUrl:"http://172.16.38.183:3003",site:"www.eufy.com",loginUserId:"test_test",welcomeMessage:"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F"}};var y=u;const h={args:{loginUserId:"test_test",apiBaseUrl:"
|
|
50
|
+
`}}},tags:["autodocs"],argTypes:{apiBaseUrl:{control:"text",description:"API \u57FA\u7840 URL"},headers:{control:"object",description:"\u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\uFF0C\u5C06\u5728\u6240\u6709 API \u8BF7\u6C42\u4E2D\u6DFB\u52A0",table:{defaultValue:{summary:"undefined"}}},needRecaptcha:{control:"boolean",description:"\u662F\u5426\u542F\u7528 Google reCAPTCHA v3 \u9A8C\u8BC1",table:{defaultValue:{summary:"false"}}},recaptchaSitekey:{control:"text",description:"Google reCAPTCHA v3 site key",table:{defaultValue:{summary:"undefined"}}},recaptchaAction:{control:"text",description:"reCAPTCHA action \u540D\u79F0\uFF0C\u7528\u4E8E\u533A\u5206\u4E0D\u540C\u7684\u9A8C\u8BC1\u573A\u666F",table:{defaultValue:{summary:'"activity"'}}},site:{control:"text",description:"Shopify \u5E97\u94FA URL"},welcomeMessage:{control:"text",description:"\u6B22\u8FCE\u6D88\u606F",table:{defaultValue:{summary:"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F"}}},logoUrl:{control:"text",description:"Logo URL"},position:{control:"object",description:"\u6C14\u6CE1\u6309\u94AE\u4F4D\u7F6E\u5BF9\u8C61",table:{defaultValue:{summary:'{ bottom: "1.5rem", right: "1.5rem" }'}}}},args:{apiBaseUrl:"http://172.16.38.183:3003",site:"www.eufy.com",loginUserId:"test_test",welcomeMessage:"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F"}};var y=u;const h={args:{loginUserId:"test_test",apiBaseUrl:"http://172.16.38.183:3003",cartId:"gid://shopify/Cart/hWN7wB3Pa12gh78d8hPOAUBI?key=0e73db1d3fb5ac21da19099c45033253",accessToken:"47b1aa2c0797043f9baba39388029d70",title:"eufy",commonText:{learnMore:"Learn More"},site:"beta.eufy.com",needRecaptcha:!1,recaptchaSitekey:"6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14 999testtest",recaptchaAction:"chat"}},C={args:{position:{bottom:"1.5rem",left:"1.5rem"}}},A={args:{logoUrl:"https://images.unsplash.com/photo-1599305445671-ac291c95aaa9?w=100&h=100&fit=crop",welcomeMessage:"\u6B22\u8FCE\u6765\u5230 Soundcore\uFF01\u6211\u662F\u60A8\u7684\u4E13\u5C5E AI \u52A9\u624B \u{1F3A7}"}},v={args:{quickReplies:[{id:"1",label:"\u67E5\u8BE2\u4EF7\u683C",value:"\u6211\u60F3\u4E86\u89E3\u4EA7\u54C1\u4EF7\u683C",icon:"\u{1F4B0}"},{id:"2",label:"\u67E5\u8BE2\u7269\u6D41",value:"\u6211\u60F3\u67E5\u8BE2\u8BA2\u5355\u7269\u6D41",icon:"\u{1F4E6}"},{id:"3",label:"\u552E\u540E\u670D\u52A1",value:"\u6211\u9700\u8981\u552E\u540E\u5E2E\u52A9",icon:"\u{1F527}"},{id:"4",label:"\u4EA7\u54C1\u63A8\u8350",value:"\u8BF7\u63A8\u8350\u9002\u5408\u6211\u7684\u4EA7\u54C1",icon:"\u2B50"}]}},f={args:{welcomeMessage:`
|
|
51
51
|
\u{1F44B} \u60A8\u597D\uFF01\u6B22\u8FCE\u6765\u5230 Soundcore \u5B98\u65B9\u5546\u57CE\u3002
|
|
52
52
|
|
|
53
53
|
\u6211\u662F\u60A8\u7684 AI \u8D2D\u7269\u52A9\u624B\uFF0C\u53EF\u4EE5\u5E2E\u60A8\uFF1A
|
|
@@ -62,7 +62,7 @@ const customRenderers = {
|
|
|
62
62
|
\u4EF7\u683C: ${e.price.currency} ${e.price.amount}`)},onCart:(e,r)=>{console.log("\u{1F6D2} [Story] Cart button clicked!"),console.log("\u{1F4CB} Cart ID:",e),console.log("\u{1F517} Checkout URL:",r),alert(`\u8D2D\u7269\u8F66\u6309\u94AE\u88AB\u70B9\u51FB\uFF01
|
|
63
63
|
|
|
64
64
|
Cart ID: ${e}
|
|
65
|
-
Checkout URL: ${r||"\u65E0"}`)}})},b={args:{customRenderers:{video:{render:t=>{const e=t;return(0,o.jsxs)("div",{className:"w-full",children:[(0,o.jsx)("video",{src:e.url,controls:!0,className:"w-full rounded-lg",poster:e.poster,children:"\u60A8\u7684\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u89C6\u9891\u64AD\u653E"}),e.title&&(0,o.jsx)("p",{className:"mt-2 text-sm text-gray-600",children:e.title})]})}}}}},
|
|
65
|
+
Checkout URL: ${r||"\u65E0"}`)}})},b={args:{customRenderers:{video:{render:t=>{const e=t;return(0,o.jsxs)("div",{className:"w-full",children:[(0,o.jsx)("video",{src:e.url,controls:!0,className:"w-full rounded-lg",poster:e.poster,children:"\u60A8\u7684\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u89C6\u9891\u64AD\u653E"}),e.title&&(0,o.jsx)("p",{className:"mt-2 text-sm text-gray-600",children:e.title})]})}}}}},w={args:{customRenderers:{image_gallery:{render:t=>{const r=t.images||[];return(0,o.jsx)("div",{className:"grid grid-cols-2 gap-2",children:r.map((s,a)=>(0,o.jsx)("div",{className:"relative aspect-square",children:(0,o.jsx)("img",{src:s.url,alt:s.alt||`Image ${a+1}`,className:"size-full rounded-lg object-cover"})},a))})}}}}},x={parameters:{viewport:{defaultViewport:"mobile1"}},args:{}},k={parameters:{viewport:{defaultViewport:"tablet"}},args:{}},I={render:()=>(0,o.jsxs)("div",{className:"relative h-screen w-full bg-gray-50",children:[(0,o.jsx)("div",{className:"absolute inset-0 flex items-center justify-center",children:(0,o.jsx)("p",{className:"text-gray-500",children:"\u9875\u9762\u5185\u5BB9\u533A\u57DF"})}),(0,o.jsx)("div",{className:"absolute bottom-4 right-4 text-xs text-gray-400",children:"\u9ED8\u8BA4\u4F4D\u7F6E (\u53F3\u4E0B\u89D2)"}),(0,o.jsx)(i.LiveChatWidget,{apiBaseUrl:"https://beta-api-livechat.anker.com",site:"www.eufy.com"})]})},U={render:t=>(0,o.jsx)(i.LiveChatWidget,{...t,onAddToCart:e=>{console.log("\u{1F6D2} [ProductComparison Story] Add to Cart clicked!"),console.log("\u{1F4E6} Product Info:",{id:e.shopifyId,title:e.title,price:e.price,imageUrl:e.imageUrl,productUrl:e.productUrl}),console.log("\u{1F4CB} Full Product Object:",e),alert(`\u2705 "${e.title}" \u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\uFF01
|
|
66
66
|
|
|
67
67
|
\u4EF7\u683C: ${e.price.currency} ${e.price.amount}`)}}),args:{welcomeMessage:"\u4EE5\u4E0B\u662F\u60A8\u5173\u6CE8\u7684\u4EA7\u54C1\u5BF9\u6BD4\u4FE1\u606F\uFF1A"},parameters:{docs:{description:{story:`
|
|
68
68
|
\u4EA7\u54C1\u5BF9\u6BD4\u6D88\u606F\u652F\u6301\u4EE5\u4E0B\u7EF4\u5EA6\uFF1A
|
|
@@ -94,7 +94,7 @@ FAQ \u5217\u8868\u6D88\u606F\u652F\u6301\uFF1A
|
|
|
94
94
|
- \u7528\u6237\u8BE2\u95EE\u5E38\u89C1\u95EE\u9898\u65F6\u8FD4\u56DE FAQ \u5217\u8868
|
|
95
95
|
- \u4EA7\u54C1\u4E13\u5C5E\u95EE\u9898\u67E5\u8BE2
|
|
96
96
|
- \u6309\u5206\u7C7B\u67E5\u8BE2 FAQ
|
|
97
|
-
`}}}},
|
|
97
|
+
`}}}},M={args:{needRecaptcha:!0,recaptchaSitekey:"6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14",recaptchaAction:"livechat"},parameters:{docs:{description:{story:`
|
|
98
98
|
### reCAPTCHA v3 \u914D\u7F6E
|
|
99
99
|
|
|
100
100
|
\u542F\u7528 reCAPTCHA v3 \u53EF\u4EE5\u4FDD\u62A4\u4F60\u7684 API \u514D\u53D7\u673A\u5668\u4EBA\u548C\u6EE5\u7528\u884C\u4E3A\u7684\u653B\u51FB\u3002
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/stories/LiveChatWidget.stories.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * LiveChatWidget Storybook Stories\n * \u5C55\u793A LiveChat \u7EC4\u4EF6\u7684\u5404\u79CD\u4F7F\u7528\u573A\u666F\u548C\u914D\u7F6E\n */\n\nimport type { Meta, StoryObj } from '@storybook/react'\nimport { LiveChatWidget } from '../components/LiveChatWidget'\nimport type { MessageRenderer, MessageContent } from '../components/LiveChatWidget'\nimport '../styles/livechat.css'\n\nconst meta: Meta<typeof LiveChatWidget> = {\n title: 'Campaign/LiveChatWidget',\n component: LiveChatWidget,\n parameters: {\n layout: 'fullscreen',\n docs: {\n story: {\n inline: false,\n iframeHeight: 500,\n },\n description: {\n component: `\n# LiveChat \u804A\u5929\u7EC4\u4EF6\n\n\u53EF\u590D\u7528\u7684\u6C14\u6CE1\u5F39\u7A97\u804A\u5929\u7EC4\u4EF6\uFF0C\u652F\u6301 SSE \u6D41\u5F0F\u6D88\u606F\u3001\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\u548C\u591A\u79CD\u6D88\u606F\u7C7B\u578B\u3002\n\n## \u529F\u80FD\u7279\u6027\n\n- \uD83C\uDF88 **\u6C14\u6CE1\u5F39\u7A97**: \u53EF\u81EA\u5B9A\u4E49\u4F4D\u7F6E\u7684\u60AC\u6D6E\u6C14\u6CE1\u6309\u94AE\n- \uD83D\uDCAC **\u6D41\u5F0F\u6D88\u606F**: \u57FA\u4E8E SSE \u7684\u5B9E\u65F6\u6D41\u5F0F\u54CD\u5E94\n- \uD83D\uDCE6 **\u591A\u79CD\u6D88\u606F\u7C7B\u578B**: \u6587\u672C\u3001\u5546\u54C1\u5361\u7247\u3001\u5546\u54C1\u5217\u8868\u3001\u653F\u7B56\u3001\u5FEB\u6377\u56DE\u590D\u7B49\n- \uD83C\uDFA8 **\u53EF\u5B9A\u5236**: \u652F\u6301\u81EA\u5B9A\u4E49\u54C1\u724C\u989C\u8272\u3001Logo\u3001\u6E32\u67D3\u5668\n- \uD83D\uDCF1 **\u54CD\u5E94\u5F0F**: \u79FB\u52A8\u7AEF\u5168\u5C4F\uFF0C\u684C\u9762\u7AEF\u56FA\u5B9A\u5C3A\u5BF8\n- \uD83D\uDCBE **\u4F1A\u8BDD\u7BA1\u7406**: \u81EA\u52A8\u7BA1\u7406 userId \u548C sessionId\n- \uD83D\uDD12 **\u5B89\u5168\u9632\u62A4**: \u5185\u7F6E XSS \u9632\u62A4\u548C\u8F93\u5165\u9A8C\u8BC1\n\n## \u57FA\u7840\u7528\u6CD5\n\n\\`\\`\\`tsx\nimport { LiveChatWidget } from '@anker-in/campaign-ui'\nimport '@anker-in/campaign-ui/livechat.css'\n\nfunction App() {\n return (\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 />\n )\n}\n\\`\\`\\`\n\n## \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n\n\\`\\`\\`tsx\nconst customRenderers = {\n video: {\n render: (content) => (\n <video src={content.url} controls className=\"w-full rounded\" />\n )\n }\n}\n\n<LiveChatWidget\n apiBaseUrl=\"...\"\n site=\"...\"\n customRenderers={customRenderers}\n/>\n\\`\\`\\`\n `,\n },\n },\n },\n tags: ['autodocs'],\n argTypes: {\n apiBaseUrl: {\n control: 'text',\n description: 'API \u57FA\u7840 URL',\n },\n headers: {\n control: 'object',\n description: '\u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\uFF0C\u5C06\u5728\u6240\u6709 API \u8BF7\u6C42\u4E2D\u6DFB\u52A0',\n table: {\n defaultValue: { summary: 'undefined' },\n },\n },\n needRecaptcha: {\n control: 'boolean',\n description: '\u662F\u5426\u542F\u7528 Google reCAPTCHA v3 \u9A8C\u8BC1',\n table: {\n defaultValue: { summary: 'false' },\n },\n },\n recaptchaSitekey: {\n control: 'text',\n description: 'Google reCAPTCHA v3 site key',\n table: {\n defaultValue: { summary: 'undefined' },\n },\n },\n recaptchaAction: {\n control: 'text',\n description: 'reCAPTCHA action \u540D\u79F0\uFF0C\u7528\u4E8E\u533A\u5206\u4E0D\u540C\u7684\u9A8C\u8BC1\u573A\u666F',\n table: {\n defaultValue: { summary: '\"activity\"' },\n },\n },\n site: {\n control: 'text',\n description: 'Shopify \u5E97\u94FA URL',\n },\n welcomeMessage: {\n control: 'text',\n description: '\u6B22\u8FCE\u6D88\u606F',\n table: {\n defaultValue: { summary: '\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F' },\n },\n },\n logoUrl: {\n control: 'text',\n description: 'Logo URL',\n },\n position: {\n control: 'object',\n description: '\u6C14\u6CE1\u6309\u94AE\u4F4D\u7F6E\u5BF9\u8C61',\n table: {\n defaultValue: { summary: '{ bottom: \"1.5rem\", right: \"1.5rem\" }' },\n },\n },\n },\n args: {\n apiBaseUrl: 'http://172.16.38.183:3003',\n site: 'www.eufy.com',\n loginUserId: 'test_test',\n welcomeMessage: '\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F',\n },\n}\n\nexport default meta\ntype Story = StoryObj<typeof LiveChatWidget>\n\n/**\n * \u9ED8\u8BA4\u914D\u7F6E\n *\n * \u6700\u7B80\u5355\u7684\u4F7F\u7528\u65B9\u5F0F\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E\u3002\n */\nexport const Default: Story = {\n args: {\n loginUserId: 'test_test',\n apiBaseUrl: 'https://beta-api-v2-livechat.anker.com',\n cartId: 'gid://shopify/Cart/hWN7oFnMh7jKxbO1CYSzWVue?key=68f4cf2c4c772b9aed2fab2902e6dab3',\n accessToken: '92301d516a0a38a0a483bc230e5bfaad',\n title: 'eufy',\n\n commonText: {\n learnMore: 'Learn More',\n },\n\n site: 'beta.eufy.com',\n\n // reCAPTCHA \u914D\u7F6E\uFF08\u4F7F\u7528\u7EC4\u4EF6 props\uFF0C\u4E0D\u662F headers\uFF09\n needRecaptcha: true,\n recaptchaSitekey: '6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14 999testtest',\n recaptchaAction: 'chat',\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u4F4D\u7F6E\n *\n * \u4F7F\u7528\u81EA\u5B9A\u4E49\u4F4D\u7F6E\u5BF9\u8C61\u6765\u63A7\u5236\u6C14\u6CE1\u6309\u94AE\u7684\u4F4D\u7F6E\u3002\n */\nexport const CustomPosition: Story = {\n args: {\n position: { bottom: '1.5rem', left: '1.5rem' },\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u54C1\u724C\u6837\u5F0F\n *\n * \u81EA\u5B9A\u4E49 Logo \u548C\u6B22\u8FCE\u6D88\u606F\u3002\n */\nexport const CustomBranding: Story = {\n args: {\n logoUrl: 'https://images.unsplash.com/photo-1599305445671-ac291c95aaa9?w=100&h=100&fit=crop',\n welcomeMessage: '\u6B22\u8FCE\u6765\u5230 Soundcore\uFF01\u6211\u662F\u60A8\u7684\u4E13\u5C5E AI \u52A9\u624B \uD83C\uDFA7',\n },\n}\n\n/**\n * \u5E26\u5FEB\u6377\u56DE\u590D\n *\n * \u5728\u6B22\u8FCE\u6D88\u606F\u540E\u663E\u793A\u5FEB\u6377\u56DE\u590D\u6309\u94AE\u3002\n */\nexport const WithQuickReplies: Story = {\n args: {\n quickReplies: [\n {\n id: '1',\n label: '\u67E5\u8BE2\u4EF7\u683C',\n value: '\u6211\u60F3\u4E86\u89E3\u4EA7\u54C1\u4EF7\u683C',\n icon: '\uD83D\uDCB0',\n },\n {\n id: '2',\n label: '\u67E5\u8BE2\u7269\u6D41',\n value: '\u6211\u60F3\u67E5\u8BE2\u8BA2\u5355\u7269\u6D41',\n icon: '\uD83D\uDCE6',\n },\n {\n id: '3',\n label: '\u552E\u540E\u670D\u52A1',\n value: '\u6211\u9700\u8981\u552E\u540E\u5E2E\u52A9',\n icon: '\uD83D\uDD27',\n },\n {\n id: '4',\n label: '\u4EA7\u54C1\u63A8\u8350',\n value: '\u8BF7\u63A8\u8350\u9002\u5408\u6211\u7684\u4EA7\u54C1',\n icon: '\u2B50',\n },\n ],\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u6B22\u8FCE\u6D88\u606F\n *\n * \u81EA\u5B9A\u4E49\u9996\u6B21\u6253\u5F00\u804A\u5929\u7A97\u53E3\u65F6\u7684\u6B22\u8FCE\u6D88\u606F\u3002\n */\nexport const CustomWelcomeMessage: Story = {\n args: {\n welcomeMessage: `\n\uD83D\uDC4B \u60A8\u597D\uFF01\u6B22\u8FCE\u6765\u5230 Soundcore \u5B98\u65B9\u5546\u57CE\u3002\n\n\u6211\u662F\u60A8\u7684 AI \u8D2D\u7269\u52A9\u624B\uFF0C\u53EF\u4EE5\u5E2E\u60A8\uFF1A\n- \uD83D\uDD0D \u67E5\u627E\u4EA7\u54C1\n- \uD83D\uDCB0 \u6BD4\u8F83\u4EF7\u683C\n- \uD83D\uDCE6 \u8DDF\u8E2A\u8BA2\u5355\n- \uD83D\uDCAC \u89E3\u7B54\u7591\u95EE\n\n\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u52A9\u60A8\u7684\u5417\uFF1F\n `.trim(),\n },\n}\n\n/**\n * \u4E8B\u4EF6\u56DE\u8C03\n *\n * \u76D1\u542C\u7EC4\u4EF6\u4E8B\u4EF6\u8FDB\u884C\u81EA\u5B9A\u4E49\u5904\u7406\u3002\n */\nexport const WithEventCallbacks: Story = {\n args: {\n title: '',\n showNewSessionButton: true,\n\n position: {\n bottom: '24px',\n right: '30px',\n },\n\n chatBubbleIcon: 'https://cdn.shopify.com/s/files/1/0504/7094/4954/files/Rectangle_400770314.png?v=1768894153',\n\n // \u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\u793A\u4F8B\n headers: {\n 'X-Custom-Header': 'custom-value',\n Authorization: 'Bearer your-token-here',\n },\n },\n\n render: args => (\n <LiveChatWidget\n {...args}\n onOpen={() => {\n console.log('[Story] Chat opened')\n }}\n onClose={() => {\n console.log('[Story] Chat closed')\n }}\n onMessageSend={(message: string) => {\n console.log('[Story] Message sent:', message)\n }}\n onError={(error: Error) => {\n console.error('[Story] Error occurred:', error)\n }}\n onTextMessage={() => {\n console.log('\uD83D\uDCDD [Story] AI started replying with text message')\n }}\n onProductList={() => {\n console.log('\uD83D\uDCE6 [Story] AI replied with product list card')\n }}\n onPromotionList={() => {\n console.log('\uD83C\uDF89 [Story] AI replied with promotion list card')\n }}\n onAddToCart={(product: any) => {\n console.log('\uD83D\uDED2 [Story] Add to Cart clicked!')\n console.log('\uD83D\uDCE6 Product Info:', {\n id: product.shopifyId,\n title: product.title,\n price: product.price,\n imageUrl: product.imageUrl,\n productUrl: product.productUrl,\n })\n console.log('\uD83D\uDCCB Full Product Object:', product)\n\n // \u6A21\u62DF\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u6210\u529F\n alert(`\u2705 \"${product.title}\" \u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\uFF01\\n\\n\u4EF7\u683C: ${product.price.currency} ${product.price.amount}`)\n }}\n onCart={(cartId: string, checkoutUrl?: string) => {\n console.log('\uD83D\uDED2 [Story] Cart button clicked!')\n console.log('\uD83D\uDCCB Cart ID:', cartId)\n console.log('\uD83D\uDD17 Checkout URL:', checkoutUrl)\n\n // \u6A21\u62DF\u8D2D\u7269\u8F66\u6309\u94AE\u70B9\u51FB\u6210\u529F\n alert(`\u8D2D\u7269\u8F66\u6309\u94AE\u88AB\u70B9\u51FB\uFF01\\n\\nCart ID: ${cartId}\\nCheckout URL: ${checkoutUrl || '\u65E0'}`)\n }}\n />\n ),\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u89C6\u9891\u6D88\u606F\u6E32\u67D3\u5668\n *\n * \u6269\u5C55\u6D88\u606F\u7C7B\u578B\uFF0C\u6DFB\u52A0\u89C6\u9891\u6D88\u606F\u652F\u6301\u3002\n */\nexport const CustomVideoRenderer: Story = {\n args: {\n customRenderers: {\n video: {\n render: (content: MessageContent) => {\n const videoContent = content as any\n return (\n <div className=\"w-full\">\n <video src={videoContent.url} controls className=\"w-full rounded-lg\" poster={videoContent.poster}>\n \u60A8\u7684\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u89C6\u9891\u64AD\u653E\n </video>\n {videoContent.title && <p className=\"mt-2 text-sm text-gray-600\">{videoContent.title}</p>}\n </div>\n )\n },\n } as MessageRenderer,\n },\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u56FE\u7247\u753B\u5ECA\u6E32\u67D3\u5668\n *\n * \u6269\u5C55\u6D88\u606F\u7C7B\u578B\uFF0C\u6DFB\u52A0\u56FE\u7247\u753B\u5ECA\u652F\u6301\u3002\n */\nexport const CustomImageGalleryRenderer: Story = {\n args: {\n customRenderers: {\n image_gallery: {\n render: (content: MessageContent) => {\n const galleryContent = content as any\n const images = galleryContent.images || []\n\n return (\n <div className=\"grid grid-cols-2 gap-2\">\n {images.map((image: any, index: number) => (\n <div key={index} className=\"relative aspect-square\">\n <img\n src={image.url}\n alt={image.alt || `Image ${index + 1}`}\n className=\"size-full rounded-lg object-cover\"\n />\n </div>\n ))}\n </div>\n )\n },\n } as MessageRenderer,\n },\n },\n}\n\n/**\n * \u79FB\u52A8\u7AEF\u9884\u89C8\n *\n * \u5728\u79FB\u52A8\u7AEF\u5C3A\u5BF8\u4E0B\u67E5\u770B\u6548\u679C\uFF08\u5168\u5C4F\u663E\u793A\uFF09\u3002\n */\nexport const MobileView: Story = {\n parameters: {\n viewport: {\n defaultViewport: 'mobile1',\n },\n },\n args: {},\n}\n\n/**\n * \u5E73\u677F\u7AEF\u9884\u89C8\n *\n * \u5728\u5E73\u677F\u7AEF\u5C3A\u5BF8\u4E0B\u67E5\u770B\u6548\u679C\u3002\n */\nexport const TabletView: Story = {\n parameters: {\n viewport: {\n defaultViewport: 'tablet',\n },\n },\n args: {},\n}\n\n/**\n * \u9ED8\u8BA4\u4F4D\u7F6E\u5C55\u793A\n *\n * \u5C55\u793A\u6C14\u6CE1\u6309\u94AE\u7684\u9ED8\u8BA4\u4F4D\u7F6E\uFF08\u53F3\u4E0B\u89D2\uFF09\u3002\n */\nexport const DefaultPositionDemo: Story = {\n render: () => (\n <div className=\"relative h-screen w-full bg-gray-50\">\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <p className=\"text-gray-500\">\u9875\u9762\u5185\u5BB9\u533A\u57DF</p>\n </div>\n\n {/* \u6F14\u793A\u9ED8\u8BA4\u4F4D\u7F6E */}\n <div className=\"absolute bottom-4 right-4 text-xs text-gray-400\">\u9ED8\u8BA4\u4F4D\u7F6E (\u53F3\u4E0B\u89D2)</div>\n\n <LiveChatWidget apiBaseUrl=\"https://beta-api-livechat.anker.com\" site=\"www.eufy.com\" />\n </div>\n ),\n}\n\n/**\n * \u4EA7\u54C1\u5BF9\u6BD4\u6D88\u606F\n *\n * \u5C55\u793A\u4EA7\u54C1\u5BF9\u6BD4\u7C7B\u578B\u7684\u6D88\u606F\u6E32\u67D3\u6548\u679C\uFF0C\u5305\u62EC\u4EF7\u683C\u3001\u4F1A\u5458\u4EF7\u3001\u53D8\u4F53\u6570\u91CF\u3001\u6298\u6263\u7B49\u591A\u4E2A\u7EF4\u5EA6\u7684\u5BF9\u6BD4\u3002\n *\n * \u4EA7\u54C1\u5BF9\u6BD4\u7EC4\u4EF6\u4F1A\u4EE5\u7F51\u683C\u5E03\u5C40\u5C55\u793A\u591A\u4E2A\u4EA7\u54C1\u7684\u57FA\u672C\u4FE1\u606F\uFF0C\u5E76\u5728\u4E0B\u65B9\u663E\u793A\u5404\u7EF4\u5EA6\u7684\u5BF9\u6BD4\u6570\u636E\u3002\n */\nexport const ProductComparisonMessage: Story = {\n render: args => (\n <LiveChatWidget\n {...args}\n onAddToCart={(product: any) => {\n console.log('\uD83D\uDED2 [ProductComparison Story] Add to Cart clicked!')\n console.log('\uD83D\uDCE6 Product Info:', {\n id: product.shopifyId,\n title: product.title,\n price: product.price,\n imageUrl: product.imageUrl,\n productUrl: product.productUrl,\n })\n console.log('\uD83D\uDCCB Full Product Object:', product)\n\n // \u6A21\u62DF\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u6210\u529F\n alert(`\u2705 \"${product.title}\" \u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\uFF01\\n\\n\u4EF7\u683C: ${product.price.currency} ${product.price.amount}`)\n }}\n />\n ),\n args: {\n welcomeMessage: '\u4EE5\u4E0B\u662F\u60A8\u5173\u6CE8\u7684\u4EA7\u54C1\u5BF9\u6BD4\u4FE1\u606F\uFF1A',\n },\n parameters: {\n docs: {\n description: {\n story: `\n\u4EA7\u54C1\u5BF9\u6BD4\u6D88\u606F\u652F\u6301\u4EE5\u4E0B\u7EF4\u5EA6\uFF1A\n\n- **\u4EF7\u683C\u5BF9\u6BD4**: \u663E\u793A\u4EA7\u54C1\u7684\u4EF7\u683C\u533A\u95F4\u548C\u4FC3\u9500\u6807\u7B7E\n- **\u4F1A\u5458\u4EF7\u5BF9\u6BD4**: \u663E\u793A\u4F1A\u5458\u4E13\u4EAB\u4EF7\u683C\uFF08\u5982\u679C\u53EF\u7528\uFF09\n- **\u53D8\u4F53\u6570\u91CF\u5BF9\u6BD4**: \u663E\u793A\u4EA7\u54C1\u7684\u53EF\u9009\u53D8\u4F53\u6570\u91CF\n- **\u6298\u6263\u5BF9\u6BD4**: \u663E\u793A\u4EA7\u54C1\u662F\u5426\u6709\u6298\u6263\u6D3B\u52A8\n\n\u7EC4\u4EF6\u4F1A\u81EA\u52A8\u5904\u7406\u4ECE\u540E\u7AEF\u8FD4\u56DE\u7684\u4EA7\u54C1\u5BF9\u6BD4\u6570\u636E\uFF0C\u5305\u62EC\uFF1A\n- \u4EA7\u54C1\u57FA\u672C\u4FE1\u606F\uFF08\u56FE\u7247\u3001\u6807\u9898\u3001\u4EF7\u683C\u7B49\uFF09\n- \u591A\u7EF4\u5EA6\u5BF9\u6BD4\u6570\u636E\uFF08\u4EF7\u683C\u3001\u4F1A\u5458\u4EF7\u3001\u53D8\u4F53\u3001\u6298\u6263\uFF09\n- \u54CD\u5E94\u5F0F\u5E03\u5C40\uFF08\u79FB\u52A8\u7AEF\u53EF\u6A2A\u5411\u6EDA\u52A8\uFF09\n\n**\u6D4B\u8BD5 Add to Cart \u529F\u80FD\uFF1A**\n- \u70B9\u51FB\u4EA7\u54C1\u5BF9\u6BD4\u5361\u7247\u4E2D\u7684 \"Add to Cart\" \u6309\u94AE\n- \u67E5\u770B\u63A7\u5236\u53F0\u8F93\u51FA\u7684\u4EA7\u54C1\u4FE1\u606F\n- \u4F1A\u5F39\u51FA\u786E\u8BA4\u63D0\u793A\u6846\u663E\u793A\u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\n `,\n },\n },\n },\n}\n\n/**\n * FAQ \u5217\u8868\u6D88\u606F\n *\n * \u5C55\u793A\u5E38\u89C1\u95EE\u9898\u5217\u8868\uFF0C\u652F\u6301\u6298\u53E0/\u5C55\u5F00\u3002\n *\n * FAQ \u7EC4\u4EF6\u4F1A\u663E\u793A\u641C\u7D22\u7ED3\u679C\u7684\u5E38\u89C1\u95EE\u9898\uFF0C\u6BCF\u4E2A\u95EE\u9898\u53EF\u4EE5\u70B9\u51FB\u5C55\u5F00\u67E5\u770B\u7B54\u6848\u3002\n */\nexport const FAQListMessage: Story = {\n args: {\n welcomeMessage: '\u4EE5\u4E0B\u662F\u4E0E\u60A8\u7684\u95EE\u9898\u76F8\u5173\u7684\u5E38\u89C1\u89E3\u7B54\uFF1A',\n },\n parameters: {\n docs: {\n description: {\n story: `\nFAQ \u5217\u8868\u6D88\u606F\u652F\u6301\uFF1A\n\n- **\u53EF\u6298\u53E0\u95EE\u9898**: \u70B9\u51FB\u95EE\u9898\u6807\u9898\u5C55\u5F00/\u6298\u53E0\u7B54\u6848\n- **Markdown \u7B54\u6848**: \u7B54\u6848\u652F\u6301 Markdown \u683C\u5F0F\uFF0C\u53EF\u663E\u793A\u5BCC\u6587\u672C\n- **\u76F8\u5173\u95EE\u9898**: \u5C55\u5F00\u7B54\u6848\u540E\u663E\u793A\u76F8\u5173\u95EE\u9898\uFF0C\u70B9\u51FB\u53EF\u89E6\u53D1\u65B0\u641C\u7D22\n- **\u5206\u7C7B\u6807\u8BB0**: \u6839\u636E\u95EE\u9898\u5206\u7C7B\u663E\u793A\uFF08\u914D\u9001\u3001\u9000\u8D27\u3001\u4EA7\u54C1\u3001\u652F\u4ED8\u3001\u901A\u7528\uFF09\n- **\u641C\u7D22\u7ED3\u679C\u7EDF\u8BA1**: \u663E\u793A\u627E\u5230\u7684\u95EE\u9898\u6570\u91CF\n\n\u4F7F\u7528\u573A\u666F\uFF1A\n- \u7528\u6237\u8BE2\u95EE\u5E38\u89C1\u95EE\u9898\u65F6\u8FD4\u56DE FAQ \u5217\u8868\n- \u4EA7\u54C1\u4E13\u5C5E\u95EE\u9898\u67E5\u8BE2\n- \u6309\u5206\u7C7B\u67E5\u8BE2 FAQ\n `,\n },\n },\n },\n}\n\n/**\n * \u5E26 reCAPTCHA \u914D\u7F6E\n *\n * \u5C55\u793A\u5982\u4F55\u914D\u7F6E Google reCAPTCHA v3 \u9A8C\u8BC1\uFF0C\u4FDD\u62A4 API \u8BF7\u6C42\u514D\u53D7\u6EE5\u7528\u3002\n *\n * reCAPTCHA \u4F1A\u5728\u540E\u53F0\u81EA\u52A8\u9A8C\u8BC1\u7528\u6237\u884C\u4E3A\uFF0C\u65E0\u9700\u7528\u6237\u624B\u52A8\u64CD\u4F5C\u3002\n */\nexport const WithRecaptcha: Story = {\n args: {\n // \u542F\u7528 reCAPTCHA\n needRecaptcha: true,\n // \u914D\u7F6E\u4F60\u7684 reCAPTCHA site key\n recaptchaSitekey: '6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14',\n // \u53EF\u9009\uFF1A\u81EA\u5B9A\u4E49 action \u540D\u79F0\n recaptchaAction: 'livechat',\n },\n parameters: {\n docs: {\n description: {\n story: `\n### reCAPTCHA v3 \u914D\u7F6E\n\n\u542F\u7528 reCAPTCHA v3 \u53EF\u4EE5\u4FDD\u62A4\u4F60\u7684 API \u514D\u53D7\u673A\u5668\u4EBA\u548C\u6EE5\u7528\u884C\u4E3A\u7684\u653B\u51FB\u3002\n\n**\u914D\u7F6E\u6B65\u9AA4\uFF1A**\n\n1. \u5728 [Google reCAPTCHA](https://www.google.com/recaptcha/admin) \u521B\u5EFA v3 \u5BC6\u94A5\n2. \u5728\u9875\u9762\u4E2D\u52A0\u8F7D reCAPTCHA \u811A\u672C\uFF1A\n \\`\\`\\`html\n <script src=\"https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY\"></script>\n \\`\\`\\`\n3. \u914D\u7F6E LiveChatWidget \u7EC4\u4EF6\uFF1A\n \\`\\`\\`tsx\n <LiveChatWidget\n needRecaptcha={true}\n recaptchaSitekey=\"YOUR_SITE_KEY\"\n recaptchaAction=\"livechat\"\n />\n \\`\\`\\`\n\n**\u5DE5\u4F5C\u539F\u7406\uFF1A**\n\n- \u6BCF\u6B21\u53D1\u9001\u6D88\u606F\u6216\u521B\u5EFA\u4F1A\u8BDD\u65F6\uFF0C\u4F1A\u81EA\u52A8\u83B7\u53D6 reCAPTCHA token\n- token \u4F1A\u901A\u8FC7 \\`X-Recaptcha-Token\\` header \u53D1\u9001\u5230\u540E\u7AEF\n- \u540E\u7AEF\u9700\u8981\u9A8C\u8BC1 token \u7684\u6709\u6548\u6027\n\n**\u6CE8\u610F\u4E8B\u9879\uFF1A**\n\n- reCAPTCHA v3 \u5728\u540E\u53F0\u8FD0\u884C\uFF0C\u4E0D\u4F1A\u6253\u65AD\u7528\u6237\u4F53\u9A8C\n- \u5EFA\u8BAE\u4E3A\u4E0D\u540C\u7684\u64CD\u4F5C\u4F7F\u7528\u4E0D\u540C\u7684 \\`recaptchaAction\\` \u4EE5\u4FBF\u5206\u6790\n- \u786E\u4FDD\u540E\u7AEF\u6B63\u786E\u9A8C\u8BC1 reCAPTCHA token\n `,\n },\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,oBAAAE,EAAA,+BAAAC,EAAA,mBAAAC,EAAA,wBAAAC,EAAA,yBAAAC,EAAA,YAAAC,EAAA,wBAAAC,EAAA,mBAAAC,EAAA,eAAAC,EAAA,6BAAAC,EAAA,eAAAC,EAAA,uBAAAC,EAAA,qBAAAC,EAAA,kBAAAC,EAAA,YAAAC,IAAA,eAAAC,EAAAjB,GAiRI,IAAAkB,EAAA,6BA3QJC,EAA+B,wCAE/BC,EAAO,kCAEP,MAAMC,EAAoC,CACxC,MAAO,0BACP,UAAW,iBACX,WAAY,CACV,OAAQ,aACR,KAAM,CACJ,MAAO,CACL,OAAQ,GACR,aAAc,GAChB,EACA,YAAa,CACX,UAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAkDb,CACF,CACF,EACA,KAAM,CAAC,UAAU,EACjB,SAAU,CACR,WAAY,CACV,QAAS,OACT,YAAa,sBACf,EACA,QAAS,CACP,QAAS,SACT,YAAa,wGACb,MAAO,CACL,aAAc,CAAE,QAAS,WAAY,CACvC,CACF,EACA,cAAe,CACb,QAAS,UACT,YAAa,4DACb,MAAO,CACL,aAAc,CAAE,QAAS,OAAQ,CACnC,CACF,EACA,iBAAkB,CAChB,QAAS,OACT,YAAa,+BACb,MAAO,CACL,aAAc,CAAE,QAAS,WAAY,CACvC,CACF,EACA,gBAAiB,CACf,QAAS,OACT,YAAa,wGACb,MAAO,CACL,aAAc,CAAE,QAAS,YAAa,CACxC,CACF,EACA,KAAM,CACJ,QAAS,OACT,YAAa,0BACf,EACA,eAAgB,CACd,QAAS,OACT,YAAa,2BACb,MAAO,CACL,aAAc,CAAE,QAAS,wHAA0B,CACrD,CACF,EACA,QAAS,CACP,QAAS,OACT,YAAa,UACf,EACA,SAAU,CACR,QAAS,SACT,YAAa,mDACb,MAAO,CACL,aAAc,CAAE,QAAS,uCAAwC,CACnE,CACF,CACF,EACA,KAAM,CACJ,WAAY,4BACZ,KAAM,eACN,YAAa,YACb,eAAgB,wHAClB,CACF,EAEA,IAAOL,EAAQK,EAQR,MAAMd,EAAiB,CAC5B,KAAM,CACJ,YAAa,YACb,WAAY,
|
|
4
|
+
"sourcesContent": ["/**\n * LiveChatWidget Storybook Stories\n * \u5C55\u793A LiveChat \u7EC4\u4EF6\u7684\u5404\u79CD\u4F7F\u7528\u573A\u666F\u548C\u914D\u7F6E\n */\n\nimport type { Meta, StoryObj } from '@storybook/react'\nimport { LiveChatWidget } from '../components/LiveChatWidget'\nimport type { MessageRenderer, MessageContent } from '../components/LiveChatWidget'\nimport '../styles/livechat.css'\n\nconst meta: Meta<typeof LiveChatWidget> = {\n title: 'Campaign/LiveChatWidget',\n component: LiveChatWidget,\n parameters: {\n layout: 'fullscreen',\n docs: {\n story: {\n inline: false,\n iframeHeight: 500,\n },\n description: {\n component: `\n# LiveChat \u804A\u5929\u7EC4\u4EF6\n\n\u53EF\u590D\u7528\u7684\u6C14\u6CE1\u5F39\u7A97\u804A\u5929\u7EC4\u4EF6\uFF0C\u652F\u6301 SSE \u6D41\u5F0F\u6D88\u606F\u3001\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\u548C\u591A\u79CD\u6D88\u606F\u7C7B\u578B\u3002\n\n## \u529F\u80FD\u7279\u6027\n\n- \uD83C\uDF88 **\u6C14\u6CE1\u5F39\u7A97**: \u53EF\u81EA\u5B9A\u4E49\u4F4D\u7F6E\u7684\u60AC\u6D6E\u6C14\u6CE1\u6309\u94AE\n- \uD83D\uDCAC **\u6D41\u5F0F\u6D88\u606F**: \u57FA\u4E8E SSE \u7684\u5B9E\u65F6\u6D41\u5F0F\u54CD\u5E94\n- \uD83D\uDCE6 **\u591A\u79CD\u6D88\u606F\u7C7B\u578B**: \u6587\u672C\u3001\u5546\u54C1\u5361\u7247\u3001\u5546\u54C1\u5217\u8868\u3001\u653F\u7B56\u3001\u5FEB\u6377\u56DE\u590D\u7B49\n- \uD83C\uDFA8 **\u53EF\u5B9A\u5236**: \u652F\u6301\u81EA\u5B9A\u4E49\u54C1\u724C\u989C\u8272\u3001Logo\u3001\u6E32\u67D3\u5668\n- \uD83D\uDCF1 **\u54CD\u5E94\u5F0F**: \u79FB\u52A8\u7AEF\u5168\u5C4F\uFF0C\u684C\u9762\u7AEF\u56FA\u5B9A\u5C3A\u5BF8\n- \uD83D\uDCBE **\u4F1A\u8BDD\u7BA1\u7406**: \u81EA\u52A8\u7BA1\u7406 userId \u548C sessionId\n- \uD83D\uDD12 **\u5B89\u5168\u9632\u62A4**: \u5185\u7F6E XSS \u9632\u62A4\u548C\u8F93\u5165\u9A8C\u8BC1\n\n## \u57FA\u7840\u7528\u6CD5\n\n\\`\\`\\`tsx\nimport { LiveChatWidget } from '@anker-in/campaign-ui'\nimport '@anker-in/campaign-ui/livechat.css'\n\nfunction App() {\n return (\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 />\n )\n}\n\\`\\`\\`\n\n## \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n\n\\`\\`\\`tsx\nconst customRenderers = {\n video: {\n render: (content) => (\n <video src={content.url} controls className=\"w-full rounded\" />\n )\n }\n}\n\n<LiveChatWidget\n apiBaseUrl=\"...\"\n site=\"...\"\n customRenderers={customRenderers}\n/>\n\\`\\`\\`\n `,\n },\n },\n },\n tags: ['autodocs'],\n argTypes: {\n apiBaseUrl: {\n control: 'text',\n description: 'API \u57FA\u7840 URL',\n },\n headers: {\n control: 'object',\n description: '\u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\uFF0C\u5C06\u5728\u6240\u6709 API \u8BF7\u6C42\u4E2D\u6DFB\u52A0',\n table: {\n defaultValue: { summary: 'undefined' },\n },\n },\n needRecaptcha: {\n control: 'boolean',\n description: '\u662F\u5426\u542F\u7528 Google reCAPTCHA v3 \u9A8C\u8BC1',\n table: {\n defaultValue: { summary: 'false' },\n },\n },\n recaptchaSitekey: {\n control: 'text',\n description: 'Google reCAPTCHA v3 site key',\n table: {\n defaultValue: { summary: 'undefined' },\n },\n },\n recaptchaAction: {\n control: 'text',\n description: 'reCAPTCHA action \u540D\u79F0\uFF0C\u7528\u4E8E\u533A\u5206\u4E0D\u540C\u7684\u9A8C\u8BC1\u573A\u666F',\n table: {\n defaultValue: { summary: '\"activity\"' },\n },\n },\n site: {\n control: 'text',\n description: 'Shopify \u5E97\u94FA URL',\n },\n welcomeMessage: {\n control: 'text',\n description: '\u6B22\u8FCE\u6D88\u606F',\n table: {\n defaultValue: { summary: '\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F' },\n },\n },\n logoUrl: {\n control: 'text',\n description: 'Logo URL',\n },\n position: {\n control: 'object',\n description: '\u6C14\u6CE1\u6309\u94AE\u4F4D\u7F6E\u5BF9\u8C61',\n table: {\n defaultValue: { summary: '{ bottom: \"1.5rem\", right: \"1.5rem\" }' },\n },\n },\n },\n args: {\n apiBaseUrl: 'http://172.16.38.183:3003',\n site: 'www.eufy.com',\n loginUserId: 'test_test',\n welcomeMessage: '\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u52A9\u4F60\u7684\u5417\uFF1F',\n },\n}\n\nexport default meta\ntype Story = StoryObj<typeof LiveChatWidget>\n\n/**\n * \u9ED8\u8BA4\u914D\u7F6E\n *\n * \u6700\u7B80\u5355\u7684\u4F7F\u7528\u65B9\u5F0F\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E\u3002\n */\nexport const Default: Story = {\n args: {\n loginUserId: 'test_test',\n apiBaseUrl: 'http://172.16.38.183:3003',\n cartId: 'gid://shopify/Cart/hWN7wB3Pa12gh78d8hPOAUBI?key=0e73db1d3fb5ac21da19099c45033253',\n accessToken: '47b1aa2c0797043f9baba39388029d70',\n title: 'eufy',\n\n commonText: {\n learnMore: 'Learn More',\n },\n\n site: 'beta.eufy.com',\n\n // reCAPTCHA \u914D\u7F6E\uFF08\u4F7F\u7528\u7EC4\u4EF6 props\uFF0C\u4E0D\u662F headers\uFF09\n needRecaptcha: false,\n recaptchaSitekey: '6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14 999testtest',\n recaptchaAction: 'chat',\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u4F4D\u7F6E\n *\n * \u4F7F\u7528\u81EA\u5B9A\u4E49\u4F4D\u7F6E\u5BF9\u8C61\u6765\u63A7\u5236\u6C14\u6CE1\u6309\u94AE\u7684\u4F4D\u7F6E\u3002\n */\nexport const CustomPosition: Story = {\n args: {\n position: { bottom: '1.5rem', left: '1.5rem' },\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u54C1\u724C\u6837\u5F0F\n *\n * \u81EA\u5B9A\u4E49 Logo \u548C\u6B22\u8FCE\u6D88\u606F\u3002\n */\nexport const CustomBranding: Story = {\n args: {\n logoUrl: 'https://images.unsplash.com/photo-1599305445671-ac291c95aaa9?w=100&h=100&fit=crop',\n welcomeMessage: '\u6B22\u8FCE\u6765\u5230 Soundcore\uFF01\u6211\u662F\u60A8\u7684\u4E13\u5C5E AI \u52A9\u624B \uD83C\uDFA7',\n },\n}\n\n/**\n * \u5E26\u5FEB\u6377\u56DE\u590D\n *\n * \u5728\u6B22\u8FCE\u6D88\u606F\u540E\u663E\u793A\u5FEB\u6377\u56DE\u590D\u6309\u94AE\u3002\n */\nexport const WithQuickReplies: Story = {\n args: {\n quickReplies: [\n {\n id: '1',\n label: '\u67E5\u8BE2\u4EF7\u683C',\n value: '\u6211\u60F3\u4E86\u89E3\u4EA7\u54C1\u4EF7\u683C',\n icon: '\uD83D\uDCB0',\n },\n {\n id: '2',\n label: '\u67E5\u8BE2\u7269\u6D41',\n value: '\u6211\u60F3\u67E5\u8BE2\u8BA2\u5355\u7269\u6D41',\n icon: '\uD83D\uDCE6',\n },\n {\n id: '3',\n label: '\u552E\u540E\u670D\u52A1',\n value: '\u6211\u9700\u8981\u552E\u540E\u5E2E\u52A9',\n icon: '\uD83D\uDD27',\n },\n {\n id: '4',\n label: '\u4EA7\u54C1\u63A8\u8350',\n value: '\u8BF7\u63A8\u8350\u9002\u5408\u6211\u7684\u4EA7\u54C1',\n icon: '\u2B50',\n },\n ],\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u6B22\u8FCE\u6D88\u606F\n *\n * \u81EA\u5B9A\u4E49\u9996\u6B21\u6253\u5F00\u804A\u5929\u7A97\u53E3\u65F6\u7684\u6B22\u8FCE\u6D88\u606F\u3002\n */\nexport const CustomWelcomeMessage: Story = {\n args: {\n welcomeMessage: `\n\uD83D\uDC4B \u60A8\u597D\uFF01\u6B22\u8FCE\u6765\u5230 Soundcore \u5B98\u65B9\u5546\u57CE\u3002\n\n\u6211\u662F\u60A8\u7684 AI \u8D2D\u7269\u52A9\u624B\uFF0C\u53EF\u4EE5\u5E2E\u60A8\uFF1A\n- \uD83D\uDD0D \u67E5\u627E\u4EA7\u54C1\n- \uD83D\uDCB0 \u6BD4\u8F83\u4EF7\u683C\n- \uD83D\uDCE6 \u8DDF\u8E2A\u8BA2\u5355\n- \uD83D\uDCAC \u89E3\u7B54\u7591\u95EE\n\n\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u52A9\u60A8\u7684\u5417\uFF1F\n `.trim(),\n },\n}\n\n/**\n * \u4E8B\u4EF6\u56DE\u8C03\n *\n * \u76D1\u542C\u7EC4\u4EF6\u4E8B\u4EF6\u8FDB\u884C\u81EA\u5B9A\u4E49\u5904\u7406\u3002\n */\nexport const WithEventCallbacks: Story = {\n args: {\n title: '',\n showNewSessionButton: true,\n\n position: {\n bottom: '24px',\n right: '30px',\n },\n\n chatBubbleIcon: 'https://cdn.shopify.com/s/files/1/0504/7094/4954/files/Rectangle_400770314.png?v=1768894153',\n\n // \u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\u793A\u4F8B\n headers: {\n 'X-Custom-Header': 'custom-value',\n Authorization: 'Bearer your-token-here',\n },\n },\n\n render: args => (\n <LiveChatWidget\n {...args}\n onOpen={() => {\n console.log('[Story] Chat opened')\n }}\n onClose={() => {\n console.log('[Story] Chat closed')\n }}\n onMessageSend={(message: string) => {\n console.log('[Story] Message sent:', message)\n }}\n onError={(error: Error) => {\n console.error('[Story] Error occurred:', error)\n }}\n onTextMessage={() => {\n console.log('\uD83D\uDCDD [Story] AI started replying with text message')\n }}\n onProductList={() => {\n console.log('\uD83D\uDCE6 [Story] AI replied with product list card')\n }}\n onPromotionList={() => {\n console.log('\uD83C\uDF89 [Story] AI replied with promotion list card')\n }}\n onAddToCart={(product: any) => {\n console.log('\uD83D\uDED2 [Story] Add to Cart clicked!')\n console.log('\uD83D\uDCE6 Product Info:', {\n id: product.shopifyId,\n title: product.title,\n price: product.price,\n imageUrl: product.imageUrl,\n productUrl: product.productUrl,\n })\n console.log('\uD83D\uDCCB Full Product Object:', product)\n\n // \u6A21\u62DF\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u6210\u529F\n alert(`\u2705 \"${product.title}\" \u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\uFF01\\n\\n\u4EF7\u683C: ${product.price.currency} ${product.price.amount}`)\n }}\n onCart={(cartId: string, checkoutUrl?: string) => {\n console.log('\uD83D\uDED2 [Story] Cart button clicked!')\n console.log('\uD83D\uDCCB Cart ID:', cartId)\n console.log('\uD83D\uDD17 Checkout URL:', checkoutUrl)\n\n // \u6A21\u62DF\u8D2D\u7269\u8F66\u6309\u94AE\u70B9\u51FB\u6210\u529F\n alert(`\u8D2D\u7269\u8F66\u6309\u94AE\u88AB\u70B9\u51FB\uFF01\\n\\nCart ID: ${cartId}\\nCheckout URL: ${checkoutUrl || '\u65E0'}`)\n }}\n />\n ),\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u89C6\u9891\u6D88\u606F\u6E32\u67D3\u5668\n *\n * \u6269\u5C55\u6D88\u606F\u7C7B\u578B\uFF0C\u6DFB\u52A0\u89C6\u9891\u6D88\u606F\u652F\u6301\u3002\n */\nexport const CustomVideoRenderer: Story = {\n args: {\n customRenderers: {\n video: {\n render: (content: MessageContent) => {\n const videoContent = content as any\n return (\n <div className=\"w-full\">\n <video src={videoContent.url} controls className=\"w-full rounded-lg\" poster={videoContent.poster}>\n \u60A8\u7684\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u89C6\u9891\u64AD\u653E\n </video>\n {videoContent.title && <p className=\"mt-2 text-sm text-gray-600\">{videoContent.title}</p>}\n </div>\n )\n },\n } as MessageRenderer,\n },\n },\n}\n\n/**\n * \u81EA\u5B9A\u4E49\u56FE\u7247\u753B\u5ECA\u6E32\u67D3\u5668\n *\n * \u6269\u5C55\u6D88\u606F\u7C7B\u578B\uFF0C\u6DFB\u52A0\u56FE\u7247\u753B\u5ECA\u652F\u6301\u3002\n */\nexport const CustomImageGalleryRenderer: Story = {\n args: {\n customRenderers: {\n image_gallery: {\n render: (content: MessageContent) => {\n const galleryContent = content as any\n const images = galleryContent.images || []\n\n return (\n <div className=\"grid grid-cols-2 gap-2\">\n {images.map((image: any, index: number) => (\n <div key={index} className=\"relative aspect-square\">\n <img\n src={image.url}\n alt={image.alt || `Image ${index + 1}`}\n className=\"size-full rounded-lg object-cover\"\n />\n </div>\n ))}\n </div>\n )\n },\n } as MessageRenderer,\n },\n },\n}\n\n/**\n * \u79FB\u52A8\u7AEF\u9884\u89C8\n *\n * \u5728\u79FB\u52A8\u7AEF\u5C3A\u5BF8\u4E0B\u67E5\u770B\u6548\u679C\uFF08\u5168\u5C4F\u663E\u793A\uFF09\u3002\n */\nexport const MobileView: Story = {\n parameters: {\n viewport: {\n defaultViewport: 'mobile1',\n },\n },\n args: {},\n}\n\n/**\n * \u5E73\u677F\u7AEF\u9884\u89C8\n *\n * \u5728\u5E73\u677F\u7AEF\u5C3A\u5BF8\u4E0B\u67E5\u770B\u6548\u679C\u3002\n */\nexport const TabletView: Story = {\n parameters: {\n viewport: {\n defaultViewport: 'tablet',\n },\n },\n args: {},\n}\n\n/**\n * \u9ED8\u8BA4\u4F4D\u7F6E\u5C55\u793A\n *\n * \u5C55\u793A\u6C14\u6CE1\u6309\u94AE\u7684\u9ED8\u8BA4\u4F4D\u7F6E\uFF08\u53F3\u4E0B\u89D2\uFF09\u3002\n */\nexport const DefaultPositionDemo: Story = {\n render: () => (\n <div className=\"relative h-screen w-full bg-gray-50\">\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <p className=\"text-gray-500\">\u9875\u9762\u5185\u5BB9\u533A\u57DF</p>\n </div>\n\n {/* \u6F14\u793A\u9ED8\u8BA4\u4F4D\u7F6E */}\n <div className=\"absolute bottom-4 right-4 text-xs text-gray-400\">\u9ED8\u8BA4\u4F4D\u7F6E (\u53F3\u4E0B\u89D2)</div>\n\n <LiveChatWidget apiBaseUrl=\"https://beta-api-livechat.anker.com\" site=\"www.eufy.com\" />\n </div>\n ),\n}\n\n/**\n * \u4EA7\u54C1\u5BF9\u6BD4\u6D88\u606F\n *\n * \u5C55\u793A\u4EA7\u54C1\u5BF9\u6BD4\u7C7B\u578B\u7684\u6D88\u606F\u6E32\u67D3\u6548\u679C\uFF0C\u5305\u62EC\u4EF7\u683C\u3001\u4F1A\u5458\u4EF7\u3001\u53D8\u4F53\u6570\u91CF\u3001\u6298\u6263\u7B49\u591A\u4E2A\u7EF4\u5EA6\u7684\u5BF9\u6BD4\u3002\n *\n * \u4EA7\u54C1\u5BF9\u6BD4\u7EC4\u4EF6\u4F1A\u4EE5\u7F51\u683C\u5E03\u5C40\u5C55\u793A\u591A\u4E2A\u4EA7\u54C1\u7684\u57FA\u672C\u4FE1\u606F\uFF0C\u5E76\u5728\u4E0B\u65B9\u663E\u793A\u5404\u7EF4\u5EA6\u7684\u5BF9\u6BD4\u6570\u636E\u3002\n */\nexport const ProductComparisonMessage: Story = {\n render: args => (\n <LiveChatWidget\n {...args}\n onAddToCart={(product: any) => {\n console.log('\uD83D\uDED2 [ProductComparison Story] Add to Cart clicked!')\n console.log('\uD83D\uDCE6 Product Info:', {\n id: product.shopifyId,\n title: product.title,\n price: product.price,\n imageUrl: product.imageUrl,\n productUrl: product.productUrl,\n })\n console.log('\uD83D\uDCCB Full Product Object:', product)\n\n // \u6A21\u62DF\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u6210\u529F\n alert(`\u2705 \"${product.title}\" \u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\uFF01\\n\\n\u4EF7\u683C: ${product.price.currency} ${product.price.amount}`)\n }}\n />\n ),\n args: {\n welcomeMessage: '\u4EE5\u4E0B\u662F\u60A8\u5173\u6CE8\u7684\u4EA7\u54C1\u5BF9\u6BD4\u4FE1\u606F\uFF1A',\n },\n parameters: {\n docs: {\n description: {\n story: `\n\u4EA7\u54C1\u5BF9\u6BD4\u6D88\u606F\u652F\u6301\u4EE5\u4E0B\u7EF4\u5EA6\uFF1A\n\n- **\u4EF7\u683C\u5BF9\u6BD4**: \u663E\u793A\u4EA7\u54C1\u7684\u4EF7\u683C\u533A\u95F4\u548C\u4FC3\u9500\u6807\u7B7E\n- **\u4F1A\u5458\u4EF7\u5BF9\u6BD4**: \u663E\u793A\u4F1A\u5458\u4E13\u4EAB\u4EF7\u683C\uFF08\u5982\u679C\u53EF\u7528\uFF09\n- **\u53D8\u4F53\u6570\u91CF\u5BF9\u6BD4**: \u663E\u793A\u4EA7\u54C1\u7684\u53EF\u9009\u53D8\u4F53\u6570\u91CF\n- **\u6298\u6263\u5BF9\u6BD4**: \u663E\u793A\u4EA7\u54C1\u662F\u5426\u6709\u6298\u6263\u6D3B\u52A8\n\n\u7EC4\u4EF6\u4F1A\u81EA\u52A8\u5904\u7406\u4ECE\u540E\u7AEF\u8FD4\u56DE\u7684\u4EA7\u54C1\u5BF9\u6BD4\u6570\u636E\uFF0C\u5305\u62EC\uFF1A\n- \u4EA7\u54C1\u57FA\u672C\u4FE1\u606F\uFF08\u56FE\u7247\u3001\u6807\u9898\u3001\u4EF7\u683C\u7B49\uFF09\n- \u591A\u7EF4\u5EA6\u5BF9\u6BD4\u6570\u636E\uFF08\u4EF7\u683C\u3001\u4F1A\u5458\u4EF7\u3001\u53D8\u4F53\u3001\u6298\u6263\uFF09\n- \u54CD\u5E94\u5F0F\u5E03\u5C40\uFF08\u79FB\u52A8\u7AEF\u53EF\u6A2A\u5411\u6EDA\u52A8\uFF09\n\n**\u6D4B\u8BD5 Add to Cart \u529F\u80FD\uFF1A**\n- \u70B9\u51FB\u4EA7\u54C1\u5BF9\u6BD4\u5361\u7247\u4E2D\u7684 \"Add to Cart\" \u6309\u94AE\n- \u67E5\u770B\u63A7\u5236\u53F0\u8F93\u51FA\u7684\u4EA7\u54C1\u4FE1\u606F\n- \u4F1A\u5F39\u51FA\u786E\u8BA4\u63D0\u793A\u6846\u663E\u793A\u5DF2\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\n `,\n },\n },\n },\n}\n\n/**\n * FAQ \u5217\u8868\u6D88\u606F\n *\n * \u5C55\u793A\u5E38\u89C1\u95EE\u9898\u5217\u8868\uFF0C\u652F\u6301\u6298\u53E0/\u5C55\u5F00\u3002\n *\n * FAQ \u7EC4\u4EF6\u4F1A\u663E\u793A\u641C\u7D22\u7ED3\u679C\u7684\u5E38\u89C1\u95EE\u9898\uFF0C\u6BCF\u4E2A\u95EE\u9898\u53EF\u4EE5\u70B9\u51FB\u5C55\u5F00\u67E5\u770B\u7B54\u6848\u3002\n */\nexport const FAQListMessage: Story = {\n args: {\n welcomeMessage: '\u4EE5\u4E0B\u662F\u4E0E\u60A8\u7684\u95EE\u9898\u76F8\u5173\u7684\u5E38\u89C1\u89E3\u7B54\uFF1A',\n },\n parameters: {\n docs: {\n description: {\n story: `\nFAQ \u5217\u8868\u6D88\u606F\u652F\u6301\uFF1A\n\n- **\u53EF\u6298\u53E0\u95EE\u9898**: \u70B9\u51FB\u95EE\u9898\u6807\u9898\u5C55\u5F00/\u6298\u53E0\u7B54\u6848\n- **Markdown \u7B54\u6848**: \u7B54\u6848\u652F\u6301 Markdown \u683C\u5F0F\uFF0C\u53EF\u663E\u793A\u5BCC\u6587\u672C\n- **\u76F8\u5173\u95EE\u9898**: \u5C55\u5F00\u7B54\u6848\u540E\u663E\u793A\u76F8\u5173\u95EE\u9898\uFF0C\u70B9\u51FB\u53EF\u89E6\u53D1\u65B0\u641C\u7D22\n- **\u5206\u7C7B\u6807\u8BB0**: \u6839\u636E\u95EE\u9898\u5206\u7C7B\u663E\u793A\uFF08\u914D\u9001\u3001\u9000\u8D27\u3001\u4EA7\u54C1\u3001\u652F\u4ED8\u3001\u901A\u7528\uFF09\n- **\u641C\u7D22\u7ED3\u679C\u7EDF\u8BA1**: \u663E\u793A\u627E\u5230\u7684\u95EE\u9898\u6570\u91CF\n\n\u4F7F\u7528\u573A\u666F\uFF1A\n- \u7528\u6237\u8BE2\u95EE\u5E38\u89C1\u95EE\u9898\u65F6\u8FD4\u56DE FAQ \u5217\u8868\n- \u4EA7\u54C1\u4E13\u5C5E\u95EE\u9898\u67E5\u8BE2\n- \u6309\u5206\u7C7B\u67E5\u8BE2 FAQ\n `,\n },\n },\n },\n}\n\n/**\n * \u5E26 reCAPTCHA \u914D\u7F6E\n *\n * \u5C55\u793A\u5982\u4F55\u914D\u7F6E Google reCAPTCHA v3 \u9A8C\u8BC1\uFF0C\u4FDD\u62A4 API \u8BF7\u6C42\u514D\u53D7\u6EE5\u7528\u3002\n *\n * reCAPTCHA \u4F1A\u5728\u540E\u53F0\u81EA\u52A8\u9A8C\u8BC1\u7528\u6237\u884C\u4E3A\uFF0C\u65E0\u9700\u7528\u6237\u624B\u52A8\u64CD\u4F5C\u3002\n */\nexport const WithRecaptcha: Story = {\n args: {\n // \u542F\u7528 reCAPTCHA\n needRecaptcha: true,\n // \u914D\u7F6E\u4F60\u7684 reCAPTCHA site key\n recaptchaSitekey: '6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14',\n // \u53EF\u9009\uFF1A\u81EA\u5B9A\u4E49 action \u540D\u79F0\n recaptchaAction: 'livechat',\n },\n parameters: {\n docs: {\n description: {\n story: `\n### reCAPTCHA v3 \u914D\u7F6E\n\n\u542F\u7528 reCAPTCHA v3 \u53EF\u4EE5\u4FDD\u62A4\u4F60\u7684 API \u514D\u53D7\u673A\u5668\u4EBA\u548C\u6EE5\u7528\u884C\u4E3A\u7684\u653B\u51FB\u3002\n\n**\u914D\u7F6E\u6B65\u9AA4\uFF1A**\n\n1. \u5728 [Google reCAPTCHA](https://www.google.com/recaptcha/admin) \u521B\u5EFA v3 \u5BC6\u94A5\n2. \u5728\u9875\u9762\u4E2D\u52A0\u8F7D reCAPTCHA \u811A\u672C\uFF1A\n \\`\\`\\`html\n <script src=\"https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY\"></script>\n \\`\\`\\`\n3. \u914D\u7F6E LiveChatWidget \u7EC4\u4EF6\uFF1A\n \\`\\`\\`tsx\n <LiveChatWidget\n needRecaptcha={true}\n recaptchaSitekey=\"YOUR_SITE_KEY\"\n recaptchaAction=\"livechat\"\n />\n \\`\\`\\`\n\n**\u5DE5\u4F5C\u539F\u7406\uFF1A**\n\n- \u6BCF\u6B21\u53D1\u9001\u6D88\u606F\u6216\u521B\u5EFA\u4F1A\u8BDD\u65F6\uFF0C\u4F1A\u81EA\u52A8\u83B7\u53D6 reCAPTCHA token\n- token \u4F1A\u901A\u8FC7 \\`X-Recaptcha-Token\\` header \u53D1\u9001\u5230\u540E\u7AEF\n- \u540E\u7AEF\u9700\u8981\u9A8C\u8BC1 token \u7684\u6709\u6548\u6027\n\n**\u6CE8\u610F\u4E8B\u9879\uFF1A**\n\n- reCAPTCHA v3 \u5728\u540E\u53F0\u8FD0\u884C\uFF0C\u4E0D\u4F1A\u6253\u65AD\u7528\u6237\u4F53\u9A8C\n- \u5EFA\u8BAE\u4E3A\u4E0D\u540C\u7684\u64CD\u4F5C\u4F7F\u7528\u4E0D\u540C\u7684 \\`recaptchaAction\\` \u4EE5\u4FBF\u5206\u6790\n- \u786E\u4FDD\u540E\u7AEF\u6B63\u786E\u9A8C\u8BC1 reCAPTCHA token\n `,\n },\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["LiveChatWidget_stories_exports", "__export", "CustomBranding", "CustomImageGalleryRenderer", "CustomPosition", "CustomVideoRenderer", "CustomWelcomeMessage", "Default", "DefaultPositionDemo", "FAQListMessage", "MobileView", "ProductComparisonMessage", "TabletView", "WithEventCallbacks", "WithQuickReplies", "WithRecaptcha", "LiveChatWidget_stories_default", "__toCommonJS", "import_jsx_runtime", "import_LiveChatWidget", "import_livechat", "meta", "args", "message", "error", "product", "cartId", "checkoutUrl", "content", "videoContent", "images", "image", "index"]
|
|
7
7
|
}
|
|
@@ -46,7 +46,7 @@ import type { LiveChatWidgetProps } from './types';
|
|
|
46
46
|
* site="www.eufy.com"
|
|
47
47
|
* needRecaptcha={true}
|
|
48
48
|
* recaptchaSitekey="6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14"
|
|
49
|
-
* recaptchaAction="
|
|
49
|
+
* recaptchaAction=""
|
|
50
50
|
* />
|
|
51
51
|
*
|
|
52
52
|
* // 使用自定义 headers 和 reCAPTCHA
|
|
@@ -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 { useChatState } from './hooks/useChatState'\nimport { useChatAPI } from './hooks/useChatAPI'\nimport { MessageRendererRegistry } from './utils/messageRenderers'\nimport { sanitizeInput } from './utils/validation'\nimport { transformProducts } from './utils/productTransformers.js'\nimport { transformCartData } from './utils/cartTransformers.js'\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\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * needRecaptcha={true}\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 * needRecaptcha={true}\n * recaptchaSitekey=\"your-site-key\"\n * />\n * ```\n */\nexport const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({\n apiBaseUrl,\n headers,\n needRecaptcha,\n recaptchaSitekey,\n recaptchaAction,\n site,\n loginUserId,\n cartId,\n accessToken,\n position,\n welcomeMessage,\n quickReplies,\n customRenderers,\n logoUrl,\n title,\n chatBubbleIcon,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n showNewSessionButton,\n commonText,\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 onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n })\n\n const {\n messages,\n isOpen,\n userId,\n sessionId,\n inputValue,\n isStreaming,\n openChat,\n closeChat,\n setInputValue,\n addMessage,\n setMessages,\n clearMessages,\n handleSSEEvent,\n saveSession,\n clearSession,\n } = chatState\n\n // API \u8C03\u7528\n const { sendMessageStream, createSession } = useChatAPI({\n apiBaseUrl,\n headers,\n recaptchaConfig: {\n needRecaptcha,\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 /**\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 if (!isOpen || !userId) return\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\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 },\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 if (!userId) return\n\n try {\n const response = await createSession({\n user_id: userId,\n site: site,\n real_user_id: loginUserId,\n })\n\n if (response.success) {\n // \u4FDD\u5B58\u65B0\u4F1A\u8BDD ID\n saveSession(response.sessionId)\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 }, [\n userId,\n site,\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 try {\n const response = await createSession({\n user_id: userId,\n session_id: existingSessionId,\n site: site,\n real_user_id: loginUserId,\n })\n\n if (response.success && response.resumed) {\n // \u4F1A\u8BDD\u6062\u590D\u6210\u529F\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\uFF09\n const normalizedMessages = response.messages.filter((msg: any) => msg != null).map(normalizeMessage)\n setMessages(normalizedMessages)\n } else {\n // \u6CA1\u6709\u5386\u53F2\u6D88\u606F\uFF0C\u663E\u793A\u6B22\u8FCE\u6D88\u606F\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\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 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 // \u6062\u590D\u5931\u8D25\uFF0C\u6E05\u7A7A\u4F1A\u8BDD\u5E76\u521B\u5EFA\u65B0\u7684\n clearSession()\n handleCreateNewSession()\n }\n },\n [\n userId,\n site,\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) => {\n const textToSend = message || inputValue.trim()\n\n if (!textToSend) return\n\n // \u6E05\u7A7A\u8F93\u5165\u6846\n if (!message) {\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\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 // \u89E6\u53D1\u6D88\u606F\u53D1\u9001\u56DE\u8C03\n onMessageSend?.(sanitized)\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 real_user_id: loginUserId,\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\n if (event.event === 'status' && event.data.type === 'session_expired') {\n clearSession()\n }\n })\n } catch (error) {\n console.error('[LiveChatWidget] Failed to send message:', error)\n onError?.(error as Error)\n }\n },\n [\n inputValue,\n userId,\n sessionId,\n site,\n loginUserId,\n cartId,\n accessToken,\n setInputValue,\n addMessage,\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 return (\n <>\n {/* \u6C14\u6CE1\u6309\u94AE */}\n <ChatBubble position={position} onClick={openChat} visible={!isOpen} iconImageUrl={chatBubbleIcon} />\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 rendererRegistry={rendererRegistry}\n inputPlaceholder=\"\"\n onAddToCart={onAddToCart}\n showNewSessionButton={showNewSessionButton}\n />\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n </>\n )\n}\n"],
|
|
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 { useChatState } from './hooks/useChatState'\nimport { useChatAPI } from './hooks/useChatAPI'\nimport { MessageRendererRegistry } from './utils/messageRenderers'\nimport { sanitizeInput } from './utils/validation'\nimport { transformProducts } from './utils/productTransformers.js'\nimport { transformCartData } from './utils/cartTransformers.js'\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\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * needRecaptcha={true}\n * recaptchaSitekey=\"6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14\"\n * recaptchaAction=\"\"\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 * needRecaptcha={true}\n * recaptchaSitekey=\"your-site-key\"\n * />\n * ```\n */\nexport const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({\n apiBaseUrl,\n headers,\n needRecaptcha,\n recaptchaSitekey,\n recaptchaAction,\n site,\n loginUserId,\n cartId,\n accessToken,\n position,\n welcomeMessage,\n quickReplies,\n customRenderers,\n logoUrl,\n title,\n chatBubbleIcon,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n showNewSessionButton,\n commonText,\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 onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n })\n\n const {\n messages,\n isOpen,\n userId,\n sessionId,\n inputValue,\n isStreaming,\n openChat,\n closeChat,\n setInputValue,\n addMessage,\n setMessages,\n clearMessages,\n handleSSEEvent,\n saveSession,\n clearSession,\n } = chatState\n\n // API \u8C03\u7528\n const { sendMessageStream, createSession } = useChatAPI({\n apiBaseUrl,\n headers,\n recaptchaConfig: {\n needRecaptcha,\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 /**\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 if (!isOpen || !userId) return\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\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 },\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 if (!userId) return\n\n try {\n const response = await createSession({\n user_id: userId,\n site: site,\n real_user_id: loginUserId,\n })\n\n if (response.success) {\n // \u4FDD\u5B58\u65B0\u4F1A\u8BDD ID\n saveSession(response.sessionId)\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 }, [\n userId,\n site,\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 try {\n const response = await createSession({\n user_id: userId,\n session_id: existingSessionId,\n site: site,\n real_user_id: loginUserId,\n })\n\n if (response.success && response.resumed) {\n // \u4F1A\u8BDD\u6062\u590D\u6210\u529F\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\uFF09\n const normalizedMessages = response.messages.filter((msg: any) => msg != null).map(normalizeMessage)\n setMessages(normalizedMessages)\n } else {\n // \u6CA1\u6709\u5386\u53F2\u6D88\u606F\uFF0C\u663E\u793A\u6B22\u8FCE\u6D88\u606F\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\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 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 // \u6062\u590D\u5931\u8D25\uFF0C\u6E05\u7A7A\u4F1A\u8BDD\u5E76\u521B\u5EFA\u65B0\u7684\n clearSession()\n handleCreateNewSession()\n }\n },\n [\n userId,\n site,\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) => {\n const textToSend = message || inputValue.trim()\n\n if (!textToSend) return\n\n // \u6E05\u7A7A\u8F93\u5165\u6846\n if (!message) {\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\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 // \u89E6\u53D1\u6D88\u606F\u53D1\u9001\u56DE\u8C03\n onMessageSend?.(sanitized)\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 real_user_id: loginUserId,\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\n if (event.event === 'status' && event.data.type === 'session_expired') {\n clearSession()\n }\n })\n } catch (error) {\n console.error('[LiveChatWidget] Failed to send message:', error)\n onError?.(error as Error)\n }\n },\n [\n inputValue,\n userId,\n sessionId,\n site,\n loginUserId,\n cartId,\n accessToken,\n setInputValue,\n addMessage,\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 return (\n <>\n {/* \u6C14\u6CE1\u6309\u94AE */}\n <ChatBubble position={position} onClick={openChat} visible={!isOpen} iconImageUrl={chatBubbleIcon} />\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 rendererRegistry={rendererRegistry}\n inputPlaceholder=\"\"\n onAddToCart={onAddToCart}\n showNewSessionButton={showNewSessionButton}\n />\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n </>\n )\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["Fragment", "jsx", "jsxs", "React", "useEffect", "useCallback", "Dialog", "DEFAULT_COMMON_TEXT", "ChatBubble", "ChatWindow", "useChatState", "useChatAPI", "MessageRendererRegistry", "sanitizeInput", "transformProducts", "transformCartData", "TextBlock", "ProductCard", "ProductList", "ProductComparisonRenderer", "PolicyBlock", "createQuickRepliesRenderer", "ThinkingBlock", "ErrorBlock", "FAQListRenderer", "PromotionListRenderer", "CartCard", "LiveChatWidget", "apiBaseUrl", "headers", "needRecaptcha", "recaptchaSitekey", "recaptchaAction", "site", "loginUserId", "cartId", "accessToken", "position", "welcomeMessage", "quickReplies", "customRenderers", "logoUrl", "title", "chatBubbleIcon", "onOpen", "onClose", "onMessageSend", "onError", "onTextMessage", "onProductList", "onPromotionList", "onAddToCart", "onCart", "showNewSessionButton", "commonText", "mergedText", "chatState", "messages", "isOpen", "userId", "sessionId", "inputValue", "isStreaming", "openChat", "closeChat", "setInputValue", "addMessage", "setMessages", "clearMessages", "handleSSEEvent", "saveSession", "clearSession", "sendMessageStream", "createSession", "handleSendMessageRef", "_message", "rendererRegistry", "registry", "quickRepliesRenderer", "reply", "currentSessionId", "handleResumeSession", "handleCreateNewSession", "normalizeMessage", "message", "contentBlocks", "structuredData", "block", "transformedData", "response", "messageText", "welcomeContent", "questions", "quickRepliesFromBackend", "question", "index", "error", "existingSessionId", "normalizedMessages", "msg", "handleSendMessage", "textToSend", "sanitized", "userMessage", "event", "open"]
|
|
7
7
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{fetcher as S}from"../utils/fetcher";async function y(n,c,a,r,e){const t=await S({url:`${n}/api/chat/stream`,method:"POST",headers:{Accept:"text/event-stream",...r},body:c,needRecaptcha:e?.needRecaptcha,recaptchaSitekey:e?.recaptchaSitekey,recaptchaAction:e?.recaptchaAction||"
|
|
2
|
-
`);d=l.pop()||"";for(const w of l){const s=w.trim();if(s===""){p=null;continue}if(s.startsWith("event:"))p=s.substring(6).trim();else if(s.startsWith("data:")){const R=s.substring(5).trim();try{const h=JSON.parse(R);a({event:p||"message",data:h})}catch(h){console.error("[LiveChat API] Failed to parse SSE data:",R,h),a({event:"error",data:{message:"Failed to parse SSE data",code:"PARSE_ERROR"}})}}}}}catch(o){throw console.error("[LiveChat API] SSE stream error:",o),o}finally{i.releaseLock()}}async function
|
|
1
|
+
import{fetcher as S}from"../utils/fetcher";async function y(n,c,a,r,e){const t=await S({url:`${n}/api/chat/stream`,method:"POST",headers:{Accept:"text/event-stream",...r},body:c,needRecaptcha:e?.needRecaptcha,recaptchaSitekey:e?.recaptchaSitekey,recaptchaAction:e?.recaptchaAction||"send_message"});if(!t.ok)throw new Error(`HTTP ${t.status}: ${t.statusText}`);const i=t.body?.getReader();if(!i)throw new Error("Response body is not readable");const u=new TextDecoder;let d="",p=null;try{for(;;){const{done:o,value:m}=await i.read();if(o)break;d+=u.decode(m,{stream:!0});const l=d.split(`
|
|
2
|
+
`);d=l.pop()||"";for(const w of l){const s=w.trim();if(s===""){p=null;continue}if(s.startsWith("event:"))p=s.substring(6).trim();else if(s.startsWith("data:")){const R=s.substring(5).trim();try{const h=JSON.parse(R);a({event:p||"message",data:h})}catch(h){console.error("[LiveChat API] Failed to parse SSE data:",R,h),a({event:"error",data:{message:"Failed to parse SSE data",code:"PARSE_ERROR"}})}}}}}catch(o){throw console.error("[LiveChat API] SSE stream error:",o),o}finally{i.releaseLock()}}async function g(n,c,a,r){const e=await S({url:`${n}/api/chat/new-session`,method:"POST",headers:a,body:c,needRecaptcha:r?.needRecaptcha,recaptchaSitekey:r?.recaptchaSitekey,recaptchaAction:r?.recaptchaAction||"new_session"});if(!e.ok){const t=await e.json().catch(()=>({success:!1,error:`HTTP ${e.status}: ${e.statusText}`,code:"HTTP_ERROR"}));throw new Error(t.error||"Failed to create session")}return e.json()}export{g as createNewSession,y as sendMessage};
|
|
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 || '
|
|
5
|
-
"mappings": "AAOA,OAAS,WAAAA,MAAe,mBAgCxB,eAAsBC,EACpBC,EACAC,EACAC,EACAC,EACAC,EACe,CAEf,MAAMC,EAAW,MAAMP,EAAQ,CAC7B,IAAK,GAAGE,CAAU,mBAClB,OAAQ,OACR,QAAS,CACP,OAAQ,oBACR,GAAGG,CACL,EACA,KAAMF,EACN,cAAeG,GAAiB,cAChC,iBAAkBA,GAAiB,iBACnC,gBAAiBA,GAAiB,iBAAmB,
|
|
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 throw new Error(`HTTP ${response.status}: ${response.statusText}`)\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: ErrorResponse = await response.json().catch(() => ({\n success: false,\n error: `HTTP ${response.status}: ${response.statusText}`,\n code: 'HTTP_ERROR',\n }))\n throw new Error(errorData.error || 'Failed to create session')\n }\n\n return response.json()\n}\n"],
|
|
5
|
+
"mappings": "AAOA,OAAS,WAAAA,MAAe,mBAgCxB,eAAsBC,EACpBC,EACAC,EACAC,EACAC,EACAC,EACe,CAEf,MAAMC,EAAW,MAAMP,EAAQ,CAC7B,IAAK,GAAGE,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,GACZ,MAAM,IAAI,MAAM,QAAQA,EAAS,MAAM,KAAKA,EAAS,UAAU,EAAE,EAGnE,MAAMC,EAASD,EAAS,MAAM,UAAU,EACxC,GAAI,CAACC,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/Bb,EAAQ,CACN,MAAQO,GAAwB,UAChC,KAAAO,CACF,CAAC,CACH,OAASC,EAAK,CACZ,QAAQ,MAAM,2CAA4CF,EAASE,CAAG,EACtEf,EAAQ,CACN,MAAO,QACP,KAAM,CACJ,QAAS,2BACT,KAAM,aACR,CACF,CAAC,CACH,CACF,CACF,CACF,CACF,OAASgB,EAAO,CACd,cAAQ,MAAM,mCAAoCA,CAAK,EACjDA,CACR,QAAE,CACAZ,EAAO,YAAY,CACrB,CACF,CAmBA,eAAsBa,EACpBnB,EACAC,EACAE,EACAC,EAC6B,CAE7B,MAAMC,EAAW,MAAMP,EAAQ,CAC7B,IAAK,GAAGE,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,MAAMe,EAA2B,MAAMf,EAAS,KAAK,EAAE,MAAM,KAAO,CAClE,QAAS,GACT,MAAO,QAAQA,EAAS,MAAM,KAAKA,EAAS,UAAU,GACtD,KAAM,YACR,EAAE,EACF,MAAM,IAAI,MAAMe,EAAU,OAAS,0BAA0B,CAC/D,CAEA,OAAOf,EAAS,KAAK,CACvB",
|
|
6
6
|
"names": ["fetcher", "sendMessage", "apiBaseUrl", "request", "onEvent", "customHeaders", "recaptchaConfig", "response", "reader", "decoder", "buffer", "currentEvent", "done", "value", "lines", "line", "trimmed", "dataStr", "data", "err", "error", "createNewSession", "errorData"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsx as e,jsxs as t}from"react/jsx-runtime";const l=()=>t("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[e("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),e("line",{x1:"6",y1:"6",x2:"18",y2:"18"})]}),c=()=>t("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[e("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),e("line",{x1:"5",y1:"12",x2:"19",y2:"12"})]}),
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";const l=()=>t("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[e("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),e("line",{x1:"6",y1:"6",x2:"18",y2:"18"})]}),c=()=>t("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[e("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),e("line",{x1:"5",y1:"12",x2:"19",y2:"12"})]}),g=({title:s="",logoUrl:o,onClose:n,onNewSession:r,showNewSessionButton:i=!0,className:a=""})=>t("div",{className:`flex items-center justify-between border-b border-[#DADCE0] bg-white px-4 pb-3 pt-0 tablet:pt-4 ${a}`,children:[t("div",{className:"flex items-center gap-3",children:[o&&e("img",{src:o,alt:"Logo",className:"size-8 rounded-full object-cover"}),e("h2",{className:"text-lg font-semibold text-gray-900",children:s})]}),t("div",{className:"flex items-center gap-2",children:[i&&r&&e("button",{type:"button",onClick:r,className:"rounded-lg p-2 text-gray-600","aria-label":"\u5F00\u59CB\u65B0\u4F1A\u8BDD",title:"\u5F00\u59CB\u65B0\u4F1A\u8BDD",children:e(c,{})}),n&&e("button",{type:"button",onClick:n,className:"tablet:block rounded-lg text-gray-600","aria-label":"Close chat",children:e(l,{})})]})]});export{g as ChatHeader};
|
|
2
2
|
//# sourceMappingURL=ChatHeader.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/components/LiveChatWidget/components/ChatHeader.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * \u804A\u5929\u7A97\u53E3\u5934\u90E8\u7EC4\u4EF6\n * \u663E\u793A\u6807\u9898\u3001Logo\u3001\u5173\u95ED\u6309\u94AE\u548C\u65B0\u4F1A\u8BDD\u6309\u94AE\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u5934\u90E8\u8BBE\u8BA1\n */\n\nimport React from 'react'\n\nexport interface ChatHeaderProps {\n /**\n * \u5934\u90E8\u6807\u9898\n */\n title?: string\n\n /**\n * Logo URL\n */\n logoUrl?: string\n\n /**\n * \u5173\u95ED\u6309\u94AE\u70B9\u51FB\u56DE\u8C03\n */\n onClose?: () => void\n\n /**\n * \u65B0\u4F1A\u8BDD\u6309\u94AE\u70B9\u51FB\u56DE\u8C03\n */\n onNewSession?: () => void\n\n /**\n * \u662F\u5426\u663E\u793A\u65B0\u4F1A\u8BDD\u6309\u94AE\n * @default true\n */\n showNewSessionButton?: boolean\n\n /**\n * \u81EA\u5B9A\u4E49\u6837\u5F0F\u7C7B\u540D\n */\n className?: string\n}\n\n/**\n * \u9ED8\u8BA4\u5173\u95ED\u56FE\u6807 (X)\n */\nconst CloseIcon: React.FC = () => (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n)\n\n/**\n * \u65B0\u4F1A\u8BDD\u56FE\u6807 (\u52A0\u53F7)\n */\nconst NewSessionIcon: React.FC = () => (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n)\n\n/**\n * \u804A\u5929\u7A97\u53E3\u5934\u90E8\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u6807\u9898\u548C Logo\n * - \u5173\u95ED\u6309\u94AE\uFF08\u79FB\u52A8\u7AEF\u9690\u85CF\uFF0C\u684C\u9762\u7AEF\u663E\u793A\uFF09\n * - \u65B0\u4F1A\u8BDD\u6309\u94AE\uFF08\u6E05\u7A7A\u5F53\u524D\u5BF9\u8BDD\uFF09\n *\n * \u5E03\u5C40\uFF1A\n * - Logo (\u53EF\u9009) + \u6807\u9898 | \u65B0\u4F1A\u8BDD\u6309\u94AE + \u5173\u95ED\u6309\u94AE\n * - \u56FA\u5B9A\u9AD8\u5EA6 64px\n * - \u8FB9\u6846\u5206\u9694\u4E0B\u65B9\u5185\u5BB9\n *\n * @example\n * ```tsx\n * <ChatHeader\n * title=\"AI \u52A9\u624B\"\n * logoUrl=\"/logo.png\"\n * onClose={() => setIsOpen(false)}\n * onNewSession={() => createNewSession()}\n * />\n * ```\n */\nexport const ChatHeader: React.FC<ChatHeaderProps> = ({\n title = '
|
|
5
|
-
"mappings": "AA6CE,OAUE,OAAAA,EAVF,QAAAC,MAAA,oBADF,MAAMC,EAAsB,IAC1BD,EAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QAEf,UAAAD,EAAC,QAAK,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,EACpCA,EAAC,QAAK,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GACtC,EAMIG,EAA2B,IAC/BF,EAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QAEf,UAAAD,EAAC,QAAK,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,EACrCA,EAAC,QAAK,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GACvC,EA0BWI,EAAwC,CAAC,CACpD,MAAAC,EAAQ,
|
|
4
|
+
"sourcesContent": ["/**\n * \u804A\u5929\u7A97\u53E3\u5934\u90E8\u7EC4\u4EF6\n * \u663E\u793A\u6807\u9898\u3001Logo\u3001\u5173\u95ED\u6309\u94AE\u548C\u65B0\u4F1A\u8BDD\u6309\u94AE\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u5934\u90E8\u8BBE\u8BA1\n */\n\nimport React from 'react'\n\nexport interface ChatHeaderProps {\n /**\n * \u5934\u90E8\u6807\u9898\n */\n title?: string\n\n /**\n * Logo URL\n */\n logoUrl?: string\n\n /**\n * \u5173\u95ED\u6309\u94AE\u70B9\u51FB\u56DE\u8C03\n */\n onClose?: () => void\n\n /**\n * \u65B0\u4F1A\u8BDD\u6309\u94AE\u70B9\u51FB\u56DE\u8C03\n */\n onNewSession?: () => void\n\n /**\n * \u662F\u5426\u663E\u793A\u65B0\u4F1A\u8BDD\u6309\u94AE\n * @default true\n */\n showNewSessionButton?: boolean\n\n /**\n * \u81EA\u5B9A\u4E49\u6837\u5F0F\u7C7B\u540D\n */\n className?: string\n}\n\n/**\n * \u9ED8\u8BA4\u5173\u95ED\u56FE\u6807 (X)\n */\nconst CloseIcon: React.FC = () => (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n)\n\n/**\n * \u65B0\u4F1A\u8BDD\u56FE\u6807 (\u52A0\u53F7)\n */\nconst NewSessionIcon: React.FC = () => (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n)\n\n/**\n * \u804A\u5929\u7A97\u53E3\u5934\u90E8\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u6807\u9898\u548C Logo\n * - \u5173\u95ED\u6309\u94AE\uFF08\u79FB\u52A8\u7AEF\u9690\u85CF\uFF0C\u684C\u9762\u7AEF\u663E\u793A\uFF09\n * - \u65B0\u4F1A\u8BDD\u6309\u94AE\uFF08\u6E05\u7A7A\u5F53\u524D\u5BF9\u8BDD\uFF09\n *\n * \u5E03\u5C40\uFF1A\n * - Logo (\u53EF\u9009) + \u6807\u9898 | \u65B0\u4F1A\u8BDD\u6309\u94AE + \u5173\u95ED\u6309\u94AE\n * - \u56FA\u5B9A\u9AD8\u5EA6 64px\n * - \u8FB9\u6846\u5206\u9694\u4E0B\u65B9\u5185\u5BB9\n *\n * @example\n * ```tsx\n * <ChatHeader\n * title=\"AI \u52A9\u624B\"\n * logoUrl=\"/logo.png\"\n * onClose={() => setIsOpen(false)}\n * onNewSession={() => createNewSession()}\n * />\n * ```\n */\nexport const ChatHeader: React.FC<ChatHeaderProps> = ({\n title = '',\n logoUrl,\n onClose,\n onNewSession,\n showNewSessionButton = true,\n className = '',\n}) => {\n return (\n <div\n className={`flex items-center justify-between border-b border-[#DADCE0] bg-white px-4 pb-3 pt-0 tablet:pt-4 ${className}`}\n >\n {/* \u5DE6\u4FA7\uFF1ALogo + \u6807\u9898 */}\n <div className=\"flex items-center gap-3\">\n {logoUrl && <img src={logoUrl} alt=\"Logo\" className=\"size-8 rounded-full object-cover\" />}\n <h2 className=\"text-lg font-semibold text-gray-900\">{title}</h2>\n </div>\n\n {/* \u53F3\u4FA7\uFF1A\u65B0\u4F1A\u8BDD\u6309\u94AE + \u5173\u95ED\u6309\u94AE */}\n <div className=\"flex items-center gap-2\">\n {/* \u65B0\u4F1A\u8BDD\u6309\u94AE */}\n {showNewSessionButton && onNewSession && (\n <button\n type=\"button\"\n onClick={onNewSession}\n className=\"rounded-lg p-2 text-gray-600\"\n aria-label=\"\u5F00\u59CB\u65B0\u4F1A\u8BDD\"\n title=\"\u5F00\u59CB\u65B0\u4F1A\u8BDD\"\n >\n <NewSessionIcon />\n </button>\n )}\n\n {/* \u5173\u95ED\u6309\u94AE */}\n {onClose && (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"tablet:block rounded-lg text-gray-600\"\n aria-label=\"Close chat\"\n >\n <CloseIcon />\n </button>\n )}\n </div>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "AA6CE,OAUE,OAAAA,EAVF,QAAAC,MAAA,oBADF,MAAMC,EAAsB,IAC1BD,EAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QAEf,UAAAD,EAAC,QAAK,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,EACpCA,EAAC,QAAK,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GACtC,EAMIG,EAA2B,IAC/BF,EAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QAEf,UAAAD,EAAC,QAAK,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,EACrCA,EAAC,QAAK,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GACvC,EA0BWI,EAAwC,CAAC,CACpD,MAAAC,EAAQ,GACR,QAAAC,EACA,QAAAC,EACA,aAAAC,EACA,qBAAAC,EAAuB,GACvB,UAAAC,EAAY,EACd,IAEIT,EAAC,OACC,UAAW,mGAAmGS,CAAS,GAGvH,UAAAT,EAAC,OAAI,UAAU,0BACZ,UAAAK,GAAWN,EAAC,OAAI,IAAKM,EAAS,IAAI,OAAO,UAAU,mCAAmC,EACvFN,EAAC,MAAG,UAAU,sCAAuC,SAAAK,EAAM,GAC7D,EAGAJ,EAAC,OAAI,UAAU,0BAEZ,UAAAQ,GAAwBD,GACvBR,EAAC,UACC,KAAK,SACL,QAASQ,EACT,UAAU,+BACV,aAAW,iCACX,MAAM,iCAEN,SAAAR,EAACG,EAAA,EAAe,EAClB,EAIDI,GACCP,EAAC,UACC,KAAK,SACL,QAASO,EACT,UAAU,yCACV,aAAW,aAEX,SAAAP,EAACE,EAAA,EAAU,EACb,GAEJ,GACF",
|
|
6
6
|
"names": ["jsx", "jsxs", "CloseIcon", "NewSessionIcon", "ChatHeader", "title", "logoUrl", "onClose", "onNewSession", "showNewSessionButton", "className"]
|
|
7
7
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{jsx as t,jsxs as x}from"react/jsx-runtime";import{MessageContent as l}from"./MessageContent";function g(e){const r=new Date(e),o=r.getHours().toString().padStart(2,"0"),i=r.getMinutes().toString().padStart(2,"0");return`${o}:${i}`}const h=({message:e,rendererRegistry:r,defaultRenderer:o,showTimestamp:i=!0,className:d="",onAddToCart:c})=>{const n=e.role==="user",v=e.role==="assistant",u=e.role==="system",m=e.role==="tool";if(u)return t("div",{className:`flex justify-center py-2 ${d}`,children:t("div",{className:"max-w-xs rounded-lg border border-yellow-200 bg-yellow-50 px-4 py-2",children:e.content.map((s,a)=>t(l,{content:s,isUser:!1,isSystem:!0,rendererRegistry:r,defaultRenderer:o},a))})});if(m)return null;const p=["product_list","product_comparison","faq_list","promotion_list","cart"],f=e.content.filter(s=>!p.includes(s.type)),y=e.content.filter(s=>p.includes(s.type));return t("div",{className:`flex ${n?"justify-end":"justify-start"} py-2 ${d}`,children:x("div",{className:"flex w-fit max-w-full flex-col gap-2",children:[f.length>0&&t("div",{className:`w-full min-w-0 overflow-hidden rounded-2xl
|
|
1
|
+
import{jsx as t,jsxs as x}from"react/jsx-runtime";import{MessageContent as l}from"./MessageContent";function g(e){const r=new Date(e),o=r.getHours().toString().padStart(2,"0"),i=r.getMinutes().toString().padStart(2,"0");return`${o}:${i}`}const h=({message:e,rendererRegistry:r,defaultRenderer:o,showTimestamp:i=!0,className:d="",onAddToCart:c})=>{const n=e.role==="user",v=e.role==="assistant",u=e.role==="system",m=e.role==="tool";if(u)return t("div",{className:`flex justify-center py-2 ${d}`,children:t("div",{className:"max-w-xs rounded-lg border border-yellow-200 bg-yellow-50 px-4 py-2",children:e.content.map((s,a)=>t(l,{content:s,isUser:!1,isSystem:!0,rendererRegistry:r,defaultRenderer:o},a))})});if(m)return null;const p=["product_list","product_comparison","faq_list","promotion_list","cart"],f=e.content.filter(s=>!p.includes(s.type)),y=e.content.filter(s=>p.includes(s.type));return t("div",{className:`flex ${n?"justify-end":"justify-start"} py-2 ${d}`,children:x("div",{className:"flex w-fit max-w-full flex-col gap-2",children:[f.length>0&&t("div",{className:`w-full min-w-0 overflow-hidden rounded-2xl p-3 ${n?"text-white":"text-[#1D1D1F]"}`,style:{backgroundColor:n?"#005D8E":"#F5F6F7"},children:t("div",{className:"flex w-full min-w-0 flex-col gap-2",children:f.map((s,a)=>t(l,{content:s,isUser:n,isSystem:!1,rendererRegistry:r,defaultRenderer:o,onAddToCart:c},a))})}),y.map((s,a)=>t("div",{className:"w-full",children:t(l,{content:s,isUser:n,isSystem:!1,rendererRegistry:r,defaultRenderer:o,onAddToCart:c})},`standalone-${a}`)),i&&t("span",{className:`
|
|
2
2
|
px-2 text-xs text-gray-400
|
|
3
3
|
${n?"text-right":"text-left"}
|
|
4
4
|
`,children:g(e.timestamp)})]})})};export{h as ChatMessage};
|