@anker-in/campaign-ui 0.2.11-beta.30 → 0.2.11-beta.32
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.js +1 -1
- package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/ChatInput.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/ChatInput.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js +2 -2
- package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/FAQList.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/FAQList.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/PolicyBlock.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/PolicyBlock.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +3 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.d.ts +1 -0
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
- package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.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/constants.js +1 -1
- package/dist/cjs/components/LiveChatWidget/constants.js.map +2 -2
- package/dist/cjs/components/LiveChatWidget/hooks/useChatState.d.ts +5 -1
- package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js +1 -1
- package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/types.d.ts +26 -0
- package/dist/cjs/components/LiveChatWidget/types.js.map +1 -1
- package/dist/cjs/stories/LiveChatWidget.stories.d.ts +8 -0
- package/dist/cjs/stories/LiveChatWidget.stories.js +21 -4
- package/dist/cjs/stories/LiveChatWidget.stories.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/LiveChatWidget.js +1 -1
- package/dist/esm/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/ChatInput.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/ChatInput.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/ChatMessage.js +2 -2
- package/dist/esm/components/LiveChatWidget/components/ChatMessage.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/ChatWindow.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/ChatWindow.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/MessageContent/FAQList.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/FAQList.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/MessageContent/PolicyBlock.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/PolicyBlock.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +3 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.d.ts +1 -0
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
- package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.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/constants.js +1 -1
- package/dist/esm/components/LiveChatWidget/constants.js.map +2 -2
- package/dist/esm/components/LiveChatWidget/hooks/useChatState.d.ts +5 -1
- package/dist/esm/components/LiveChatWidget/hooks/useChatState.js +1 -1
- package/dist/esm/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/types.d.ts +26 -0
- package/dist/esm/stories/LiveChatWidget.stories.d.ts +8 -0
- package/dist/esm/stories/LiveChatWidget.stories.js +24 -7
- package/dist/esm/stories/LiveChatWidget.stories.js.map +3 -3
- package/package.json +1 -1
- package/src/components/LiveChatWidget/LiveChatWidget.tsx +97 -48
- package/src/components/LiveChatWidget/components/ChatInput.tsx +26 -1
- package/src/components/LiveChatWidget/components/ChatMessage.tsx +11 -4
- package/src/components/LiveChatWidget/components/ChatWindow.tsx +2 -0
- package/src/components/LiveChatWidget/components/MessageContent/FAQList.tsx +1 -1
- package/src/components/LiveChatWidget/components/MessageContent/PolicyBlock.tsx +1 -1
- package/src/components/LiveChatWidget/components/MessageContent/ProductCard.tsx +12 -4
- package/src/components/LiveChatWidget/components/MessageContent/ProductComparison.tsx +23 -6
- package/src/components/LiveChatWidget/components/MessageContent/ProductList.tsx +1 -1
- package/src/components/LiveChatWidget/components/MessageContent/QuickReplies.tsx +1 -1
- package/src/components/LiveChatWidget/components/MessageContent/TextBlock.tsx +2 -2
- package/src/components/LiveChatWidget/constants.ts +12 -5
- package/src/components/LiveChatWidget/hooks/useChatState.ts +141 -10
- package/src/components/LiveChatWidget/types.ts +27 -0
- package/src/stories/LiveChatWidget.stories.tsx +182 -0
- package/src/styles/livechat.css +1 -2
|
@@ -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-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=\"
|
|
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,
|
|
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=\"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-bold\" />,\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,iBAAiB,EACtE,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,YAAY,EACzE,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 s=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var
|
|
1
|
+
"use strict";var s=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var n=Object.prototype.hasOwnProperty;var N=(e,o)=>{for(var r in o)s(e,r,{get:o[r],enumerable:!0})},a=(e,o,r,D)=>{if(o&&typeof o=="object"||typeof o=="function")for(let t of T(o))!n.call(e,t)&&t!==r&&s(e,t,{get:()=>o[t],enumerable:!(D=C(o,t))||D.enumerable});return e};var i=e=>a(s({},"__esModule",{value:!0}),e);var M={};N(M,{CURRENCY_SYMBOLS:()=>A,DEFAULT_COMMON_TEXT:()=>L});module.exports=i(M);const A={USD:"$",CAD:"CA$",GBP:"\xA3",EUR:"\u20AC",AUD:"$",SGD:"$",NZD:"NZ$",AED:"AED ",VND:"\u20AB",TWD:"NT$",PLN:"z\u0142",RON:"Lei"},L={learnMore:"Learn More",showLess:"Show Less",addToCart:"Add to Cart",viewMore:"View More",off:"OFF",total:"Total"};
|
|
2
2
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/LiveChatWidget/constants.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * LiveChat \u7EC4\u4EF6\u5E38\u91CF\u5B9A\u4E49\n */\n\nimport type { CommonText } from './types'\n\n/**\n * \u8D27\u5E01\u7B26\u53F7\u6620\u5C04\u8868\n * \u7528\u4E8E\u5C06\u8D27\u5E01\u4EE3\u7801\u8F6C\u6362\u4E3A\u5BF9\u5E94\u7684\u7B26\u53F7\n */\nexport const CURRENCY_SYMBOLS: Record<string, string> = {\n
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,sBAAAE,EAAA,wBAAAC,IAAA,eAAAC,EAAAJ,GAUO,MAAME,EAA2C,
|
|
4
|
+
"sourcesContent": ["/**\n * LiveChat \u7EC4\u4EF6\u5E38\u91CF\u5B9A\u4E49\n */\n\nimport type { CommonText } from './types'\n\n/**\n * \u8D27\u5E01\u7B26\u53F7\u6620\u5C04\u8868\n * \u7528\u4E8E\u5C06\u8D27\u5E01\u4EE3\u7801\u8F6C\u6362\u4E3A\u5BF9\u5E94\u7684\u7B26\u53F7\n */\nexport const CURRENCY_SYMBOLS: Record<string, string> = {\n USD: \"$\",\n CAD: \"CA$\",\n GBP: \"\u00A3\",\n EUR: \"\u20AC\",\n AUD: \"$\",\n SGD: \"$\",\n NZD: \"NZ$\",\n AED: \"AED \",\n VND: \"\u20AB\",\n TWD: \"NT$\",\n PLN: \"z\u0142\",\n RON: \"Lei\"\n}\n\n/**\n * \u9ED8\u8BA4\u6587\u6848\u914D\u7F6E\n */\nexport const DEFAULT_COMMON_TEXT: Required<CommonText> = {\n learnMore: 'Learn More',\n showLess: 'Show Less',\n addToCart: 'Add to Cart',\n viewMore: 'View More',\n off: 'OFF',\n total: 'Total',\n}\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,sBAAAE,EAAA,wBAAAC,IAAA,eAAAC,EAAAJ,GAUO,MAAME,EAA2C,CACpD,IAAK,IACL,IAAK,MACL,IAAK,OACL,IAAK,SACL,IAAK,IACL,IAAK,IACL,IAAK,MACL,IAAK,OACL,IAAK,SACL,IAAK,MACL,IAAK,UACL,IAAK,KACT,EAKaC,EAA4C,CACvD,UAAW,aACX,SAAU,YACV,UAAW,cACX,SAAU,YACV,IAAK,MACL,MAAO,OACT",
|
|
6
6
|
"names": ["constants_exports", "__export", "CURRENCY_SYMBOLS", "DEFAULT_COMMON_TEXT", "__toCommonJS"]
|
|
7
7
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* 管理消息列表、窗口状态、输入框状态、流式消息累积
|
|
4
4
|
* 基于 specs/livechat-widget/plan.md 的状态管理策略
|
|
5
5
|
*/
|
|
6
|
-
import type { Message, SSEEvent } from '../types';
|
|
6
|
+
import type { Message, SSEEvent, Product } from '../types';
|
|
7
7
|
export interface UseChatStateOptions {
|
|
8
8
|
/**
|
|
9
9
|
* 初始欢迎消息
|
|
@@ -64,6 +64,10 @@ export interface UseChatStateOptions {
|
|
|
64
64
|
* 购物车按钮点击回调
|
|
65
65
|
*/
|
|
66
66
|
onCart?: (cartId: string, checkoutUrl?: string) => void;
|
|
67
|
+
/**
|
|
68
|
+
* 自定义产品卡片渲染函数
|
|
69
|
+
*/
|
|
70
|
+
productCardRender?: (product: Product) => React.ReactNode;
|
|
67
71
|
}
|
|
68
72
|
export interface UseChatStateReturn {
|
|
69
73
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var W=Object.defineProperty;var ct=Object.getOwnPropertyDescriptor;var it=Object.getOwnPropertyNames;var dt=Object.prototype.hasOwnProperty;var ut=(t,i)=>{for(var C in i)W(t,C,{get:i[C],enumerable:!0})},lt=(t,i,C,M)=>{if(i&&typeof i=="object"||typeof i=="function")for(let f of it(i))!dt.call(t,f)&&f!==C&&W(t,f,{get:()=>i[f],enumerable:!(M=ct(i,f))||M.enumerable});return t};var pt=t=>lt(W({},"__esModule",{value:!0}),t);var yt={};ut(yt,{useChatState:()=>mt});module.exports=pt(yt);var l=require("react"),J=require("../utils/userId"),K=require("./useSession"),F=require("../utils/productTransformers"),j=require("../utils/cartTransformers");function ft(t,i,C,M,f){const o=[],g=/\{\{(?:product:)?([^}]+)\}\}/g;let I=0,r,m=!1;for(;(r=g.exec(t))!==null;){m=!0;const a=t.slice(I,r.index);a&&o.push({type:"text",text:a});const d=r[1].trim(),S=i.get(d),P=C.get(d);S?(console.log("[useChatState] \u{1F3AF} \u5B9E\u65F6\u68C0\u6D4B\u5230\u4EA7\u54C1:",d,"\u2192",S.title),o.push({type:"product_card",data:{product:S,rawProduct:P,onAddToCart:M,productCardRender:f}})):(console.warn("[useChatState] \u26A0\uFE0F \u5B9E\u65F6\u89E3\u6790\u672A\u627E\u5230\u4EA7\u54C1:",d),o.push({type:"text",text:r[0]})),I=g.lastIndex}if(m){const a=t.slice(I);return{contents:o,remainingBuffer:a}}else{const a=t.match(/\{\{[^}]*$/);if(a){const d=t.slice(0,a.index);return d&&o.push({type:"text",text:d}),{contents:o,remainingBuffer:a[0]}}else return t&&o.push({type:"text",text:t}),{contents:o,remainingBuffer:""}}}function gt(t,i,C,M,f){const o=[],g=/\{\{(?:product:)?([^}]+)\}\}/g;let I=0,r;for(;(r=g.exec(t))!==null;){const a=t.slice(I,r.index).trim(),d=r[1].trim();a&&o.push({type:"text",text:a});const S=i.get(d),P=C.get(d);if(S)console.log(`[useChatState] \u2705 \u627E\u5230\u4EA7\u54C1\u5339\u914D: ${d} \u2192 ${S.title}`),o.push({type:"product_card",data:{product:S,rawProduct:P,onAddToCart:M,productCardRender:f}});else{const w=Array.from(i.keys());console.warn(`[useChatState] \u274C Product not found for ID: "${d}"`),console.warn("[useChatState] \u53EF\u7528\u7684\u4EA7\u54C1ID\u5217\u8868:",w),console.warn("[useChatState] ID\u7C7B\u578B:",typeof d,"\u957F\u5EA6:",d.length),o.push({type:"text",text:r[0]})}I=g.lastIndex}const m=t.slice(I).trim();return m&&o.push({type:"text",text:m}),o}function ht(t,i,C,M,f){const o=[];for(const g of t)if(g.type==="text"){const r=gt(g.text,i,C,M,f);o.push(...r)}else{if(g.type==="product_list")continue;o.push(g)}return o}function Ct(t,i,C){if(!t.content.some(r=>r.type==="product_list")||!t.content.some(r=>r.type==="text"&&/\{\{(?:product:)?[^}]+\}\}/.test(r.text)))return t;console.log("[useChatState] \u68C0\u6D4B\u5230\u5386\u53F2\u6D88\u606F\u9700\u8981\u91CD\u7EC4, \u6D88\u606FID:",t.id);const o=new Map,g=new Map;t.structured_content&&t.structured_content.forEach(r=>{r.type==="product_list"&&Array.isArray(r.data)&&r.data.forEach(m=>{if(m&&m.shopify_product_id){const a=m.shopify_product_id;g.set(a,m);const d=a.match(/\d+$/)?.[0];d&&g.set(d,m),console.log("[useChatState] \u4ECE structured_content \u63D0\u53D6\u539F\u59CB\u4EA7\u54C1\u6570\u636E:",{fullId:a,numericId:d,title:m.title})}})}),t.content.forEach(r=>{r.type==="product_list"&&r.data.products.forEach(a=>{if(a&&a.shopifyId){o.set(a.shopifyId,a);const d=a.shopifyId.match(/\d+$/)?.[0];d&&o.set(d,a)}})});const I=ht(t.content,o,g,i,C);return{...t,content:I}}function mt(t={}){const{welcomeMessage:i,site:C,open:M,onOpenChange:f,onOpen:o,onClose:g,onMessageSend:I,onError:r,onTextMessage:m,onProductList:a,onPromotionList:d,onAddToCart:S,onCart:P,productCardRender:w}=t,{sessionId:L,saveSession:B,clearSession:U}=(0,K.useSession)(),[G,Q]=(0,l.useState)("");(0,l.useEffect)(()=>{(0,J.getUserId)().then(x=>Q(x))},[]);const[X,k]=(0,l.useState)(()=>i?[{id:`welcome-${Date.now()}`,role:"assistant",content:[{type:"text",text:i}],timestamp:Date.now()}]:[]),[Y,N]=(0,l.useState)(!1),v=M!==void 0,q=v?M:Y,[Z,tt]=(0,l.useState)(""),[et,b]=(0,l.useState)(!1),e=(0,l.useRef)(null),z=(0,l.useRef)(!1),A=(0,l.useRef)(new Map),$=(0,l.useRef)(new Map),T=(0,l.useRef)(""),D=(0,l.useRef)(null),nt=(0,l.useCallback)(()=>{v||N(!0),f?.(!0),o?.()},[v,f,o]),st=(0,l.useCallback)(()=>{v||N(!1),f?.(!1),g?.()},[v,f,g]),ot=(0,l.useCallback)(()=>{const x=!q;v||N(x),f?.(x),x?o?.():g?.()},[v,q,f,o,g]),O=(0,l.useCallback)(x=>{if(!x){console.warn("[useChatState] Attempted to add null/undefined message");return}k(R=>[...R,x])},[]),rt=(0,l.useCallback)(x=>{const R=x.filter(c=>c!=null);R.length!==x.length&&console.warn("[useChatState] Filtered out null/undefined messages from batch set");const E=R.map(c=>Ct(c,S,w));k(E)},[S,w]),V=(0,l.useCallback)(()=>{k([])},[]),at=(0,l.useCallback)(x=>{const{event:R,data:E}=x;switch(R){case"message_start":{b(!0),z.current=!1,T.current="";const c=E;c.sessionId&&c.sessionId!==L&&B(c.sessionId),k(u=>{const n=u[u.length-1];if(n&&n.role==="assistant"&&n.content.length===1&&n.content[0].type==="thinking")return e.current=n,u;{const y=`msg-${Date.now()}`;return e.current={id:y,role:"assistant",content:[{type:"thinking",data:{status:"thinking"}}],timestamp:Date.now()},[...u,e.current]}});break}case"content_delta":{const c=E,u=c.delta||c.text||"";if(e.current&&u){z.current||(z.current=!0,m?.()),e.current.content.some(s=>s.type==="thinking")&&(e.current.content=e.current.content.filter(s=>s.type!=="thinking"),D.current&&(clearTimeout(D.current),D.current=null)),T.current+=u;const{contents:p,remainingBuffer:y}=ft(T.current,A.current,$.current,S,w);T.current=y,p.length>0&&(p.forEach(s=>{const h=e.current.content[e.current.content.length-1];s.type==="text"&&h&&h.type==="text"?h.text+=s.text:e.current.content.push(s)}),k(s=>{if(!e.current)return s;const h=[...s],_=h.findIndex(H=>H&&H.id===e.current.id);return _>=0?h[_]={...e.current}:h.push({...e.current}),h}))}break}case"content_block":{const c=E;if(e.current){const u=c.type||c.data?.type,n=c.data;if(!u||!n){console.warn("[useChatState] Invalid content_block:",c);break}let p;if(u==="product_list"&&Array.isArray(n)){a?.(),n.forEach(s=>{if(s&&s.shopify_product_id){const h=s.shopify_product_id;$.current.set(h,s);const _=h.match(/\d+$/)?.[0];_&&$.current.set(_,s)}}),(0,F.transformProducts)(n,C).forEach(s=>{if(s&&s.shopifyId){A.current.set(s.shopifyId,s);const h=s.shopifyId.match(/\d+$/)?.[0];h&&A.current.set(h,s),console.log("[useChatState] \u5EFA\u7ACB\u4EA7\u54C1\u6620\u5C04:",{fullId:s.shopifyId,numericId:h,title:s.title})}}),console.log("[useChatState] \u2705 \u4EA7\u54C1\u5217\u8868\u5DF2\u7F13\u5B58\uFF0C\u4E0D\u6DFB\u52A0\u5230\u6D88\u606F\u4E2D\uFF08\u907F\u514D\u95EA\u70C1\uFF09");break}else u==="product_comparison"&&n.products?p={type:"product_comparison",data:{products:(0,F.transformProducts)(n.products,C),dimensions:n.dimensions||{}}}:u==="faq_list"&&n.found!==void 0?p={type:"faq_list",data:n}:u==="quick_replies"&&n.replies?p={type:"quick_replies",data:{replies:n.replies}}:u==="policy"&&n.title&&n.content?p={type:"policy",data:{title:n.title,content:n.content}}:u==="promotion_list"&&n.found!==void 0?(d?.(),p={type:"promotion_list",data:n}):u==="cart"&&n.id!==void 0?p={type:"cart",data:{...(0,j.transformCartData)(n),onCart:P}}:p={type:u,data:n};e.current.content.push(p),k(y=>{if(!e.current)return y;const s=[...y],h=s.findIndex(_=>_&&_.id===e.current.id);return h>=0?s[h]={...e.current}:s.push({...e.current}),s})}break}case"tool_start":case"tool_end":break;case"message_end":{if(b(!1),e.current&&T.current){console.log("[useChatState] \u5904\u7406\u5269\u4F59\u7F13\u51B2\u533A:",T.current);const c=e.current.content[e.current.content.length-1];c&&c.type==="text"?c.text+=T.current:e.current.content.push({type:"text",text:T.current}),k(u=>{if(!e.current)return u;const n=[...u],p=n.findIndex(y=>y&&y.id===e.current.id);return p>=0&&(n[p]={...e.current}),n})}T.current="",A.current.clear(),$.current.clear(),e.current=null;break}case"status":{E.type==="session_expired"&&(V(),U(),i&&O({id:`welcome-${Date.now()}`,role:"assistant",content:[{type:"text",text:i}],timestamp:Date.now()}));break}case"error":{const c=E;b(!1),e.current=null,O({id:`error-${Date.now()}`,role:"system",content:[{type:"error",data:{message:c.message,code:c.code}}],timestamp:Date.now()}),r?.(new Error(c.message));break}case"done":{if(b(!1),e.current&&e.current.content.some(u=>u.type==="thinking")){const u=e.current;D.current=setTimeout(()=>{k(n=>n.map(p=>{if(p.id===u.id){const y=p.content.filter(s=>s.type!=="thinking");return{...p,content:[...y,{type:"text",text:"Session timed out, please try again."}]}}return p}))},3*60*1e3)}e.current=null;break}default:break}},[i,C,O,V,U,B,L,r,m,a,d,S,P]);return(0,l.useEffect)(()=>()=>{D.current&&(clearTimeout(D.current),D.current=null)},[]),{messages:X,isOpen:q,userId:G,sessionId:L,inputValue:Z,isStreaming:et,openChat:nt,closeChat:st,toggleChat:ot,setInputValue:tt,addMessage:O,setMessages:rt,clearMessages:V,handleSSEEvent:at,saveSession:B,clearSession:U}}
|
|
2
2
|
//# sourceMappingURL=useChatState.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/components/LiveChatWidget/hooks/useChatState.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * \u804A\u5929\u72B6\u6001\u7BA1\u7406 Hook\n * \u7BA1\u7406\u6D88\u606F\u5217\u8868\u3001\u7A97\u53E3\u72B6\u6001\u3001\u8F93\u5165\u6846\u72B6\u6001\u3001\u6D41\u5F0F\u6D88\u606F\u7D2F\u79EF\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u72B6\u6001\u7BA1\u7406\u7B56\u7565\n */\n\nimport { useState, useCallback, useRef, useEffect } from 'react'\nimport type { Message, MessageContent, SSEEvent, StatusData, ErrorData, MessageStartData, BackendCartData, Product, TextContent, ProductCardContent, ProductListContent } from '../types'\nimport { getUserId } from '../utils/userId'\nimport { useSession } from './useSession'\nimport { transformProducts } from '../utils/productTransformers'\nimport { transformCartData } from '../utils/cartTransformers'\n\n// ============================================================================\n// \u8F85\u52A9\u51FD\u6570\uFF1A\u6587\u672C\u89E3\u6790\u548C\u6D88\u606F\u91CD\u7EC4\n// ============================================================================\n\n/**\n * \u5B9E\u65F6\u89E3\u6790\u6D41\u5F0F\u6587\u672C\u4E2D\u7684\u4EA7\u54C1\u5360\u4F4D\u7B26\n * \u5904\u7406\u7F13\u51B2\u533A\u4E2D\u7684\u6587\u672C\uFF0C\u68C0\u6D4B\u5B8C\u6574\u7684 {{product:xxx}} \u5360\u4F4D\u7B26\n *\n * @param buffer \u5F53\u524D\u7F13\u51B2\u533A\u5185\u5BB9\uFF08\u5305\u542B\u65B0\u63A5\u6536\u7684\u6587\u672C\uFF09\n * @param productMap shopifyId \u2192 Product \u7684\u6620\u5C04\u8868\n * @param onAddToCart \u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n * @returns { contents: \u9700\u8981\u6DFB\u52A0\u7684\u5185\u5BB9\u6570\u7EC4, remainingBuffer: \u5269\u4F59\u7F13\u51B2\u533A\u5185\u5BB9 }\n */\nfunction parseStreamingText(\n buffer: string,\n productMap: Map<string, Product>,\n onAddToCart?: (product: Product) => void\n): { contents: MessageContent[]; remainingBuffer: string } {\n const contents: MessageContent[] = []\n const regex = /\\{\\{(?:product:)?([^}]+)\\}\\}/g\n\n let lastIndex = 0\n let match: RegExpExecArray | null\n let foundMatch = false\n\n // \u67E5\u627E\u6240\u6709\u5B8C\u6574\u7684\u5360\u4F4D\u7B26\n while ((match = regex.exec(buffer)) !== null) {\n foundMatch = true\n\n // \u63D0\u53D6\u5360\u4F4D\u7B26\u524D\u7684\u6587\u672C\n const beforeText = buffer.slice(lastIndex, match.index)\n if (beforeText) {\n contents.push({ type: 'text', text: beforeText } as TextContent)\n }\n\n // \u63D0\u53D6\u4EA7\u54C1 ID \u5E76\u521B\u5EFA\u4EA7\u54C1\u5361\u7247\n const productId = match[1].trim()\n const product = productMap.get(productId)\n\n if (product) {\n console.log('[useChatState] \uD83C\uDFAF \u5B9E\u65F6\u68C0\u6D4B\u5230\u4EA7\u54C1:', productId, '\u2192', product.title)\n contents.push({\n type: 'product_card',\n data: { product: product, onAddToCart: onAddToCart }\n } as ProductCardContent)\n } else {\n // \u4EA7\u54C1\u672A\u627E\u5230\uFF0C\u4FDD\u7559\u539F\u59CB\u6587\u672C\n console.warn('[useChatState] \u26A0\uFE0F \u5B9E\u65F6\u89E3\u6790\u672A\u627E\u5230\u4EA7\u54C1:', productId)\n contents.push({ type: 'text', text: match[0] } as TextContent)\n }\n\n lastIndex = regex.lastIndex\n }\n\n // \u5982\u679C\u627E\u5230\u4E86\u81F3\u5C11\u4E00\u4E2A\u5B8C\u6574\u5360\u4F4D\u7B26\n if (foundMatch) {\n // \u8FD4\u56DE\u5269\u4F59\u7684\u6587\u672C\u4F5C\u4E3A\u7F13\u51B2\u533A\uFF08\u53EF\u80FD\u5305\u542B\u4E0D\u5B8C\u6574\u7684\u5360\u4F4D\u7B26\uFF09\n const remainingBuffer = buffer.slice(lastIndex)\n return { contents, remainingBuffer }\n } else {\n // \u6CA1\u6709\u627E\u5230\u5B8C\u6574\u5360\u4F4D\u7B26\uFF0C\u68C0\u67E5\u662F\u5426\u6709\u4E0D\u5B8C\u6574\u7684\u5360\u4F4D\u7B26\u5F00\u5934\n // \u4F8B\u5982\uFF1A\u7F13\u51B2\u533A\u662F \"some text {{prod\"\uFF0C\u6211\u4EEC\u9700\u8981\u4FDD\u7559 \"{{prod\" \u7B49\u5F85\u66F4\u591A\u6587\u672C\n const incompleteMatch = buffer.match(/\\{\\{[^}]*$/)\n\n if (incompleteMatch) {\n // \u6709\u4E0D\u5B8C\u6574\u7684\u5360\u4F4D\u7B26\u5F00\u5934\n const completeText = buffer.slice(0, incompleteMatch.index)\n if (completeText) {\n contents.push({ type: 'text', text: completeText } as TextContent)\n }\n return { contents, remainingBuffer: incompleteMatch[0] }\n } else {\n // \u6CA1\u6709\u4E0D\u5B8C\u6574\u7684\u5360\u4F4D\u7B26\uFF0C\u6574\u4E2A\u7F13\u51B2\u533A\u90FD\u662F\u666E\u901A\u6587\u672C\n if (buffer) {\n contents.push({ type: 'text', text: buffer } as TextContent)\n }\n return { contents, remainingBuffer: '' }\n }\n }\n}\n\n/**\n * \u89E3\u6790\u6587\u672C\u4E2D\u7684 {{shopifyId}}\uFF0C\u8FD4\u56DE [text, product_card, text, ...] \u6570\u7EC4\uFF08\u7528\u4E8E\u5386\u53F2\u6D88\u606F\u91CD\u7EC4\uFF09\n *\n * @param text \u5305\u542B {{shopifyId}} \u6807\u8BB0\u7684\u6587\u672C\n * @param productMap shopifyId \u2192 Product \u7684\u6620\u5C04\u8868\n * @param onAddToCart \u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n * @returns MessageContent \u6570\u7EC4\n *\n * @example\n * \u8F93\u5165\uFF1A\n * text: \"\u6211\u63A8\u8350\u4EE5\u4E0B\u4EA7\u54C1\uFF1A\\n{{12345}}\\n\u8FD9\u6B3E\u4EA7\u54C1\u6027\u4EF7\u6BD4\u5F88\u9AD8\u3002\"\n * productMap: Map { '12345' => Product {...} }\n * \u8F93\u51FA\uFF1A\n * [\n * { type: 'text', text: '\u6211\u63A8\u8350\u4EE5\u4E0B\u4EA7\u54C1\uFF1A' },\n * { type: 'product_card', data: { product: {...}, onAddToCart } },\n * { type: 'text', text: '\u8FD9\u6B3E\u4EA7\u54C1\u6027\u4EF7\u6BD4\u5F88\u9AD8\u3002' }\n * ]\n */\nfunction parseTextWithProductIds(\n text: string,\n productMap: Map<string, Product>,\n onAddToCart?: (product: Product) => void\n): MessageContent[] {\n const result: MessageContent[] = []\n // \u4FEE\u6539\u6B63\u5219\u8868\u8FBE\u5F0F\u4EE5\u5339\u914D {{product:ID}} \u683C\u5F0F\n // \u5339\u914D {{product:xxx}} \u6216 {{xxx}}\uFF08\u517C\u5BB9\u4E24\u79CD\u683C\u5F0F\uFF09\n const regex = /\\{\\{(?:product:)?([^}]+)\\}\\}/g\n\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n while ((match = regex.exec(text)) !== null) {\n const beforeText = text.slice(lastIndex, match.index).trim()\n // match[1] \u662F\u6355\u83B7\u7EC4\u4E2D\u7684\u5185\u5BB9\uFF0C\u5373 product: \u540E\u9762\u7684 ID\n const productId = match[1].trim()\n\n // \u6DFB\u52A0\u524D\u9762\u7684\u6587\u672C\uFF08\u5982\u679C\u6709\uFF09\n if (beforeText) {\n result.push({\n type: 'text',\n text: beforeText,\n } as TextContent)\n }\n\n // \u6DFB\u52A0\u4EA7\u54C1\u5361\u7247\n const product = productMap.get(productId)\n if (product) {\n console.log(`[useChatState] \u2705 \u627E\u5230\u4EA7\u54C1\u5339\u914D: ${productId} \u2192 ${product.title}`)\n result.push({\n type: 'product_card',\n data: {\n product: product,\n onAddToCart: onAddToCart,\n },\n } as ProductCardContent)\n } else {\n // \u627E\u4E0D\u5230\u4EA7\u54C1\u65F6\uFF0C\u4FDD\u7559\u539F\u59CB\u6807\u8BB0\uFF08\u4E0D\u5E94\u8BE5\u53D1\u751F\uFF0C\u4F46\u505A\u515C\u5E95\u5904\u7406\uFF09\n const availableKeys = Array.from(productMap.keys())\n console.warn(`[useChatState] \u274C Product not found for ID: \"${productId}\"`)\n console.warn('[useChatState] \u53EF\u7528\u7684\u4EA7\u54C1ID\u5217\u8868:', availableKeys)\n console.warn('[useChatState] ID\u7C7B\u578B:', typeof productId, '\u957F\u5EA6:', productId.length)\n result.push({\n type: 'text',\n text: match[0], // \u4FDD\u7559\u539F\u59CB\u5B8C\u6574\u6807\u8BB0\uFF0C\u5305\u62EC product: \u524D\u7F00\n } as TextContent)\n }\n\n lastIndex = regex.lastIndex\n }\n\n // \u6DFB\u52A0\u6700\u540E\u5269\u4F59\u7684\u6587\u672C\n const remainingText = text.slice(lastIndex).trim()\n if (remainingText) {\n result.push({\n type: 'text',\n text: remainingText,\n } as TextContent)\n }\n\n return result\n}\n\n/**\n * \u91CD\u7EC4\u6D88\u606F\u5185\u5BB9\uFF1A\u89E3\u6790\u6587\u672C\u4E2D\u7684 {{shopifyId}}\uFF0C\u66FF\u6362\u4E3A\u4EA7\u54C1\u5361\u7247\n *\n * \u5904\u7406\u903B\u8F91\uFF1A\n * 1. \u904D\u5386\u6D88\u606F\u7684\u6240\u6709 content blocks\n * 2. \u5BF9\u4E8E text \u7C7B\u578B\uFF0C\u89E3\u6790\u5176\u4E2D\u7684 {{shopifyId}} \u5E76\u62C6\u5206\u4E3A\u591A\u4E2A content\n * 3. \u8DF3\u8FC7 product_list \u7C7B\u578B\uFF08\u5DF2\u7ECF\u88AB\u62C6\u5206\u5230\u6587\u672C\u4E2D\uFF09\n * 4. \u5176\u4ED6\u7C7B\u578B\u76F4\u63A5\u4FDD\u7559\n *\n * @param contents \u539F\u59CB\u6D88\u606F\u5185\u5BB9\u6570\u7EC4\n * @param productMap shopifyId \u2192 Product \u7684\u6620\u5C04\u8868\n * @param onAddToCart \u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n * @returns \u91CD\u7EC4\u540E\u7684\u6D88\u606F\u5185\u5BB9\u6570\u7EC4\n */\nfunction reorganizeMessageContent(\n contents: MessageContent[],\n productMap: Map<string, Product>,\n onAddToCart?: (product: Product) => void\n): MessageContent[] {\n const result: MessageContent[] = []\n\n for (const content of contents) {\n // \u53EA\u5904\u7406\u6587\u672C\u7C7B\u578B\n if (content.type === 'text') {\n const textContent = content as TextContent\n const segments = parseTextWithProductIds(textContent.text, productMap, onAddToCart)\n result.push(...segments)\n }\n // \u8DF3\u8FC7 product_list\uFF08\u5DF2\u7ECF\u88AB\u62C6\u5206\u5230\u6587\u672C\u4E2D\uFF09\n else if (content.type === 'product_list') {\n continue\n }\n // \u5176\u4ED6\u7C7B\u578B\u76F4\u63A5\u4FDD\u7559\n else {\n result.push(content)\n }\n }\n\n return result\n}\n\n/**\n * \u5904\u7406\u5355\u6761\u6D88\u606F\u7684\u91CD\u7EC4\uFF08\u7528\u4E8E\u5386\u53F2\u6D88\u606F\u52A0\u8F7D\uFF09\n * \u5982\u679C\u6D88\u606F\u5305\u542B product_list \u548C\u5E26\u6709 {{}} \u7684\u6587\u672C\uFF0C\u5219\u8FDB\u884C\u91CD\u7EC4\n *\n * @param message \u539F\u59CB\u6D88\u606F\n * @param onAddToCart \u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n * @returns \u91CD\u7EC4\u540E\u7684\u6D88\u606F\uFF08\u5982\u679C\u9700\u8981\u91CD\u7EC4\uFF09\uFF0C\u5426\u5219\u8FD4\u56DE\u539F\u6D88\u606F\n */\nfunction maybeReorganizeHistoricalMessage(\n message: Message,\n onAddToCart?: (product: Product) => void\n): Message {\n // \u68C0\u67E5\u6D88\u606F\u662F\u5426\u5305\u542B product_list\n const hasProductList = message.content.some(c => c.type === 'product_list')\n if (!hasProductList) {\n return message // \u6CA1\u6709 product_list\uFF0C\u4E0D\u9700\u8981\u91CD\u7EC4\n }\n\n // \u68C0\u67E5\u6D88\u606F\u662F\u5426\u5305\u542B\u5E26\u6709 {{}} \u7684\u6587\u672C\n const hasPlaceholder = message.content.some(\n c => c.type === 'text' && /\\{\\{(?:product:)?[^}]+\\}\\}/.test((c as TextContent).text)\n )\n if (!hasPlaceholder) {\n return message // \u6CA1\u6709\u5360\u4F4D\u7B26\uFF0C\u4E0D\u9700\u8981\u91CD\u7EC4\n }\n\n console.log('[useChatState] \u68C0\u6D4B\u5230\u5386\u53F2\u6D88\u606F\u9700\u8981\u91CD\u7EC4, \u6D88\u606FID:', message.id)\n\n // \u6784\u5EFA\u4EA7\u54C1\u6620\u5C04\n const productMap = new Map<string, Product>()\n message.content.forEach(content => {\n if (content.type === 'product_list') {\n const productListContent = content as ProductListContent\n productListContent.data.products.forEach(product => {\n if (product && product.shopifyId) {\n // 1. \u4F7F\u7528\u5B8C\u6574\u7684 shopifyId \u4F5C\u4E3Akey\n productMap.set(product.shopifyId, product)\n\n // 2. \u540C\u65F6\u63D0\u53D6\u6570\u5B57\u90E8\u5206\u4F5C\u4E3Akey\n const numericId = product.shopifyId.match(/\\d+$/)?.[0]\n if (numericId) {\n productMap.set(numericId, product)\n }\n }\n })\n }\n })\n\n // \u91CD\u7EC4\u6D88\u606F\u5185\u5BB9\n const reorganizedContent = reorganizeMessageContent(message.content, productMap, onAddToCart)\n\n // \u8FD4\u56DE\u65B0\u6D88\u606F\u5BF9\u8C61\n return {\n ...message,\n content: reorganizedContent,\n }\n}\n\nexport interface UseChatStateOptions {\n /**\n * \u521D\u59CB\u6B22\u8FCE\u6D88\u606F\n */\n welcomeMessage?: string\n\n /**\n * Shopify \u5E97\u94FA\u57DF\u540D\n */\n site?: string\n\n /**\n * \u53D7\u63A7\u6A21\u5F0F\uFF1A\u662F\u5426\u6253\u5F00\u804A\u5929\u7A97\u53E3\n * \u63D0\u4F9B\u6B64\u53C2\u6570\u65F6\uFF0C\u7EC4\u4EF6\u5904\u4E8E\u53D7\u63A7\u6A21\u5F0F\n */\n open?: boolean\n\n /**\n * \u53D7\u63A7\u6A21\u5F0F\uFF1A\u72B6\u6001\u53D8\u5316\u56DE\u8C03\uFF08\u5FC5\u9700\uFF09\n * \u7528\u4E8E\u540C\u6B65\u72B6\u6001\u5230\u7236\u7EC4\u4EF6\n */\n onOpenChange?: (open: boolean) => void\n\n /**\n * \u7A97\u53E3\u6253\u5F00\u4E8B\u4EF6\u76D1\u542C\uFF08\u53EF\u9009\uFF09\n * \u7528\u4E8E\u57CB\u70B9\u3001\u65E5\u5FD7\u7B49\u526F\u4F5C\u7528\n */\n onOpen?: () => void\n\n /**\n * \u7A97\u53E3\u5173\u95ED\u4E8B\u4EF6\u76D1\u542C\uFF08\u53EF\u9009\uFF09\n * \u7528\u4E8E\u57CB\u70B9\u3001\u65E5\u5FD7\u7B49\u526F\u4F5C\u7528\n */\n onClose?: () => void\n\n /**\n * \u6D88\u606F\u53D1\u9001\u56DE\u8C03\n */\n onMessageSend?: (message: string) => void\n\n /**\n * \u9519\u8BEF\u5904\u7406\u56DE\u8C03\n */\n onError?: (error: Error) => void\n\n /**\n * AI \u6D88\u606F\u56DE\u8C03\n */\n /**\n * AI \u56DE\u590D\u6587\u672C\u6D88\u606F\u65F6\u89E6\u53D1\n */\n onTextMessage?: () => void\n\n /**\n * AI \u56DE\u590D\u5546\u54C1\u5217\u8868\u5361\u7247\u65F6\u89E6\u53D1\n */\n onProductList?: () => void\n\n /**\n * AI \u56DE\u590D\u4FC3\u9500\u5361\u7247\u65F6\u89E6\u53D1\n */\n onPromotionList?: () => void\n\n /**\n * \u5546\u54C1\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n */\n onAddToCart?: (product: any) => void\n\n /**\n * \u8D2D\u7269\u8F66\u6309\u94AE\u70B9\u51FB\u56DE\u8C03\n */\n onCart?: (cartId: string, checkoutUrl?: string) => void\n}\n\nexport interface UseChatStateReturn {\n /**\n * \u6D88\u606F\u5217\u8868\n */\n messages: Message[]\n\n /**\n * \u804A\u5929\u7A97\u53E3\u662F\u5426\u6253\u5F00\n */\n isOpen: boolean\n\n /**\n * \u7528\u6237 ID\n */\n userId: string\n\n /**\n * \u4F1A\u8BDD ID\n */\n sessionId: string | null\n\n /**\n * \u8F93\u5165\u6846\u5185\u5BB9\n */\n inputValue: string\n\n /**\n * \u662F\u5426\u6B63\u5728\u63A5\u6536\u6D41\u5F0F\u6D88\u606F\n */\n isStreaming: boolean\n\n /**\n * \u6253\u5F00\u804A\u5929\u7A97\u53E3\n */\n openChat: () => void\n\n /**\n * \u5173\u95ED\u804A\u5929\u7A97\u53E3\n */\n closeChat: () => void\n\n /**\n * \u5207\u6362\u804A\u5929\u7A97\u53E3\u72B6\u6001\n */\n toggleChat: () => void\n\n /**\n * \u8BBE\u7F6E\u8F93\u5165\u6846\u5185\u5BB9\n */\n setInputValue: (value: string) => void\n\n /**\n * \u6DFB\u52A0\u6D88\u606F\u5230\u5217\u8868\n */\n addMessage: (message: Message) => void\n\n /**\n * \u6279\u91CF\u8BBE\u7F6E\u6D88\u606F\u5217\u8868\uFF08\u7528\u4E8E\u52A0\u8F7D\u5386\u53F2\uFF09\n */\n setMessages: (messages: Message[]) => void\n\n /**\n * \u6E05\u7A7A\u6D88\u606F\u5217\u8868\n */\n clearMessages: () => void\n\n /**\n * \u5904\u7406 SSE \u4E8B\u4EF6\n */\n handleSSEEvent: (event: SSEEvent) => void\n\n /**\n * \u4FDD\u5B58\u4F1A\u8BDD ID\n */\n saveSession: (id: string) => void\n\n /**\n * \u6E05\u7A7A\u4F1A\u8BDD\n */\n clearSession: () => void\n}\n\n/**\n * \u804A\u5929\u72B6\u6001\u7BA1\u7406 Hook\n *\n * \u529F\u80FD\uFF1A\n * 1. \u7BA1\u7406\u6D88\u606F\u5217\u8868\uFF08\u6DFB\u52A0\u3001\u6E05\u7A7A\u3001\u6279\u91CF\u8BBE\u7F6E\uFF09\n * 2. \u7BA1\u7406\u7A97\u53E3\u72B6\u6001\uFF08\u6253\u5F00\u3001\u5173\u95ED\u3001\u5207\u6362\uFF09\n * 3. \u7BA1\u7406\u8F93\u5165\u6846\u72B6\u6001\n * 4. \u5904\u7406 SSE \u6D41\u5F0F\u6D88\u606F\u4E8B\u4EF6\n * 5. \u7D2F\u79EF\u6D41\u5F0F\u6587\u672C\u5185\u5BB9\n *\n * @param options Hook \u914D\u7F6E\u9009\u9879\n * @returns \u72B6\u6001\u7BA1\u7406\u5DE5\u5177\u5BF9\u8C61\n */\nexport function useChatState(options: UseChatStateOptions = {}): UseChatStateReturn {\n const {\n welcomeMessage,\n site,\n open: controlledOpen,\n onOpenChange,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n } = options\n\n // \u4F1A\u8BDD\u7BA1\u7406\n const { sessionId, saveSession, clearSession } = useSession()\n\n // \u7528\u6237 ID (\u521D\u59CB\u5316\u65F6\u5F02\u6B65\u751F\u6210)\n const [userId, setUserId] = useState<string>('')\n\n // \u521D\u59CB\u5316 userId\n useEffect(() => {\n getUserId().then(id => setUserId(id))\n }, [])\n\n // \u6D88\u606F\u5217\u8868\n const [messages, setMessagesState] = useState<Message[]>(() => {\n // \u5982\u679C\u6709\u6B22\u8FCE\u6D88\u606F\uFF0C\u521D\u59CB\u5316\u65F6\u6DFB\u52A0\n if (welcomeMessage) {\n return [\n {\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: [{ type: 'text', text: welcomeMessage }],\n timestamp: Date.now(),\n },\n ]\n }\n return []\n })\n\n // \u804A\u5929\u7A97\u53E3\u662F\u5426\u6253\u5F00\uFF08\u652F\u6301\u53D7\u63A7\u548C\u975E\u53D7\u63A7\u4E24\u79CD\u6A21\u5F0F\uFF09\n const [internalOpen, setInternalOpen] = useState(false)\n const isControlled = controlledOpen !== undefined\n const isOpen = isControlled ? controlledOpen : internalOpen\n\n // \u8F93\u5165\u6846\u5185\u5BB9\n const [inputValue, setInputValue] = useState('')\n\n // \u662F\u5426\u6B63\u5728\u63A5\u6536\u6D41\u5F0F\u6D88\u606F\n const [isStreaming, setIsStreaming] = useState(false)\n\n // \u5F53\u524D\u6B63\u5728\u7D2F\u79EF\u7684\u6D41\u5F0F\u6D88\u606F (\u4E34\u65F6\u5B58\u50A8)\n const currentMessageRef = useRef<Message | null>(null)\n\n // \u6807\u8BB0\u5F53\u524D\u6D88\u606F\u662F\u5426\u5DF2\u89E6\u53D1 onTextMessage \u56DE\u8C03\uFF08\u907F\u514D\u91CD\u590D\u89E6\u53D1\uFF09\n const textMessageCallbackTriggeredRef = useRef<boolean>(false)\n\n // \u4EA7\u54C1\u6620\u5C04\u7F13\u5B58 (shopifyId \u2192 Product)\uFF0C\u7528\u4E8E\u5B9E\u65F6\u89E3\u6790\u5360\u4F4D\u7B26\n const productMapRef = useRef<Map<string, Product>>(new Map())\n\n // \u6587\u672C\u7F13\u51B2\u533A\uFF0C\u7528\u4E8E\u5B58\u50A8\u672A\u5B8C\u6210\u7684\u6587\u672C\uFF08\u5904\u7406\u5360\u4F4D\u7B26\u8DE8\u8D8A\u591A\u4E2A delta \u7684\u60C5\u51B5\uFF09\n const textBufferRef = useRef<string>('')\n\n /**\n * \u6253\u5F00\u804A\u5929\u7A97\u53E3\n */\n const openChat = useCallback(() => {\n if (!isControlled) {\n setInternalOpen(true)\n }\n onOpenChange?.(true)\n onOpen?.()\n }, [isControlled, onOpenChange, onOpen])\n\n /**\n * \u5173\u95ED\u804A\u5929\u7A97\u53E3\n */\n const closeChat = useCallback(() => {\n if (!isControlled) {\n setInternalOpen(false)\n }\n onOpenChange?.(false)\n onClose?.()\n }, [isControlled, onOpenChange, onClose])\n\n /**\n * \u5207\u6362\u804A\u5929\u7A97\u53E3\u72B6\u6001\n */\n const toggleChat = useCallback(() => {\n const newState = !isOpen\n if (!isControlled) {\n setInternalOpen(newState)\n }\n onOpenChange?.(newState)\n if (newState) {\n onOpen?.()\n } else {\n onClose?.()\n }\n }, [isControlled, isOpen, onOpenChange, onOpen, onClose])\n\n /**\n * \u6DFB\u52A0\u6D88\u606F\u5230\u5217\u8868\n */\n const addMessage = useCallback((message: Message) => {\n // \u9632\u62A4\uFF1A\u5982\u679C\u6D88\u606F\u4E3A null \u6216 undefined\uFF0C\u4E0D\u6DFB\u52A0\n if (!message) {\n console.warn('[useChatState] Attempted to add null/undefined message')\n return\n }\n setMessagesState(prev => [...prev, message])\n }, [])\n\n /**\n * \u6279\u91CF\u8BBE\u7F6E\u6D88\u606F\u5217\u8868\uFF08\u7528\u4E8E\u52A0\u8F7D\u5386\u53F2\uFF09\n */\n const setMessages = useCallback(\n (newMessages: Message[]) => {\n // \u9632\u62A4\uFF1A\u8FC7\u6EE4\u6389 null/undefined \u6D88\u606F\n const validMessages = newMessages.filter(msg => msg != null)\n if (validMessages.length !== newMessages.length) {\n console.warn('[useChatState] Filtered out null/undefined messages from batch set')\n }\n\n // \u5BF9\u6BCF\u6761\u5386\u53F2\u6D88\u606F\u8FDB\u884C\u91CD\u7EC4\uFF08\u5982\u679C\u9700\u8981\uFF09\n const reorganizedMessages = validMessages.map(msg => maybeReorganizeHistoricalMessage(msg, onAddToCart))\n\n setMessagesState(reorganizedMessages)\n },\n [onAddToCart]\n )\n\n /**\n * \u6E05\u7A7A\u6D88\u606F\u5217\u8868\n */\n const clearMessages = useCallback(() => {\n setMessagesState([])\n }, [])\n\n /**\n * \u5904\u7406 SSE \u4E8B\u4EF6\n * \u6839\u636E\u4E8B\u4EF6\u7C7B\u578B\u8FDB\u884C\u4E0D\u540C\u7684\u5904\u7406\n */\n const handleSSEEvent = useCallback(\n (event: SSEEvent) => {\n const { event: eventType, data } = event\n\n switch (eventType) {\n case 'message_start': {\n // \u5F00\u59CB\u63A5\u6536\u65B0\u6D88\u606F\n setIsStreaming(true)\n\n // \u91CD\u7F6E\u6587\u672C\u6D88\u606F\u56DE\u8C03\u6807\u8BB0\n textMessageCallbackTriggeredRef.current = false\n\n // \u91CD\u7F6E\u6587\u672C\u7F13\u51B2\u533A\n textBufferRef.current = ''\n\n // T039: \u4FDD\u5B58 sessionId\uFF08\u5982\u679C\u540E\u7AEF\u8FD4\u56DE\uFF09\n const messageStartData = data as MessageStartData\n if (messageStartData.sessionId && messageStartData.sessionId !== sessionId) {\n saveSession(messageStartData.sessionId)\n }\n\n // \u68C0\u67E5\u6700\u540E\u4E00\u6761\u6D88\u606F\u662F\u5426\u662F thinking \u6D88\u606F\uFF08\u7528\u6237\u53D1\u9001\u6D88\u606F\u65F6\u5DF2\u6DFB\u52A0\uFF09\n setMessagesState(prev => {\n const lastMessage = prev[prev.length - 1]\n const hasThinking =\n lastMessage &&\n lastMessage.role === 'assistant' &&\n lastMessage.content.length === 1 &&\n lastMessage.content[0].type === 'thinking'\n\n if (hasThinking) {\n // \u590D\u7528\u5DF2\u5B58\u5728\u7684 thinking \u6D88\u606F\n currentMessageRef.current = lastMessage\n return prev // \u4E0D\u9700\u8981\u6DFB\u52A0\u65B0\u6D88\u606F\n } else {\n // \u6CA1\u6709 thinking \u6D88\u606F\uFF0C\u521B\u5EFA\u65B0\u7684\uFF08\u517C\u5BB9\u5176\u4ED6\u573A\u666F\uFF09\n const messageId = `msg-${Date.now()}`\n currentMessageRef.current = {\n id: messageId,\n role: 'assistant',\n content: [{ type: 'thinking', data: { status: 'thinking' } }],\n timestamp: Date.now(),\n }\n return [...prev, currentMessageRef.current!]\n }\n })\n break\n }\n\n case 'content_delta': {\n // \u7D2F\u79EF\u6D41\u5F0F\u6587\u672C\u5185\u5BB9\uFF0C\u5E76\u5B9E\u65F6\u68C0\u6D4B\u4EA7\u54C1\u5360\u4F4D\u7B26\n const deltaData = data as any\n const deltaText = deltaData.delta || deltaData.text || ''\n\n if (currentMessageRef.current && deltaText) {\n // \u89E6\u53D1\u6587\u672C\u6D88\u606F\u56DE\u8C03\uFF08\u4EC5\u89E6\u53D1\u4E00\u6B21\uFF09\n if (!textMessageCallbackTriggeredRef.current) {\n textMessageCallbackTriggeredRef.current = true\n onTextMessage?.()\n }\n\n // \u79FB\u9664\u601D\u8003\u6C14\u6CE1\uFF08\u5982\u679C\u5B58\u5728\uFF09\n const hasThinking = currentMessageRef.current.content.some(c => c.type === 'thinking')\n if (hasThinking) {\n currentMessageRef.current.content = currentMessageRef.current.content.filter(c => c.type !== 'thinking')\n }\n\n // \u5C06\u65B0\u6587\u672C\u6DFB\u52A0\u5230\u7F13\u51B2\u533A\n textBufferRef.current += deltaText\n\n // \u5B9E\u65F6\u89E3\u6790\u7F13\u51B2\u533A\u4E2D\u7684\u5360\u4F4D\u7B26\n const { contents, remainingBuffer } = parseStreamingText(\n textBufferRef.current,\n productMapRef.current,\n onAddToCart\n )\n\n // \u66F4\u65B0\u7F13\u51B2\u533A\u4E3A\u5269\u4F59\u5185\u5BB9\n textBufferRef.current = remainingBuffer\n\n // \u5C06\u89E3\u6790\u51FA\u7684\u5185\u5BB9\u6DFB\u52A0\u5230\u6D88\u606F\u4E2D\n if (contents.length > 0) {\n contents.forEach(content => {\n const lastContent = currentMessageRef.current!.content[\n currentMessageRef.current!.content.length - 1\n ] as MessageContent | undefined\n\n // \u5982\u679C\u662F\u6587\u672C\u5185\u5BB9\u4E14\u6700\u540E\u4E00\u4E2A\u4E5F\u662F\u6587\u672C\uFF0C\u5219\u5408\u5E76\n if (content.type === 'text' && lastContent && lastContent.type === 'text') {\n lastContent.text += content.text\n } else {\n // \u5426\u5219\u6DFB\u52A0\u65B0\u5185\u5BB9\n currentMessageRef.current!.content.push(content)\n }\n })\n\n // \u66F4\u65B0\u6D88\u606F\u5217\u8868\u4EE5\u89E6\u53D1\u6E32\u67D3\n setMessagesState(prev => {\n if (!currentMessageRef.current) return prev\n\n const updated = [...prev]\n const existingIndex = updated.findIndex(m => m && m.id === currentMessageRef.current!.id)\n\n if (existingIndex >= 0) {\n updated[existingIndex] = { ...currentMessageRef.current! }\n } else {\n updated.push({ ...currentMessageRef.current! })\n }\n\n return updated\n })\n }\n }\n break\n }\n\n case 'content_block': {\n // \u63A5\u6536\u7ED3\u6784\u5316\u5185\u5BB9\u5757\uFF08\u5546\u54C1\u3001\u653F\u7B56\u7B49\uFF09\n // API \u8FD4\u56DE\u683C\u5F0F\u53D8\u66F4:\n // \u65B0\u683C\u5F0F: {index: number, type: string, data: {...}} <- type \u5728\u5916\u5C42\n // \u65E7\u683C\u5F0F: {index: number, data: {type: string, ...}} <- type \u5728 data \u5185\n const blockData = data as any\n if (currentMessageRef.current) {\n // \u83B7\u53D6 type \u548C data\n // \u4F18\u5148\u4ECE\u5916\u5C42\u83B7\u53D6 type\uFF0C\u517C\u5BB9\u65E7\u683C\u5F0F\u4ECE data \u5185\u83B7\u53D6\n const contentType = blockData.type || blockData.data?.type\n const contentData = blockData.data\n\n if (!contentType || !contentData) {\n console.warn('[useChatState] Invalid content_block:', blockData)\n break\n }\n\n // ============================================================\n // \u8F6C\u6362\u6570\u636E\u7ED3\u6784\u4EE5\u5339\u914D\u7C7B\u578B\u5B9A\u4E49\n // \u6839\u636E\u540E\u7AEF\u6570\u636E\u7ED3\u6784\u89C4\u8303\uFF08\u98DE\u4E66\u6587\u6863\uFF09\u8FDB\u884C\u8F6C\u6362\n // ============================================================\n let messageContent: MessageContent\n\n // ========== 1. \u4EA7\u54C1\u5217\u8868\u5361\u7247 (Product List) ==========\n // \u540E\u7AEF\u683C\u5F0F: {type: \"product_list\", data: [product1, product2, ...]}\n // data \u76F4\u63A5\u662F\u4EA7\u54C1\u6570\u7EC4\uFF0C\u4E0D\u662F {products: [...]}\n if (contentType === 'product_list' && Array.isArray(contentData)) {\n // \u89E6\u53D1\u5546\u54C1\u5217\u8868\u56DE\u8C03\n onProductList?.()\n\n // \u4F7F\u7528\u7EDF\u4E00\u7684\u4EA7\u54C1\u8F6C\u6362\u5DE5\u5177\u51FD\u6570\n const transformedProducts = transformProducts(contentData, site)\n\n // \u5EFA\u7ACB\u4EA7\u54C1\u6620\u5C04\u7F13\u5B58 (shopifyId \u2192 Product)\n // \u7528\u4E8E\u540E\u7EED\u5728 message_end \u65F6\u89E3\u6790\u6587\u672C\u4E2D\u7684 {{shopifyId}}\n transformedProducts.forEach(product => {\n if (product && product.shopifyId) {\n // 1. \u4F7F\u7528\u5B8C\u6574\u7684 shopifyId \u4F5C\u4E3Akey\uFF08\u5982 \"gid://shopify/Product/123\"\uFF09\n productMapRef.current.set(product.shopifyId, product)\n\n // 2. \u540C\u65F6\u63D0\u53D6\u6570\u5B57\u90E8\u5206\u4F5C\u4E3Akey\uFF08\u5982 \"123\"\uFF09\uFF0C\u652F\u6301\u7B80\u5199\u683C\u5F0F\n // \u4ECE \"gid://shopify/Product/123\" \u6216 \"123\" \u4E2D\u63D0\u53D6\u7EAF\u6570\u5B57\n const numericId = product.shopifyId.match(/\\d+$/)?.[0]\n if (numericId) {\n productMapRef.current.set(numericId, product)\n }\n\n console.log('[useChatState] \u5EFA\u7ACB\u4EA7\u54C1\u6620\u5C04:', {\n fullId: product.shopifyId,\n numericId,\n title: product.title,\n })\n }\n })\n\n // \u26A0\uFE0F \u4E0D\u8981\u628A product_list \u6DFB\u52A0\u5230\u6D88\u606F\u4E2D\uFF0C\u907F\u514D\u95EA\u70C1\n // \u7B49\u5230 message_end \u65F6\uFF0C\u901A\u8FC7\u6587\u672C\u89E3\u6790\u521B\u5EFA product_card\uFF0C\u76F4\u63A5\u663E\u793A\u6700\u7EC8\u7684\u4EA4\u66FF\u683C\u5F0F\n console.log('[useChatState] \u2705 \u4EA7\u54C1\u5217\u8868\u5DF2\u7F13\u5B58\uFF0C\u4E0D\u6DFB\u52A0\u5230\u6D88\u606F\u4E2D\uFF08\u907F\u514D\u95EA\u70C1\uFF09')\n break // \u76F4\u63A5\u8DF3\u51FA\uFF0C\u4E0D\u6267\u884C\u540E\u7EED\u7684 push \u64CD\u4F5C\n }\n // ========== 2. \u4EA7\u54C1\u5BF9\u6BD4\u5361\u7247 (Product Comparison) ==========\n // \u540E\u7AEF\u683C\u5F0F: {type: \"product_comparison\", data: {products: [...], dimensions: {...}}}\n else if (contentType === 'product_comparison' && contentData.products) {\n // \u4F7F\u7528\u7EDF\u4E00\u7684\u4EA7\u54C1\u8F6C\u6362\u5DE5\u5177\u51FD\u6570\n const transformedProducts = transformProducts(contentData.products, site)\n\n messageContent = {\n type: 'product_comparison',\n data: {\n products: transformedProducts,\n dimensions: contentData.dimensions || {},\n },\n } as MessageContent\n }\n // ========== 3. FAQ \u5217\u8868\u5361\u7247 (FAQ List) ==========\n // \u540E\u7AEF\u683C\u5F0F: {type: \"faq_list\", data: {found, count, total, results: [...]}}\n else if (contentType === 'faq_list' && contentData.found !== undefined) {\n messageContent = {\n type: 'faq_list',\n data: contentData, // \u76F4\u63A5\u4F7F\u7528\uFF0C\u7ED3\u6784\u5DF2\u5339\u914D\n } as MessageContent\n }\n // ========== 4. \u5FEB\u6377\u56DE\u590D (Quick Replies) ==========\n else if (contentType === 'quick_replies' && contentData.replies) {\n messageContent = {\n type: 'quick_replies',\n data: {\n replies: contentData.replies,\n },\n } as MessageContent\n }\n // ========== 5. \u653F\u7B56\u5185\u5BB9 (Policy) ==========\n else if (contentType === 'policy' && contentData.title && contentData.content) {\n messageContent = {\n type: 'policy',\n data: {\n title: contentData.title,\n content: contentData.content,\n },\n } as MessageContent\n }\n // ========== 6. \u4FC3\u9500\u6D3B\u52A8\u5217\u8868 (Promotion List) ==========\n // \u540E\u7AEF\u683C\u5F0F: {type: \"promotion_list\", data: {found, count, total, results: [...]}}\n else if (contentType === 'promotion_list' && contentData.found !== undefined) {\n // \u89E6\u53D1\u4FC3\u9500\u5361\u7247\u56DE\u8C03\n onPromotionList?.()\n\n messageContent = {\n type: 'promotion_list',\n data: contentData, // \u76F4\u63A5\u4F7F\u7528\uFF0C\u7ED3\u6784\u5DF2\u5339\u914D\n } as MessageContent\n }\n // ========== 7. \u8D2D\u7269\u8F66 (Cart) ==========\n // \u540E\u7AEF\u683C\u5F0F: {type: \"cart\", data: {id, lines: {edges: [...]}, cost, ...}} (Shopify GraphQL)\n // \u9700\u8981\u8F6C\u6362\u4E3A\u524D\u7AEF\u683C\u5F0F: {cartId, lines: [...], cost, ...}\n else if (contentType === 'cart' && contentData.id !== undefined) {\n // \u8F6C\u6362\u540E\u7AEF Shopify GraphQL \u683C\u5F0F\u4E3A\u524D\u7AEF\u6807\u51C6\u683C\u5F0F\n const transformedData = transformCartData(contentData as BackendCartData)\n messageContent = {\n type: 'cart',\n data: {\n ...transformedData,\n onCart: onCart, // \u6CE8\u5165\u8D2D\u7269\u8F66\u6309\u94AE\u56DE\u8C03\n },\n } as MessageContent\n }\n // ========== 8. \u5176\u4ED6\u7C7B\u578B\uFF08\u901A\u7528\u5904\u7406\uFF09 ==========\n else {\n messageContent = {\n type: contentType,\n data: contentData,\n } as MessageContent\n }\n\n currentMessageRef.current.content.push(messageContent)\n\n setMessagesState(prev => {\n if (!currentMessageRef.current) return prev\n\n const updated = [...prev]\n const existingIndex = updated.findIndex(m => m && m.id === currentMessageRef.current!.id)\n\n if (existingIndex >= 0) {\n updated[existingIndex] = { ...currentMessageRef.current! }\n } else {\n updated.push({ ...currentMessageRef.current! })\n }\n\n return updated\n })\n }\n break\n }\n\n case 'tool_start':\n case 'tool_end': {\n // \u5DE5\u5177\u8C03\u7528\u4E8B\u4EF6\uFF0C\u6682\u65F6\u5FFD\u7565\n // \u53EF\u4EE5\u5728\u672A\u6765\u7528\u4E8E\u663E\u793A\u5DE5\u5177\u8C03\u7528\u72B6\u6001\n break\n }\n\n case 'message_end': {\n // \u6D88\u606F\u63A5\u6536\u5B8C\u6210\n setIsStreaming(false)\n\n // \u5904\u7406\u7F13\u51B2\u533A\u4E2D\u5269\u4F59\u7684\u6587\u672C\uFF08\u5982\u679C\u6709\uFF09\n if (currentMessageRef.current && textBufferRef.current) {\n console.log('[useChatState] \u5904\u7406\u5269\u4F59\u7F13\u51B2\u533A:', textBufferRef.current)\n\n const lastContent = currentMessageRef.current.content[\n currentMessageRef.current.content.length - 1\n ] as MessageContent | undefined\n\n // \u5982\u679C\u6700\u540E\u4E00\u4E2A\u5185\u5BB9\u662F\u6587\u672C\uFF0C\u5219\u5408\u5E76\n if (lastContent && lastContent.type === 'text') {\n lastContent.text += textBufferRef.current\n } else {\n // \u5426\u5219\u6DFB\u52A0\u4E3A\u65B0\u7684\u6587\u672C\u5757\n currentMessageRef.current.content.push({\n type: 'text',\n text: textBufferRef.current,\n })\n }\n\n // \u66F4\u65B0\u6D88\u606F\u5217\u8868\n setMessagesState(prev => {\n if (!currentMessageRef.current) return prev\n\n const updated = [...prev]\n const existingIndex = updated.findIndex(m => m && m.id === currentMessageRef.current!.id)\n\n if (existingIndex >= 0) {\n updated[existingIndex] = { ...currentMessageRef.current! }\n }\n\n return updated\n })\n }\n\n // \u6E05\u7A7A\u7F13\u51B2\u533A\u548C\u4EA7\u54C1\u6620\u5C04\n textBufferRef.current = ''\n productMapRef.current.clear()\n currentMessageRef.current = null\n break\n }\n\n case 'status': {\n // T040: \u72B6\u6001\u66F4\u65B0\uFF08\u5982\u4F1A\u8BDD\u8FC7\u671F\uFF09\n const statusData = data as StatusData\n if (statusData.type === 'session_expired') {\n // \u4F1A\u8BDD\u8FC7\u671F\uFF0C\u6E05\u7A7A\u6D88\u606F\u5217\u8868\u548C\u4F1A\u8BDD\n clearMessages()\n clearSession()\n if (welcomeMessage) {\n addMessage({\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: [{ type: 'text', text: welcomeMessage }],\n timestamp: Date.now(),\n })\n }\n }\n break\n }\n\n case 'error': {\n // \u9519\u8BEF\u5904\u7406\n const errorData = data as ErrorData\n setIsStreaming(false)\n currentMessageRef.current = null\n\n // \u6DFB\u52A0\u9519\u8BEF\u6D88\u606F\u5230\u754C\u9762\n addMessage({\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: errorData.message,\n code: errorData.code,\n },\n },\n ],\n timestamp: Date.now(),\n })\n\n onError?.(new Error(errorData.message))\n break\n }\n\n case 'done': {\n // \u6D41\u7ED3\u675F\n setIsStreaming(false)\n currentMessageRef.current = null\n break\n }\n\n default:\n // \u5176\u4ED6\u4E8B\u4EF6\u7C7B\u578B\uFF08tool_start, tool_end \u7B49\uFF09\n break\n }\n },\n [\n welcomeMessage,\n site,\n addMessage,\n clearMessages,\n clearSession,\n saveSession,\n sessionId,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n ]\n )\n\n return {\n messages,\n isOpen,\n userId,\n sessionId,\n inputValue,\n isStreaming,\n openChat,\n closeChat,\n toggleChat,\n setInputValue,\n addMessage,\n setMessages,\n clearMessages,\n handleSSEEvent,\n saveSession,\n clearSession,\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["useChatState_exports", "__export", "useChatState", "__toCommonJS", "import_react", "import_userId", "import_useSession", "import_productTransformers", "import_cartTransformers", "parseStreamingText", "buffer", "productMap", "onAddToCart", "contents", "regex", "lastIndex", "match", "foundMatch", "beforeText", "productId", "product", "remainingBuffer", "incompleteMatch", "completeText", "parseTextWithProductIds", "text", "result", "availableKeys", "remainingText", "reorganizeMessageContent", "content", "segments", "maybeReorganizeHistoricalMessage", "message", "c", "numericId", "reorganizedContent", "options", "welcomeMessage", "site", "controlledOpen", "onOpenChange", "onOpen", "onClose", "onMessageSend", "onError", "onTextMessage", "onProductList", "onPromotionList", "onCart", "sessionId", "saveSession", "clearSession", "userId", "setUserId", "id", "messages", "setMessagesState", "internalOpen", "setInternalOpen", "isControlled", "isOpen", "inputValue", "setInputValue", "isStreaming", "setIsStreaming", "currentMessageRef", "textMessageCallbackTriggeredRef", "productMapRef", "textBufferRef", "openChat", "closeChat", "toggleChat", "newState", "addMessage", "prev", "setMessages", "newMessages", "validMessages", "msg", "reorganizedMessages", "clearMessages", "handleSSEEvent", "event", "eventType", "data", "messageStartData", "lastMessage", "messageId", "deltaData", "deltaText", "lastContent", "updated", "existingIndex", "m", "blockData", "contentType", "contentData", "messageContent", "errorData"]
|
|
4
|
+
"sourcesContent": ["/**\n * \u804A\u5929\u72B6\u6001\u7BA1\u7406 Hook\n * \u7BA1\u7406\u6D88\u606F\u5217\u8868\u3001\u7A97\u53E3\u72B6\u6001\u3001\u8F93\u5165\u6846\u72B6\u6001\u3001\u6D41\u5F0F\u6D88\u606F\u7D2F\u79EF\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u72B6\u6001\u7BA1\u7406\u7B56\u7565\n */\n\nimport { useState, useCallback, useRef, useEffect } from 'react'\nimport type { Message, MessageContent, SSEEvent, StatusData, ErrorData, MessageStartData, BackendCartData, Product, TextContent, ProductCardContent, ProductListContent } from '../types'\nimport { getUserId } from '../utils/userId'\nimport { useSession } from './useSession'\nimport { transformProducts } from '../utils/productTransformers'\nimport { transformCartData } from '../utils/cartTransformers'\n\n// ============================================================================\n// \u8F85\u52A9\u51FD\u6570\uFF1A\u6587\u672C\u89E3\u6790\u548C\u6D88\u606F\u91CD\u7EC4\n// ============================================================================\n\n/**\n * \u5B9E\u65F6\u89E3\u6790\u6D41\u5F0F\u6587\u672C\u4E2D\u7684\u4EA7\u54C1\u5360\u4F4D\u7B26\n * \u5904\u7406\u7F13\u51B2\u533A\u4E2D\u7684\u6587\u672C\uFF0C\u68C0\u6D4B\u5B8C\u6574\u7684 {{product:xxx}} \u5360\u4F4D\u7B26\n *\n * @param buffer \u5F53\u524D\u7F13\u51B2\u533A\u5185\u5BB9\uFF08\u5305\u542B\u65B0\u63A5\u6536\u7684\u6587\u672C\uFF09\n * @param productMap shopifyId \u2192 Product \u7684\u6620\u5C04\u8868\n * @param rawProductMap shopifyId \u2192 raw backend product \u7684\u6620\u5C04\u8868\n * @param onAddToCart \u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n * @param productCardRender \u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6E32\u67D3\u51FD\u6570\n * @returns { contents: \u9700\u8981\u6DFB\u52A0\u7684\u5185\u5BB9\u6570\u7EC4, remainingBuffer: \u5269\u4F59\u7F13\u51B2\u533A\u5185\u5BB9 }\n */\nfunction parseStreamingText(\n buffer: string,\n productMap: Map<string, Product>,\n rawProductMap: Map<string, any>,\n onAddToCart?: (product: Product) => void,\n productCardRender?: (product: any) => React.ReactNode\n): { contents: MessageContent[]; remainingBuffer: string } {\n const contents: MessageContent[] = []\n const regex = /\\{\\{(?:product:)?([^}]+)\\}\\}/g\n\n let lastIndex = 0\n let match: RegExpExecArray | null\n let foundMatch = false\n\n // \u67E5\u627E\u6240\u6709\u5B8C\u6574\u7684\u5360\u4F4D\u7B26\n while ((match = regex.exec(buffer)) !== null) {\n foundMatch = true\n\n // \u63D0\u53D6\u5360\u4F4D\u7B26\u524D\u7684\u6587\u672C\n const beforeText = buffer.slice(lastIndex, match.index)\n if (beforeText) {\n contents.push({ type: 'text', text: beforeText } as TextContent)\n }\n\n // \u63D0\u53D6\u4EA7\u54C1 ID \u5E76\u521B\u5EFA\u4EA7\u54C1\u5361\u7247\n const productId = match[1].trim()\n const product = productMap.get(productId)\n const rawProduct = rawProductMap.get(productId)\n\n if (product) {\n console.log('[useChatState] \uD83C\uDFAF \u5B9E\u65F6\u68C0\u6D4B\u5230\u4EA7\u54C1:', productId, '\u2192', product.title)\n contents.push({\n type: 'product_card',\n data: {\n product: product,\n rawProduct: rawProduct,\n onAddToCart: onAddToCart,\n productCardRender: productCardRender\n }\n } as ProductCardContent)\n } else {\n // \u4EA7\u54C1\u672A\u627E\u5230\uFF0C\u4FDD\u7559\u539F\u59CB\u6587\u672C\n console.warn('[useChatState] \u26A0\uFE0F \u5B9E\u65F6\u89E3\u6790\u672A\u627E\u5230\u4EA7\u54C1:', productId)\n contents.push({ type: 'text', text: match[0] } as TextContent)\n }\n\n lastIndex = regex.lastIndex\n }\n\n // \u5982\u679C\u627E\u5230\u4E86\u81F3\u5C11\u4E00\u4E2A\u5B8C\u6574\u5360\u4F4D\u7B26\n if (foundMatch) {\n // \u8FD4\u56DE\u5269\u4F59\u7684\u6587\u672C\u4F5C\u4E3A\u7F13\u51B2\u533A\uFF08\u53EF\u80FD\u5305\u542B\u4E0D\u5B8C\u6574\u7684\u5360\u4F4D\u7B26\uFF09\n const remainingBuffer = buffer.slice(lastIndex)\n return { contents, remainingBuffer }\n } else {\n // \u6CA1\u6709\u627E\u5230\u5B8C\u6574\u5360\u4F4D\u7B26\uFF0C\u68C0\u67E5\u662F\u5426\u6709\u4E0D\u5B8C\u6574\u7684\u5360\u4F4D\u7B26\u5F00\u5934\n // \u4F8B\u5982\uFF1A\u7F13\u51B2\u533A\u662F \"some text {{prod\"\uFF0C\u6211\u4EEC\u9700\u8981\u4FDD\u7559 \"{{prod\" \u7B49\u5F85\u66F4\u591A\u6587\u672C\n const incompleteMatch = buffer.match(/\\{\\{[^}]*$/)\n\n if (incompleteMatch) {\n // \u6709\u4E0D\u5B8C\u6574\u7684\u5360\u4F4D\u7B26\u5F00\u5934\n const completeText = buffer.slice(0, incompleteMatch.index)\n if (completeText) {\n contents.push({ type: 'text', text: completeText } as TextContent)\n }\n return { contents, remainingBuffer: incompleteMatch[0] }\n } else {\n // \u6CA1\u6709\u4E0D\u5B8C\u6574\u7684\u5360\u4F4D\u7B26\uFF0C\u6574\u4E2A\u7F13\u51B2\u533A\u90FD\u662F\u666E\u901A\u6587\u672C\n if (buffer) {\n contents.push({ type: 'text', text: buffer } as TextContent)\n }\n return { contents, remainingBuffer: '' }\n }\n }\n}\n\n/**\n * \u89E3\u6790\u6587\u672C\u4E2D\u7684 {{shopifyId}}\uFF0C\u8FD4\u56DE [text, product_card, text, ...] \u6570\u7EC4\uFF08\u7528\u4E8E\u5386\u53F2\u6D88\u606F\u91CD\u7EC4\uFF09\n *\n * @param text \u5305\u542B {{shopifyId}} \u6807\u8BB0\u7684\u6587\u672C\n * @param productMap shopifyId \u2192 Product \u7684\u6620\u5C04\u8868\n * @param onAddToCart \u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n * @param productCardRender \u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6E32\u67D3\u51FD\u6570\n * @returns MessageContent \u6570\u7EC4\n *\n * @example\n * \u8F93\u5165\uFF1A\n * text: \"\u6211\u63A8\u8350\u4EE5\u4E0B\u4EA7\u54C1\uFF1A\\n{{12345}}\\n\u8FD9\u6B3E\u4EA7\u54C1\u6027\u4EF7\u6BD4\u5F88\u9AD8\u3002\"\n * productMap: Map { '12345' => Product {...} }\n * \u8F93\u51FA\uFF1A\n * [\n * { type: 'text', text: '\u6211\u63A8\u8350\u4EE5\u4E0B\u4EA7\u54C1\uFF1A' },\n * { type: 'product_card', data: { product: {...}, onAddToCart } },\n * { type: 'text', text: '\u8FD9\u6B3E\u4EA7\u54C1\u6027\u4EF7\u6BD4\u5F88\u9AD8\u3002' }\n * ]\n */\nfunction parseTextWithProductIds(\n text: string,\n productMap: Map<string, Product>,\n rawProductMap: Map<string, any>,\n onAddToCart?: (product: Product) => void,\n productCardRender?: (product: any) => React.ReactNode\n): MessageContent[] {\n const result: MessageContent[] = []\n // \u4FEE\u6539\u6B63\u5219\u8868\u8FBE\u5F0F\u4EE5\u5339\u914D {{product:ID}} \u683C\u5F0F\n // \u5339\u914D {{product:xxx}} \u6216 {{xxx}}\uFF08\u517C\u5BB9\u4E24\u79CD\u683C\u5F0F\uFF09\n const regex = /\\{\\{(?:product:)?([^}]+)\\}\\}/g\n\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n while ((match = regex.exec(text)) !== null) {\n const beforeText = text.slice(lastIndex, match.index).trim()\n // match[1] \u662F\u6355\u83B7\u7EC4\u4E2D\u7684\u5185\u5BB9\uFF0C\u5373 product: \u540E\u9762\u7684 ID\n const productId = match[1].trim()\n\n // \u6DFB\u52A0\u524D\u9762\u7684\u6587\u672C\uFF08\u5982\u679C\u6709\uFF09\n if (beforeText) {\n result.push({\n type: 'text',\n text: beforeText,\n } as TextContent)\n }\n\n // \u6DFB\u52A0\u4EA7\u54C1\u5361\u7247\n const product = productMap.get(productId)\n const rawProduct = rawProductMap.get(productId)\n if (product) {\n console.log(`[useChatState] \u2705 \u627E\u5230\u4EA7\u54C1\u5339\u914D: ${productId} \u2192 ${product.title}`)\n result.push({\n type: 'product_card',\n data: {\n product: product,\n rawProduct: rawProduct,\n onAddToCart: onAddToCart,\n productCardRender: productCardRender,\n },\n } as ProductCardContent)\n } else {\n // \u627E\u4E0D\u5230\u4EA7\u54C1\u65F6\uFF0C\u4FDD\u7559\u539F\u59CB\u6807\u8BB0\uFF08\u4E0D\u5E94\u8BE5\u53D1\u751F\uFF0C\u4F46\u505A\u515C\u5E95\u5904\u7406\uFF09\n const availableKeys = Array.from(productMap.keys())\n console.warn(`[useChatState] \u274C Product not found for ID: \"${productId}\"`)\n console.warn('[useChatState] \u53EF\u7528\u7684\u4EA7\u54C1ID\u5217\u8868:', availableKeys)\n console.warn('[useChatState] ID\u7C7B\u578B:', typeof productId, '\u957F\u5EA6:', productId.length)\n result.push({\n type: 'text',\n text: match[0], // \u4FDD\u7559\u539F\u59CB\u5B8C\u6574\u6807\u8BB0\uFF0C\u5305\u62EC product: \u524D\u7F00\n } as TextContent)\n }\n\n lastIndex = regex.lastIndex\n }\n\n // \u6DFB\u52A0\u6700\u540E\u5269\u4F59\u7684\u6587\u672C\n const remainingText = text.slice(lastIndex).trim()\n if (remainingText) {\n result.push({\n type: 'text',\n text: remainingText,\n } as TextContent)\n }\n\n return result\n}\n\n/**\n * \u91CD\u7EC4\u6D88\u606F\u5185\u5BB9\uFF1A\u89E3\u6790\u6587\u672C\u4E2D\u7684 {{shopifyId}}\uFF0C\u66FF\u6362\u4E3A\u4EA7\u54C1\u5361\u7247\n *\n * \u5904\u7406\u903B\u8F91\uFF1A\n * 1. \u904D\u5386\u6D88\u606F\u7684\u6240\u6709 content blocks\n * 2. \u5BF9\u4E8E text \u7C7B\u578B\uFF0C\u89E3\u6790\u5176\u4E2D\u7684 {{shopifyId}} \u5E76\u62C6\u5206\u4E3A\u591A\u4E2A content\n * 3. \u8DF3\u8FC7 product_list \u7C7B\u578B\uFF08\u5DF2\u7ECF\u88AB\u62C6\u5206\u5230\u6587\u672C\u4E2D\uFF09\n * 4. \u5176\u4ED6\u7C7B\u578B\u76F4\u63A5\u4FDD\u7559\n *\n * @param contents \u539F\u59CB\u6D88\u606F\u5185\u5BB9\u6570\u7EC4\n * @param productMap shopifyId \u2192 Product \u7684\u6620\u5C04\u8868\n * @param onAddToCart \u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n * @param productCardRender \u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6E32\u67D3\u51FD\u6570\n * @returns \u91CD\u7EC4\u540E\u7684\u6D88\u606F\u5185\u5BB9\u6570\u7EC4\n */\nfunction reorganizeMessageContent(\n contents: MessageContent[],\n productMap: Map<string, Product>,\n rawProductMap: Map<string, any>,\n onAddToCart?: (product: Product) => void,\n productCardRender?: (product: any) => React.ReactNode\n): MessageContent[] {\n const result: MessageContent[] = []\n\n for (const content of contents) {\n // \u53EA\u5904\u7406\u6587\u672C\u7C7B\u578B\n if (content.type === 'text') {\n const textContent = content as TextContent\n const segments = parseTextWithProductIds(textContent.text, productMap, rawProductMap, onAddToCart, productCardRender)\n result.push(...segments)\n }\n // \u8DF3\u8FC7 product_list\uFF08\u5DF2\u7ECF\u88AB\u62C6\u5206\u5230\u6587\u672C\u4E2D\uFF09\n else if (content.type === 'product_list') {\n continue\n }\n // \u5176\u4ED6\u7C7B\u578B\u76F4\u63A5\u4FDD\u7559\n else {\n result.push(content)\n }\n }\n\n return result\n}\n\n/**\n * \u5904\u7406\u5355\u6761\u6D88\u606F\u7684\u91CD\u7EC4\uFF08\u7528\u4E8E\u5386\u53F2\u6D88\u606F\u52A0\u8F7D\uFF09\n * \u5982\u679C\u6D88\u606F\u5305\u542B product_list \u548C\u5E26\u6709 {{}} \u7684\u6587\u672C\uFF0C\u5219\u8FDB\u884C\u91CD\u7EC4\n *\n * @param message \u539F\u59CB\u6D88\u606F\n * @param onAddToCart \u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n * @param productCardRender \u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6E32\u67D3\u51FD\u6570\n * @returns \u91CD\u7EC4\u540E\u7684\u6D88\u606F\uFF08\u5982\u679C\u9700\u8981\u91CD\u7EC4\uFF09\uFF0C\u5426\u5219\u8FD4\u56DE\u539F\u6D88\u606F\n */\nfunction maybeReorganizeHistoricalMessage(\n message: Message,\n onAddToCart?: (product: Product) => void,\n productCardRender?: (product: any) => React.ReactNode\n): Message {\n // \u68C0\u67E5\u6D88\u606F\u662F\u5426\u5305\u542B product_list\n const hasProductList = message.content.some(c => c.type === 'product_list')\n if (!hasProductList) {\n return message // \u6CA1\u6709 product_list\uFF0C\u4E0D\u9700\u8981\u91CD\u7EC4\n }\n\n // \u68C0\u67E5\u6D88\u606F\u662F\u5426\u5305\u542B\u5E26\u6709 {{}} \u7684\u6587\u672C\n const hasPlaceholder = message.content.some(\n c => c.type === 'text' && /\\{\\{(?:product:)?[^}]+\\}\\}/.test((c as TextContent).text)\n )\n if (!hasPlaceholder) {\n return message // \u6CA1\u6709\u5360\u4F4D\u7B26\uFF0C\u4E0D\u9700\u8981\u91CD\u7EC4\n }\n\n console.log('[useChatState] \u68C0\u6D4B\u5230\u5386\u53F2\u6D88\u606F\u9700\u8981\u91CD\u7EC4, \u6D88\u606FID:', message.id)\n\n // \u6784\u5EFA\u4EA7\u54C1\u6620\u5C04\n const productMap = new Map<string, Product>()\n // \u4ECE structured_content \u4E2D\u63D0\u53D6\u539F\u59CB\u540E\u7AEF\u4EA7\u54C1\u6570\u636E\n const rawProductMap = new Map<string, any>()\n\n // \u4F18\u5148\u4ECE structured_content \u83B7\u53D6\u539F\u59CB\u4EA7\u54C1\u6570\u636E\uFF08\u5982\u679C\u5B58\u5728\uFF09\n if (message.structured_content) {\n message.structured_content.forEach(structuredContent => {\n if (structuredContent.type === 'product_list' && Array.isArray(structuredContent.data)) {\n structuredContent.data.forEach((rawProduct: any) => {\n if (rawProduct && rawProduct.shopify_product_id) {\n const shopifyId = rawProduct.shopify_product_id\n // 1. \u4F7F\u7528\u5B8C\u6574\u7684 shopifyId \u4F5C\u4E3Akey\n rawProductMap.set(shopifyId, rawProduct)\n\n // 2. \u540C\u65F6\u63D0\u53D6\u6570\u5B57\u90E8\u5206\u4F5C\u4E3Akey\n const numericId = shopifyId.match(/\\d+$/)?.[0]\n if (numericId) {\n rawProductMap.set(numericId, rawProduct)\n }\n\n console.log('[useChatState] \u4ECE structured_content \u63D0\u53D6\u539F\u59CB\u4EA7\u54C1\u6570\u636E:', {\n fullId: shopifyId,\n numericId,\n title: rawProduct.title,\n })\n }\n })\n }\n })\n }\n\n // \u6784\u5EFA\u8F6C\u6362\u540E\u7684\u4EA7\u54C1\u6620\u5C04\uFF08\u7528\u4E8E\u9ED8\u8BA4\u6E32\u67D3\uFF09\n message.content.forEach(content => {\n if (content.type === 'product_list') {\n const productListContent = content as ProductListContent\n productListContent.data.products.forEach(product => {\n if (product && product.shopifyId) {\n // 1. \u4F7F\u7528\u5B8C\u6574\u7684 shopifyId \u4F5C\u4E3Akey\n productMap.set(product.shopifyId, product)\n\n // 2. \u540C\u65F6\u63D0\u53D6\u6570\u5B57\u90E8\u5206\u4F5C\u4E3Akey\n const numericId = product.shopifyId.match(/\\d+$/)?.[0]\n if (numericId) {\n productMap.set(numericId, product)\n }\n }\n })\n }\n })\n\n // \u91CD\u7EC4\u6D88\u606F\u5185\u5BB9\n const reorganizedContent = reorganizeMessageContent(message.content, productMap, rawProductMap, onAddToCart, productCardRender)\n\n // \u8FD4\u56DE\u65B0\u6D88\u606F\u5BF9\u8C61\n return {\n ...message,\n content: reorganizedContent,\n }\n}\n\nexport interface UseChatStateOptions {\n /**\n * \u521D\u59CB\u6B22\u8FCE\u6D88\u606F\n */\n welcomeMessage?: string\n\n /**\n * Shopify \u5E97\u94FA\u57DF\u540D\n */\n site?: string\n\n /**\n * \u53D7\u63A7\u6A21\u5F0F\uFF1A\u662F\u5426\u6253\u5F00\u804A\u5929\u7A97\u53E3\n * \u63D0\u4F9B\u6B64\u53C2\u6570\u65F6\uFF0C\u7EC4\u4EF6\u5904\u4E8E\u53D7\u63A7\u6A21\u5F0F\n */\n open?: boolean\n\n /**\n * \u53D7\u63A7\u6A21\u5F0F\uFF1A\u72B6\u6001\u53D8\u5316\u56DE\u8C03\uFF08\u5FC5\u9700\uFF09\n * \u7528\u4E8E\u540C\u6B65\u72B6\u6001\u5230\u7236\u7EC4\u4EF6\n */\n onOpenChange?: (open: boolean) => void\n\n /**\n * \u7A97\u53E3\u6253\u5F00\u4E8B\u4EF6\u76D1\u542C\uFF08\u53EF\u9009\uFF09\n * \u7528\u4E8E\u57CB\u70B9\u3001\u65E5\u5FD7\u7B49\u526F\u4F5C\u7528\n */\n onOpen?: () => void\n\n /**\n * \u7A97\u53E3\u5173\u95ED\u4E8B\u4EF6\u76D1\u542C\uFF08\u53EF\u9009\uFF09\n * \u7528\u4E8E\u57CB\u70B9\u3001\u65E5\u5FD7\u7B49\u526F\u4F5C\u7528\n */\n onClose?: () => void\n\n /**\n * \u6D88\u606F\u53D1\u9001\u56DE\u8C03\n */\n onMessageSend?: (message: string) => void\n\n /**\n * \u9519\u8BEF\u5904\u7406\u56DE\u8C03\n */\n onError?: (error: Error) => void\n\n /**\n * AI \u6D88\u606F\u56DE\u8C03\n */\n /**\n * AI \u56DE\u590D\u6587\u672C\u6D88\u606F\u65F6\u89E6\u53D1\n */\n onTextMessage?: () => void\n\n /**\n * AI \u56DE\u590D\u5546\u54C1\u5217\u8868\u5361\u7247\u65F6\u89E6\u53D1\n */\n onProductList?: () => void\n\n /**\n * AI \u56DE\u590D\u4FC3\u9500\u5361\u7247\u65F6\u89E6\u53D1\n */\n onPromotionList?: () => void\n\n /**\n * \u5546\u54C1\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n */\n onAddToCart?: (product: any) => void\n\n /**\n * \u8D2D\u7269\u8F66\u6309\u94AE\u70B9\u51FB\u56DE\u8C03\n */\n onCart?: (cartId: string, checkoutUrl?: string) => void\n\n /**\n * \u81EA\u5B9A\u4E49\u4EA7\u54C1\u5361\u7247\u6E32\u67D3\u51FD\u6570\n */\n productCardRender?: (product: Product) => React.ReactNode\n}\n\nexport interface UseChatStateReturn {\n /**\n * \u6D88\u606F\u5217\u8868\n */\n messages: Message[]\n\n /**\n * \u804A\u5929\u7A97\u53E3\u662F\u5426\u6253\u5F00\n */\n isOpen: boolean\n\n /**\n * \u7528\u6237 ID\n */\n userId: string\n\n /**\n * \u4F1A\u8BDD ID\n */\n sessionId: string | null\n\n /**\n * \u8F93\u5165\u6846\u5185\u5BB9\n */\n inputValue: string\n\n /**\n * \u662F\u5426\u6B63\u5728\u63A5\u6536\u6D41\u5F0F\u6D88\u606F\n */\n isStreaming: boolean\n\n /**\n * \u6253\u5F00\u804A\u5929\u7A97\u53E3\n */\n openChat: () => void\n\n /**\n * \u5173\u95ED\u804A\u5929\u7A97\u53E3\n */\n closeChat: () => void\n\n /**\n * \u5207\u6362\u804A\u5929\u7A97\u53E3\u72B6\u6001\n */\n toggleChat: () => void\n\n /**\n * \u8BBE\u7F6E\u8F93\u5165\u6846\u5185\u5BB9\n */\n setInputValue: (value: string) => void\n\n /**\n * \u6DFB\u52A0\u6D88\u606F\u5230\u5217\u8868\n */\n addMessage: (message: Message) => void\n\n /**\n * \u6279\u91CF\u8BBE\u7F6E\u6D88\u606F\u5217\u8868\uFF08\u7528\u4E8E\u52A0\u8F7D\u5386\u53F2\uFF09\n */\n setMessages: (messages: Message[]) => void\n\n /**\n * \u6E05\u7A7A\u6D88\u606F\u5217\u8868\n */\n clearMessages: () => void\n\n /**\n * \u5904\u7406 SSE \u4E8B\u4EF6\n */\n handleSSEEvent: (event: SSEEvent) => void\n\n /**\n * \u4FDD\u5B58\u4F1A\u8BDD ID\n */\n saveSession: (id: string) => void\n\n /**\n * \u6E05\u7A7A\u4F1A\u8BDD\n */\n clearSession: () => void\n}\n\n/**\n * \u804A\u5929\u72B6\u6001\u7BA1\u7406 Hook\n *\n * \u529F\u80FD\uFF1A\n * 1. \u7BA1\u7406\u6D88\u606F\u5217\u8868\uFF08\u6DFB\u52A0\u3001\u6E05\u7A7A\u3001\u6279\u91CF\u8BBE\u7F6E\uFF09\n * 2. \u7BA1\u7406\u7A97\u53E3\u72B6\u6001\uFF08\u6253\u5F00\u3001\u5173\u95ED\u3001\u5207\u6362\uFF09\n * 3. \u7BA1\u7406\u8F93\u5165\u6846\u72B6\u6001\n * 4. \u5904\u7406 SSE \u6D41\u5F0F\u6D88\u606F\u4E8B\u4EF6\n * 5. \u7D2F\u79EF\u6D41\u5F0F\u6587\u672C\u5185\u5BB9\n *\n * @param options Hook \u914D\u7F6E\u9009\u9879\n * @returns \u72B6\u6001\u7BA1\u7406\u5DE5\u5177\u5BF9\u8C61\n */\nexport function useChatState(options: UseChatStateOptions = {}): UseChatStateReturn {\n const {\n welcomeMessage,\n site,\n open: controlledOpen,\n onOpenChange,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n productCardRender,\n } = options\n\n // \u4F1A\u8BDD\u7BA1\u7406\n const { sessionId, saveSession, clearSession } = useSession()\n\n // \u7528\u6237 ID (\u521D\u59CB\u5316\u65F6\u5F02\u6B65\u751F\u6210)\n const [userId, setUserId] = useState<string>('')\n\n // \u521D\u59CB\u5316 userId\n useEffect(() => {\n getUserId().then(id => setUserId(id))\n }, [])\n\n // \u6D88\u606F\u5217\u8868\n const [messages, setMessagesState] = useState<Message[]>(() => {\n // \u5982\u679C\u6709\u6B22\u8FCE\u6D88\u606F\uFF0C\u521D\u59CB\u5316\u65F6\u6DFB\u52A0\n if (welcomeMessage) {\n return [\n {\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: [{ type: 'text', text: welcomeMessage }],\n timestamp: Date.now(),\n },\n ]\n }\n return []\n })\n\n // \u804A\u5929\u7A97\u53E3\u662F\u5426\u6253\u5F00\uFF08\u652F\u6301\u53D7\u63A7\u548C\u975E\u53D7\u63A7\u4E24\u79CD\u6A21\u5F0F\uFF09\n const [internalOpen, setInternalOpen] = useState(false)\n const isControlled = controlledOpen !== undefined\n const isOpen = isControlled ? controlledOpen : internalOpen\n\n // \u8F93\u5165\u6846\u5185\u5BB9\n const [inputValue, setInputValue] = useState('')\n\n // \u662F\u5426\u6B63\u5728\u63A5\u6536\u6D41\u5F0F\u6D88\u606F\n const [isStreaming, setIsStreaming] = useState(false)\n\n // \u5F53\u524D\u6B63\u5728\u7D2F\u79EF\u7684\u6D41\u5F0F\u6D88\u606F (\u4E34\u65F6\u5B58\u50A8)\n const currentMessageRef = useRef<Message | null>(null)\n\n // \u6807\u8BB0\u5F53\u524D\u6D88\u606F\u662F\u5426\u5DF2\u89E6\u53D1 onTextMessage \u56DE\u8C03\uFF08\u907F\u514D\u91CD\u590D\u89E6\u53D1\uFF09\n const textMessageCallbackTriggeredRef = useRef<boolean>(false)\n\n // \u4EA7\u54C1\u6620\u5C04\u7F13\u5B58 (shopifyId \u2192 Product)\uFF0C\u7528\u4E8E\u5B9E\u65F6\u89E3\u6790\u5360\u4F4D\u7B26\n const productMapRef = useRef<Map<string, Product>>(new Map())\n\n // \u539F\u59CB\u4EA7\u54C1\u6570\u636E\u7F13\u5B58 (shopifyId \u2192 raw backend product)\uFF0C\u7528\u4E8E productCardRender\n const rawProductMapRef = useRef<Map<string, any>>(new Map())\n\n // \u6587\u672C\u7F13\u51B2\u533A\uFF0C\u7528\u4E8E\u5B58\u50A8\u672A\u5B8C\u6210\u7684\u6587\u672C\uFF08\u5904\u7406\u5360\u4F4D\u7B26\u8DE8\u8D8A\u591A\u4E2A delta \u7684\u60C5\u51B5\uFF09\n const textBufferRef = useRef<string>('')\n\n // \u8D85\u65F6\u5B9A\u65F6\u5668 ref\uFF0C\u7528\u4E8E\u5904\u7406 3 \u5206\u949F\u65E0\u54CD\u5E94\u7684\u60C5\u51B5\n const timeoutTimerRef = useRef<NodeJS.Timeout | null>(null)\n\n /**\n * \u6253\u5F00\u804A\u5929\u7A97\u53E3\n */\n const openChat = useCallback(() => {\n if (!isControlled) {\n setInternalOpen(true)\n }\n onOpenChange?.(true)\n onOpen?.()\n }, [isControlled, onOpenChange, onOpen])\n\n /**\n * \u5173\u95ED\u804A\u5929\u7A97\u53E3\n */\n const closeChat = useCallback(() => {\n if (!isControlled) {\n setInternalOpen(false)\n }\n onOpenChange?.(false)\n onClose?.()\n }, [isControlled, onOpenChange, onClose])\n\n /**\n * \u5207\u6362\u804A\u5929\u7A97\u53E3\u72B6\u6001\n */\n const toggleChat = useCallback(() => {\n const newState = !isOpen\n if (!isControlled) {\n setInternalOpen(newState)\n }\n onOpenChange?.(newState)\n if (newState) {\n onOpen?.()\n } else {\n onClose?.()\n }\n }, [isControlled, isOpen, onOpenChange, onOpen, onClose])\n\n /**\n * \u6DFB\u52A0\u6D88\u606F\u5230\u5217\u8868\n */\n const addMessage = useCallback((message: Message) => {\n // \u9632\u62A4\uFF1A\u5982\u679C\u6D88\u606F\u4E3A null \u6216 undefined\uFF0C\u4E0D\u6DFB\u52A0\n if (!message) {\n console.warn('[useChatState] Attempted to add null/undefined message')\n return\n }\n setMessagesState(prev => [...prev, message])\n }, [])\n\n /**\n * \u6279\u91CF\u8BBE\u7F6E\u6D88\u606F\u5217\u8868\uFF08\u7528\u4E8E\u52A0\u8F7D\u5386\u53F2\uFF09\n */\n const setMessages = useCallback(\n (newMessages: Message[]) => {\n // \u9632\u62A4\uFF1A\u8FC7\u6EE4\u6389 null/undefined \u6D88\u606F\n const validMessages = newMessages.filter(msg => msg != null)\n if (validMessages.length !== newMessages.length) {\n console.warn('[useChatState] Filtered out null/undefined messages from batch set')\n }\n\n // \u5BF9\u6BCF\u6761\u5386\u53F2\u6D88\u606F\u8FDB\u884C\u91CD\u7EC4\uFF08\u5982\u679C\u9700\u8981\uFF09\n const reorganizedMessages = validMessages.map(msg => maybeReorganizeHistoricalMessage(msg, onAddToCart, productCardRender))\n\n setMessagesState(reorganizedMessages)\n },\n [onAddToCart, productCardRender]\n )\n\n /**\n * \u6E05\u7A7A\u6D88\u606F\u5217\u8868\n */\n const clearMessages = useCallback(() => {\n setMessagesState([])\n }, [])\n\n /**\n * \u5904\u7406 SSE \u4E8B\u4EF6\n * \u6839\u636E\u4E8B\u4EF6\u7C7B\u578B\u8FDB\u884C\u4E0D\u540C\u7684\u5904\u7406\n */\n const handleSSEEvent = useCallback(\n (event: SSEEvent) => {\n const { event: eventType, data } = event\n\n switch (eventType) {\n case 'message_start': {\n // \u5F00\u59CB\u63A5\u6536\u65B0\u6D88\u606F\n setIsStreaming(true)\n\n // \u91CD\u7F6E\u6587\u672C\u6D88\u606F\u56DE\u8C03\u6807\u8BB0\n textMessageCallbackTriggeredRef.current = false\n\n // \u91CD\u7F6E\u6587\u672C\u7F13\u51B2\u533A\n textBufferRef.current = ''\n\n // T039: \u4FDD\u5B58 sessionId\uFF08\u5982\u679C\u540E\u7AEF\u8FD4\u56DE\uFF09\n const messageStartData = data as MessageStartData\n if (messageStartData.sessionId && messageStartData.sessionId !== sessionId) {\n saveSession(messageStartData.sessionId)\n }\n\n // \u68C0\u67E5\u6700\u540E\u4E00\u6761\u6D88\u606F\u662F\u5426\u662F thinking \u6D88\u606F\uFF08\u7528\u6237\u53D1\u9001\u6D88\u606F\u65F6\u5DF2\u6DFB\u52A0\uFF09\n setMessagesState(prev => {\n const lastMessage = prev[prev.length - 1]\n const hasThinking =\n lastMessage &&\n lastMessage.role === 'assistant' &&\n lastMessage.content.length === 1 &&\n lastMessage.content[0].type === 'thinking'\n\n if (hasThinking) {\n // \u590D\u7528\u5DF2\u5B58\u5728\u7684 thinking \u6D88\u606F\n currentMessageRef.current = lastMessage\n return prev // \u4E0D\u9700\u8981\u6DFB\u52A0\u65B0\u6D88\u606F\n } else {\n // \u6CA1\u6709 thinking \u6D88\u606F\uFF0C\u521B\u5EFA\u65B0\u7684\uFF08\u517C\u5BB9\u5176\u4ED6\u573A\u666F\uFF09\n const messageId = `msg-${Date.now()}`\n currentMessageRef.current = {\n id: messageId,\n role: 'assistant',\n content: [{ type: 'thinking', data: { status: 'thinking' } }],\n timestamp: Date.now(),\n }\n return [...prev, currentMessageRef.current!]\n }\n })\n break\n }\n\n case 'content_delta': {\n // \u7D2F\u79EF\u6D41\u5F0F\u6587\u672C\u5185\u5BB9\uFF0C\u5E76\u5B9E\u65F6\u68C0\u6D4B\u4EA7\u54C1\u5360\u4F4D\u7B26\n const deltaData = data as any\n const deltaText = deltaData.delta || deltaData.text || ''\n\n if (currentMessageRef.current && deltaText) {\n // \u89E6\u53D1\u6587\u672C\u6D88\u606F\u56DE\u8C03\uFF08\u4EC5\u89E6\u53D1\u4E00\u6B21\uFF09\n if (!textMessageCallbackTriggeredRef.current) {\n textMessageCallbackTriggeredRef.current = true\n onTextMessage?.()\n }\n\n // \u79FB\u9664\u601D\u8003\u6C14\u6CE1\uFF08\u5982\u679C\u5B58\u5728\uFF09\n const hasThinking = currentMessageRef.current.content.some(c => c.type === 'thinking')\n if (hasThinking) {\n currentMessageRef.current.content = currentMessageRef.current.content.filter(c => c.type !== 'thinking')\n\n // \u6E05\u9664\u8D85\u65F6\u5B9A\u65F6\u5668\uFF08\u5DF2\u6536\u5230\u5185\u5BB9\uFF0C\u4E0D\u518D\u9700\u8981\u8D85\u65F6\u63D0\u793A\uFF09\n if (timeoutTimerRef.current) {\n clearTimeout(timeoutTimerRef.current)\n timeoutTimerRef.current = null\n }\n }\n\n // \u5C06\u65B0\u6587\u672C\u6DFB\u52A0\u5230\u7F13\u51B2\u533A\n textBufferRef.current += deltaText\n\n // \u5B9E\u65F6\u89E3\u6790\u7F13\u51B2\u533A\u4E2D\u7684\u5360\u4F4D\u7B26\n const { contents, remainingBuffer } = parseStreamingText(\n textBufferRef.current,\n productMapRef.current,\n rawProductMapRef.current,\n onAddToCart,\n productCardRender\n )\n\n // \u66F4\u65B0\u7F13\u51B2\u533A\u4E3A\u5269\u4F59\u5185\u5BB9\n textBufferRef.current = remainingBuffer\n\n // \u5C06\u89E3\u6790\u51FA\u7684\u5185\u5BB9\u6DFB\u52A0\u5230\u6D88\u606F\u4E2D\n if (contents.length > 0) {\n contents.forEach(content => {\n const lastContent = currentMessageRef.current!.content[\n currentMessageRef.current!.content.length - 1\n ] as MessageContent | undefined\n\n // \u5982\u679C\u662F\u6587\u672C\u5185\u5BB9\u4E14\u6700\u540E\u4E00\u4E2A\u4E5F\u662F\u6587\u672C\uFF0C\u5219\u5408\u5E76\n if (content.type === 'text' && lastContent && lastContent.type === 'text') {\n lastContent.text += content.text\n } else {\n // \u5426\u5219\u6DFB\u52A0\u65B0\u5185\u5BB9\n currentMessageRef.current!.content.push(content)\n }\n })\n\n // \u66F4\u65B0\u6D88\u606F\u5217\u8868\u4EE5\u89E6\u53D1\u6E32\u67D3\n setMessagesState(prev => {\n if (!currentMessageRef.current) return prev\n\n const updated = [...prev]\n const existingIndex = updated.findIndex(m => m && m.id === currentMessageRef.current!.id)\n\n if (existingIndex >= 0) {\n updated[existingIndex] = { ...currentMessageRef.current! }\n } else {\n updated.push({ ...currentMessageRef.current! })\n }\n\n return updated\n })\n }\n }\n break\n }\n\n case 'content_block': {\n // \u63A5\u6536\u7ED3\u6784\u5316\u5185\u5BB9\u5757\uFF08\u5546\u54C1\u3001\u653F\u7B56\u7B49\uFF09\n // API \u8FD4\u56DE\u683C\u5F0F\u53D8\u66F4:\n // \u65B0\u683C\u5F0F: {index: number, type: string, data: {...}} <- type \u5728\u5916\u5C42\n // \u65E7\u683C\u5F0F: {index: number, data: {type: string, ...}} <- type \u5728 data \u5185\n const blockData = data as any\n if (currentMessageRef.current) {\n // \u83B7\u53D6 type \u548C data\n // \u4F18\u5148\u4ECE\u5916\u5C42\u83B7\u53D6 type\uFF0C\u517C\u5BB9\u65E7\u683C\u5F0F\u4ECE data \u5185\u83B7\u53D6\n const contentType = blockData.type || blockData.data?.type\n const contentData = blockData.data\n\n if (!contentType || !contentData) {\n console.warn('[useChatState] Invalid content_block:', blockData)\n break\n }\n\n // ============================================================\n // \u8F6C\u6362\u6570\u636E\u7ED3\u6784\u4EE5\u5339\u914D\u7C7B\u578B\u5B9A\u4E49\n // \u6839\u636E\u540E\u7AEF\u6570\u636E\u7ED3\u6784\u89C4\u8303\uFF08\u98DE\u4E66\u6587\u6863\uFF09\u8FDB\u884C\u8F6C\u6362\n // ============================================================\n let messageContent: MessageContent\n\n // ========== 1. \u4EA7\u54C1\u5217\u8868\u5361\u7247 (Product List) ==========\n // \u540E\u7AEF\u683C\u5F0F: {type: \"product_list\", data: [product1, product2, ...]}\n // data \u76F4\u63A5\u662F\u4EA7\u54C1\u6570\u7EC4\uFF0C\u4E0D\u662F {products: [...]}\n if (contentType === 'product_list' && Array.isArray(contentData)) {\n // \u89E6\u53D1\u5546\u54C1\u5217\u8868\u56DE\u8C03\n onProductList?.()\n\n // \u7F13\u5B58\u539F\u59CB\u540E\u7AEF\u4EA7\u54C1\u6570\u636E\uFF08\u7528\u4E8E productCardRender\uFF09\n contentData.forEach((rawProduct: any) => {\n if (rawProduct && rawProduct.shopify_product_id) {\n const shopifyId = rawProduct.shopify_product_id\n // 1. \u4F7F\u7528\u5B8C\u6574\u7684 shopifyId \u4F5C\u4E3Akey\n rawProductMapRef.current.set(shopifyId, rawProduct)\n\n // 2. \u540C\u65F6\u63D0\u53D6\u6570\u5B57\u90E8\u5206\u4F5C\u4E3Akey\n const numericId = shopifyId.match(/\\d+$/)?.[0]\n if (numericId) {\n rawProductMapRef.current.set(numericId, rawProduct)\n }\n }\n })\n\n // \u4F7F\u7528\u7EDF\u4E00\u7684\u4EA7\u54C1\u8F6C\u6362\u5DE5\u5177\u51FD\u6570\n const transformedProducts = transformProducts(contentData, site)\n\n // \u5EFA\u7ACB\u4EA7\u54C1\u6620\u5C04\u7F13\u5B58 (shopifyId \u2192 Product)\n // \u7528\u4E8E\u540E\u7EED\u5728 message_end \u65F6\u89E3\u6790\u6587\u672C\u4E2D\u7684 {{shopifyId}}\n transformedProducts.forEach(product => {\n if (product && product.shopifyId) {\n // 1. \u4F7F\u7528\u5B8C\u6574\u7684 shopifyId \u4F5C\u4E3Akey\uFF08\u5982 \"gid://shopify/Product/123\"\uFF09\n productMapRef.current.set(product.shopifyId, product)\n\n // 2. \u540C\u65F6\u63D0\u53D6\u6570\u5B57\u90E8\u5206\u4F5C\u4E3Akey\uFF08\u5982 \"123\"\uFF09\uFF0C\u652F\u6301\u7B80\u5199\u683C\u5F0F\n // \u4ECE \"gid://shopify/Product/123\" \u6216 \"123\" \u4E2D\u63D0\u53D6\u7EAF\u6570\u5B57\n const numericId = product.shopifyId.match(/\\d+$/)?.[0]\n if (numericId) {\n productMapRef.current.set(numericId, product)\n }\n\n console.log('[useChatState] \u5EFA\u7ACB\u4EA7\u54C1\u6620\u5C04:', {\n fullId: product.shopifyId,\n numericId,\n title: product.title,\n })\n }\n })\n\n // \u26A0\uFE0F \u4E0D\u8981\u628A product_list \u6DFB\u52A0\u5230\u6D88\u606F\u4E2D\uFF0C\u907F\u514D\u95EA\u70C1\n // \u7B49\u5230 message_end \u65F6\uFF0C\u901A\u8FC7\u6587\u672C\u89E3\u6790\u521B\u5EFA product_card\uFF0C\u76F4\u63A5\u663E\u793A\u6700\u7EC8\u7684\u4EA4\u66FF\u683C\u5F0F\n console.log('[useChatState] \u2705 \u4EA7\u54C1\u5217\u8868\u5DF2\u7F13\u5B58\uFF0C\u4E0D\u6DFB\u52A0\u5230\u6D88\u606F\u4E2D\uFF08\u907F\u514D\u95EA\u70C1\uFF09')\n break // \u76F4\u63A5\u8DF3\u51FA\uFF0C\u4E0D\u6267\u884C\u540E\u7EED\u7684 push \u64CD\u4F5C\n }\n // ========== 2. \u4EA7\u54C1\u5BF9\u6BD4\u5361\u7247 (Product Comparison) ==========\n // \u540E\u7AEF\u683C\u5F0F: {type: \"product_comparison\", data: {products: [...], dimensions: {...}}}\n else if (contentType === 'product_comparison' && contentData.products) {\n // \u4F7F\u7528\u7EDF\u4E00\u7684\u4EA7\u54C1\u8F6C\u6362\u5DE5\u5177\u51FD\u6570\n const transformedProducts = transformProducts(contentData.products, site)\n\n messageContent = {\n type: 'product_comparison',\n data: {\n products: transformedProducts,\n dimensions: contentData.dimensions || {},\n },\n } as MessageContent\n }\n // ========== 3. FAQ \u5217\u8868\u5361\u7247 (FAQ List) ==========\n // \u540E\u7AEF\u683C\u5F0F: {type: \"faq_list\", data: {found, count, total, results: [...]}}\n else if (contentType === 'faq_list' && contentData.found !== undefined) {\n messageContent = {\n type: 'faq_list',\n data: contentData, // \u76F4\u63A5\u4F7F\u7528\uFF0C\u7ED3\u6784\u5DF2\u5339\u914D\n } as MessageContent\n }\n // ========== 4. \u5FEB\u6377\u56DE\u590D (Quick Replies) ==========\n else if (contentType === 'quick_replies' && contentData.replies) {\n messageContent = {\n type: 'quick_replies',\n data: {\n replies: contentData.replies,\n },\n } as MessageContent\n }\n // ========== 5. \u653F\u7B56\u5185\u5BB9 (Policy) ==========\n else if (contentType === 'policy' && contentData.title && contentData.content) {\n messageContent = {\n type: 'policy',\n data: {\n title: contentData.title,\n content: contentData.content,\n },\n } as MessageContent\n }\n // ========== 6. \u4FC3\u9500\u6D3B\u52A8\u5217\u8868 (Promotion List) ==========\n // \u540E\u7AEF\u683C\u5F0F: {type: \"promotion_list\", data: {found, count, total, results: [...]}}\n else if (contentType === 'promotion_list' && contentData.found !== undefined) {\n // \u89E6\u53D1\u4FC3\u9500\u5361\u7247\u56DE\u8C03\n onPromotionList?.()\n\n messageContent = {\n type: 'promotion_list',\n data: contentData, // \u76F4\u63A5\u4F7F\u7528\uFF0C\u7ED3\u6784\u5DF2\u5339\u914D\n } as MessageContent\n }\n // ========== 7. \u8D2D\u7269\u8F66 (Cart) ==========\n // \u540E\u7AEF\u683C\u5F0F: {type: \"cart\", data: {id, lines: {edges: [...]}, cost, ...}} (Shopify GraphQL)\n // \u9700\u8981\u8F6C\u6362\u4E3A\u524D\u7AEF\u683C\u5F0F: {cartId, lines: [...], cost, ...}\n else if (contentType === 'cart' && contentData.id !== undefined) {\n // \u8F6C\u6362\u540E\u7AEF Shopify GraphQL \u683C\u5F0F\u4E3A\u524D\u7AEF\u6807\u51C6\u683C\u5F0F\n const transformedData = transformCartData(contentData as BackendCartData)\n messageContent = {\n type: 'cart',\n data: {\n ...transformedData,\n onCart: onCart, // \u6CE8\u5165\u8D2D\u7269\u8F66\u6309\u94AE\u56DE\u8C03\n },\n } as MessageContent\n }\n // ========== 8. \u5176\u4ED6\u7C7B\u578B\uFF08\u901A\u7528\u5904\u7406\uFF09 ==========\n else {\n messageContent = {\n type: contentType,\n data: contentData,\n } as MessageContent\n }\n\n currentMessageRef.current.content.push(messageContent)\n\n setMessagesState(prev => {\n if (!currentMessageRef.current) return prev\n\n const updated = [...prev]\n const existingIndex = updated.findIndex(m => m && m.id === currentMessageRef.current!.id)\n\n if (existingIndex >= 0) {\n updated[existingIndex] = { ...currentMessageRef.current! }\n } else {\n updated.push({ ...currentMessageRef.current! })\n }\n\n return updated\n })\n }\n break\n }\n\n case 'tool_start':\n case 'tool_end': {\n // \u5DE5\u5177\u8C03\u7528\u4E8B\u4EF6\uFF0C\u6682\u65F6\u5FFD\u7565\n // \u53EF\u4EE5\u5728\u672A\u6765\u7528\u4E8E\u663E\u793A\u5DE5\u5177\u8C03\u7528\u72B6\u6001\n break\n }\n\n case 'message_end': {\n // \u6D88\u606F\u63A5\u6536\u5B8C\u6210\n setIsStreaming(false)\n\n // \u5904\u7406\u7F13\u51B2\u533A\u4E2D\u5269\u4F59\u7684\u6587\u672C\uFF08\u5982\u679C\u6709\uFF09\n if (currentMessageRef.current && textBufferRef.current) {\n console.log('[useChatState] \u5904\u7406\u5269\u4F59\u7F13\u51B2\u533A:', textBufferRef.current)\n\n const lastContent = currentMessageRef.current.content[\n currentMessageRef.current.content.length - 1\n ] as MessageContent | undefined\n\n // \u5982\u679C\u6700\u540E\u4E00\u4E2A\u5185\u5BB9\u662F\u6587\u672C\uFF0C\u5219\u5408\u5E76\n if (lastContent && lastContent.type === 'text') {\n lastContent.text += textBufferRef.current\n } else {\n // \u5426\u5219\u6DFB\u52A0\u4E3A\u65B0\u7684\u6587\u672C\u5757\n currentMessageRef.current.content.push({\n type: 'text',\n text: textBufferRef.current,\n })\n }\n\n // \u66F4\u65B0\u6D88\u606F\u5217\u8868\n setMessagesState(prev => {\n if (!currentMessageRef.current) return prev\n\n const updated = [...prev]\n const existingIndex = updated.findIndex(m => m && m.id === currentMessageRef.current!.id)\n\n if (existingIndex >= 0) {\n updated[existingIndex] = { ...currentMessageRef.current! }\n }\n\n return updated\n })\n }\n\n // \u6E05\u7A7A\u7F13\u51B2\u533A\u548C\u4EA7\u54C1\u6620\u5C04\n textBufferRef.current = ''\n productMapRef.current.clear()\n rawProductMapRef.current.clear()\n currentMessageRef.current = null\n break\n }\n\n case 'status': {\n // T040: \u72B6\u6001\u66F4\u65B0\uFF08\u5982\u4F1A\u8BDD\u8FC7\u671F\uFF09\n const statusData = data as StatusData\n if (statusData.type === 'session_expired') {\n // \u4F1A\u8BDD\u8FC7\u671F\uFF0C\u6E05\u7A7A\u6D88\u606F\u5217\u8868\u548C\u4F1A\u8BDD\n clearMessages()\n clearSession()\n if (welcomeMessage) {\n addMessage({\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: [{ type: 'text', text: welcomeMessage }],\n timestamp: Date.now(),\n })\n }\n }\n break\n }\n\n case 'error': {\n // \u9519\u8BEF\u5904\u7406\n const errorData = data as ErrorData\n setIsStreaming(false)\n currentMessageRef.current = null\n\n // \u6DFB\u52A0\u9519\u8BEF\u6D88\u606F\u5230\u754C\u9762\n addMessage({\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: errorData.message,\n code: errorData.code,\n },\n },\n ],\n timestamp: Date.now(),\n })\n\n onError?.(new Error(errorData.message))\n break\n }\n\n case 'done': {\n // \u6D41\u7ED3\u675F\n setIsStreaming(false)\n\n // \u68C0\u67E5\u5F53\u524D\u6D88\u606F\u662F\u5426\u8FD8\u5B58\u5728 thinking block\n if (currentMessageRef.current) {\n const hasThinking = currentMessageRef.current.content.some(c => c.type === 'thinking')\n\n if (hasThinking) {\n // \u5982\u679C\u8FD8\u5B58\u5728 thinking block\uFF0C\u8BBE\u7F6E 3 \u5206\u949F\u8D85\u65F6\n const currentMessage = currentMessageRef.current\n\n timeoutTimerRef.current = setTimeout(() => {\n setMessagesState(prev => {\n return prev.map(msg => {\n if (msg.id === currentMessage.id) {\n // \u79FB\u9664 thinking block\uFF0C\u6DFB\u52A0\u8D85\u65F6\u63D0\u793A\n const contentWithoutThinking = msg.content.filter(c => c.type !== 'thinking')\n return {\n ...msg,\n content: [\n ...contentWithoutThinking,\n {\n type: 'text',\n text: 'Session timed out, please try again.',\n } as TextContent,\n ],\n }\n }\n return msg\n })\n })\n }, 3 * 60 * 1000) // 3 \u5206\u949F\n }\n }\n\n currentMessageRef.current = null\n break\n }\n\n default:\n // \u5176\u4ED6\u4E8B\u4EF6\u7C7B\u578B\uFF08tool_start, tool_end \u7B49\uFF09\n break\n }\n },\n [\n welcomeMessage,\n site,\n addMessage,\n clearMessages,\n clearSession,\n saveSession,\n sessionId,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n ]\n )\n\n // \u7EC4\u4EF6\u5378\u8F7D\u65F6\u6E05\u7406\u8D85\u65F6\u5B9A\u65F6\u5668\n useEffect(() => {\n return () => {\n if (timeoutTimerRef.current) {\n clearTimeout(timeoutTimerRef.current)\n timeoutTimerRef.current = null\n }\n }\n }, [])\n\n return {\n messages,\n isOpen,\n userId,\n sessionId,\n inputValue,\n isStreaming,\n openChat,\n closeChat,\n toggleChat,\n setInputValue,\n addMessage,\n setMessages,\n clearMessages,\n handleSSEEvent,\n saveSession,\n clearSession,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["useChatState_exports", "__export", "useChatState", "__toCommonJS", "import_react", "import_userId", "import_useSession", "import_productTransformers", "import_cartTransformers", "parseStreamingText", "buffer", "productMap", "rawProductMap", "onAddToCart", "productCardRender", "contents", "regex", "lastIndex", "match", "foundMatch", "beforeText", "productId", "product", "rawProduct", "remainingBuffer", "incompleteMatch", "completeText", "parseTextWithProductIds", "text", "result", "availableKeys", "remainingText", "reorganizeMessageContent", "content", "segments", "maybeReorganizeHistoricalMessage", "message", "c", "structuredContent", "shopifyId", "numericId", "reorganizedContent", "options", "welcomeMessage", "site", "controlledOpen", "onOpenChange", "onOpen", "onClose", "onMessageSend", "onError", "onTextMessage", "onProductList", "onPromotionList", "onCart", "sessionId", "saveSession", "clearSession", "userId", "setUserId", "id", "messages", "setMessagesState", "internalOpen", "setInternalOpen", "isControlled", "isOpen", "inputValue", "setInputValue", "isStreaming", "setIsStreaming", "currentMessageRef", "textMessageCallbackTriggeredRef", "productMapRef", "rawProductMapRef", "textBufferRef", "timeoutTimerRef", "openChat", "closeChat", "toggleChat", "newState", "addMessage", "prev", "setMessages", "newMessages", "validMessages", "msg", "reorganizedMessages", "clearMessages", "handleSSEEvent", "event", "eventType", "data", "messageStartData", "lastMessage", "messageId", "deltaData", "deltaText", "lastContent", "updated", "existingIndex", "m", "blockData", "contentType", "contentData", "messageContent", "errorData", "currentMessage", "contentWithoutThinking"]
|
|
7
7
|
}
|
|
@@ -30,6 +30,10 @@ export interface Message {
|
|
|
30
30
|
content: MessageContent[];
|
|
31
31
|
timestamp: number;
|
|
32
32
|
metadata?: MessageMetadata;
|
|
33
|
+
structured_content?: Array<{
|
|
34
|
+
type: string;
|
|
35
|
+
data: any;
|
|
36
|
+
}>;
|
|
33
37
|
}
|
|
34
38
|
export type MessageContentType = 'text' | 'product_card' | 'product_list' | 'product_comparison' | 'policy' | 'quick_replies' | 'thinking' | 'error' | 'faq_list' | 'cart';
|
|
35
39
|
export type MessageContent = TextContent | ProductCardContent | ProductListContent | ProductComparisonContent | PolicyContent | QuickRepliesContent | ThinkingContent | ErrorContent | FAQListContent | PromotionListContent | CartContent;
|
|
@@ -41,7 +45,9 @@ export interface ProductCardContent {
|
|
|
41
45
|
type: 'product_card';
|
|
42
46
|
data: {
|
|
43
47
|
product: Product;
|
|
48
|
+
rawProduct?: any;
|
|
44
49
|
onAddToCart?: (product: Product) => void;
|
|
50
|
+
productCardRender?: (product: any) => React.ReactNode;
|
|
45
51
|
};
|
|
46
52
|
}
|
|
47
53
|
export interface ProductListContent {
|
|
@@ -472,6 +478,7 @@ export interface StatusData {
|
|
|
472
478
|
export interface ErrorData {
|
|
473
479
|
message: string;
|
|
474
480
|
code?: string;
|
|
481
|
+
type?: string;
|
|
475
482
|
}
|
|
476
483
|
/**
|
|
477
484
|
* 流式对话请求参数
|
|
@@ -726,6 +733,25 @@ export interface LiveChatWidgetProps {
|
|
|
726
733
|
* 用于自定义按钮文案
|
|
727
734
|
*/
|
|
728
735
|
commonText?: CommonText;
|
|
736
|
+
/**
|
|
737
|
+
* 自定义产品卡片渲染函数
|
|
738
|
+
* 用于自定义渲染 product_card 类型的产品卡片
|
|
739
|
+
* 当提供此函数时,将替代默认的产品卡片渲染逻辑
|
|
740
|
+
* @param product 原始后端产品数据(未经转换的数据)
|
|
741
|
+
* @returns React 可渲染的内容
|
|
742
|
+
* @example
|
|
743
|
+
* ```tsx
|
|
744
|
+
* <LiveChatWidget
|
|
745
|
+
* productCardRender={(product) => (
|
|
746
|
+
* <div>
|
|
747
|
+
* <h3>{product.title}</h3>
|
|
748
|
+
* <p>{product.price_range?.min}</p>
|
|
749
|
+
* </div>
|
|
750
|
+
* )}
|
|
751
|
+
* />
|
|
752
|
+
* ```
|
|
753
|
+
*/
|
|
754
|
+
productCardRender?: (product: any) => React.ReactNode;
|
|
729
755
|
}
|
|
730
756
|
export interface MessageRenderer {
|
|
731
757
|
render: (content: MessageContent, isUser: boolean, isSystem: boolean) => React.ReactNode;
|