@anker-in/campaign-ui 0.2.11-beta.26 → 0.2.11-beta.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/cjs/components/LiveChatWidget/LiveChatWidget.d.ts +1 -1
  2. package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js.map +1 -1
  3. package/dist/cjs/components/LiveChatWidget/api/chat.js +1 -1
  4. package/dist/cjs/components/LiveChatWidget/api/chat.js.map +2 -2
  5. package/dist/cjs/components/LiveChatWidget/components/ChatHeader.js +1 -1
  6. package/dist/cjs/components/LiveChatWidget/components/ChatHeader.js.map +2 -2
  7. package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js +1 -1
  8. package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js.map +2 -2
  9. package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js +1 -1
  10. package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js.map +3 -3
  11. package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
  12. package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js.map +2 -2
  13. package/dist/cjs/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
  14. package/dist/cjs/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
  15. package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
  16. package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +2 -2
  17. package/dist/cjs/components/LiveChatWidget/utils/fetcher.js +1 -1
  18. package/dist/cjs/components/LiveChatWidget/utils/fetcher.js.map +2 -2
  19. package/dist/cjs/stories/LiveChatWidget.stories.js +4 -4
  20. package/dist/cjs/stories/LiveChatWidget.stories.js.map +2 -2
  21. package/dist/esm/components/LiveChatWidget/LiveChatWidget.d.ts +1 -1
  22. package/dist/esm/components/LiveChatWidget/LiveChatWidget.js.map +1 -1
  23. package/dist/esm/components/LiveChatWidget/api/chat.js +2 -2
  24. package/dist/esm/components/LiveChatWidget/api/chat.js.map +2 -2
  25. package/dist/esm/components/LiveChatWidget/components/ChatHeader.js +1 -1
  26. package/dist/esm/components/LiveChatWidget/components/ChatHeader.js.map +2 -2
  27. package/dist/esm/components/LiveChatWidget/components/ChatMessage.js +1 -1
  28. package/dist/esm/components/LiveChatWidget/components/ChatMessage.js.map +2 -2
  29. package/dist/esm/components/LiveChatWidget/components/ChatWindow.js +1 -1
  30. package/dist/esm/components/LiveChatWidget/components/ChatWindow.js.map +3 -3
  31. package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
  32. package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js.map +2 -2
  33. package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
  34. package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
  35. package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
  36. package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +2 -2
  37. package/dist/esm/components/LiveChatWidget/utils/fetcher.js +1 -1
  38. package/dist/esm/components/LiveChatWidget/utils/fetcher.js.map +2 -2
  39. package/dist/esm/stories/LiveChatWidget.stories.js +5 -5
  40. package/dist/esm/stories/LiveChatWidget.stories.js.map +2 -2
  41. package/dist/index.d.mts +1305 -0
  42. package/dist/index.d.ts +1305 -0
  43. package/dist/index.js +26656 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/index.mjs +26641 -0
  46. package/dist/index.mjs.map +1 -0
  47. package/package.json +1 -1
  48. package/src/components/LiveChatWidget/LiveChatWidget.tsx +1 -1
  49. package/src/components/LiveChatWidget/api/chat.ts +1 -1
  50. package/src/components/LiveChatWidget/components/ChatHeader.tsx +2 -3
  51. package/src/components/LiveChatWidget/components/ChatMessage.tsx +1 -1
  52. package/src/components/LiveChatWidget/components/ChatWindow.tsx +69 -20
  53. package/src/components/LiveChatWidget/components/MessageContent/CartCard.tsx +5 -5
  54. package/src/components/LiveChatWidget/components/MessageContent/QuickReplies.tsx +2 -2
  55. package/src/components/LiveChatWidget/components/MessageContent/TextBlock.tsx +1 -1
  56. package/src/components/LiveChatWidget/utils/fetcher.ts +1 -1
  57. package/src/stories/LiveChatWidget.stories.tsx +4 -4
  58. package/src/styles/livechat.css +8 -7
@@ -46,7 +46,7 @@ import type { LiveChatWidgetProps } from './types';
46
46
  * site="www.eufy.com"
47
47
  * needRecaptcha={true}
48
48
  * recaptchaSitekey="6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14"
49
- * recaptchaAction="livechat"
49
+ * recaptchaAction=""
50
50
  * />
51
51
  *
52
52
  * // 使用自定义 headers 和 reCAPTCHA
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/components/LiveChatWidget/LiveChatWidget.tsx"],
4
- "sourcesContent": ["/**\n * LiveChat \u4E3B\u7EC4\u4EF6\n * \u96C6\u6210\u6240\u6709\u5B50\u7EC4\u4EF6\uFF0C\u63D0\u4F9B\u5B8C\u6574\u7684\u804A\u5929\u529F\u80FD\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u4E09\u5C42\u67B6\u6784\u8BBE\u8BA1\n */\n\nimport React, { useEffect, useCallback } from 'react'\nimport * as Dialog from '@radix-ui/react-dialog'\nimport type {\n LiveChatWidgetProps,\n QuickReply,\n Message,\n MessageContent,\n ChatStreamRequest,\n BackendCartData,\n CommonText,\n} from './types'\nimport { DEFAULT_COMMON_TEXT } from './constants'\nimport { ChatBubble } from './components/ChatBubble'\nimport { ChatWindow } from './components/ChatWindow'\nimport { useChatState } from './hooks/useChatState'\nimport { useChatAPI } from './hooks/useChatAPI'\nimport { MessageRendererRegistry } from './utils/messageRenderers'\nimport { sanitizeInput } from './utils/validation'\nimport { transformProducts } from './utils/productTransformers.js'\nimport { transformCartData } from './utils/cartTransformers.js'\nimport {\n TextBlock,\n ProductCard,\n ProductList,\n ProductComparisonRenderer,\n PolicyBlock,\n createQuickRepliesRenderer,\n ThinkingBlock,\n ErrorBlock,\n FAQListRenderer,\n PromotionListRenderer,\n CartCard,\n} from './components/MessageContent/index.js'\n\n/**\n * LiveChat \u804A\u5929\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u6C14\u6CE1\u5F39\u7A97\u804A\u5929\u754C\u9762\n * - SSE \u6D41\u5F0F\u6D88\u606F\u63A5\u6536\n * - \u4F1A\u8BDD\u7BA1\u7406\uFF08userId, sessionId\uFF09\n * - \u5386\u53F2\u6D88\u606F\u52A0\u8F7D\n * - \u591A\u79CD\u6D88\u606F\u7C7B\u578B\u6E32\u67D3\n * - \u81EA\u5B9A\u4E49\u6269\u5C55\u673A\u5236\n * - reCAPTCHA v3 \u5B89\u5168\u9632\u62A4\n *\n * \u67B6\u6784\uFF1A\n * - UI Layer: ChatBubble, ChatWindow, MessageList, etc.\n * - Logic Layer: useChatState, useChatAPI, useSession\n * - Core Layer: MessageRendererRegistry, \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n *\n * @example\n * ```tsx\n * // \u57FA\u7840\u4F7F\u7528\uFF08\u4F7F\u7528\u9ED8\u8BA4\u4F4D\u7F6E\uFF09\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * welcomeMessage=\"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\"\n * onMessageSend={(msg) => console.log('Sent:', msg)}\n * />\n *\n * // \u4F7F\u7528\u81EA\u5B9A\u4E49\u4F4D\u7F6E\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * position={{ bottom: \"20px\", right: \"30px\" }}\n * onMessageSend={(msg) => console.log('Sent:', msg)}\n * />\n *\n * // \u542F\u7528 reCAPTCHA v3 \u5B89\u5168\u9632\u62A4\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * needRecaptcha={true}\n * recaptchaSitekey=\"6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14\"\n * recaptchaAction=\"livechat\"\n * />\n *\n * // \u4F7F\u7528\u81EA\u5B9A\u4E49 headers \u548C reCAPTCHA\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * headers={{\n * \"Authorization\": \"Bearer your-token\",\n * \"X-Custom-Header\": \"value\"\n * }}\n * needRecaptcha={true}\n * recaptchaSitekey=\"your-site-key\"\n * />\n * ```\n */\nexport const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({\n apiBaseUrl,\n headers,\n needRecaptcha,\n recaptchaSitekey,\n recaptchaAction,\n site,\n loginUserId,\n cartId,\n accessToken,\n position,\n welcomeMessage,\n quickReplies,\n customRenderers,\n logoUrl,\n title,\n chatBubbleIcon,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n showNewSessionButton,\n commonText,\n}) => {\n // \u5408\u5E76\u9ED8\u8BA4\u6587\u6848\u548C\u81EA\u5B9A\u4E49\u6587\u6848\n const mergedText: Required<CommonText> = React.useMemo(\n () => ({\n ...DEFAULT_COMMON_TEXT,\n ...commonText,\n }),\n [commonText]\n )\n\n // \u72B6\u6001\u7BA1\u7406\n const chatState = useChatState({\n welcomeMessage,\n site,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n })\n\n const {\n messages,\n isOpen,\n userId,\n sessionId,\n inputValue,\n isStreaming,\n openChat,\n closeChat,\n setInputValue,\n addMessage,\n setMessages,\n clearMessages,\n handleSSEEvent,\n saveSession,\n clearSession,\n } = chatState\n\n // API \u8C03\u7528\n const { sendMessageStream, createSession } = useChatAPI({\n apiBaseUrl,\n headers,\n recaptchaConfig: {\n needRecaptcha,\n recaptchaSitekey,\n recaptchaAction,\n },\n onError,\n })\n\n // \u4F7F\u7528 ref \u5B58\u50A8\u6700\u65B0\u7684 handleSendMessage\uFF0C\u907F\u514D\u5FAA\u73AF\u4F9D\u8D56\n const handleSendMessageRef = React.useRef<(_message?: string) => Promise<void>>(async (_message?: string) => {})\n\n // \u6D88\u606F\u6E32\u67D3\u5668\u6CE8\u518C\u8868\n const rendererRegistry = React.useMemo(() => {\n const registry = new MessageRendererRegistry()\n\n // \u6CE8\u518C\u9ED8\u8BA4\u6E32\u67D3\u5668\n registry.register('text', TextBlock)\n registry.register('product_card', ProductCard)\n registry.register('product_list', ProductList)\n registry.register('product_comparison', ProductComparisonRenderer)\n registry.register('policy', PolicyBlock)\n registry.register('thinking', ThinkingBlock)\n registry.register('error', ErrorBlock)\n registry.register('faq_list', FAQListRenderer)\n registry.register('promotion_list', PromotionListRenderer)\n registry.register('cart', CartCard)\n\n // \u6CE8\u518C\u5FEB\u6377\u56DE\u590D\u6E32\u67D3\u5668\uFF08\u5E26\u56DE\u8C03\uFF09\n const quickRepliesRenderer = createQuickRepliesRenderer((reply: QuickReply) => {\n // \u4F7F\u7528 ref \u8C03\u7528\u6700\u65B0\u7684 handleSendMessage\n handleSendMessageRef.current(reply.value)\n })\n registry.register('quick_replies', quickRepliesRenderer)\n\n // \u6CE8\u518C\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n if (customRenderers) {\n registry.registerMany(customRenderers)\n }\n\n return registry\n }, [customRenderers])\n\n /**\n * T043: \u6253\u5F00\u804A\u5929\u7A97\u53E3\u65F6\u521D\u59CB\u5316\u4F1A\u8BDD\n * \u4F7F\u7528 API v2.0.0 \u7684\u7EDF\u4E00\u63A5\u53E3\uFF1A\n * - \u5982\u679C\u6CA1\u6709 sessionId\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n * - \u5982\u679C\u6709 sessionId\uFF0C\u6062\u590D\u4F1A\u8BDD\u5E76\u52A0\u8F7D\u5386\u53F2\u6D88\u606F\n */\n useEffect(() => {\n if (!isOpen || !userId) return\n\n const currentSessionId = sessionId\n\n if (!currentSessionId) {\n // \u6CA1\u6709\u4F1A\u8BDD\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n handleCreateNewSession()\n } else {\n // \u6709\u4F1A\u8BDD\uFF0C\u5C1D\u8BD5\u6062\u590D\u4F1A\u8BDD\u548C\u5386\u53F2\u6D88\u606F\n handleResumeSession(currentSessionId)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen, userId])\n\n /**\n * \u89C4\u8303\u5316\u6D88\u606F\u683C\u5F0F\uFF08\u786E\u4FDD content \u662F\u6570\u7EC4\uFF09\n * \u540E\u7AEF\u8FD4\u56DE\u683C\u5F0F\uFF1A\n * - content: \u5B57\u7B26\u4E32\uFF08\u6587\u672C\u5185\u5BB9\uFF09\n * - structuredContent: \u6570\u7EC4\uFF08\u7ED3\u6784\u5316\u5185\u5BB9\uFF0C\u5982\u4EA7\u54C1\u5217\u8868\u3001\u653F\u7B56\u7B49\uFF09\n * \u9700\u8981\u5408\u5E76\u4E3A\u7EDF\u4E00\u7684 content \u6570\u7EC4\u683C\u5F0F\n */\n const normalizeMessage = useCallback(\n (message: any): Message => {\n // \u5982\u679C content \u5DF2\u7ECF\u662F\u6570\u7EC4\uFF0C\u76F4\u63A5\u8FD4\u56DE\n if (Array.isArray(message.content)) {\n return message as Message\n }\n\n const contentBlocks: MessageContent[] = []\n\n // \u5904\u7406\u6587\u672C\u5185\u5BB9\n if (typeof message.content === 'string' && message.content.trim()) {\n contentBlocks.push({\n type: 'text',\n text: message.content,\n })\n }\n\n // \u5904\u7406\u7ED3\u6784\u5316\u5185\u5BB9\n // \u5386\u53F2\u6D88\u606F\u683C\u5F0F: structured_content: [{type, data}]\n const structuredData = message.structuredContent || message.structured_content\n\n if (Array.isArray(structuredData)) {\n structuredData.forEach((block: any) => {\n if (block.type === 'product_list' && Array.isArray(block.data)) {\n // \u8F6C\u6362\u4EA7\u54C1\u5217\u8868\u6570\u636E\u7ED3\u6784 - data \u76F4\u63A5\u662F\u4EA7\u54C1\u6570\u7EC4\n contentBlocks.push({\n type: 'product_list',\n data: {\n products: transformProducts(block.data, site),\n title: undefined, // \u5386\u53F2\u6D88\u606F\u4E0D\u5305\u542B title\n commonText: mergedText,\n },\n })\n } else if (block.type === 'quick_replies' && block.data?.replies) {\n contentBlocks.push({\n type: 'quick_replies',\n data: {\n replies: block.data.replies,\n },\n })\n } else if (block.type === 'policy' && block.data?.title && block.data?.content) {\n contentBlocks.push({\n type: 'policy',\n data: {\n title: block.data.title,\n content: block.data.content,\n },\n })\n } else if (block.type === 'product_comparison' && block.data?.products && block.data?.dimensions) {\n // \u8F6C\u6362\u4EA7\u54C1\u5BF9\u6BD4\u6570\u636E\u7ED3\u6784\u5E76\u6CE8\u5165 onAddToCart \u56DE\u8C03\n contentBlocks.push({\n type: 'product_comparison',\n data: {\n products: transformProducts(block.data.products, site),\n dimensions: block.data.dimensions,\n onAddToCart: onAddToCart,\n commonText: mergedText,\n },\n })\n } else if (block.type === 'faq_list' && block.data?.found !== undefined) {\n // FAQ \u5217\u8868\u5361\u7247 - \u76F4\u63A5\u4F7F\u7528\u540E\u7AEF\u6570\u636E\n contentBlocks.push({\n type: 'faq_list',\n data: block.data,\n })\n } else if (block.type === 'promotion_list' && block.data?.found !== undefined) {\n // \u4FC3\u9500\u6D3B\u52A8\u5217\u8868\u5361\u7247 - \u76F4\u63A5\u4F7F\u7528\u540E\u7AEF\u6570\u636E\n contentBlocks.push({\n type: 'promotion_list',\n data: {\n ...block.data,\n commonText: mergedText,\n },\n })\n } else if (block.type === 'cart' && block.data?.id !== undefined) {\n // \u8D2D\u7269\u8F66\u5361\u7247 - \u8F6C\u6362\u540E\u7AEF\u6570\u636E\u683C\u5F0F\u5E76\u6CE8\u5165 onCart \u56DE\u8C03\n const transformedData = transformCartData(block.data as BackendCartData)\n contentBlocks.push({\n type: 'cart',\n data: {\n ...transformedData,\n onCart: onCart,\n commonText: mergedText,\n },\n })\n } else {\n // \u5176\u4ED6\u7C7B\u578B\u76F4\u63A5\u6DFB\u52A0\n contentBlocks.push(block)\n }\n })\n }\n\n // \u5982\u679C\u6CA1\u6709\u4EFB\u4F55\u5185\u5BB9\u5757\uFF0C\u8FD4\u56DE\u7A7A\u6587\u672C\u5757\n if (contentBlocks.length === 0) {\n contentBlocks.push({\n type: 'text',\n text: '',\n })\n }\n\n return {\n ...message,\n content: contentBlocks,\n } as Message\n },\n [site, onCart, onAddToCart, mergedText]\n )\n\n /**\n * \u521B\u5EFA\u65B0\u4F1A\u8BDD\n */\n const handleCreateNewSession = useCallback(async () => {\n if (!userId) return\n\n try {\n const response = await createSession({\n user_id: userId,\n site: site,\n real_user_id: loginUserId,\n })\n\n if (response.success) {\n // \u4FDD\u5B58\u65B0\u4F1A\u8BDD ID\n saveSession(response.sessionId)\n\n // \u6E05\u7A7A\u6D88\u606F\u5217\u8868\n clearMessages()\n\n // \u4F7F\u7528\u540E\u7AEF\u8FD4\u56DE\u7684\u6B22\u8FCE\u6D88\u606F\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u4F7F\u7528 props \u4E2D\u7684\u9ED8\u8BA4\u503C\n const messageText = response.welcomeMessage || welcomeMessage\n\n if (messageText) {\n const welcomeContent: MessageContent[] = [{ type: 'text', text: messageText }]\n\n // \u4F7F\u7528\u540E\u7AEF\u8FD4\u56DE\u7684\u5FEB\u6377\u95EE\u9898\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u4F7F\u7528 props \u4E2D\u7684\u5FEB\u6377\u56DE\u590D\n const questions = response.quickQuestions\n if (questions && questions.length > 0) {\n // \u5C06\u540E\u7AEF\u7684 quickQuestions (\u5B57\u7B26\u4E32\u6570\u7EC4) \u8F6C\u6362\u4E3A QuickReply \u683C\u5F0F\n const quickRepliesFromBackend = questions.map((question, index) => ({\n id: `quick-${index}`,\n label: question,\n value: question,\n }))\n\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickRepliesFromBackend,\n },\n })\n } else if (quickReplies && quickReplies.length > 0) {\n // \u5982\u679C\u540E\u7AEF\u6CA1\u6709\u8FD4\u56DE\uFF0C\u4F7F\u7528 props \u4E2D\u7684\u5FEB\u6377\u56DE\u590D\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickReplies,\n },\n })\n }\n\n addMessage({\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: welcomeContent,\n timestamp: Date.now(),\n })\n }\n }\n } catch (error) {\n console.error('[LiveChatWidget] Failed to create new session:', error)\n onError?.(error as Error)\n }\n }, [\n userId,\n site,\n loginUserId,\n createSession,\n saveSession,\n clearMessages,\n welcomeMessage,\n quickReplies,\n addMessage,\n onError,\n ])\n\n /**\n * \u6062\u590D\u4F1A\u8BDD\u548C\u5386\u53F2\u6D88\u606F\uFF08\u4F7F\u7528\u65B0\u7684 API v2.0.0\uFF09\n */\n const handleResumeSession = useCallback(\n async (existingSessionId: string) => {\n try {\n const response = await createSession({\n user_id: userId,\n session_id: existingSessionId,\n site: site,\n real_user_id: loginUserId,\n })\n\n if (response.success && response.resumed) {\n // \u4F1A\u8BDD\u6062\u590D\u6210\u529F\n if (response.messages && response.messages.length > 0) {\n // \u6709\u5386\u53F2\u6D88\u606F\uFF0C\u89C4\u8303\u5316\u5E76\u52A0\u8F7D\uFF08\u8FC7\u6EE4\u6389 null/undefined \u503C\uFF09\n const normalizedMessages = response.messages.filter((msg: any) => msg != null).map(normalizeMessage)\n setMessages(normalizedMessages)\n } else {\n // \u6CA1\u6709\u5386\u53F2\u6D88\u606F\uFF0C\u663E\u793A\u6B22\u8FCE\u6D88\u606F\n clearMessages()\n\n // \u4F7F\u7528\u540E\u7AEF\u8FD4\u56DE\u7684\u6B22\u8FCE\u6D88\u606F\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u4F7F\u7528 props \u4E2D\u7684\u9ED8\u8BA4\u503C\n const messageText = response.welcomeMessage || welcomeMessage\n\n if (messageText) {\n const welcomeContent: MessageContent[] = [{ type: 'text', text: messageText }]\n\n // \u4F7F\u7528\u540E\u7AEF\u8FD4\u56DE\u7684\u5FEB\u6377\u95EE\u9898\n const questions = response.quickQuestions\n if (questions && questions.length > 0) {\n const quickRepliesFromBackend = questions.map((question, index) => ({\n id: `quick-${index}`,\n label: question,\n value: question,\n }))\n\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickRepliesFromBackend,\n },\n })\n } else if (quickReplies && quickReplies.length > 0) {\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickReplies,\n },\n })\n }\n\n addMessage({\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: welcomeContent,\n timestamp: Date.now(),\n })\n }\n }\n } else if (!response.resumed) {\n // \u4F1A\u8BDD\u65E0\u6548\u6216\u8FC7\u671F\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n clearSession()\n handleCreateNewSession()\n }\n } catch (error) {\n console.error('[LiveChatWidget] Failed to resume session:', error)\n // \u6062\u590D\u5931\u8D25\uFF0C\u6E05\u7A7A\u4F1A\u8BDD\u5E76\u521B\u5EFA\u65B0\u7684\n clearSession()\n handleCreateNewSession()\n }\n },\n [\n userId,\n site,\n loginUserId,\n createSession,\n setMessages,\n clearSession,\n clearMessages,\n normalizeMessage,\n handleCreateNewSession,\n welcomeMessage,\n quickReplies,\n addMessage,\n ]\n )\n\n /**\n * \u53D1\u9001\u6D88\u606F\n */\n const handleSendMessage = useCallback(\n async (message?: string) => {\n const textToSend = message || inputValue.trim()\n\n if (!textToSend) return\n\n // \u6E05\u7A7A\u8F93\u5165\u6846\n if (!message) {\n setInputValue('')\n }\n\n // \u8F93\u5165\u9A8C\u8BC1\n const sanitized = sanitizeInput(textToSend)\n if (!sanitized) {\n onError?.(new Error('Invalid message'))\n return\n }\n\n // \u6DFB\u52A0\u7528\u6237\u6D88\u606F\u5230\u754C\u9762\n const userMessage = {\n id: `user-${Date.now()}`,\n role: 'user' as const,\n content: [{ type: 'text' as const, text: sanitized }],\n timestamp: Date.now(),\n }\n addMessage(userMessage)\n\n // \u89E6\u53D1\u6D88\u606F\u53D1\u9001\u56DE\u8C03\n onMessageSend?.(sanitized)\n\n try {\n // \u786E\u4FDD\u6709 sessionId\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u5148\u521B\u5EFA\u4F1A\u8BDD\n let currentSessionId = sessionId\n if (!currentSessionId) {\n // \u6CA1\u6709\u4F1A\u8BDD\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n const response = await createSession({\n user_id: userId,\n site: site,\n real_user_id: loginUserId,\n })\n if (response.success) {\n currentSessionId = response.sessionId\n saveSession(currentSessionId)\n } else {\n throw new Error('Failed to create session')\n }\n }\n\n // \u6784\u5EFA\u8BF7\u6C42\u53C2\u6570\uFF08session_id \u73B0\u5728\u662F\u5FC5\u586B\u7684\uFF09\n const requestPayload: ChatStreamRequest = {\n message: sanitized,\n user_id: userId,\n session_id: currentSessionId,\n context: {\n cartId: cartId,\n accessToken: accessToken,\n real_user_id: loginUserId,\n },\n }\n\n // \u53D1\u9001\u6D88\u606F\u5230\u540E\u7AEF\n await sendMessageStream(requestPayload, event => {\n // \u5904\u7406 SSE \u4E8B\u4EF6\n handleSSEEvent(event)\n\n // \u7279\u6B8A\u5904\u7406\uFF1A\u4F1A\u8BDD\u8FC7\u671F\n if (event.event === 'status' && event.data.type === 'session_expired') {\n clearSession()\n }\n })\n } catch (error) {\n console.error('[LiveChatWidget] Failed to send message:', error)\n onError?.(error as Error)\n }\n },\n [\n inputValue,\n userId,\n sessionId,\n site,\n loginUserId,\n cartId,\n accessToken,\n setInputValue,\n addMessage,\n createSession,\n sendMessageStream,\n handleSSEEvent,\n saveSession,\n clearSession,\n onMessageSend,\n onError,\n ]\n )\n\n // \u66F4\u65B0 ref \u4EE5\u4FDD\u6301\u6700\u65B0\u7684 handleSendMessage\n React.useEffect(() => {\n handleSendMessageRef.current = handleSendMessage\n }, [handleSendMessage])\n\n return (\n <>\n {/* \u6C14\u6CE1\u6309\u94AE */}\n <ChatBubble position={position} onClick={openChat} visible={!isOpen} iconImageUrl={chatBubbleIcon} />\n\n {/* \u804A\u5929\u7A97\u53E3\uFF08\u4F7F\u7528 Radix UI Dialog\uFF09 */}\n <Dialog.Root open={isOpen} onOpenChange={open => (open ? openChat() : closeChat())}>\n <Dialog.Portal>\n <Dialog.Content\n className=\"livechat-window-enter\"\n style={{\n position: 'fixed',\n zIndex: 9998,\n }}\n >\n <ChatWindow\n messages={messages}\n inputValue={inputValue}\n onInputChange={setInputValue}\n onSend={() => handleSendMessage()}\n onClose={closeChat}\n onNewSession={handleCreateNewSession}\n title={title}\n logoUrl={logoUrl}\n isSending={isStreaming}\n rendererRegistry={rendererRegistry}\n inputPlaceholder=\"\"\n onAddToCart={onAddToCart}\n showNewSessionButton={showNewSessionButton}\n />\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n </>\n )\n}\n"],
4
+ "sourcesContent": ["/**\n * LiveChat \u4E3B\u7EC4\u4EF6\n * \u96C6\u6210\u6240\u6709\u5B50\u7EC4\u4EF6\uFF0C\u63D0\u4F9B\u5B8C\u6574\u7684\u804A\u5929\u529F\u80FD\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u4E09\u5C42\u67B6\u6784\u8BBE\u8BA1\n */\n\nimport React, { useEffect, useCallback } from 'react'\nimport * as Dialog from '@radix-ui/react-dialog'\nimport type {\n LiveChatWidgetProps,\n QuickReply,\n Message,\n MessageContent,\n ChatStreamRequest,\n BackendCartData,\n CommonText,\n} from './types'\nimport { DEFAULT_COMMON_TEXT } from './constants'\nimport { ChatBubble } from './components/ChatBubble'\nimport { ChatWindow } from './components/ChatWindow'\nimport { useChatState } from './hooks/useChatState'\nimport { useChatAPI } from './hooks/useChatAPI'\nimport { MessageRendererRegistry } from './utils/messageRenderers'\nimport { sanitizeInput } from './utils/validation'\nimport { transformProducts } from './utils/productTransformers.js'\nimport { transformCartData } from './utils/cartTransformers.js'\nimport {\n TextBlock,\n ProductCard,\n ProductList,\n ProductComparisonRenderer,\n PolicyBlock,\n createQuickRepliesRenderer,\n ThinkingBlock,\n ErrorBlock,\n FAQListRenderer,\n PromotionListRenderer,\n CartCard,\n} from './components/MessageContent/index.js'\n\n/**\n * LiveChat \u804A\u5929\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u6C14\u6CE1\u5F39\u7A97\u804A\u5929\u754C\u9762\n * - SSE \u6D41\u5F0F\u6D88\u606F\u63A5\u6536\n * - \u4F1A\u8BDD\u7BA1\u7406\uFF08userId, sessionId\uFF09\n * - \u5386\u53F2\u6D88\u606F\u52A0\u8F7D\n * - \u591A\u79CD\u6D88\u606F\u7C7B\u578B\u6E32\u67D3\n * - \u81EA\u5B9A\u4E49\u6269\u5C55\u673A\u5236\n * - reCAPTCHA v3 \u5B89\u5168\u9632\u62A4\n *\n * \u67B6\u6784\uFF1A\n * - UI Layer: ChatBubble, ChatWindow, MessageList, etc.\n * - Logic Layer: useChatState, useChatAPI, useSession\n * - Core Layer: MessageRendererRegistry, \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n *\n * @example\n * ```tsx\n * // \u57FA\u7840\u4F7F\u7528\uFF08\u4F7F\u7528\u9ED8\u8BA4\u4F4D\u7F6E\uFF09\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * welcomeMessage=\"\u4F60\u597D\uFF01\u6211\u662F AI \u52A9\u624B\"\n * onMessageSend={(msg) => console.log('Sent:', msg)}\n * />\n *\n * // \u4F7F\u7528\u81EA\u5B9A\u4E49\u4F4D\u7F6E\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * position={{ bottom: \"20px\", right: \"30px\" }}\n * onMessageSend={(msg) => console.log('Sent:', msg)}\n * />\n *\n * // \u542F\u7528 reCAPTCHA v3 \u5B89\u5168\u9632\u62A4\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * needRecaptcha={true}\n * recaptchaSitekey=\"6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14\"\n * recaptchaAction=\"\"\n * />\n *\n * // \u4F7F\u7528\u81EA\u5B9A\u4E49 headers \u548C reCAPTCHA\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * headers={{\n * \"Authorization\": \"Bearer your-token\",\n * \"X-Custom-Header\": \"value\"\n * }}\n * needRecaptcha={true}\n * recaptchaSitekey=\"your-site-key\"\n * />\n * ```\n */\nexport const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({\n apiBaseUrl,\n headers,\n needRecaptcha,\n recaptchaSitekey,\n recaptchaAction,\n site,\n loginUserId,\n cartId,\n accessToken,\n position,\n welcomeMessage,\n quickReplies,\n customRenderers,\n logoUrl,\n title,\n chatBubbleIcon,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n showNewSessionButton,\n commonText,\n}) => {\n // \u5408\u5E76\u9ED8\u8BA4\u6587\u6848\u548C\u81EA\u5B9A\u4E49\u6587\u6848\n const mergedText: Required<CommonText> = React.useMemo(\n () => ({\n ...DEFAULT_COMMON_TEXT,\n ...commonText,\n }),\n [commonText]\n )\n\n // \u72B6\u6001\u7BA1\u7406\n const chatState = useChatState({\n welcomeMessage,\n site,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n })\n\n const {\n messages,\n isOpen,\n userId,\n sessionId,\n inputValue,\n isStreaming,\n openChat,\n closeChat,\n setInputValue,\n addMessage,\n setMessages,\n clearMessages,\n handleSSEEvent,\n saveSession,\n clearSession,\n } = chatState\n\n // API \u8C03\u7528\n const { sendMessageStream, createSession } = useChatAPI({\n apiBaseUrl,\n headers,\n recaptchaConfig: {\n needRecaptcha,\n recaptchaSitekey,\n recaptchaAction,\n },\n onError,\n })\n\n // \u4F7F\u7528 ref \u5B58\u50A8\u6700\u65B0\u7684 handleSendMessage\uFF0C\u907F\u514D\u5FAA\u73AF\u4F9D\u8D56\n const handleSendMessageRef = React.useRef<(_message?: string) => Promise<void>>(async (_message?: string) => {})\n\n // \u6D88\u606F\u6E32\u67D3\u5668\u6CE8\u518C\u8868\n const rendererRegistry = React.useMemo(() => {\n const registry = new MessageRendererRegistry()\n\n // \u6CE8\u518C\u9ED8\u8BA4\u6E32\u67D3\u5668\n registry.register('text', TextBlock)\n registry.register('product_card', ProductCard)\n registry.register('product_list', ProductList)\n registry.register('product_comparison', ProductComparisonRenderer)\n registry.register('policy', PolicyBlock)\n registry.register('thinking', ThinkingBlock)\n registry.register('error', ErrorBlock)\n registry.register('faq_list', FAQListRenderer)\n registry.register('promotion_list', PromotionListRenderer)\n registry.register('cart', CartCard)\n\n // \u6CE8\u518C\u5FEB\u6377\u56DE\u590D\u6E32\u67D3\u5668\uFF08\u5E26\u56DE\u8C03\uFF09\n const quickRepliesRenderer = createQuickRepliesRenderer((reply: QuickReply) => {\n // \u4F7F\u7528 ref \u8C03\u7528\u6700\u65B0\u7684 handleSendMessage\n handleSendMessageRef.current(reply.value)\n })\n registry.register('quick_replies', quickRepliesRenderer)\n\n // \u6CE8\u518C\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n if (customRenderers) {\n registry.registerMany(customRenderers)\n }\n\n return registry\n }, [customRenderers])\n\n /**\n * T043: \u6253\u5F00\u804A\u5929\u7A97\u53E3\u65F6\u521D\u59CB\u5316\u4F1A\u8BDD\n * \u4F7F\u7528 API v2.0.0 \u7684\u7EDF\u4E00\u63A5\u53E3\uFF1A\n * - \u5982\u679C\u6CA1\u6709 sessionId\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n * - \u5982\u679C\u6709 sessionId\uFF0C\u6062\u590D\u4F1A\u8BDD\u5E76\u52A0\u8F7D\u5386\u53F2\u6D88\u606F\n */\n useEffect(() => {\n if (!isOpen || !userId) return\n\n const currentSessionId = sessionId\n\n if (!currentSessionId) {\n // \u6CA1\u6709\u4F1A\u8BDD\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n handleCreateNewSession()\n } else {\n // \u6709\u4F1A\u8BDD\uFF0C\u5C1D\u8BD5\u6062\u590D\u4F1A\u8BDD\u548C\u5386\u53F2\u6D88\u606F\n handleResumeSession(currentSessionId)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen, userId])\n\n /**\n * \u89C4\u8303\u5316\u6D88\u606F\u683C\u5F0F\uFF08\u786E\u4FDD content \u662F\u6570\u7EC4\uFF09\n * \u540E\u7AEF\u8FD4\u56DE\u683C\u5F0F\uFF1A\n * - content: \u5B57\u7B26\u4E32\uFF08\u6587\u672C\u5185\u5BB9\uFF09\n * - structuredContent: \u6570\u7EC4\uFF08\u7ED3\u6784\u5316\u5185\u5BB9\uFF0C\u5982\u4EA7\u54C1\u5217\u8868\u3001\u653F\u7B56\u7B49\uFF09\n * \u9700\u8981\u5408\u5E76\u4E3A\u7EDF\u4E00\u7684 content \u6570\u7EC4\u683C\u5F0F\n */\n const normalizeMessage = useCallback(\n (message: any): Message => {\n // \u5982\u679C content \u5DF2\u7ECF\u662F\u6570\u7EC4\uFF0C\u76F4\u63A5\u8FD4\u56DE\n if (Array.isArray(message.content)) {\n return message as Message\n }\n\n const contentBlocks: MessageContent[] = []\n\n // \u5904\u7406\u6587\u672C\u5185\u5BB9\n if (typeof message.content === 'string' && message.content.trim()) {\n contentBlocks.push({\n type: 'text',\n text: message.content,\n })\n }\n\n // \u5904\u7406\u7ED3\u6784\u5316\u5185\u5BB9\n // \u5386\u53F2\u6D88\u606F\u683C\u5F0F: structured_content: [{type, data}]\n const structuredData = message.structuredContent || message.structured_content\n\n if (Array.isArray(structuredData)) {\n structuredData.forEach((block: any) => {\n if (block.type === 'product_list' && Array.isArray(block.data)) {\n // \u8F6C\u6362\u4EA7\u54C1\u5217\u8868\u6570\u636E\u7ED3\u6784 - data \u76F4\u63A5\u662F\u4EA7\u54C1\u6570\u7EC4\n contentBlocks.push({\n type: 'product_list',\n data: {\n products: transformProducts(block.data, site),\n title: undefined, // \u5386\u53F2\u6D88\u606F\u4E0D\u5305\u542B title\n commonText: mergedText,\n },\n })\n } else if (block.type === 'quick_replies' && block.data?.replies) {\n contentBlocks.push({\n type: 'quick_replies',\n data: {\n replies: block.data.replies,\n },\n })\n } else if (block.type === 'policy' && block.data?.title && block.data?.content) {\n contentBlocks.push({\n type: 'policy',\n data: {\n title: block.data.title,\n content: block.data.content,\n },\n })\n } else if (block.type === 'product_comparison' && block.data?.products && block.data?.dimensions) {\n // \u8F6C\u6362\u4EA7\u54C1\u5BF9\u6BD4\u6570\u636E\u7ED3\u6784\u5E76\u6CE8\u5165 onAddToCart \u56DE\u8C03\n contentBlocks.push({\n type: 'product_comparison',\n data: {\n products: transformProducts(block.data.products, site),\n dimensions: block.data.dimensions,\n onAddToCart: onAddToCart,\n commonText: mergedText,\n },\n })\n } else if (block.type === 'faq_list' && block.data?.found !== undefined) {\n // FAQ \u5217\u8868\u5361\u7247 - \u76F4\u63A5\u4F7F\u7528\u540E\u7AEF\u6570\u636E\n contentBlocks.push({\n type: 'faq_list',\n data: block.data,\n })\n } else if (block.type === 'promotion_list' && block.data?.found !== undefined) {\n // \u4FC3\u9500\u6D3B\u52A8\u5217\u8868\u5361\u7247 - \u76F4\u63A5\u4F7F\u7528\u540E\u7AEF\u6570\u636E\n contentBlocks.push({\n type: 'promotion_list',\n data: {\n ...block.data,\n commonText: mergedText,\n },\n })\n } else if (block.type === 'cart' && block.data?.id !== undefined) {\n // \u8D2D\u7269\u8F66\u5361\u7247 - \u8F6C\u6362\u540E\u7AEF\u6570\u636E\u683C\u5F0F\u5E76\u6CE8\u5165 onCart \u56DE\u8C03\n const transformedData = transformCartData(block.data as BackendCartData)\n contentBlocks.push({\n type: 'cart',\n data: {\n ...transformedData,\n onCart: onCart,\n commonText: mergedText,\n },\n })\n } else {\n // \u5176\u4ED6\u7C7B\u578B\u76F4\u63A5\u6DFB\u52A0\n contentBlocks.push(block)\n }\n })\n }\n\n // \u5982\u679C\u6CA1\u6709\u4EFB\u4F55\u5185\u5BB9\u5757\uFF0C\u8FD4\u56DE\u7A7A\u6587\u672C\u5757\n if (contentBlocks.length === 0) {\n contentBlocks.push({\n type: 'text',\n text: '',\n })\n }\n\n return {\n ...message,\n content: contentBlocks,\n } as Message\n },\n [site, onCart, onAddToCart, mergedText]\n )\n\n /**\n * \u521B\u5EFA\u65B0\u4F1A\u8BDD\n */\n const handleCreateNewSession = useCallback(async () => {\n if (!userId) return\n\n try {\n const response = await createSession({\n user_id: userId,\n site: site,\n real_user_id: loginUserId,\n })\n\n if (response.success) {\n // \u4FDD\u5B58\u65B0\u4F1A\u8BDD ID\n saveSession(response.sessionId)\n\n // \u6E05\u7A7A\u6D88\u606F\u5217\u8868\n clearMessages()\n\n // \u4F7F\u7528\u540E\u7AEF\u8FD4\u56DE\u7684\u6B22\u8FCE\u6D88\u606F\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u4F7F\u7528 props \u4E2D\u7684\u9ED8\u8BA4\u503C\n const messageText = response.welcomeMessage || welcomeMessage\n\n if (messageText) {\n const welcomeContent: MessageContent[] = [{ type: 'text', text: messageText }]\n\n // \u4F7F\u7528\u540E\u7AEF\u8FD4\u56DE\u7684\u5FEB\u6377\u95EE\u9898\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u4F7F\u7528 props \u4E2D\u7684\u5FEB\u6377\u56DE\u590D\n const questions = response.quickQuestions\n if (questions && questions.length > 0) {\n // \u5C06\u540E\u7AEF\u7684 quickQuestions (\u5B57\u7B26\u4E32\u6570\u7EC4) \u8F6C\u6362\u4E3A QuickReply \u683C\u5F0F\n const quickRepliesFromBackend = questions.map((question, index) => ({\n id: `quick-${index}`,\n label: question,\n value: question,\n }))\n\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickRepliesFromBackend,\n },\n })\n } else if (quickReplies && quickReplies.length > 0) {\n // \u5982\u679C\u540E\u7AEF\u6CA1\u6709\u8FD4\u56DE\uFF0C\u4F7F\u7528 props \u4E2D\u7684\u5FEB\u6377\u56DE\u590D\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickReplies,\n },\n })\n }\n\n addMessage({\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: welcomeContent,\n timestamp: Date.now(),\n })\n }\n }\n } catch (error) {\n console.error('[LiveChatWidget] Failed to create new session:', error)\n onError?.(error as Error)\n }\n }, [\n userId,\n site,\n loginUserId,\n createSession,\n saveSession,\n clearMessages,\n welcomeMessage,\n quickReplies,\n addMessage,\n onError,\n ])\n\n /**\n * \u6062\u590D\u4F1A\u8BDD\u548C\u5386\u53F2\u6D88\u606F\uFF08\u4F7F\u7528\u65B0\u7684 API v2.0.0\uFF09\n */\n const handleResumeSession = useCallback(\n async (existingSessionId: string) => {\n try {\n const response = await createSession({\n user_id: userId,\n session_id: existingSessionId,\n site: site,\n real_user_id: loginUserId,\n })\n\n if (response.success && response.resumed) {\n // \u4F1A\u8BDD\u6062\u590D\u6210\u529F\n if (response.messages && response.messages.length > 0) {\n // \u6709\u5386\u53F2\u6D88\u606F\uFF0C\u89C4\u8303\u5316\u5E76\u52A0\u8F7D\uFF08\u8FC7\u6EE4\u6389 null/undefined \u503C\uFF09\n const normalizedMessages = response.messages.filter((msg: any) => msg != null).map(normalizeMessage)\n setMessages(normalizedMessages)\n } else {\n // \u6CA1\u6709\u5386\u53F2\u6D88\u606F\uFF0C\u663E\u793A\u6B22\u8FCE\u6D88\u606F\n clearMessages()\n\n // \u4F7F\u7528\u540E\u7AEF\u8FD4\u56DE\u7684\u6B22\u8FCE\u6D88\u606F\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u4F7F\u7528 props \u4E2D\u7684\u9ED8\u8BA4\u503C\n const messageText = response.welcomeMessage || welcomeMessage\n\n if (messageText) {\n const welcomeContent: MessageContent[] = [{ type: 'text', text: messageText }]\n\n // \u4F7F\u7528\u540E\u7AEF\u8FD4\u56DE\u7684\u5FEB\u6377\u95EE\u9898\n const questions = response.quickQuestions\n if (questions && questions.length > 0) {\n const quickRepliesFromBackend = questions.map((question, index) => ({\n id: `quick-${index}`,\n label: question,\n value: question,\n }))\n\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickRepliesFromBackend,\n },\n })\n } else if (quickReplies && quickReplies.length > 0) {\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickReplies,\n },\n })\n }\n\n addMessage({\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: welcomeContent,\n timestamp: Date.now(),\n })\n }\n }\n } else if (!response.resumed) {\n // \u4F1A\u8BDD\u65E0\u6548\u6216\u8FC7\u671F\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n clearSession()\n handleCreateNewSession()\n }\n } catch (error) {\n console.error('[LiveChatWidget] Failed to resume session:', error)\n // \u6062\u590D\u5931\u8D25\uFF0C\u6E05\u7A7A\u4F1A\u8BDD\u5E76\u521B\u5EFA\u65B0\u7684\n clearSession()\n handleCreateNewSession()\n }\n },\n [\n userId,\n site,\n loginUserId,\n createSession,\n setMessages,\n clearSession,\n clearMessages,\n normalizeMessage,\n handleCreateNewSession,\n welcomeMessage,\n quickReplies,\n addMessage,\n ]\n )\n\n /**\n * \u53D1\u9001\u6D88\u606F\n */\n const handleSendMessage = useCallback(\n async (message?: string) => {\n const textToSend = message || inputValue.trim()\n\n if (!textToSend) return\n\n // \u6E05\u7A7A\u8F93\u5165\u6846\n if (!message) {\n setInputValue('')\n }\n\n // \u8F93\u5165\u9A8C\u8BC1\n const sanitized = sanitizeInput(textToSend)\n if (!sanitized) {\n onError?.(new Error('Invalid message'))\n return\n }\n\n // \u6DFB\u52A0\u7528\u6237\u6D88\u606F\u5230\u754C\u9762\n const userMessage = {\n id: `user-${Date.now()}`,\n role: 'user' as const,\n content: [{ type: 'text' as const, text: sanitized }],\n timestamp: Date.now(),\n }\n addMessage(userMessage)\n\n // \u89E6\u53D1\u6D88\u606F\u53D1\u9001\u56DE\u8C03\n onMessageSend?.(sanitized)\n\n try {\n // \u786E\u4FDD\u6709 sessionId\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u5148\u521B\u5EFA\u4F1A\u8BDD\n let currentSessionId = sessionId\n if (!currentSessionId) {\n // \u6CA1\u6709\u4F1A\u8BDD\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n const response = await createSession({\n user_id: userId,\n site: site,\n real_user_id: loginUserId,\n })\n if (response.success) {\n currentSessionId = response.sessionId\n saveSession(currentSessionId)\n } else {\n throw new Error('Failed to create session')\n }\n }\n\n // \u6784\u5EFA\u8BF7\u6C42\u53C2\u6570\uFF08session_id \u73B0\u5728\u662F\u5FC5\u586B\u7684\uFF09\n const requestPayload: ChatStreamRequest = {\n message: sanitized,\n user_id: userId,\n session_id: currentSessionId,\n context: {\n cartId: cartId,\n accessToken: accessToken,\n real_user_id: loginUserId,\n },\n }\n\n // \u53D1\u9001\u6D88\u606F\u5230\u540E\u7AEF\n await sendMessageStream(requestPayload, event => {\n // \u5904\u7406 SSE \u4E8B\u4EF6\n handleSSEEvent(event)\n\n // \u7279\u6B8A\u5904\u7406\uFF1A\u4F1A\u8BDD\u8FC7\u671F\n if (event.event === 'status' && event.data.type === 'session_expired') {\n clearSession()\n }\n })\n } catch (error) {\n console.error('[LiveChatWidget] Failed to send message:', error)\n onError?.(error as Error)\n }\n },\n [\n inputValue,\n userId,\n sessionId,\n site,\n loginUserId,\n cartId,\n accessToken,\n setInputValue,\n addMessage,\n createSession,\n sendMessageStream,\n handleSSEEvent,\n saveSession,\n clearSession,\n onMessageSend,\n onError,\n ]\n )\n\n // \u66F4\u65B0 ref \u4EE5\u4FDD\u6301\u6700\u65B0\u7684 handleSendMessage\n React.useEffect(() => {\n handleSendMessageRef.current = handleSendMessage\n }, [handleSendMessage])\n\n return (\n <>\n {/* \u6C14\u6CE1\u6309\u94AE */}\n <ChatBubble position={position} onClick={openChat} visible={!isOpen} iconImageUrl={chatBubbleIcon} />\n\n {/* \u804A\u5929\u7A97\u53E3\uFF08\u4F7F\u7528 Radix UI Dialog\uFF09 */}\n <Dialog.Root open={isOpen} onOpenChange={open => (open ? openChat() : closeChat())}>\n <Dialog.Portal>\n <Dialog.Content\n className=\"livechat-window-enter\"\n style={{\n position: 'fixed',\n zIndex: 9998,\n }}\n >\n <ChatWindow\n messages={messages}\n inputValue={inputValue}\n onInputChange={setInputValue}\n onSend={() => handleSendMessage()}\n onClose={closeChat}\n onNewSession={handleCreateNewSession}\n title={title}\n logoUrl={logoUrl}\n isSending={isStreaming}\n rendererRegistry={rendererRegistry}\n inputPlaceholder=\"\"\n onAddToCart={onAddToCart}\n showNewSessionButton={showNewSessionButton}\n />\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n </>\n )\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["LiveChatWidget_exports", "__export", "LiveChatWidget", "__toCommonJS", "import_jsx_runtime", "import_react", "Dialog", "import_constants", "import_ChatBubble", "import_ChatWindow", "import_useChatState", "import_useChatAPI", "import_messageRenderers", "import_validation", "import_productTransformers", "import_cartTransformers", "import_MessageContent", "apiBaseUrl", "headers", "needRecaptcha", "recaptchaSitekey", "recaptchaAction", "site", "loginUserId", "cartId", "accessToken", "position", "welcomeMessage", "quickReplies", "customRenderers", "logoUrl", "title", "chatBubbleIcon", "onOpen", "onClose", "onMessageSend", "onError", "onTextMessage", "onProductList", "onPromotionList", "onAddToCart", "onCart", "showNewSessionButton", "commonText", "mergedText", "React", "chatState", "messages", "isOpen", "userId", "sessionId", "inputValue", "isStreaming", "openChat", "closeChat", "setInputValue", "addMessage", "setMessages", "clearMessages", "handleSSEEvent", "saveSession", "clearSession", "sendMessageStream", "createSession", "handleSendMessageRef", "_message", "rendererRegistry", "registry", "quickRepliesRenderer", "reply", "currentSessionId", "handleResumeSession", "handleCreateNewSession", "normalizeMessage", "message", "contentBlocks", "structuredData", "block", "transformedData", "response", "messageText", "welcomeContent", "questions", "quickRepliesFromBackend", "question", "index", "error", "existingSessionId", "normalizedMessages", "msg", "handleSendMessage", "textToSend", "sanitized", "userMessage", "event", "open"]
7
7
  }
@@ -1,3 +1,3 @@
1
- "use strict";var l=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var v=(r,t)=>{for(var s in t)l(r,s,{get:t[s],enumerable:!0})},T=(r,t,s,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of E(t))!g.call(r,e)&&e!==s&&l(r,e,{get:()=>t[e],enumerable:!(a=y(t,e))||a.enumerable});return r};var b=r=>T(l({},"__esModule",{value:!0}),r);var A={};v(A,{createNewSession:()=>k,sendMessage:()=>P});module.exports=b(A);var R=require("../utils/fetcher");async function P(r,t,s,a,e){const o=await(0,R.fetcher)({url:`${r}/api/chat/stream`,method:"POST",headers:{Accept:"text/event-stream",...a},body:t,needRecaptcha:e?.needRecaptcha,recaptchaSitekey:e?.recaptchaSitekey,recaptchaAction:e?.recaptchaAction||"chat_stream"});if(!o.ok)throw new Error(`HTTP ${o.status}: ${o.statusText}`);const i=o.body?.getReader();if(!i)throw new Error("Response body is not readable");const m=new TextDecoder;let d="",p=null;try{for(;;){const{done:c,value:w}=await i.read();if(c)break;d+=m.decode(w,{stream:!0});const S=d.split(`
1
+ "use strict";var l=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var v=(r,t)=>{for(var s in t)l(r,s,{get:t[s],enumerable:!0})},T=(r,t,s,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of g(t))!E.call(r,e)&&e!==s&&l(r,e,{get:()=>t[e],enumerable:!(a=y(t,e))||a.enumerable});return r};var b=r=>T(l({},"__esModule",{value:!0}),r);var A={};v(A,{createNewSession:()=>k,sendMessage:()=>P});module.exports=b(A);var R=require("../utils/fetcher");async function P(r,t,s,a,e){const o=await(0,R.fetcher)({url:`${r}/api/chat/stream`,method:"POST",headers:{Accept:"text/event-stream",...a},body:t,needRecaptcha:e?.needRecaptcha,recaptchaSitekey:e?.recaptchaSitekey,recaptchaAction:e?.recaptchaAction||"send_message"});if(!o.ok)throw new Error(`HTTP ${o.status}: ${o.statusText}`);const i=o.body?.getReader();if(!i)throw new Error("Response body is not readable");const m=new TextDecoder;let d="",p=null;try{for(;;){const{done:c,value:w}=await i.read();if(c)break;d+=m.decode(w,{stream:!0});const S=d.split(`
2
2
  `);d=S.pop()||"";for(const f of S){const n=f.trim();if(n===""){p=null;continue}if(n.startsWith("event:"))p=n.substring(6).trim();else if(n.startsWith("data:")){const u=n.substring(5).trim();try{const h=JSON.parse(u);s({event:p||"message",data:h})}catch(h){console.error("[LiveChat API] Failed to parse SSE data:",u,h),s({event:"error",data:{message:"Failed to parse SSE data",code:"PARSE_ERROR"}})}}}}}catch(c){throw console.error("[LiveChat API] SSE stream error:",c),c}finally{i.releaseLock()}}async function k(r,t,s,a){const e=await(0,R.fetcher)({url:`${r}/api/chat/new-session`,method:"POST",headers:s,body:t,needRecaptcha:a?.needRecaptcha,recaptchaSitekey:a?.recaptchaSitekey,recaptchaAction:a?.recaptchaAction||"new_session"});if(!e.ok){const o=await e.json().catch(()=>({success:!1,error:`HTTP ${e.status}: ${e.statusText}`,code:"HTTP_ERROR"}));throw new Error(o.error||"Failed to create session")}return e.json()}
3
3
  //# sourceMappingURL=chat.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/components/LiveChatWidget/api/chat.ts"],
4
- "sourcesContent": ["/**\n * LiveChat API \u5C42\n * \u5904\u7406 SSE \u6D41\u5F0F\u901A\u4FE1\u548C HTTP \u8BF7\u6C42\n * \u57FA\u4E8E specs/livechat-widget/contracts/*.yaml\n */\n\nimport type { ChatStreamRequest, SSEEvent, NewSessionRequest, NewSessionResponse, ErrorResponse } from '../types'\nimport { fetcher } from '../utils/fetcher'\n\n/**\n * Recaptcha \u914D\u7F6E\n */\nexport interface RecaptchaConfig {\n /**\n * \u662F\u5426\u542F\u7528 reCAPTCHA\n */\n needRecaptcha?: boolean\n /**\n * reCAPTCHA site key\n */\n recaptchaSitekey?: string\n /**\n * reCAPTCHA action \u540D\u79F0\n */\n recaptchaAction?: string\n}\n\n// ============================================================================\n// SSE \u6D41\u5F0F\u6D88\u606F\u5904\u7406 (T006)\n// ============================================================================\n\n/**\n * \u53D1\u9001\u6D88\u606F\u5E76\u63A5\u6536 SSE \u6D41\u5F0F\u54CD\u5E94\n * @param apiBaseUrl API \u57FA\u7840 URL\n * @param request \u8BF7\u6C42\u53C2\u6570\n * @param onEvent SSE \u4E8B\u4EF6\u56DE\u8C03\n * @param customHeaders \u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\n * @param recaptchaConfig reCAPTCHA \u914D\u7F6E\n */\nexport async function sendMessage(\n apiBaseUrl: string,\n request: ChatStreamRequest,\n onEvent: (event: SSEEvent) => void,\n customHeaders?: Record<string, string>,\n recaptchaConfig?: RecaptchaConfig\n): Promise<void> {\n // \u4F7F\u7528 fetcher \u5904\u7406 reCAPTCHA \u548C\u81EA\u5B9A\u4E49 headers\n const response = await fetcher({\n url: `${apiBaseUrl}/api/chat/stream`,\n method: 'POST',\n headers: {\n Accept: 'text/event-stream',\n ...customHeaders,\n },\n body: request,\n needRecaptcha: recaptchaConfig?.needRecaptcha,\n recaptchaSitekey: recaptchaConfig?.recaptchaSitekey,\n recaptchaAction: recaptchaConfig?.recaptchaAction || 'chat_stream',\n })\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const reader = response.body?.getReader()\n if (!reader) {\n throw new Error('Response body is not readable')\n }\n\n const decoder = new TextDecoder()\n let buffer = ''\n let currentEvent: string | null = null\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n // \u89E3\u7801\u5E76\u8FFD\u52A0\u5230\u7F13\u51B2\u533A\n buffer += decoder.decode(value, { stream: true })\n\n // \u6309\u884C\u5206\u5272\n const lines = buffer.split('\\n')\n\n // \u4FDD\u7559\u6700\u540E\u4E00\u4E2A\u4E0D\u5B8C\u6574\u7684\u884C\n buffer = lines.pop() || ''\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n if (trimmed === '') {\n // \u7A7A\u884C\u8868\u793A\u4E8B\u4EF6\u8FB9\u754C\uFF0C\u91CD\u7F6E\u5F53\u524D\u4E8B\u4EF6\u7C7B\u578B\n currentEvent = null\n continue\n }\n\n if (trimmed.startsWith('event:')) {\n // \u63D0\u53D6\u4E8B\u4EF6\u7C7B\u578B\n currentEvent = trimmed.substring(6).trim()\n } else if (trimmed.startsWith('data:')) {\n // \u63D0\u53D6\u6570\u636E\u5E76\u89E3\u6790 JSON\n const dataStr = trimmed.substring(5).trim()\n try {\n const data = JSON.parse(dataStr)\n onEvent({\n event: (currentEvent as any) || 'message',\n data,\n })\n } catch (err) {\n console.error('[LiveChat API] Failed to parse SSE data:', dataStr, err)\n onEvent({\n event: 'error',\n data: {\n message: 'Failed to parse SSE data',\n code: 'PARSE_ERROR',\n },\n })\n }\n }\n }\n }\n } catch (error) {\n console.error('[LiveChat API] SSE stream error:', error)\n throw error\n } finally {\n reader.releaseLock()\n }\n}\n\n// ============================================================================\n// \u521B\u5EFA/\u6062\u590D\u4F1A\u8BDD (T009)\n// ============================================================================\n\n/**\n * \u521B\u5EFA\u65B0\u4F1A\u8BDD\u6216\u6062\u590D\u73B0\u6709\u4F1A\u8BDD\n *\n * \u4F7F\u7528\u573A\u666F\uFF1A\n * 1. \u521B\u5EFA\u65B0\u4F1A\u8BDD\uFF1A\u4E0D\u4F20 session_id\uFF0C\u8FD4\u56DE\u65B0\u7684 sessionId\n * 2. \u6062\u590D\u4F1A\u8BDD\uFF1A\u4F20\u5165 session_id\uFF0C\u9A8C\u8BC1\u4F1A\u8BDD\u6709\u6548\u6027\u5E76\u8FD4\u56DE\u5386\u53F2\u6D88\u606F\uFF08resumed: true, messages: [...]\uFF09\n *\n * @param apiBaseUrl API \u57FA\u7840 URL\n * @param request \u8BF7\u6C42\u53C2\u6570\n * @param customHeaders \u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\n * @param recaptchaConfig reCAPTCHA \u914D\u7F6E\n * @returns \u4F1A\u8BDD\u4FE1\u606F\uFF0C\u53EF\u80FD\u5305\u542B\u5386\u53F2\u6D88\u606F\n */\nexport async function createNewSession(\n apiBaseUrl: string,\n request: NewSessionRequest,\n customHeaders?: Record<string, string>,\n recaptchaConfig?: RecaptchaConfig\n): Promise<NewSessionResponse> {\n // \u4F7F\u7528 fetcher \u5904\u7406 reCAPTCHA \u548C\u81EA\u5B9A\u4E49 headers\n const response = await fetcher({\n url: `${apiBaseUrl}/api/chat/new-session`,\n method: 'POST',\n headers: customHeaders,\n body: request,\n needRecaptcha: recaptchaConfig?.needRecaptcha,\n recaptchaSitekey: recaptchaConfig?.recaptchaSitekey,\n recaptchaAction: recaptchaConfig?.recaptchaAction || 'new_session',\n })\n\n if (!response.ok) {\n const errorData: ErrorResponse = await response.json().catch(() => ({\n success: false,\n error: `HTTP ${response.status}: ${response.statusText}`,\n code: 'HTTP_ERROR',\n }))\n throw new Error(errorData.error || 'Failed to create session')\n }\n\n return response.json()\n}\n"],
5
- "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,sBAAAE,EAAA,gBAAAC,IAAA,eAAAC,EAAAJ,GAOA,IAAAK,EAAwB,4BAgCxB,eAAsBF,EACpBG,EACAC,EACAC,EACAC,EACAC,EACe,CAEf,MAAMC,EAAW,QAAM,WAAQ,CAC7B,IAAK,GAAGL,CAAU,mBAClB,OAAQ,OACR,QAAS,CACP,OAAQ,oBACR,GAAGG,CACL,EACA,KAAMF,EACN,cAAeG,GAAiB,cAChC,iBAAkBA,GAAiB,iBACnC,gBAAiBA,GAAiB,iBAAmB,aACvD,CAAC,EAED,GAAI,CAACC,EAAS,GACZ,MAAM,IAAI,MAAM,QAAQA,EAAS,MAAM,KAAKA,EAAS,UAAU,EAAE,EAGnE,MAAMC,EAASD,EAAS,MAAM,UAAU,EACxC,GAAI,CAACC,EACH,MAAM,IAAI,MAAM,+BAA+B,EAGjD,MAAMC,EAAU,IAAI,YACpB,IAAIC,EAAS,GACTC,EAA8B,KAElC,GAAI,CACF,OAAa,CACX,KAAM,CAAE,KAAAC,EAAM,MAAAC,CAAM,EAAI,MAAML,EAAO,KAAK,EAC1C,GAAII,EAAM,MAGVF,GAAUD,EAAQ,OAAOI,EAAO,CAAE,OAAQ,EAAK,CAAC,EAGhD,MAAMC,EAAQJ,EAAO,MAAM;AAAA,CAAI,EAG/BA,EAASI,EAAM,IAAI,GAAK,GAExB,UAAWC,KAAQD,EAAO,CACxB,MAAME,EAAUD,EAAK,KAAK,EAE1B,GAAIC,IAAY,GAAI,CAElBL,EAAe,KACf,QACF,CAEA,GAAIK,EAAQ,WAAW,QAAQ,EAE7BL,EAAeK,EAAQ,UAAU,CAAC,EAAE,KAAK,UAChCA,EAAQ,WAAW,OAAO,EAAG,CAEtC,MAAMC,EAAUD,EAAQ,UAAU,CAAC,EAAE,KAAK,EAC1C,GAAI,CACF,MAAME,EAAO,KAAK,MAAMD,CAAO,EAC/Bb,EAAQ,CACN,MAAQO,GAAwB,UAChC,KAAAO,CACF,CAAC,CACH,OAASC,EAAK,CACZ,QAAQ,MAAM,2CAA4CF,EAASE,CAAG,EACtEf,EAAQ,CACN,MAAO,QACP,KAAM,CACJ,QAAS,2BACT,KAAM,aACR,CACF,CAAC,CACH,CACF,CACF,CACF,CACF,OAASgB,EAAO,CACd,cAAQ,MAAM,mCAAoCA,CAAK,EACjDA,CACR,QAAE,CACAZ,EAAO,YAAY,CACrB,CACF,CAmBA,eAAsBV,EACpBI,EACAC,EACAE,EACAC,EAC6B,CAE7B,MAAMC,EAAW,QAAM,WAAQ,CAC7B,IAAK,GAAGL,CAAU,wBAClB,OAAQ,OACR,QAASG,EACT,KAAMF,EACN,cAAeG,GAAiB,cAChC,iBAAkBA,GAAiB,iBACnC,gBAAiBA,GAAiB,iBAAmB,aACvD,CAAC,EAED,GAAI,CAACC,EAAS,GAAI,CAChB,MAAMc,EAA2B,MAAMd,EAAS,KAAK,EAAE,MAAM,KAAO,CAClE,QAAS,GACT,MAAO,QAAQA,EAAS,MAAM,KAAKA,EAAS,UAAU,GACtD,KAAM,YACR,EAAE,EACF,MAAM,IAAI,MAAMc,EAAU,OAAS,0BAA0B,CAC/D,CAEA,OAAOd,EAAS,KAAK,CACvB",
4
+ "sourcesContent": ["/**\n * LiveChat API \u5C42\n * \u5904\u7406 SSE \u6D41\u5F0F\u901A\u4FE1\u548C HTTP \u8BF7\u6C42\n * \u57FA\u4E8E specs/livechat-widget/contracts/*.yaml\n */\n\nimport type { ChatStreamRequest, SSEEvent, NewSessionRequest, NewSessionResponse, ErrorResponse } from '../types'\nimport { fetcher } from '../utils/fetcher'\n\n/**\n * Recaptcha \u914D\u7F6E\n */\nexport interface RecaptchaConfig {\n /**\n * \u662F\u5426\u542F\u7528 reCAPTCHA\n */\n needRecaptcha?: boolean\n /**\n * reCAPTCHA site key\n */\n recaptchaSitekey?: string\n /**\n * reCAPTCHA action \u540D\u79F0\n */\n recaptchaAction?: string\n}\n\n// ============================================================================\n// SSE \u6D41\u5F0F\u6D88\u606F\u5904\u7406 (T006)\n// ============================================================================\n\n/**\n * \u53D1\u9001\u6D88\u606F\u5E76\u63A5\u6536 SSE \u6D41\u5F0F\u54CD\u5E94\n * @param apiBaseUrl API \u57FA\u7840 URL\n * @param request \u8BF7\u6C42\u53C2\u6570\n * @param onEvent SSE \u4E8B\u4EF6\u56DE\u8C03\n * @param customHeaders \u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\n * @param recaptchaConfig reCAPTCHA \u914D\u7F6E\n */\nexport async function sendMessage(\n apiBaseUrl: string,\n request: ChatStreamRequest,\n onEvent: (event: SSEEvent) => void,\n customHeaders?: Record<string, string>,\n recaptchaConfig?: RecaptchaConfig\n): Promise<void> {\n // \u4F7F\u7528 fetcher \u5904\u7406 reCAPTCHA \u548C\u81EA\u5B9A\u4E49 headers\n const response = await fetcher({\n url: `${apiBaseUrl}/api/chat/stream`,\n method: 'POST',\n headers: {\n Accept: 'text/event-stream',\n ...customHeaders,\n },\n body: request,\n needRecaptcha: recaptchaConfig?.needRecaptcha,\n recaptchaSitekey: recaptchaConfig?.recaptchaSitekey,\n recaptchaAction: recaptchaConfig?.recaptchaAction || 'send_message',\n })\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const reader = response.body?.getReader()\n if (!reader) {\n throw new Error('Response body is not readable')\n }\n\n const decoder = new TextDecoder()\n let buffer = ''\n let currentEvent: string | null = null\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n // \u89E3\u7801\u5E76\u8FFD\u52A0\u5230\u7F13\u51B2\u533A\n buffer += decoder.decode(value, { stream: true })\n\n // \u6309\u884C\u5206\u5272\n const lines = buffer.split('\\n')\n\n // \u4FDD\u7559\u6700\u540E\u4E00\u4E2A\u4E0D\u5B8C\u6574\u7684\u884C\n buffer = lines.pop() || ''\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n if (trimmed === '') {\n // \u7A7A\u884C\u8868\u793A\u4E8B\u4EF6\u8FB9\u754C\uFF0C\u91CD\u7F6E\u5F53\u524D\u4E8B\u4EF6\u7C7B\u578B\n currentEvent = null\n continue\n }\n\n if (trimmed.startsWith('event:')) {\n // \u63D0\u53D6\u4E8B\u4EF6\u7C7B\u578B\n currentEvent = trimmed.substring(6).trim()\n } else if (trimmed.startsWith('data:')) {\n // \u63D0\u53D6\u6570\u636E\u5E76\u89E3\u6790 JSON\n const dataStr = trimmed.substring(5).trim()\n try {\n const data = JSON.parse(dataStr)\n onEvent({\n event: (currentEvent as any) || 'message',\n data,\n })\n } catch (err) {\n console.error('[LiveChat API] Failed to parse SSE data:', dataStr, err)\n onEvent({\n event: 'error',\n data: {\n message: 'Failed to parse SSE data',\n code: 'PARSE_ERROR',\n },\n })\n }\n }\n }\n }\n } catch (error) {\n console.error('[LiveChat API] SSE stream error:', error)\n throw error\n } finally {\n reader.releaseLock()\n }\n}\n\n// ============================================================================\n// \u521B\u5EFA/\u6062\u590D\u4F1A\u8BDD (T009)\n// ============================================================================\n\n/**\n * \u521B\u5EFA\u65B0\u4F1A\u8BDD\u6216\u6062\u590D\u73B0\u6709\u4F1A\u8BDD\n *\n * \u4F7F\u7528\u573A\u666F\uFF1A\n * 1. \u521B\u5EFA\u65B0\u4F1A\u8BDD\uFF1A\u4E0D\u4F20 session_id\uFF0C\u8FD4\u56DE\u65B0\u7684 sessionId\n * 2. \u6062\u590D\u4F1A\u8BDD\uFF1A\u4F20\u5165 session_id\uFF0C\u9A8C\u8BC1\u4F1A\u8BDD\u6709\u6548\u6027\u5E76\u8FD4\u56DE\u5386\u53F2\u6D88\u606F\uFF08resumed: true, messages: [...]\uFF09\n *\n * @param apiBaseUrl API \u57FA\u7840 URL\n * @param request \u8BF7\u6C42\u53C2\u6570\n * @param customHeaders \u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\n * @param recaptchaConfig reCAPTCHA \u914D\u7F6E\n * @returns \u4F1A\u8BDD\u4FE1\u606F\uFF0C\u53EF\u80FD\u5305\u542B\u5386\u53F2\u6D88\u606F\n */\nexport async function createNewSession(\n apiBaseUrl: string,\n request: NewSessionRequest,\n customHeaders?: Record<string, string>,\n recaptchaConfig?: RecaptchaConfig\n): Promise<NewSessionResponse> {\n // \u4F7F\u7528 fetcher \u5904\u7406 reCAPTCHA \u548C\u81EA\u5B9A\u4E49 headers\n const response = await fetcher({\n url: `${apiBaseUrl}/api/chat/new-session`,\n method: 'POST',\n headers: customHeaders,\n body: request,\n needRecaptcha: recaptchaConfig?.needRecaptcha,\n recaptchaSitekey: recaptchaConfig?.recaptchaSitekey,\n recaptchaAction: recaptchaConfig?.recaptchaAction || 'new_session',\n })\n\n if (!response.ok) {\n const errorData: ErrorResponse = await response.json().catch(() => ({\n success: false,\n error: `HTTP ${response.status}: ${response.statusText}`,\n code: 'HTTP_ERROR',\n }))\n throw new Error(errorData.error || 'Failed to create session')\n }\n\n return response.json()\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,sBAAAE,EAAA,gBAAAC,IAAA,eAAAC,EAAAJ,GAOA,IAAAK,EAAwB,4BAgCxB,eAAsBF,EACpBG,EACAC,EACAC,EACAC,EACAC,EACe,CAEf,MAAMC,EAAW,QAAM,WAAQ,CAC7B,IAAK,GAAGL,CAAU,mBAClB,OAAQ,OACR,QAAS,CACP,OAAQ,oBACR,GAAGG,CACL,EACA,KAAMF,EACN,cAAeG,GAAiB,cAChC,iBAAkBA,GAAiB,iBACnC,gBAAiBA,GAAiB,iBAAmB,cACvD,CAAC,EAED,GAAI,CAACC,EAAS,GACZ,MAAM,IAAI,MAAM,QAAQA,EAAS,MAAM,KAAKA,EAAS,UAAU,EAAE,EAGnE,MAAMC,EAASD,EAAS,MAAM,UAAU,EACxC,GAAI,CAACC,EACH,MAAM,IAAI,MAAM,+BAA+B,EAGjD,MAAMC,EAAU,IAAI,YACpB,IAAIC,EAAS,GACTC,EAA8B,KAElC,GAAI,CACF,OAAa,CACX,KAAM,CAAE,KAAAC,EAAM,MAAAC,CAAM,EAAI,MAAML,EAAO,KAAK,EAC1C,GAAII,EAAM,MAGVF,GAAUD,EAAQ,OAAOI,EAAO,CAAE,OAAQ,EAAK,CAAC,EAGhD,MAAMC,EAAQJ,EAAO,MAAM;AAAA,CAAI,EAG/BA,EAASI,EAAM,IAAI,GAAK,GAExB,UAAWC,KAAQD,EAAO,CACxB,MAAME,EAAUD,EAAK,KAAK,EAE1B,GAAIC,IAAY,GAAI,CAElBL,EAAe,KACf,QACF,CAEA,GAAIK,EAAQ,WAAW,QAAQ,EAE7BL,EAAeK,EAAQ,UAAU,CAAC,EAAE,KAAK,UAChCA,EAAQ,WAAW,OAAO,EAAG,CAEtC,MAAMC,EAAUD,EAAQ,UAAU,CAAC,EAAE,KAAK,EAC1C,GAAI,CACF,MAAME,EAAO,KAAK,MAAMD,CAAO,EAC/Bb,EAAQ,CACN,MAAQO,GAAwB,UAChC,KAAAO,CACF,CAAC,CACH,OAASC,EAAK,CACZ,QAAQ,MAAM,2CAA4CF,EAASE,CAAG,EACtEf,EAAQ,CACN,MAAO,QACP,KAAM,CACJ,QAAS,2BACT,KAAM,aACR,CACF,CAAC,CACH,CACF,CACF,CACF,CACF,OAASgB,EAAO,CACd,cAAQ,MAAM,mCAAoCA,CAAK,EACjDA,CACR,QAAE,CACAZ,EAAO,YAAY,CACrB,CACF,CAmBA,eAAsBV,EACpBI,EACAC,EACAE,EACAC,EAC6B,CAE7B,MAAMC,EAAW,QAAM,WAAQ,CAC7B,IAAK,GAAGL,CAAU,wBAClB,OAAQ,OACR,QAASG,EACT,KAAMF,EACN,cAAeG,GAAiB,cAChC,iBAAkBA,GAAiB,iBACnC,gBAAiBA,GAAiB,iBAAmB,aACvD,CAAC,EAED,GAAI,CAACC,EAAS,GAAI,CAChB,MAAMc,EAA2B,MAAMd,EAAS,KAAK,EAAE,MAAM,KAAO,CAClE,QAAS,GACT,MAAO,QAAQA,EAAS,MAAM,KAAKA,EAAS,UAAU,GACtD,KAAM,YACR,EAAE,EACF,MAAM,IAAI,MAAMc,EAAU,OAAS,0BAA0B,CAC/D,CAEA,OAAOd,EAAS,KAAK,CACvB",
6
6
  "names": ["chat_exports", "__export", "createNewSession", "sendMessage", "__toCommonJS", "import_fetcher", "apiBaseUrl", "request", "onEvent", "customHeaders", "recaptchaConfig", "response", "reader", "decoder", "buffer", "currentEvent", "done", "value", "lines", "line", "trimmed", "dataStr", "data", "err", "error", "errorData"]
7
7
  }
@@ -1,2 +1,2 @@
1
- "use strict";var r=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var g=(o,t)=>{for(var i in t)r(o,i,{get:t[i],enumerable:!0})},x=(o,t,i,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of c(t))!d.call(o,n)&&n!==i&&r(o,n,{get:()=>t[n],enumerable:!(s=l(t,n))||s.enumerable});return o};var u=o=>x(r({},"__esModule",{value:!0}),o);var m={};g(m,{ChatHeader:()=>y});module.exports=u(m);var e=require("react/jsx-runtime");const b=()=>(0,e.jsxs)("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,e.jsx)("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),(0,e.jsx)("line",{x1:"6",y1:"6",x2:"18",y2:"18"})]}),h=()=>(0,e.jsxs)("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,e.jsx)("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),(0,e.jsx)("line",{x1:"5",y1:"12",x2:"19",y2:"12"})]}),y=({title:o="AI \u52A9\u624B",logoUrl:t,onClose:i,onNewSession:s,showNewSessionButton:n=!0,className:a=""})=>(0,e.jsxs)("div",{className:`flex items-center justify-between border-b border-[#DADCE0] bg-white px-4 py-3 ${a}`,style:{minHeight:"48px"},children:[(0,e.jsxs)("div",{className:"flex items-center gap-3",children:[t&&(0,e.jsx)("img",{src:t,alt:"Logo",className:"size-8 rounded-full object-cover"}),(0,e.jsx)("h2",{className:"text-lg font-semibold text-gray-900",children:o})]}),(0,e.jsxs)("div",{className:"flex items-center gap-2",children:[n&&s&&(0,e.jsx)("button",{type:"button",onClick:s,className:"rounded-lg p-2 text-gray-600","aria-label":"\u5F00\u59CB\u65B0\u4F1A\u8BDD",title:"\u5F00\u59CB\u65B0\u4F1A\u8BDD",children:(0,e.jsx)(h,{})}),i&&(0,e.jsx)("button",{type:"button",onClick:i,className:"tablet:block rounded-lg text-gray-600","aria-label":"Close chat",children:(0,e.jsx)(b,{})})]})]});
1
+ "use strict";var i=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var b=(o,t)=>{for(var n in t)i(o,n,{get:t[n],enumerable:!0})},g=(o,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of c(t))!d.call(o,r)&&r!==n&&i(o,r,{get:()=>t[r],enumerable:!(s=l(t,r))||s.enumerable});return o};var u=o=>g(i({},"__esModule",{value:!0}),o);var v={};b(v,{ChatHeader:()=>p});module.exports=u(v);var e=require("react/jsx-runtime");const x=()=>(0,e.jsxs)("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,e.jsx)("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),(0,e.jsx)("line",{x1:"6",y1:"6",x2:"18",y2:"18"})]}),h=()=>(0,e.jsxs)("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,e.jsx)("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),(0,e.jsx)("line",{x1:"5",y1:"12",x2:"19",y2:"12"})]}),p=({title:o="",logoUrl:t,onClose:n,onNewSession:s,showNewSessionButton:r=!0,className:a=""})=>(0,e.jsxs)("div",{className:`flex items-center justify-between border-b border-[#DADCE0] bg-white px-4 pb-3 pt-0 tablet:pt-4 ${a}`,children:[(0,e.jsxs)("div",{className:"flex items-center gap-3",children:[t&&(0,e.jsx)("img",{src:t,alt:"Logo",className:"size-8 rounded-full object-cover"}),(0,e.jsx)("h2",{className:"text-lg font-semibold text-gray-900",children:o})]}),(0,e.jsxs)("div",{className:"flex items-center gap-2",children:[r&&s&&(0,e.jsx)("button",{type:"button",onClick:s,className:"rounded-lg p-2 text-gray-600","aria-label":"\u5F00\u59CB\u65B0\u4F1A\u8BDD",title:"\u5F00\u59CB\u65B0\u4F1A\u8BDD",children:(0,e.jsx)(h,{})}),n&&(0,e.jsx)("button",{type:"button",onClick:n,className:"tablet:block rounded-lg text-gray-600","aria-label":"Close chat",children:(0,e.jsx)(x,{})})]})]});
2
2
  //# sourceMappingURL=ChatHeader.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/components/LiveChatWidget/components/ChatHeader.tsx"],
4
- "sourcesContent": ["/**\n * \u804A\u5929\u7A97\u53E3\u5934\u90E8\u7EC4\u4EF6\n * \u663E\u793A\u6807\u9898\u3001Logo\u3001\u5173\u95ED\u6309\u94AE\u548C\u65B0\u4F1A\u8BDD\u6309\u94AE\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u5934\u90E8\u8BBE\u8BA1\n */\n\nimport React from 'react'\n\nexport interface ChatHeaderProps {\n /**\n * \u5934\u90E8\u6807\u9898\n */\n title?: string\n\n /**\n * Logo URL\n */\n logoUrl?: string\n\n /**\n * \u5173\u95ED\u6309\u94AE\u70B9\u51FB\u56DE\u8C03\n */\n onClose?: () => void\n\n /**\n * \u65B0\u4F1A\u8BDD\u6309\u94AE\u70B9\u51FB\u56DE\u8C03\n */\n onNewSession?: () => void\n\n /**\n * \u662F\u5426\u663E\u793A\u65B0\u4F1A\u8BDD\u6309\u94AE\n * @default true\n */\n showNewSessionButton?: boolean\n\n /**\n * \u81EA\u5B9A\u4E49\u6837\u5F0F\u7C7B\u540D\n */\n className?: string\n}\n\n/**\n * \u9ED8\u8BA4\u5173\u95ED\u56FE\u6807 (X)\n */\nconst CloseIcon: React.FC = () => (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n)\n\n/**\n * \u65B0\u4F1A\u8BDD\u56FE\u6807 (\u52A0\u53F7)\n */\nconst NewSessionIcon: React.FC = () => (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n)\n\n/**\n * \u804A\u5929\u7A97\u53E3\u5934\u90E8\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u6807\u9898\u548C Logo\n * - \u5173\u95ED\u6309\u94AE\uFF08\u79FB\u52A8\u7AEF\u9690\u85CF\uFF0C\u684C\u9762\u7AEF\u663E\u793A\uFF09\n * - \u65B0\u4F1A\u8BDD\u6309\u94AE\uFF08\u6E05\u7A7A\u5F53\u524D\u5BF9\u8BDD\uFF09\n *\n * \u5E03\u5C40\uFF1A\n * - Logo (\u53EF\u9009) + \u6807\u9898 | \u65B0\u4F1A\u8BDD\u6309\u94AE + \u5173\u95ED\u6309\u94AE\n * - \u56FA\u5B9A\u9AD8\u5EA6 64px\n * - \u8FB9\u6846\u5206\u9694\u4E0B\u65B9\u5185\u5BB9\n *\n * @example\n * ```tsx\n * <ChatHeader\n * title=\"AI \u52A9\u624B\"\n * logoUrl=\"/logo.png\"\n * onClose={() => setIsOpen(false)}\n * onNewSession={() => createNewSession()}\n * />\n * ```\n */\nexport const ChatHeader: React.FC<ChatHeaderProps> = ({\n title = 'AI \u52A9\u624B',\n logoUrl,\n onClose,\n onNewSession,\n showNewSessionButton = true,\n className = '',\n}) => {\n return (\n <div\n className={`flex items-center justify-between border-b border-[#DADCE0] bg-white px-4 py-3 ${className}`}\n style={{ minHeight: '48px' }}\n >\n {/* \u5DE6\u4FA7\uFF1ALogo + \u6807\u9898 */}\n <div className=\"flex items-center gap-3\">\n {logoUrl && <img src={logoUrl} alt=\"Logo\" className=\"size-8 rounded-full object-cover\" />}\n <h2 className=\"text-lg font-semibold text-gray-900\">{title}</h2>\n </div>\n\n {/* \u53F3\u4FA7\uFF1A\u65B0\u4F1A\u8BDD\u6309\u94AE + \u5173\u95ED\u6309\u94AE */}\n <div className=\"flex items-center gap-2\">\n {/* \u65B0\u4F1A\u8BDD\u6309\u94AE */}\n {showNewSessionButton && onNewSession && (\n <button\n type=\"button\"\n onClick={onNewSession}\n className=\"rounded-lg p-2 text-gray-600\"\n aria-label=\"\u5F00\u59CB\u65B0\u4F1A\u8BDD\"\n title=\"\u5F00\u59CB\u65B0\u4F1A\u8BDD\"\n >\n <NewSessionIcon />\n </button>\n )}\n\n {/* \u5173\u95ED\u6309\u94AE */}\n {onClose && (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"tablet:block rounded-lg text-gray-600\"\n aria-label=\"Close chat\"\n >\n <CloseIcon />\n </button>\n )}\n </div>\n </div>\n )\n}\n"],
5
- "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,gBAAAE,IAAA,eAAAC,EAAAH,GA6CE,IAAAI,EAAA,6BADF,MAAMC,EAAsB,OAC1B,QAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QAEf,oBAAC,QAAK,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,KACpC,OAAC,QAAK,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GACtC,EAMIC,EAA2B,OAC/B,QAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QAEf,oBAAC,QAAK,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,KACrC,OAAC,QAAK,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GACvC,EA0BWJ,EAAwC,CAAC,CACpD,MAAAK,EAAQ,kBACR,QAAAC,EACA,QAAAC,EACA,aAAAC,EACA,qBAAAC,EAAuB,GACvB,UAAAC,EAAY,EACd,OAEI,QAAC,OACC,UAAW,kFAAkFA,CAAS,GACtG,MAAO,CAAE,UAAW,MAAO,EAG3B,qBAAC,OAAI,UAAU,0BACZ,UAAAJ,MAAW,OAAC,OAAI,IAAKA,EAAS,IAAI,OAAO,UAAU,mCAAmC,KACvF,OAAC,MAAG,UAAU,sCAAuC,SAAAD,EAAM,GAC7D,KAGA,QAAC,OAAI,UAAU,0BAEZ,UAAAI,GAAwBD,MACvB,OAAC,UACC,KAAK,SACL,QAASA,EACT,UAAU,+BACV,aAAW,iCACX,MAAM,iCAEN,mBAACJ,EAAA,EAAe,EAClB,EAIDG,MACC,OAAC,UACC,KAAK,SACL,QAASA,EACT,UAAU,yCACV,aAAW,aAEX,mBAACJ,EAAA,EAAU,EACb,GAEJ,GACF",
4
+ "sourcesContent": ["/**\n * \u804A\u5929\u7A97\u53E3\u5934\u90E8\u7EC4\u4EF6\n * \u663E\u793A\u6807\u9898\u3001Logo\u3001\u5173\u95ED\u6309\u94AE\u548C\u65B0\u4F1A\u8BDD\u6309\u94AE\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u5934\u90E8\u8BBE\u8BA1\n */\n\nimport React from 'react'\n\nexport interface ChatHeaderProps {\n /**\n * \u5934\u90E8\u6807\u9898\n */\n title?: string\n\n /**\n * Logo URL\n */\n logoUrl?: string\n\n /**\n * \u5173\u95ED\u6309\u94AE\u70B9\u51FB\u56DE\u8C03\n */\n onClose?: () => void\n\n /**\n * \u65B0\u4F1A\u8BDD\u6309\u94AE\u70B9\u51FB\u56DE\u8C03\n */\n onNewSession?: () => void\n\n /**\n * \u662F\u5426\u663E\u793A\u65B0\u4F1A\u8BDD\u6309\u94AE\n * @default true\n */\n showNewSessionButton?: boolean\n\n /**\n * \u81EA\u5B9A\u4E49\u6837\u5F0F\u7C7B\u540D\n */\n className?: string\n}\n\n/**\n * \u9ED8\u8BA4\u5173\u95ED\u56FE\u6807 (X)\n */\nconst CloseIcon: React.FC = () => (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n)\n\n/**\n * \u65B0\u4F1A\u8BDD\u56FE\u6807 (\u52A0\u53F7)\n */\nconst NewSessionIcon: React.FC = () => (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n)\n\n/**\n * \u804A\u5929\u7A97\u53E3\u5934\u90E8\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u6807\u9898\u548C Logo\n * - \u5173\u95ED\u6309\u94AE\uFF08\u79FB\u52A8\u7AEF\u9690\u85CF\uFF0C\u684C\u9762\u7AEF\u663E\u793A\uFF09\n * - \u65B0\u4F1A\u8BDD\u6309\u94AE\uFF08\u6E05\u7A7A\u5F53\u524D\u5BF9\u8BDD\uFF09\n *\n * \u5E03\u5C40\uFF1A\n * - Logo (\u53EF\u9009) + \u6807\u9898 | \u65B0\u4F1A\u8BDD\u6309\u94AE + \u5173\u95ED\u6309\u94AE\n * - \u56FA\u5B9A\u9AD8\u5EA6 64px\n * - \u8FB9\u6846\u5206\u9694\u4E0B\u65B9\u5185\u5BB9\n *\n * @example\n * ```tsx\n * <ChatHeader\n * title=\"AI \u52A9\u624B\"\n * logoUrl=\"/logo.png\"\n * onClose={() => setIsOpen(false)}\n * onNewSession={() => createNewSession()}\n * />\n * ```\n */\nexport const ChatHeader: React.FC<ChatHeaderProps> = ({\n title = '',\n logoUrl,\n onClose,\n onNewSession,\n showNewSessionButton = true,\n className = '',\n}) => {\n return (\n <div\n className={`flex items-center justify-between border-b border-[#DADCE0] bg-white px-4 pb-3 pt-0 tablet:pt-4 ${className}`}\n >\n {/* \u5DE6\u4FA7\uFF1ALogo + \u6807\u9898 */}\n <div className=\"flex items-center gap-3\">\n {logoUrl && <img src={logoUrl} alt=\"Logo\" className=\"size-8 rounded-full object-cover\" />}\n <h2 className=\"text-lg font-semibold text-gray-900\">{title}</h2>\n </div>\n\n {/* \u53F3\u4FA7\uFF1A\u65B0\u4F1A\u8BDD\u6309\u94AE + \u5173\u95ED\u6309\u94AE */}\n <div className=\"flex items-center gap-2\">\n {/* \u65B0\u4F1A\u8BDD\u6309\u94AE */}\n {showNewSessionButton && onNewSession && (\n <button\n type=\"button\"\n onClick={onNewSession}\n className=\"rounded-lg p-2 text-gray-600\"\n aria-label=\"\u5F00\u59CB\u65B0\u4F1A\u8BDD\"\n title=\"\u5F00\u59CB\u65B0\u4F1A\u8BDD\"\n >\n <NewSessionIcon />\n </button>\n )}\n\n {/* \u5173\u95ED\u6309\u94AE */}\n {onClose && (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"tablet:block rounded-lg text-gray-600\"\n aria-label=\"Close chat\"\n >\n <CloseIcon />\n </button>\n )}\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,gBAAAE,IAAA,eAAAC,EAAAH,GA6CE,IAAAI,EAAA,6BADF,MAAMC,EAAsB,OAC1B,QAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QAEf,oBAAC,QAAK,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,KACpC,OAAC,QAAK,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GACtC,EAMIC,EAA2B,OAC/B,QAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QAEf,oBAAC,QAAK,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,KACrC,OAAC,QAAK,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GACvC,EA0BWJ,EAAwC,CAAC,CACpD,MAAAK,EAAQ,GACR,QAAAC,EACA,QAAAC,EACA,aAAAC,EACA,qBAAAC,EAAuB,GACvB,UAAAC,EAAY,EACd,OAEI,QAAC,OACC,UAAW,mGAAmGA,CAAS,GAGvH,qBAAC,OAAI,UAAU,0BACZ,UAAAJ,MAAW,OAAC,OAAI,IAAKA,EAAS,IAAI,OAAO,UAAU,mCAAmC,KACvF,OAAC,MAAG,UAAU,sCAAuC,SAAAD,EAAM,GAC7D,KAGA,QAAC,OAAI,UAAU,0BAEZ,UAAAI,GAAwBD,MACvB,OAAC,UACC,KAAK,SACL,QAASA,EACT,UAAU,+BACV,aAAW,iCACX,MAAM,iCAEN,mBAACJ,EAAA,EAAe,EAClB,EAIDG,MACC,OAAC,UACC,KAAK,SACL,QAASA,EACT,UAAU,yCACV,aAAW,aAEX,mBAACJ,EAAA,EAAU,EACb,GAEJ,GACF",
6
6
  "names": ["ChatHeader_exports", "__export", "ChatHeader", "__toCommonJS", "import_jsx_runtime", "CloseIcon", "NewSessionIcon", "title", "logoUrl", "onClose", "onNewSession", "showNewSessionButton", "className"]
7
7
  }
@@ -1,4 +1,4 @@
1
- "use strict";var c=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var R=(e,t)=>{for(var r in t)c(e,r,{get:t[r],enumerable:!0})},C=(e,t,r,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of v(t))!w.call(e,o)&&o!==r&&c(e,o,{get:()=>t[o],enumerable:!(a=x(t,o))||a.enumerable});return e};var M=e=>C(c({},"__esModule",{value:!0}),e);var N={};R(N,{ChatMessage:()=>h});module.exports=M(N);var s=require("react/jsx-runtime"),d=require("./MessageContent");function b(e){const t=new Date(e),r=t.getHours().toString().padStart(2,"0"),a=t.getMinutes().toString().padStart(2,"0");return`${r}:${a}`}const h=({message:e,rendererRegistry:t,defaultRenderer:r,showTimestamp:a=!0,className:o="",onAddToCart:p})=>{const i=e.role==="user",S=e.role==="assistant",m=e.role==="system",y=e.role==="tool";if(m)return(0,s.jsx)("div",{className:`flex justify-center py-2 ${o}`,children:(0,s.jsx)("div",{className:"max-w-xs rounded-lg border border-yellow-200 bg-yellow-50 px-4 py-2",children:e.content.map((n,l)=>(0,s.jsx)(d.MessageContent,{content:n,isUser:!1,isSystem:!0,rendererRegistry:t,defaultRenderer:r},l))})});if(y)return null;const f=["product_list","product_comparison","faq_list","promotion_list","cart"],u=e.content.filter(n=>!f.includes(n.type)),g=e.content.filter(n=>f.includes(n.type));return(0,s.jsx)("div",{className:`flex ${i?"justify-end":"justify-start"} py-2 ${o}`,children:(0,s.jsxs)("div",{className:"flex w-fit max-w-full flex-col gap-2",children:[u.length>0&&(0,s.jsx)("div",{className:`w-full min-w-0 overflow-hidden rounded-2xl px-4 py-2 ${i?"text-white":"text-[#1D1D1F]"}`,style:{backgroundColor:i?"#005D8E":"#F5F6F7"},children:(0,s.jsx)("div",{className:"flex w-full min-w-0 flex-col gap-2",children:u.map((n,l)=>(0,s.jsx)(d.MessageContent,{content:n,isUser:i,isSystem:!1,rendererRegistry:t,defaultRenderer:r,onAddToCart:p},l))})}),g.map((n,l)=>(0,s.jsx)("div",{className:"w-full",children:(0,s.jsx)(d.MessageContent,{content:n,isUser:i,isSystem:!1,rendererRegistry:t,defaultRenderer:r,onAddToCart:p})},`standalone-${l}`)),a&&(0,s.jsx)("span",{className:`
1
+ "use strict";var c=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var R=(e,t)=>{for(var r in t)c(e,r,{get:t[r],enumerable:!0})},C=(e,t,r,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of v(t))!w.call(e,o)&&o!==r&&c(e,o,{get:()=>t[o],enumerable:!(a=x(t,o))||a.enumerable});return e};var M=e=>C(c({},"__esModule",{value:!0}),e);var N={};R(N,{ChatMessage:()=>h});module.exports=M(N);var s=require("react/jsx-runtime"),d=require("./MessageContent");function b(e){const t=new Date(e),r=t.getHours().toString().padStart(2,"0"),a=t.getMinutes().toString().padStart(2,"0");return`${r}:${a}`}const h=({message:e,rendererRegistry:t,defaultRenderer:r,showTimestamp:a=!0,className:o="",onAddToCart:p})=>{const i=e.role==="user",S=e.role==="assistant",m=e.role==="system",y=e.role==="tool";if(m)return(0,s.jsx)("div",{className:`flex justify-center py-2 ${o}`,children:(0,s.jsx)("div",{className:"max-w-xs rounded-lg border border-yellow-200 bg-yellow-50 px-4 py-2",children:e.content.map((n,l)=>(0,s.jsx)(d.MessageContent,{content:n,isUser:!1,isSystem:!0,rendererRegistry:t,defaultRenderer:r},l))})});if(y)return null;const f=["product_list","product_comparison","faq_list","promotion_list","cart"],u=e.content.filter(n=>!f.includes(n.type)),g=e.content.filter(n=>f.includes(n.type));return(0,s.jsx)("div",{className:`flex ${i?"justify-end":"justify-start"} py-2 ${o}`,children:(0,s.jsxs)("div",{className:"flex w-fit max-w-full flex-col gap-2",children:[u.length>0&&(0,s.jsx)("div",{className:`w-full min-w-0 overflow-hidden rounded-2xl p-3 ${i?"text-white":"text-[#1D1D1F]"}`,style:{backgroundColor:i?"#005D8E":"#F5F6F7"},children:(0,s.jsx)("div",{className:"flex w-full min-w-0 flex-col gap-2",children:u.map((n,l)=>(0,s.jsx)(d.MessageContent,{content:n,isUser:i,isSystem:!1,rendererRegistry:t,defaultRenderer:r,onAddToCart:p},l))})}),g.map((n,l)=>(0,s.jsx)("div",{className:"w-full",children:(0,s.jsx)(d.MessageContent,{content:n,isUser:i,isSystem:!1,rendererRegistry:t,defaultRenderer:r,onAddToCart:p})},`standalone-${l}`)),a&&(0,s.jsx)("span",{className:`
2
2
  px-2 text-xs text-gray-400
3
3
  ${i?"text-right":"text-left"}
4
4
  `,children:b(e.timestamp)})]})})};
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/components/LiveChatWidget/components/ChatMessage.tsx"],
4
- "sourcesContent": ["/**\n * \u5355\u6761\u804A\u5929\u6D88\u606F\u7EC4\u4EF6\n * \u663E\u793A\u6D88\u606F\u6C14\u6CE1\u3001\u53D1\u9001\u8005\u3001\u65F6\u95F4\u6233\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u6D88\u606F\u5C55\u793A\u8BBE\u8BA1\n */\n\nimport React from 'react'\nimport type { Message, MessageRenderer } from '../types'\nimport { MessageContent } from './MessageContent'\nimport { MessageRendererRegistry } from '../utils/messageRenderers'\n\nexport interface ChatMessageProps {\n /**\n * \u6D88\u606F\u6570\u636E\n */\n message: Message\n\n /**\n * \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\u6CE8\u518C\u8868\n */\n rendererRegistry?: MessageRendererRegistry\n\n /**\n * \u9ED8\u8BA4\u6E32\u67D3\u5668\n */\n defaultRenderer?: MessageRenderer\n\n /**\n * \u662F\u5426\u663E\u793A\u65F6\u95F4\u6233\n * @default true\n */\n showTimestamp?: boolean\n\n /**\n * \u81EA\u5B9A\u4E49\u6837\u5F0F\u7C7B\u540D\n */\n className?: string\n\n /**\n * \u5546\u54C1\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n */\n onAddToCart?: (product: any) => void\n}\n\n/**\n * \u683C\u5F0F\u5316\u65F6\u95F4\u6233\u4E3A\u53EF\u8BFB\u683C\u5F0F\n * @param timestamp Unix \u65F6\u95F4\u6233\uFF08\u6BEB\u79D2\uFF09\n * @returns \u683C\u5F0F\u5316\u540E\u7684\u65F6\u95F4\u5B57\u7B26\u4E32 (\u5982 \"14:30\")\n */\nfunction formatTimestamp(timestamp: number): string {\n const date = new Date(timestamp)\n const hours = date.getHours().toString().padStart(2, '0')\n const minutes = date.getMinutes().toString().padStart(2, '0')\n return `${hours}:${minutes}`\n}\n\n/**\n * \u5355\u6761\u804A\u5929\u6D88\u606F\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u6D88\u606F\u6C14\u6CE1\uFF08\u7528\u6237 vs AI \u52A9\u624B\u4E0D\u540C\u6837\u5F0F\uFF09\n * - \u652F\u6301\u591A\u4E2A content \u5757\uFF08\u4E00\u6761\u6D88\u606F\u53EF\u5305\u542B\u591A\u79CD\u5185\u5BB9\u7C7B\u578B\uFF09\n * - \u663E\u793A\u65F6\u95F4\u6233\n * - \u7CFB\u7EDF\u6D88\u606F\u5C45\u4E2D\u663E\u793A\n *\n * \u6837\u5F0F\u89C4\u5219\uFF1A\n * - \u7528\u6237\u6D88\u606F\uFF1A\u53F3\u5BF9\u9F50\uFF0C\u7070\u8272\u80CC\u666F\n * - AI \u52A9\u624B\u6D88\u606F\uFF1A\u5DE6\u5BF9\u9F50\uFF0C\u767D\u8272\u80CC\u666F\n * - \u7CFB\u7EDF\u6D88\u606F\uFF1A\u5C45\u4E2D\uFF0C\u9EC4\u8272\u80CC\u666F\n *\n * @example\n * ```tsx\n * <ChatMessage\n * message={message}\n * rendererRegistry={customRegistry}\n * showTimestamp={true}\n * />\n * ```\n */\nexport const ChatMessage: React.FC<ChatMessageProps> = ({\n message,\n rendererRegistry,\n defaultRenderer,\n showTimestamp = true,\n className = '',\n onAddToCart,\n}) => {\n const isUser = message.role === 'user'\n const isAssistant = message.role === 'assistant'\n const isSystem = message.role === 'system'\n const isTool = message.role === 'tool'\n\n // \u7CFB\u7EDF\u6D88\u606F\u7279\u6B8A\u5904\u7406\uFF08\u5C45\u4E2D\u663E\u793A\uFF09\n if (isSystem) {\n return (\n <div className={`flex justify-center py-2 ${className}`}>\n <div className=\"max-w-xs rounded-lg border border-yellow-200 bg-yellow-50 px-4 py-2\">\n {message.content.map((content, index) => (\n <MessageContent\n key={index}\n content={content}\n isUser={false}\n isSystem={true}\n rendererRegistry={rendererRegistry}\n defaultRenderer={defaultRenderer}\n />\n ))}\n </div>\n </div>\n )\n }\n\n // \u5DE5\u5177\u6D88\u606F\uFF08\u901A\u5E38\u4E0D\u663E\u793A\uFF0C\u6216\u663E\u793A\u4E3A\u7CFB\u7EDF\u6D88\u606F\uFF09\n if (isTool) {\n return null\n }\n\n // \u5206\u79BB\u5185\u5BB9\u7C7B\u578B\uFF1A\u9700\u8981\u72EC\u7ACB\u6E32\u67D3\u7684\u7ED3\u6784\u5316\u5185\u5BB9\n const structuredContentTypes = ['product_list', 'product_comparison', 'faq_list', 'promotion_list', 'cart']\n\n // \u5C06\u5185\u5BB9\u5206\u4E3A\u4E24\u7EC4\uFF1A\u6C14\u6CE1\u5185\u5BB9 \u548C \u72EC\u7ACB\u5185\u5BB9\n const bubbleContent = message.content.filter(c => !structuredContentTypes.includes(c.type))\n const standaloneContent = message.content.filter(c => structuredContentTypes.includes(c.type))\n\n // \u7528\u6237/\u52A9\u624B\u6D88\u606F\n return (\n <div className={`flex ${isUser ? 'justify-end' : 'justify-start'} py-2 ${className}`}>\n <div className=\"flex w-fit max-w-full flex-col gap-2\">\n {/* \u6D88\u606F\u6C14\u6CE1\uFF08\u4EC5\u5305\u542B\u6587\u672C\u548C\u5FEB\u6377\u56DE\u590D\u7B49\uFF09 */}\n {bubbleContent.length > 0 && (\n <div\n className={`w-full min-w-0 overflow-hidden rounded-2xl px-4 py-2 ${isUser ? 'text-white' : 'text-[#1D1D1F]'}`}\n style={{\n backgroundColor: isUser ? '#005D8E' : '#F5F6F7',\n }}\n >\n {/* \u6E32\u67D3\u6C14\u6CE1\u5185\u7684\u5185\u5BB9\u5757 */}\n <div className=\"flex w-full min-w-0 flex-col gap-2\">\n {bubbleContent.map((content, index) => (\n <MessageContent\n key={index}\n content={content}\n isUser={isUser}\n isSystem={false}\n rendererRegistry={rendererRegistry}\n defaultRenderer={defaultRenderer}\n onAddToCart={onAddToCart}\n />\n ))}\n </div>\n </div>\n )}\n\n {/* \u72EC\u7ACB\u6E32\u67D3\u7684\u7ED3\u6784\u5316\u5185\u5BB9\uFF08\u4E0D\u5728\u6C14\u6CE1\u5185\uFF09 */}\n {standaloneContent.map((content, index) => (\n <div key={`standalone-${index}`} className=\"w-full\">\n <MessageContent\n content={content}\n isUser={isUser}\n isSystem={false}\n rendererRegistry={rendererRegistry}\n defaultRenderer={defaultRenderer}\n onAddToCart={onAddToCart}\n />\n </div>\n ))}\n\n {/* \u65F6\u95F4\u6233 */}\n {showTimestamp && (\n <span\n className={`\n px-2 text-xs text-gray-400\n ${isUser ? 'text-right' : 'text-left'}\n `}\n >\n {formatTimestamp(message.timestamp)}\n </span>\n )}\n </div>\n </div>\n )\n}\n"],
5
- "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,iBAAAE,IAAA,eAAAC,EAAAH,GAkGY,IAAAI,EAAA,6BA1FZC,EAA+B,4BAyC/B,SAASC,EAAgBC,EAA2B,CAClD,MAAMC,EAAO,IAAI,KAAKD,CAAS,EACzBE,EAAQD,EAAK,SAAS,EAAE,SAAS,EAAE,SAAS,EAAG,GAAG,EAClDE,EAAUF,EAAK,WAAW,EAAE,SAAS,EAAE,SAAS,EAAG,GAAG,EAC5D,MAAO,GAAGC,CAAK,IAAIC,CAAO,EAC5B,CAyBO,MAAMR,EAA0C,CAAC,CACtD,QAAAS,EACA,iBAAAC,EACA,gBAAAC,EACA,cAAAC,EAAgB,GAChB,UAAAC,EAAY,GACZ,YAAAC,CACF,IAAM,CACJ,MAAMC,EAASN,EAAQ,OAAS,OAC1BO,EAAcP,EAAQ,OAAS,YAC/BQ,EAAWR,EAAQ,OAAS,SAC5BS,EAAST,EAAQ,OAAS,OAGhC,GAAIQ,EACF,SACE,OAAC,OAAI,UAAW,4BAA4BJ,CAAS,GACnD,mBAAC,OAAI,UAAU,sEACZ,SAAAJ,EAAQ,QAAQ,IAAI,CAACU,EAASC,OAC7B,OAAC,kBAEC,QAASD,EACT,OAAQ,GACR,SAAU,GACV,iBAAkBT,EAClB,gBAAiBC,GALZS,CAMP,CACD,EACH,EACF,EAKJ,GAAIF,EACF,OAAO,KAIT,MAAMG,EAAyB,CAAC,eAAgB,qBAAsB,WAAY,iBAAkB,MAAM,EAGpGC,EAAgBb,EAAQ,QAAQ,OAAOc,GAAK,CAACF,EAAuB,SAASE,EAAE,IAAI,CAAC,EACpFC,EAAoBf,EAAQ,QAAQ,OAAOc,GAAKF,EAAuB,SAASE,EAAE,IAAI,CAAC,EAG7F,SACE,OAAC,OAAI,UAAW,QAAQR,EAAS,cAAgB,eAAe,SAASF,CAAS,GAChF,oBAAC,OAAI,UAAU,uCAEZ,UAAAS,EAAc,OAAS,MACtB,OAAC,OACC,UAAW,wDAAwDP,EAAS,aAAe,gBAAgB,GAC3G,MAAO,CACL,gBAAiBA,EAAS,UAAY,SACxC,EAGA,mBAAC,OAAI,UAAU,qCACZ,SAAAO,EAAc,IAAI,CAACH,EAASC,OAC3B,OAAC,kBAEC,QAASD,EACT,OAAQJ,EACR,SAAU,GACV,iBAAkBL,EAClB,gBAAiBC,EACjB,YAAaG,GANRM,CAOP,CACD,EACH,EACF,EAIDI,EAAkB,IAAI,CAACL,EAASC,OAC/B,OAAC,OAAgC,UAAU,SACzC,mBAAC,kBACC,QAASD,EACT,OAAQJ,EACR,SAAU,GACV,iBAAkBL,EAClB,gBAAiBC,EACjB,YAAaG,EACf,GARQ,cAAcM,CAAK,EAS7B,CACD,EAGAR,MACC,OAAC,QACC,UAAW;AAAA;AAAA,gBAEPG,EAAS,aAAe,WAAW;AAAA,cAGtC,SAAAX,EAAgBK,EAAQ,SAAS,EACpC,GAEJ,EACF,CAEJ",
4
+ "sourcesContent": ["/**\n * \u5355\u6761\u804A\u5929\u6D88\u606F\u7EC4\u4EF6\n * \u663E\u793A\u6D88\u606F\u6C14\u6CE1\u3001\u53D1\u9001\u8005\u3001\u65F6\u95F4\u6233\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u6D88\u606F\u5C55\u793A\u8BBE\u8BA1\n */\n\nimport React from 'react'\nimport type { Message, MessageRenderer } from '../types'\nimport { MessageContent } from './MessageContent'\nimport { MessageRendererRegistry } from '../utils/messageRenderers'\n\nexport interface ChatMessageProps {\n /**\n * \u6D88\u606F\u6570\u636E\n */\n message: Message\n\n /**\n * \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\u6CE8\u518C\u8868\n */\n rendererRegistry?: MessageRendererRegistry\n\n /**\n * \u9ED8\u8BA4\u6E32\u67D3\u5668\n */\n defaultRenderer?: MessageRenderer\n\n /**\n * \u662F\u5426\u663E\u793A\u65F6\u95F4\u6233\n * @default true\n */\n showTimestamp?: boolean\n\n /**\n * \u81EA\u5B9A\u4E49\u6837\u5F0F\u7C7B\u540D\n */\n className?: string\n\n /**\n * \u5546\u54C1\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n */\n onAddToCart?: (product: any) => void\n}\n\n/**\n * \u683C\u5F0F\u5316\u65F6\u95F4\u6233\u4E3A\u53EF\u8BFB\u683C\u5F0F\n * @param timestamp Unix \u65F6\u95F4\u6233\uFF08\u6BEB\u79D2\uFF09\n * @returns \u683C\u5F0F\u5316\u540E\u7684\u65F6\u95F4\u5B57\u7B26\u4E32 (\u5982 \"14:30\")\n */\nfunction formatTimestamp(timestamp: number): string {\n const date = new Date(timestamp)\n const hours = date.getHours().toString().padStart(2, '0')\n const minutes = date.getMinutes().toString().padStart(2, '0')\n return `${hours}:${minutes}`\n}\n\n/**\n * \u5355\u6761\u804A\u5929\u6D88\u606F\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u6D88\u606F\u6C14\u6CE1\uFF08\u7528\u6237 vs AI \u52A9\u624B\u4E0D\u540C\u6837\u5F0F\uFF09\n * - \u652F\u6301\u591A\u4E2A content \u5757\uFF08\u4E00\u6761\u6D88\u606F\u53EF\u5305\u542B\u591A\u79CD\u5185\u5BB9\u7C7B\u578B\uFF09\n * - \u663E\u793A\u65F6\u95F4\u6233\n * - \u7CFB\u7EDF\u6D88\u606F\u5C45\u4E2D\u663E\u793A\n *\n * \u6837\u5F0F\u89C4\u5219\uFF1A\n * - \u7528\u6237\u6D88\u606F\uFF1A\u53F3\u5BF9\u9F50\uFF0C\u7070\u8272\u80CC\u666F\n * - AI \u52A9\u624B\u6D88\u606F\uFF1A\u5DE6\u5BF9\u9F50\uFF0C\u767D\u8272\u80CC\u666F\n * - \u7CFB\u7EDF\u6D88\u606F\uFF1A\u5C45\u4E2D\uFF0C\u9EC4\u8272\u80CC\u666F\n *\n * @example\n * ```tsx\n * <ChatMessage\n * message={message}\n * rendererRegistry={customRegistry}\n * showTimestamp={true}\n * />\n * ```\n */\nexport const ChatMessage: React.FC<ChatMessageProps> = ({\n message,\n rendererRegistry,\n defaultRenderer,\n showTimestamp = true,\n className = '',\n onAddToCart,\n}) => {\n const isUser = message.role === 'user'\n const isAssistant = message.role === 'assistant'\n const isSystem = message.role === 'system'\n const isTool = message.role === 'tool'\n\n // \u7CFB\u7EDF\u6D88\u606F\u7279\u6B8A\u5904\u7406\uFF08\u5C45\u4E2D\u663E\u793A\uFF09\n if (isSystem) {\n return (\n <div className={`flex justify-center py-2 ${className}`}>\n <div className=\"max-w-xs rounded-lg border border-yellow-200 bg-yellow-50 px-4 py-2\">\n {message.content.map((content, index) => (\n <MessageContent\n key={index}\n content={content}\n isUser={false}\n isSystem={true}\n rendererRegistry={rendererRegistry}\n defaultRenderer={defaultRenderer}\n />\n ))}\n </div>\n </div>\n )\n }\n\n // \u5DE5\u5177\u6D88\u606F\uFF08\u901A\u5E38\u4E0D\u663E\u793A\uFF0C\u6216\u663E\u793A\u4E3A\u7CFB\u7EDF\u6D88\u606F\uFF09\n if (isTool) {\n return null\n }\n\n // \u5206\u79BB\u5185\u5BB9\u7C7B\u578B\uFF1A\u9700\u8981\u72EC\u7ACB\u6E32\u67D3\u7684\u7ED3\u6784\u5316\u5185\u5BB9\n const structuredContentTypes = ['product_list', 'product_comparison', 'faq_list', 'promotion_list', 'cart']\n\n // \u5C06\u5185\u5BB9\u5206\u4E3A\u4E24\u7EC4\uFF1A\u6C14\u6CE1\u5185\u5BB9 \u548C \u72EC\u7ACB\u5185\u5BB9\n const bubbleContent = message.content.filter(c => !structuredContentTypes.includes(c.type))\n const standaloneContent = message.content.filter(c => structuredContentTypes.includes(c.type))\n\n // \u7528\u6237/\u52A9\u624B\u6D88\u606F\n return (\n <div className={`flex ${isUser ? 'justify-end' : 'justify-start'} py-2 ${className}`}>\n <div className=\"flex w-fit max-w-full flex-col gap-2\">\n {/* \u6D88\u606F\u6C14\u6CE1\uFF08\u4EC5\u5305\u542B\u6587\u672C\u548C\u5FEB\u6377\u56DE\u590D\u7B49\uFF09 */}\n {bubbleContent.length > 0 && (\n <div\n className={`w-full min-w-0 overflow-hidden rounded-2xl p-3 ${isUser ? 'text-white' : 'text-[#1D1D1F]'}`}\n style={{\n backgroundColor: isUser ? '#005D8E' : '#F5F6F7',\n }}\n >\n {/* \u6E32\u67D3\u6C14\u6CE1\u5185\u7684\u5185\u5BB9\u5757 */}\n <div className=\"flex w-full min-w-0 flex-col gap-2\">\n {bubbleContent.map((content, index) => (\n <MessageContent\n key={index}\n content={content}\n isUser={isUser}\n isSystem={false}\n rendererRegistry={rendererRegistry}\n defaultRenderer={defaultRenderer}\n onAddToCart={onAddToCart}\n />\n ))}\n </div>\n </div>\n )}\n\n {/* \u72EC\u7ACB\u6E32\u67D3\u7684\u7ED3\u6784\u5316\u5185\u5BB9\uFF08\u4E0D\u5728\u6C14\u6CE1\u5185\uFF09 */}\n {standaloneContent.map((content, index) => (\n <div key={`standalone-${index}`} className=\"w-full\">\n <MessageContent\n content={content}\n isUser={isUser}\n isSystem={false}\n rendererRegistry={rendererRegistry}\n defaultRenderer={defaultRenderer}\n onAddToCart={onAddToCart}\n />\n </div>\n ))}\n\n {/* \u65F6\u95F4\u6233 */}\n {showTimestamp && (\n <span\n className={`\n px-2 text-xs text-gray-400\n ${isUser ? 'text-right' : 'text-left'}\n `}\n >\n {formatTimestamp(message.timestamp)}\n </span>\n )}\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,iBAAAE,IAAA,eAAAC,EAAAH,GAkGY,IAAAI,EAAA,6BA1FZC,EAA+B,4BAyC/B,SAASC,EAAgBC,EAA2B,CAClD,MAAMC,EAAO,IAAI,KAAKD,CAAS,EACzBE,EAAQD,EAAK,SAAS,EAAE,SAAS,EAAE,SAAS,EAAG,GAAG,EAClDE,EAAUF,EAAK,WAAW,EAAE,SAAS,EAAE,SAAS,EAAG,GAAG,EAC5D,MAAO,GAAGC,CAAK,IAAIC,CAAO,EAC5B,CAyBO,MAAMR,EAA0C,CAAC,CACtD,QAAAS,EACA,iBAAAC,EACA,gBAAAC,EACA,cAAAC,EAAgB,GAChB,UAAAC,EAAY,GACZ,YAAAC,CACF,IAAM,CACJ,MAAMC,EAASN,EAAQ,OAAS,OAC1BO,EAAcP,EAAQ,OAAS,YAC/BQ,EAAWR,EAAQ,OAAS,SAC5BS,EAAST,EAAQ,OAAS,OAGhC,GAAIQ,EACF,SACE,OAAC,OAAI,UAAW,4BAA4BJ,CAAS,GACnD,mBAAC,OAAI,UAAU,sEACZ,SAAAJ,EAAQ,QAAQ,IAAI,CAACU,EAASC,OAC7B,OAAC,kBAEC,QAASD,EACT,OAAQ,GACR,SAAU,GACV,iBAAkBT,EAClB,gBAAiBC,GALZS,CAMP,CACD,EACH,EACF,EAKJ,GAAIF,EACF,OAAO,KAIT,MAAMG,EAAyB,CAAC,eAAgB,qBAAsB,WAAY,iBAAkB,MAAM,EAGpGC,EAAgBb,EAAQ,QAAQ,OAAOc,GAAK,CAACF,EAAuB,SAASE,EAAE,IAAI,CAAC,EACpFC,EAAoBf,EAAQ,QAAQ,OAAOc,GAAKF,EAAuB,SAASE,EAAE,IAAI,CAAC,EAG7F,SACE,OAAC,OAAI,UAAW,QAAQR,EAAS,cAAgB,eAAe,SAASF,CAAS,GAChF,oBAAC,OAAI,UAAU,uCAEZ,UAAAS,EAAc,OAAS,MACtB,OAAC,OACC,UAAW,kDAAkDP,EAAS,aAAe,gBAAgB,GACrG,MAAO,CACL,gBAAiBA,EAAS,UAAY,SACxC,EAGA,mBAAC,OAAI,UAAU,qCACZ,SAAAO,EAAc,IAAI,CAACH,EAASC,OAC3B,OAAC,kBAEC,QAASD,EACT,OAAQJ,EACR,SAAU,GACV,iBAAkBL,EAClB,gBAAiBC,EACjB,YAAaG,GANRM,CAOP,CACD,EACH,EACF,EAIDI,EAAkB,IAAI,CAACL,EAASC,OAC/B,OAAC,OAAgC,UAAU,SACzC,mBAAC,kBACC,QAASD,EACT,OAAQJ,EACR,SAAU,GACV,iBAAkBL,EAClB,gBAAiBC,EACjB,YAAaG,EACf,GARQ,cAAcM,CAAK,EAS7B,CACD,EAGAR,MACC,OAAC,QACC,UAAW;AAAA;AAAA,gBAEPG,EAAS,aAAe,WAAW;AAAA,cAGtC,SAAAX,EAAgBK,EAAQ,SAAS,EACpC,GAEJ,EACF,CAEJ",
6
6
  "names": ["ChatMessage_exports", "__export", "ChatMessage", "__toCommonJS", "import_jsx_runtime", "import_MessageContent", "formatTimestamp", "timestamp", "date", "hours", "minutes", "message", "rendererRegistry", "defaultRenderer", "showTimestamp", "className", "onAddToCart", "isUser", "isAssistant", "isSystem", "isTool", "content", "index", "structuredContentTypes", "bubbleContent", "c", "standaloneContent"]
7
7
  }
@@ -1,2 +1,2 @@
1
- "use strict";var u=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var F=Object.prototype.hasOwnProperty;var V=(o,t)=>{for(var i in t)u(o,i,{get:t[i],enumerable:!0})},j=(o,t,i,d)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of U(t))!F.call(o,s)&&s!==i&&u(o,s,{get:()=>t[s],enumerable:!(d=B(t,s))||d.enumerable});return o};var q=o=>j(u({},"__esModule",{value:!0}),o);var J={};V(J,{ChatWindow:()=>G});module.exports=q(J);var r=require("react/jsx-runtime"),n=require("react"),b=require("./ChatHeader"),E=require("./MessageList"),M=require("./ChatInput");const G=({messages:o,inputValue:t,onInputChange:i,onSend:d,onClose:s,onNewSession:y,title:C,logoUrl:S,isSending:L=!1,isLoadingHistory:x=!1,showTimestamp:D=!0,autoScroll:H=!0,rendererRegistry:T,defaultRenderer:Y,inputPlaceholder:N,className:P="",onAddToCart:W,showNewSessionButton:A=!0})=>{const[h,I]=(0,n.useState)(375),[g,m]=(0,n.useState)(!1),v=(0,n.useRef)(0),f=(0,n.useRef)(375),c=(0,n.useRef)(null),[p,k]=(0,n.useState)(()=>typeof window<"u"?window.innerWidth<768:!1);(0,n.useEffect)(()=>{const e=()=>{k(window.innerWidth<768)};return window.addEventListener("resize",e),()=>window.removeEventListener("resize",e)},[]);const w=e=>{if(!p)return;m(!0);const a="touches"in e?e.touches[0].clientY:e.clientY;v.current=a,f.current=h,e.preventDefault()};return(0,n.useEffect)(()=>{if(!g)return;const e=l=>{const z="touches"in l?l.touches[0].clientY:l.clientY,$=v.current-z,R=Math.max(375,Math.min(window.innerHeight*.9,f.current+$));I(R),c.current&&c.current.style.setProperty("--livechat-mobile-height",`${R}px`)},a=()=>{m(!1)};return document.addEventListener("mousemove",e),document.addEventListener("mouseup",a),document.addEventListener("touchmove",e,{passive:!1}),document.addEventListener("touchend",a),()=>{document.removeEventListener("mousemove",e),document.removeEventListener("mouseup",a),document.removeEventListener("touchmove",e),document.removeEventListener("touchend",a)}},[g]),(0,r.jsxs)("div",{ref:c,className:`livechat-window flex flex-col overflow-hidden bg-white ${P}`,style:{borderRadius:"16px","--livechat-mobile-height":`${h}px`},children:[p&&(0,r.jsx)("div",{className:"flex cursor-ns-resize items-center justify-center",style:{touchAction:"none",backgroundColor:"#ffffff",paddingTop:"8px"},onMouseDown:w,onTouchStart:w,children:(0,r.jsx)("div",{style:{width:"64px",height:"6px",borderRadius:"999px",backgroundColor:"#DADCE0"}})}),(0,r.jsx)(b.ChatHeader,{title:C,logoUrl:S,onClose:s,onNewSession:y,showNewSessionButton:A}),(0,r.jsx)(E.MessageList,{messages:o,rendererRegistry:T,defaultRenderer:Y,showTimestamp:D,autoScroll:H,isLoadingHistory:x,onAddToCart:W}),(0,r.jsx)(M.ChatInput,{value:t,onChange:i,onSend:d,placeholder:N,disabled:L})]})};
1
+ "use strict";var m=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var V=(i,o)=>{for(var d in o)m(i,d,{get:o[d],enumerable:!0})},j=(i,o,d,l)=>{if(o&&typeof o=="object"||typeof o=="function")for(let a of $(o))!B.call(i,a)&&a!==d&&m(i,a,{get:()=>o[a],enumerable:!(l=X(o,a))||l.enumerable});return i};var q=i=>j(m({},"__esModule",{value:!0}),i);var K={};V(K,{ChatWindow:()=>J});module.exports=q(K);var s=require("react/jsx-runtime"),n=require("react"),R=require("./ChatHeader"),M=require("./MessageList"),b=require("./ChatInput");const J=({messages:i,inputValue:o,onInputChange:d,onSend:l,onClose:a,onNewSession:L,title:y,logoUrl:A,isSending:C=!1,isLoadingHistory:_=!1,showTimestamp:D=!0,autoScroll:S=!0,rendererRegistry:x,defaultRenderer:G,inputPlaceholder:N,className:U="",onAddToCart:Y,showNewSessionButton:F=!0})=>{const f=(0,n.useCallback)(()=>typeof window>"u"?{minHeight:680,maxHeight:680}:{minHeight:window.innerHeight*.4,maxHeight:window.innerHeight*.8},[.4,.8,680]),c=(0,n.useCallback)(e=>{const{minHeight:t,maxHeight:r}=f();return Math.max(t,Math.min(r,e))},[f]),H=()=>typeof window>"u"?680:c(680),[u,O]=(0,n.useState)(H),[v,p]=(0,n.useState)(!1),w=(0,n.useRef)(0),E=(0,n.useRef)(H()),h=(0,n.useRef)(null),g=(0,n.useCallback)(e=>{const t=c(e);return O(t),h.current&&h.current.style.setProperty("--livechat-mobile-height",`${t}px`),t},[c]),[T,P]=(0,n.useState)(()=>typeof window<"u"?window.innerWidth<768:!1);(0,n.useEffect)(()=>{const e=()=>{const t=window.innerWidth<768;if(P(t),t){const r=c(u);r!==u&&g(r)}};return window.addEventListener("resize",e),()=>window.removeEventListener("resize",e)},[u,c,g]);const I=e=>{if(!T)return;p(!0);const t="touches"in e?e.touches[0].clientY:e.clientY;w.current=t,E.current=u,e.preventDefault()};return(0,n.useEffect)(()=>{if(!v)return;const e=r=>{const W="touches"in r?r.touches[0].clientY:r.clientY,z=w.current-W,k=E.current+z;g(k)},t=()=>{p(!1)};return document.addEventListener("mousemove",e),document.addEventListener("mouseup",t),document.addEventListener("touchmove",e,{passive:!1}),document.addEventListener("touchend",t),()=>{document.removeEventListener("mousemove",e),document.removeEventListener("mouseup",t),document.removeEventListener("touchmove",e),document.removeEventListener("touchend",t)}},[v,g]),(0,s.jsxs)("div",{ref:h,className:`livechat-window flex flex-col overflow-hidden bg-white ${U}`,style:{borderRadius:"16px","--livechat-mobile-height":`${u}px`},children:[T&&(0,s.jsx)("div",{className:"flex cursor-ns-resize items-center justify-center py-2",style:{touchAction:"none",backgroundColor:"#ffffff"},onMouseDown:I,onTouchStart:I,children:(0,s.jsx)("div",{style:{width:"48px",height:"6px",borderRadius:"999px",backgroundColor:"#DADCE0"}})}),(0,s.jsx)(R.ChatHeader,{title:y,logoUrl:A,onClose:a,onNewSession:L,showNewSessionButton:F}),(0,s.jsx)(M.MessageList,{messages:i,rendererRegistry:x,defaultRenderer:G,showTimestamp:D,autoScroll:S,isLoadingHistory:_,onAddToCart:Y}),(0,s.jsx)(b.ChatInput,{value:o,onChange:d,onSend:l,placeholder:N,disabled:C})]})};
2
2
  //# sourceMappingURL=ChatWindow.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/components/LiveChatWidget/components/ChatWindow.tsx"],
4
- "sourcesContent": ["/**\n * \u804A\u5929\u7A97\u53E3\u5BB9\u5668\u7EC4\u4EF6\n * \u5305\u542B\u5934\u90E8\u3001\u6D88\u606F\u5217\u8868\u3001\u8F93\u5165\u6846\u7684\u5B8C\u6574\u804A\u5929\u754C\u9762\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u7A97\u53E3\u8BBE\u8BA1\n */\n\nimport React, { useState, useRef, useEffect } from 'react'\nimport type { Message, MessageRenderer } from '../types'\nimport { ChatHeader } from './ChatHeader'\nimport { MessageList } from './MessageList'\nimport { ChatInput } from './ChatInput'\nimport { MessageRendererRegistry } from '../utils/messageRenderers'\n\nexport interface ChatWindowProps {\n /**\n * \u6D88\u606F\u5217\u8868\n */\n messages: Message[]\n\n /**\n * \u8F93\u5165\u6846\u5F53\u524D\u503C\n */\n inputValue: string\n\n /**\n * \u8F93\u5165\u6846\u503C\u53D8\u5316\u56DE\u8C03\n */\n onInputChange: (value: string) => void\n\n /**\n * \u53D1\u9001\u6D88\u606F\u56DE\u8C03\n */\n onSend: () => void\n\n /**\n * \u5173\u95ED\u7A97\u53E3\u56DE\u8C03\n */\n onClose?: () => void\n\n /**\n * \u65B0\u4F1A\u8BDD\u56DE\u8C03\n */\n onNewSession?: () => void\n\n /**\n * \u5934\u90E8\u6807\u9898\n */\n title?: string\n\n /**\n * Logo URL\n */\n logoUrl?: string\n\n /**\n * \u662F\u5426\u6B63\u5728\u53D1\u9001\u6D88\u606F\n * @default false\n */\n isSending?: boolean\n\n /**\n * \u662F\u5426\u6B63\u5728\u52A0\u8F7D\u5386\u53F2\u6D88\u606F\n * @default false\n */\n isLoadingHistory?: boolean\n\n /**\n * \u662F\u5426\u663E\u793A\u65F6\u95F4\u6233\n * @default true\n */\n showTimestamp?: boolean\n\n /**\n * \u662F\u5426\u81EA\u52A8\u6EDA\u52A8\n * @default true\n */\n autoScroll?: boolean\n\n /**\n * \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\u6CE8\u518C\u8868\n */\n rendererRegistry?: MessageRendererRegistry\n\n /**\n * \u9ED8\u8BA4\u6E32\u67D3\u5668\n */\n defaultRenderer?: MessageRenderer\n\n /**\n * \u8F93\u5165\u6846\u5360\u4F4D\u7B26\n */\n inputPlaceholder?: string\n\n /**\n * \u81EA\u5B9A\u4E49\u6837\u5F0F\u7C7B\u540D\n */\n className?: string\n\n /**\n * \u5546\u54C1\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n */\n onAddToCart?: (product: any) => void\n\n /**\n * \u662F\u5426\u663E\u793A\u65B0\u4F1A\u8BDD\u6309\u94AE\n * @default true\n */\n showNewSessionButton?: boolean\n}\n\n/**\n * \u804A\u5929\u7A97\u53E3\u5BB9\u5668\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u7EC4\u5408\u5934\u90E8\u3001\u6D88\u606F\u5217\u8868\u3001\u8F93\u5165\u6846\n * - \u54CD\u5E94\u5F0F\u5E03\u5C40\uFF08\u79FB\u52A8\u7AEF\u5168\u5C4F\uFF0C\u684C\u9762\u7AEF\u56FA\u5B9A\u5C3A\u5BF8\uFF09\n * - \u52A8\u753B\u8FDB\u5165/\u9000\u51FA\u6548\u679C\n *\n * \u5E03\u5C40\u7ED3\u6784\uFF1A\n * ```\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 ChatHeader \u2502 (\u56FA\u5B9A\u9876\u90E8)\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 MessageList \u2502 (\u53EF\u6EDA\u52A8\u533A\u57DF)\n * \u2502 (flex-1) \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 ChatInput \u2502 (\u56FA\u5B9A\u5E95\u90E8)\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * ```\n *\n * \u54CD\u5E94\u5F0F\u8BBE\u8BA1\uFF1A\n * - \u79FB\u52A8\u7AEF (< 768px): \u5168\u5C4F\u663E\u793A\n * - \u5E73\u677F\u53CA\u4EE5\u4E0A (>= 768px): \u56FA\u5B9A\u5C3A\u5BF8\u5F39\u7A97\n *\n * @example\n * ```tsx\n * <ChatWindow\n * messages={messages}\n * inputValue={inputValue}\n * onInputChange={setInputValue}\n * onSend={handleSend}\n * onClose={() => setIsOpen(false)}\n * onNewSession={handleNewSession}\n * isSending={isStreaming}\n * title=\"AI \u52A9\u624B\"\n * logoUrl=\"/logo.png\"\n * />\n * ```\n */\nexport const ChatWindow: React.FC<ChatWindowProps> = ({\n messages,\n inputValue,\n onInputChange,\n onSend,\n onClose,\n onNewSession,\n title,\n logoUrl,\n isSending = false,\n isLoadingHistory = false,\n showTimestamp = true,\n autoScroll = true,\n rendererRegistry,\n defaultRenderer,\n inputPlaceholder,\n className = '',\n onAddToCart,\n showNewSessionButton = true,\n}) => {\n // \u79FB\u52A8\u7AEF\u9AD8\u5EA6\u8C03\u8282\u72B6\u6001\uFF08\u9ED8\u8BA4375px\uFF09\n const [mobileHeight, setMobileHeight] = useState(375)\n const [isDragging, setIsDragging] = useState(false)\n const dragStartY = useRef(0)\n const dragStartHeight = useRef(375)\n const windowRef = useRef<HTMLDivElement>(null)\n\n // \u68C0\u6D4B\u662F\u5426\u4E3A\u79FB\u52A8\u7AEF\uFF08\u521D\u59CB\u5316\u65F6\u7ACB\u5373\u68C0\u6D4B\uFF09\n const [isMobile, setIsMobile] = useState(() => {\n if (typeof window !== 'undefined') {\n return window.innerWidth < 768\n }\n return false\n })\n\n useEffect(() => {\n const checkMobile = () => {\n setIsMobile(window.innerWidth < 768)\n }\n window.addEventListener('resize', checkMobile)\n return () => window.removeEventListener('resize', checkMobile)\n }, [])\n\n // \u62D6\u62FD\u5F00\u59CB\n const handleDragStart = (e: React.MouseEvent | React.TouchEvent) => {\n if (!isMobile) return\n\n setIsDragging(true)\n const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY\n dragStartY.current = clientY\n dragStartHeight.current = mobileHeight\n\n // \u963B\u6B62\u9ED8\u8BA4\u884C\u4E3A\n e.preventDefault()\n }\n\n // \u62D6\u62FD\u4E2D\n useEffect(() => {\n if (!isDragging) return\n\n const handleDragMove = (e: MouseEvent | TouchEvent) => {\n const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY\n const deltaY = dragStartY.current - clientY // \u5411\u4E0A\u62D6\u52A8\u4E3A\u6B63\u503C\n const newHeight = Math.max(375, Math.min(window.innerHeight * 0.9, dragStartHeight.current + deltaY))\n\n setMobileHeight(newHeight)\n\n // \u66F4\u65B0CSS\u53D8\u91CF\n if (windowRef.current) {\n windowRef.current.style.setProperty('--livechat-mobile-height', `${newHeight}px`)\n }\n }\n\n const handleDragEnd = () => {\n setIsDragging(false)\n }\n\n document.addEventListener('mousemove', handleDragMove)\n document.addEventListener('mouseup', handleDragEnd)\n document.addEventListener('touchmove', handleDragMove, { passive: false })\n document.addEventListener('touchend', handleDragEnd)\n\n return () => {\n document.removeEventListener('mousemove', handleDragMove)\n document.removeEventListener('mouseup', handleDragEnd)\n document.removeEventListener('touchmove', handleDragMove)\n document.removeEventListener('touchend', handleDragEnd)\n }\n }, [isDragging])\n\n return (\n <div\n ref={windowRef}\n className={`livechat-window flex flex-col overflow-hidden bg-white ${className}`}\n style={\n {\n borderRadius: '16px',\n '--livechat-mobile-height': `${mobileHeight}px`,\n } as React.CSSProperties\n }\n >\n {/* \u79FB\u52A8\u7AEF\u62D6\u62FD\u624B\u67C4 */}\n {isMobile && (\n <div\n className=\"flex cursor-ns-resize items-center justify-center\"\n style={{\n touchAction: 'none',\n backgroundColor: '#ffffff',\n paddingTop: '8px',\n }}\n onMouseDown={handleDragStart}\n onTouchStart={handleDragStart}\n >\n <div\n style={{\n width: '64px',\n height: '6px',\n borderRadius: '999px',\n backgroundColor: '#DADCE0',\n }}\n />\n </div>\n )}\n\n {/* \u5934\u90E8 */}\n <ChatHeader\n title={title}\n logoUrl={logoUrl}\n onClose={onClose}\n onNewSession={onNewSession}\n showNewSessionButton={showNewSessionButton}\n />\n\n {/* \u6D88\u606F\u5217\u8868\uFF08\u53EF\u6EDA\u52A8\u533A\u57DF\uFF09 */}\n <MessageList\n messages={messages}\n rendererRegistry={rendererRegistry}\n defaultRenderer={defaultRenderer}\n showTimestamp={showTimestamp}\n autoScroll={autoScroll}\n isLoadingHistory={isLoadingHistory}\n onAddToCart={onAddToCart}\n />\n\n {/* \u8F93\u5165\u6846 */}\n <ChatInput\n value={inputValue}\n onChange={onInputChange}\n onSend={onSend}\n placeholder={inputPlaceholder}\n disabled={isSending}\n />\n </div>\n )\n}\n"],
5
- "mappings": "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",
6
- "names": ["ChatWindow_exports", "__export", "ChatWindow", "__toCommonJS", "import_jsx_runtime", "import_react", "import_ChatHeader", "import_MessageList", "import_ChatInput", "messages", "inputValue", "onInputChange", "onSend", "onClose", "onNewSession", "title", "logoUrl", "isSending", "isLoadingHistory", "showTimestamp", "autoScroll", "rendererRegistry", "defaultRenderer", "inputPlaceholder", "className", "onAddToCart", "showNewSessionButton", "mobileHeight", "setMobileHeight", "isDragging", "setIsDragging", "dragStartY", "dragStartHeight", "windowRef", "isMobile", "setIsMobile", "checkMobile", "handleDragStart", "clientY", "handleDragMove", "e", "deltaY", "newHeight", "handleDragEnd"]
4
+ "sourcesContent": ["/**\n * \u804A\u5929\u7A97\u53E3\u5BB9\u5668\u7EC4\u4EF6\n * \u5305\u542B\u5934\u90E8\u3001\u6D88\u606F\u5217\u8868\u3001\u8F93\u5165\u6846\u7684\u5B8C\u6574\u804A\u5929\u754C\u9762\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u7A97\u53E3\u8BBE\u8BA1\n */\n\nimport React, { useState, useRef, useEffect, useCallback } from 'react'\nimport type { Message, MessageRenderer } from '../types'\nimport { ChatHeader } from './ChatHeader'\nimport { MessageList } from './MessageList'\nimport { ChatInput } from './ChatInput'\nimport { MessageRendererRegistry } from '../utils/messageRenderers'\n\nexport interface ChatWindowProps {\n /**\n * \u6D88\u606F\u5217\u8868\n */\n messages: Message[]\n\n /**\n * \u8F93\u5165\u6846\u5F53\u524D\u503C\n */\n inputValue: string\n\n /**\n * \u8F93\u5165\u6846\u503C\u53D8\u5316\u56DE\u8C03\n */\n onInputChange: (value: string) => void\n\n /**\n * \u53D1\u9001\u6D88\u606F\u56DE\u8C03\n */\n onSend: () => void\n\n /**\n * \u5173\u95ED\u7A97\u53E3\u56DE\u8C03\n */\n onClose?: () => void\n\n /**\n * \u65B0\u4F1A\u8BDD\u56DE\u8C03\n */\n onNewSession?: () => void\n\n /**\n * \u5934\u90E8\u6807\u9898\n */\n title?: string\n\n /**\n * Logo URL\n */\n logoUrl?: string\n\n /**\n * \u662F\u5426\u6B63\u5728\u53D1\u9001\u6D88\u606F\n * @default false\n */\n isSending?: boolean\n\n /**\n * \u662F\u5426\u6B63\u5728\u52A0\u8F7D\u5386\u53F2\u6D88\u606F\n * @default false\n */\n isLoadingHistory?: boolean\n\n /**\n * \u662F\u5426\u663E\u793A\u65F6\u95F4\u6233\n * @default true\n */\n showTimestamp?: boolean\n\n /**\n * \u662F\u5426\u81EA\u52A8\u6EDA\u52A8\n * @default true\n */\n autoScroll?: boolean\n\n /**\n * \u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\u6CE8\u518C\u8868\n */\n rendererRegistry?: MessageRendererRegistry\n\n /**\n * \u9ED8\u8BA4\u6E32\u67D3\u5668\n */\n defaultRenderer?: MessageRenderer\n\n /**\n * \u8F93\u5165\u6846\u5360\u4F4D\u7B26\n */\n inputPlaceholder?: string\n\n /**\n * \u81EA\u5B9A\u4E49\u6837\u5F0F\u7C7B\u540D\n */\n className?: string\n\n /**\n * \u5546\u54C1\u6DFB\u52A0\u5230\u8D2D\u7269\u8F66\u56DE\u8C03\n */\n onAddToCart?: (product: any) => void\n\n /**\n * \u662F\u5426\u663E\u793A\u65B0\u4F1A\u8BDD\u6309\u94AE\n * @default true\n */\n showNewSessionButton?: boolean\n}\n\n/**\n * \u804A\u5929\u7A97\u53E3\u5BB9\u5668\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u7EC4\u5408\u5934\u90E8\u3001\u6D88\u606F\u5217\u8868\u3001\u8F93\u5165\u6846\n * - \u54CD\u5E94\u5F0F\u5E03\u5C40\uFF08\u79FB\u52A8\u7AEF\u5168\u5C4F\uFF0C\u684C\u9762\u7AEF\u56FA\u5B9A\u5C3A\u5BF8\uFF09\n * - \u52A8\u753B\u8FDB\u5165/\u9000\u51FA\u6548\u679C\n *\n * \u5E03\u5C40\u7ED3\u6784\uFF1A\n * ```\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 ChatHeader \u2502 (\u56FA\u5B9A\u9876\u90E8)\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 MessageList \u2502 (\u53EF\u6EDA\u52A8\u533A\u57DF)\n * \u2502 (flex-1) \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 ChatInput \u2502 (\u56FA\u5B9A\u5E95\u90E8)\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * ```\n *\n * \u54CD\u5E94\u5F0F\u8BBE\u8BA1\uFF1A\n * - \u79FB\u52A8\u7AEF (< 768px): \u5168\u5C4F\u663E\u793A\n * - \u5E73\u677F\u53CA\u4EE5\u4E0A (>= 768px): \u56FA\u5B9A\u5C3A\u5BF8\u5F39\u7A97\n *\n * @example\n * ```tsx\n * <ChatWindow\n * messages={messages}\n * inputValue={inputValue}\n * onInputChange={setInputValue}\n * onSend={handleSend}\n * onClose={() => setIsOpen(false)}\n * onNewSession={handleNewSession}\n * isSending={isStreaming}\n * title=\"AI \u52A9\u624B\"\n * logoUrl=\"/logo.png\"\n * />\n * ```\n */\nexport const ChatWindow: React.FC<ChatWindowProps> = ({\n messages,\n inputValue,\n onInputChange,\n onSend,\n onClose,\n onNewSession,\n title,\n logoUrl,\n isSending = false,\n isLoadingHistory = false,\n showTimestamp = true,\n autoScroll = true,\n rendererRegistry,\n defaultRenderer,\n inputPlaceholder,\n className = '',\n onAddToCart,\n showNewSessionButton = true,\n}) => {\n // \u5E38\u91CF\uFF1A\u9AD8\u5EA6\u9650\u5236\u6BD4\u4F8B\n const MIN_HEIGHT_RATIO = 0.4 // \u6700\u5C0F40%\n const MAX_HEIGHT_RATIO = 0.8 // \u6700\u592780%\n const DEFAULT_HEIGHT = 680 // \u9ED8\u8BA4680px\n\n // \u83B7\u53D6\u5F53\u524D\u5141\u8BB8\u7684\u9AD8\u5EA6\u8303\u56F4\n const getHeightConstraints = useCallback(() => {\n if (typeof window === 'undefined') {\n return { minHeight: DEFAULT_HEIGHT, maxHeight: DEFAULT_HEIGHT }\n }\n return {\n minHeight: window.innerHeight * MIN_HEIGHT_RATIO,\n maxHeight: window.innerHeight * MAX_HEIGHT_RATIO,\n }\n }, [MIN_HEIGHT_RATIO, MAX_HEIGHT_RATIO, DEFAULT_HEIGHT])\n\n // \u5C06\u9AD8\u5EA6\u9650\u5236\u5728\u5141\u8BB8\u8303\u56F4\u5185\n const clampHeight = useCallback(\n (height: number) => {\n const { minHeight, maxHeight } = getHeightConstraints()\n return Math.max(minHeight, Math.min(maxHeight, height))\n },\n [getHeightConstraints]\n )\n\n // \u8BA1\u7B97\u521D\u59CB\u9AD8\u5EA6\uFF1A\u9ED8\u8BA4680px\uFF0C\u4F46\u9700\u8981\u572830%-80%\u8303\u56F4\u5185\n const getInitialHeight = () => {\n if (typeof window === 'undefined') return DEFAULT_HEIGHT\n return clampHeight(DEFAULT_HEIGHT)\n }\n\n // \u79FB\u52A8\u7AEF\u9AD8\u5EA6\u8C03\u8282\u72B6\u6001\uFF08\u9ED8\u8BA4680px\u6216\u5C4F\u5E5580%\uFF0C\u53D6\u8F83\u5C0F\u503C\uFF09\n const [mobileHeight, setMobileHeight] = useState(getInitialHeight)\n const [isDragging, setIsDragging] = useState(false)\n const dragStartY = useRef(0)\n const dragStartHeight = useRef(getInitialHeight())\n const windowRef = useRef<HTMLDivElement>(null)\n\n // \u66F4\u65B0\u9AD8\u5EA6\u5E76\u540C\u6B65CSS\u53D8\u91CF\n const updateHeight = useCallback(\n (newHeight: number) => {\n const clampedHeight = clampHeight(newHeight)\n setMobileHeight(clampedHeight)\n // \u540C\u6B65\u66F4\u65B0CSS\u53D8\u91CF\uFF0C\u4F9B livechat.css \u4F7F\u7528\n if (windowRef.current) {\n windowRef.current.style.setProperty('--livechat-mobile-height', `${clampedHeight}px`)\n }\n return clampedHeight\n },\n [clampHeight]\n )\n\n // \u68C0\u6D4B\u662F\u5426\u4E3A\u79FB\u52A8\u7AEF\uFF08\u521D\u59CB\u5316\u65F6\u7ACB\u5373\u68C0\u6D4B\uFF09\n const [isMobile, setIsMobile] = useState(() => {\n if (typeof window !== 'undefined') {\n return window.innerWidth < 768\n }\n return false\n })\n\n useEffect(() => {\n const handleResize = () => {\n const newIsMobile = window.innerWidth < 768\n setIsMobile(newIsMobile)\n\n // \u5982\u679C\u662F\u79FB\u52A8\u7AEF\uFF0C\u68C0\u67E5\u5F53\u524D\u9AD8\u5EA6\u662F\u5426\u5728\u5141\u8BB8\u8303\u56F4\u5185\n if (newIsMobile) {\n const clampedHeight = clampHeight(mobileHeight)\n if (clampedHeight !== mobileHeight) {\n updateHeight(clampedHeight)\n }\n }\n }\n\n window.addEventListener('resize', handleResize)\n return () => window.removeEventListener('resize', handleResize)\n }, [mobileHeight, clampHeight, updateHeight])\n\n // \u62D6\u62FD\u5F00\u59CB\n const handleDragStart = (e: React.MouseEvent | React.TouchEvent) => {\n if (!isMobile) return\n\n setIsDragging(true)\n const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY\n dragStartY.current = clientY\n dragStartHeight.current = mobileHeight\n\n // \u963B\u6B62\u9ED8\u8BA4\u884C\u4E3A\n e.preventDefault()\n }\n\n // \u62D6\u62FD\u4E2D\n useEffect(() => {\n if (!isDragging) return\n\n const handleDragMove = (e: MouseEvent | TouchEvent) => {\n const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY\n const deltaY = dragStartY.current - clientY // \u5411\u4E0A\u62D6\u52A8\u4E3A\u6B63\u503C\n const newHeight = dragStartHeight.current + deltaY\n\n updateHeight(newHeight)\n }\n\n const handleDragEnd = () => {\n setIsDragging(false)\n }\n\n document.addEventListener('mousemove', handleDragMove)\n document.addEventListener('mouseup', handleDragEnd)\n document.addEventListener('touchmove', handleDragMove, { passive: false })\n document.addEventListener('touchend', handleDragEnd)\n\n return () => {\n document.removeEventListener('mousemove', handleDragMove)\n document.removeEventListener('mouseup', handleDragEnd)\n document.removeEventListener('touchmove', handleDragMove)\n document.removeEventListener('touchend', handleDragEnd)\n }\n }, [isDragging, updateHeight])\n\n return (\n <div\n ref={windowRef}\n className={`livechat-window flex flex-col overflow-hidden bg-white ${className}`}\n style={\n {\n borderRadius: '16px',\n '--livechat-mobile-height': `${mobileHeight}px`,\n } as React.CSSProperties\n }\n >\n {/* \u79FB\u52A8\u7AEF\u62D6\u62FD\u624B\u67C4 */}\n {isMobile && (\n <div\n className=\"flex cursor-ns-resize items-center justify-center py-2\"\n style={{\n touchAction: 'none',\n backgroundColor: '#ffffff',\n }}\n onMouseDown={handleDragStart}\n onTouchStart={handleDragStart}\n >\n <div\n style={{\n width: '48px',\n height: '6px',\n borderRadius: '999px',\n backgroundColor: '#DADCE0',\n }}\n />\n </div>\n )}\n\n {/* \u5934\u90E8 */}\n <ChatHeader\n title={title}\n logoUrl={logoUrl}\n onClose={onClose}\n onNewSession={onNewSession}\n showNewSessionButton={showNewSessionButton}\n />\n\n {/* \u6D88\u606F\u5217\u8868\uFF08\u53EF\u6EDA\u52A8\u533A\u57DF\uFF09 */}\n <MessageList\n messages={messages}\n rendererRegistry={rendererRegistry}\n defaultRenderer={defaultRenderer}\n showTimestamp={showTimestamp}\n autoScroll={autoScroll}\n isLoadingHistory={isLoadingHistory}\n onAddToCart={onAddToCart}\n />\n\n {/* \u8F93\u5165\u6846 */}\n <ChatInput\n value={inputValue}\n onChange={onInputChange}\n onSend={onSend}\n placeholder={inputPlaceholder}\n disabled={isSending}\n />\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["ChatWindow_exports", "__export", "ChatWindow", "__toCommonJS", "import_jsx_runtime", "import_react", "import_ChatHeader", "import_MessageList", "import_ChatInput", "messages", "inputValue", "onInputChange", "onSend", "onClose", "onNewSession", "title", "logoUrl", "isSending", "isLoadingHistory", "showTimestamp", "autoScroll", "rendererRegistry", "defaultRenderer", "inputPlaceholder", "className", "onAddToCart", "showNewSessionButton", "getHeightConstraints", "clampHeight", "height", "minHeight", "maxHeight", "getInitialHeight", "mobileHeight", "setMobileHeight", "isDragging", "setIsDragging", "dragStartY", "dragStartHeight", "windowRef", "updateHeight", "newHeight", "clampedHeight", "isMobile", "setIsMobile", "handleResize", "newIsMobile", "handleDragStart", "clientY", "handleDragMove", "e", "deltaY", "handleDragEnd"]
7
7
  }
@@ -1,2 +1,2 @@
1
- "use strict";var c=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var y=(e,a)=>{for(var l in a)c(e,l,{get:a[l],enumerable:!0})},N=(e,a,l,o)=>{if(a&&typeof a=="object"||typeof a=="function")for(let n of b(a))!C.call(e,n)&&n!==l&&c(e,n,{get:()=>a[n],enumerable:!(o=v(a,n))||o.enumerable});return e};var h=e=>N(c({},"__esModule",{value:!0}),e);var F={};y(F,{CartCard:()=>w});module.exports=h(F);var t=require("react/jsx-runtime"),d=require("../../constants.js");function u(e){const{amount:a,currencyCode:l}=e,o=d.CURRENCY_SYMBOLS[l]||l,n=parseFloat(a);return`${o}${n.toFixed(2)}`}const A=({line:e})=>{const{quantity:a,merchandise:l,cost:o}=e,{product:n,title:i,image:s}=l,r=s?.url||"",m=parseFloat(o.totalAmount.amount)<parseFloat(o.subtotalAmount.amount)&&o.totalAmount.currencyCode===o.subtotalAmount.currencyCode;return(0,t.jsxs)("div",{className:"flex gap-4",children:[(0,t.jsx)("div",{className:"shrink-0 overflow-hidden rounded-md",style:{width:"72px",height:"72px"},children:(0,t.jsx)("img",{src:r,alt:n.title,className:"size-full object-cover",loading:"lazy"})}),(0,t.jsx)("div",{className:"flex flex-1 flex-col",children:(0,t.jsxs)("div",{className:"flex items-end justify-between gap-2",children:[(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)("h4",{className:"line-clamp-2 text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]",children:n.title}),i&&(0,t.jsx)("p",{className:"mt-0.5 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]",children:i}),(0,t.jsxs)("p",{className:"mt-1 text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]",children:["\xD7",a]})]}),(0,t.jsxs)("div",{className:"flex gap-1 text-right",children:[(0,t.jsx)("div",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900",children:u(o.totalAmount)}),m&&(0,t.jsx)("div",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56] line-through",children:u(o.subtotalAmount)})]})]})})]})},k=({total:e})=>(0,t.jsx)("div",{className:"border-t border-gray-200 p-4",children:(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsx)("span",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900",children:"Total"}),(0,t.jsx)("span",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900",children:u(e)})]})}),w={render:e=>{const a=e,{data:l}=a;if(!l)return null;const{isEmpty:o,lines:n,cost:i,checkoutUrl:s,onCart:r,cartId:m,commonText:g}=l,f={...d.DEFAULT_COMMON_TEXT,...g},p=()=>{r?r(m,s):s&&window.open(s,"_blank","noopener,noreferrer")};return o||!n||n.length===0?null:(0,t.jsxs)("div",{className:"w-full max-w-md overflow-hidden rounded-2xl shadow-sm",style:{backgroundColor:"#F5F6F7"},children:[(0,t.jsx)("div",{className:"flex max-h-[400px] flex-col gap-4 overflow-y-auto p-3",children:n.map(x=>(0,t.jsx)(A,{line:x},x.id))}),(0,t.jsx)(k,{total:i.totalAmount}),(s||r)&&(0,t.jsx)("div",{className:"px-4 pb-4",children:(0,t.jsx)("button",{type:"button",onClick:p,className:"w-full rounded-full py-[10px] text-center text-sm font-bold leading-[1.4] tracking-[-0.02em] text-white",style:{backgroundColor:"#1D1D1F"},children:f.viewMore})})]})}};
1
+ "use strict";var c=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var y=(e,a)=>{for(var l in a)c(e,l,{get:a[l],enumerable:!0})},N=(e,a,l,o)=>{if(a&&typeof a=="object"||typeof a=="function")for(let n of b(a))!C.call(e,n)&&n!==l&&c(e,n,{get:()=>a[n],enumerable:!(o=v(a,n))||o.enumerable});return e};var h=e=>N(c({},"__esModule",{value:!0}),e);var F={};y(F,{CartCard:()=>w});module.exports=h(F);var t=require("react/jsx-runtime"),d=require("../../constants.js");function u(e){const{amount:a,currencyCode:l}=e,o=d.CURRENCY_SYMBOLS[l]||l,n=parseFloat(a);return`${o}${n.toFixed(2)}`}const A=({line:e})=>{const{quantity:a,merchandise:l,cost:o}=e,{product:n,title:i,image:s}=l,r=s?.url||"",m=parseFloat(o.totalAmount.amount)<parseFloat(o.subtotalAmount.amount)&&o.totalAmount.currencyCode===o.subtotalAmount.currencyCode;return(0,t.jsxs)("div",{className:"flex gap-4",children:[(0,t.jsx)("div",{className:"shrink-0 overflow-hidden rounded-md",style:{width:"72px",height:"72px"},children:(0,t.jsx)("img",{src:r,alt:n.title,className:"size-full object-cover",loading:"lazy"})}),(0,t.jsxs)("div",{className:"flex flex-1 flex-col",children:[(0,t.jsx)("h4",{className:"line-clamp-2 text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]",children:n.title}),(0,t.jsxs)("div",{className:"flex items-end justify-between gap-2",children:[(0,t.jsxs)("div",{className:"flex-1",children:[i&&(0,t.jsx)("p",{className:"mt-0.5 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]",children:i}),(0,t.jsxs)("p",{className:"mt-1 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]",children:["\xD7",a]})]}),(0,t.jsxs)("div",{className:"flex gap-1 text-right",children:[(0,t.jsx)("div",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900",children:u(o.totalAmount)}),m&&(0,t.jsx)("div",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56] line-through",children:u(o.subtotalAmount)})]})]})]})]})},k=({total:e})=>(0,t.jsx)("div",{className:"border-t border-gray-200 p-4",children:(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsx)("span",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900",children:"Total"}),(0,t.jsx)("span",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900",children:u(e)})]})}),w={render:e=>{const a=e,{data:l}=a;if(!l)return null;const{isEmpty:o,lines:n,cost:i,checkoutUrl:s,onCart:r,cartId:m,commonText:f}=l,x={...d.DEFAULT_COMMON_TEXT,...f},p=()=>{r?r(m,s):s&&window.open(s,"_blank","noopener,noreferrer")};return o||!n||n.length===0?null:(0,t.jsxs)("div",{className:"w-full max-w-md overflow-hidden rounded-2xl shadow-sm",style:{backgroundColor:"#F5F6F7"},children:[(0,t.jsx)("div",{className:"flex flex-col gap-4 overflow-y-auto p-3",children:n.map(g=>(0,t.jsx)(A,{line:g},g.id))}),(0,t.jsx)(k,{total:i.totalAmount}),(s||r)&&(0,t.jsx)("div",{className:"px-4 pb-4",children:(0,t.jsx)("button",{type:"button",onClick:p,className:"w-full rounded-full py-[10px] text-center text-sm font-bold leading-[1.4] tracking-[-0.02em] text-white",style:{backgroundColor:"#1D1D1F"},children:x.viewMore})})]})}};
2
2
  //# sourceMappingURL=CartCard.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/components/LiveChatWidget/components/MessageContent/CartCard.tsx"],
4
- "sourcesContent": ["/**\n * \u8D2D\u7269\u8F66\u5361\u7247\u6E32\u67D3\u5668\n * \u663E\u793A\u8D2D\u7269\u8F66\u5185\u5BB9\u3001\u4EF7\u683C\u6C47\u603B\u548C\u7ED3\u8D26\u6309\u94AE\n * \u57FA\u4E8E\u540E\u7AEF\u8FD4\u56DE\u7684\u8D2D\u7269\u8F66\u6570\u636E\u7ED3\u6784\n */\n\nimport React from 'react'\nimport type { MessageRenderer, CartContent, CartLine, CartAmount } from '../../types'\nimport { CURRENCY_SYMBOLS, DEFAULT_COMMON_TEXT } from '../../constants.js'\n\n/**\n * \u683C\u5F0F\u5316\u91D1\u989D\n * @param amount \u91D1\u989D\u5BF9\u8C61\n * @returns \u683C\u5F0F\u5316\u540E\u7684\u91D1\u989D\u5B57\u7B26\u4E32\uFF08\u5982 \"$99.99\"\uFF09\n */\nfunction formatAmount(amount: CartAmount): string {\n const { amount: value, currencyCode } = amount\n\n const symbol = CURRENCY_SYMBOLS[currencyCode] || currencyCode\n const numValue = parseFloat(value)\n\n return `${symbol}${numValue.toFixed(2)}`\n}\n\n/**\n * \u8D2D\u7269\u8F66\u5546\u54C1\u884C\u7EC4\u4EF6\n */\nconst CartLineItem: React.FC<{\n line: CartLine\n}> = ({ line }) => {\n const { quantity, merchandise, cost } = line\n const { product, title: variantTitle, image } = merchandise\n\n // \u5546\u54C1\u56FE\u7247 URL\n const imageUrl = image?.url || ''\n\n // \u5224\u65AD\u662F\u5426\u6709\u6298\u6263\uFF08\u603B\u4EF7 < \u539F\u4EF7\uFF09\n const hasDiscount =\n parseFloat(cost.totalAmount.amount) < parseFloat(cost.subtotalAmount.amount) &&\n cost.totalAmount.currencyCode === cost.subtotalAmount.currencyCode\n\n return (\n <div className=\"flex gap-4\">\n {/* \u5546\u54C1\u56FE\u7247 */}\n <div className=\"shrink-0 overflow-hidden rounded-md\" style={{ width: '72px', height: '72px' }}>\n <img src={imageUrl} alt={product.title} className=\"size-full object-cover\" loading=\"lazy\" />\n </div>\n\n {/* \u5546\u54C1\u4FE1\u606F */}\n <div className=\"flex flex-1 flex-col\">\n <div className=\"flex items-end justify-between gap-2\">\n {/* \u5DE6\u4FA7\uFF1A\u6807\u9898\u3001\u53D8\u4F53\u3001\u6570\u91CF */}\n <div className=\"flex-1\">\n <h4 className=\"line-clamp-2 text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]\">\n {product.title}\n </h4>\n {variantTitle && (\n <p className=\"mt-0.5 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]\">{variantTitle}</p>\n )}\n <p className=\"mt-1 text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]\">\u00D7{quantity}</p>\n </div>\n\n {/* \u53F3\u4FA7\uFF1A\u4EF7\u683C */}\n <div className=\"flex gap-1 text-right\">\n <div className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900\">\n {formatAmount(cost.totalAmount)}\n </div>\n {hasDiscount && (\n <div className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56] line-through\">\n {formatAmount(cost.subtotalAmount)}\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * \u4EF7\u683C\u6C47\u603B\u7EC4\u4EF6\uFF08\u7B80\u5316\u7248\uFF09\n */\nconst CartSummary: React.FC<{\n total: CartAmount\n}> = ({ total }) => {\n return (\n <div className=\"border-t border-gray-200 p-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900\">Total</span>\n <span className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900\">\n {formatAmount(total)}\n </span>\n </div>\n </div>\n )\n}\n\n/**\n * \u8D2D\u7269\u8F66\u5361\u7247\u6E32\u67D3\u5668\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u8D2D\u7269\u8F66\u5546\u54C1\u5217\u8868\n * - \u663E\u793A\u4EF7\u683C\u6C47\u603B\uFF08\u5C0F\u8BA1\u3001\u6298\u6263\u3001\u603B\u8BA1\uFF09\n * - \u663E\u793A\u6298\u6263\u7801\n * - \u63D0\u4F9B Checkout \u6309\u94AE\n * - \u7A7A\u8D2D\u7269\u8F66\u72B6\u6001\n *\n * \u5E03\u5C40\uFF1A\n * ```\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 \u8D2D\u7269\u8F66 (3 \u4EF6\u5546\u54C1) \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 [\u56FE] \u5546\u54C11 \u2502\n * \u2502 \u53D8\u4F53: Black \u2502\n * \u2502 \u6570\u91CF: 2 $199.98 \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 [\u56FE] \u5546\u54C12 \u2502\n * \u2502 \u53D8\u4F53: White \u2502\n * \u2502 \u6570\u91CF: 1 $99.99 \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 \u5C0F\u8BA1 $299.97 \u2502\n * \u2502 \u6298\u6263 [SPRING20] -$30.00 \u2502\n * \u2502 \u603B\u8BA1 $269.97 \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 [Checkout \u6309\u94AE] \u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * ```\n *\n * @example\n * ```tsx\n * const content: CartContent = {\n * type: 'cart',\n * data: {\n * isEmpty: false,\n * cartId: \"gid://...\",\n * totalQuantity: 3,\n * lines: [...],\n * cost: {...},\n * checkoutUrl: \"https://...\"\n * }\n * }\n * <CartCard.render(content, false, false) />\n * ```\n */\nexport const CartCard: MessageRenderer = {\n render: content => {\n const cartContent = content as CartContent\n const { data } = cartContent\n\n if (!data) {\n return null\n }\n\n const { isEmpty, lines, cost, checkoutUrl, onCart, cartId, commonText } = data\n\n // \u5408\u5E76\u9ED8\u8BA4\u6587\u6848\u548C\u81EA\u5B9A\u4E49\u6587\u6848\n const mergedText = { ...DEFAULT_COMMON_TEXT, ...commonText }\n\n // \u5904\u7406\u8D2D\u7269\u8F66\u6309\u94AE\u70B9\u51FB\n const handleCart = () => {\n if (onCart) {\n onCart(cartId, checkoutUrl)\n } else if (checkoutUrl) {\n window.open(checkoutUrl, '_blank', 'noopener,noreferrer')\n }\n }\n\n // \u7A7A\u8D2D\u7269\u8F66\u72B6\u6001 - \u4E0D\u5C55\u793A\u7EC4\u4EF6\n if (isEmpty || !lines || lines.length === 0) {\n return null\n }\n\n return (\n <div className=\"w-full max-w-md overflow-hidden rounded-2xl shadow-sm\" style={{ backgroundColor: '#F5F6F7' }}>\n {/* \u5546\u54C1\u5217\u8868 */}\n <div className=\"flex max-h-[400px] flex-col gap-4 overflow-y-auto p-3\">\n {lines.map(line => (\n <CartLineItem key={line.id} line={line} />\n ))}\n </div>\n\n {/* \u4EF7\u683C\u6C47\u603B */}\n <CartSummary total={cost.totalAmount} />\n\n {/* Checkout \u6309\u94AE */}\n {(checkoutUrl || onCart) && (\n <div className=\"px-4 pb-4\">\n <button\n type=\"button\"\n onClick={handleCart}\n className=\"w-full rounded-full py-[10px] text-center text-sm font-bold leading-[1.4] tracking-[-0.02em] text-white\"\n style={{ backgroundColor: '#1D1D1F' }}\n >\n {mergedText.viewMore}\n </button>\n </div>\n )}\n </div>\n )\n },\n}\n"],
5
- "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,cAAAE,IAAA,eAAAC,EAAAH,GA6CQ,IAAAI,EAAA,6BArCRC,EAAsD,8BAOtD,SAASC,EAAaC,EAA4B,CAChD,KAAM,CAAE,OAAQC,EAAO,aAAAC,CAAa,EAAIF,EAElCG,EAAS,mBAAiBD,CAAY,GAAKA,EAC3CE,EAAW,WAAWH,CAAK,EAEjC,MAAO,GAAGE,CAAM,GAAGC,EAAS,QAAQ,CAAC,CAAC,EACxC,CAKA,MAAMC,EAED,CAAC,CAAE,KAAAC,CAAK,IAAM,CACjB,KAAM,CAAE,SAAAC,EAAU,YAAAC,EAAa,KAAAC,CAAK,EAAIH,EAClC,CAAE,QAAAI,EAAS,MAAOC,EAAc,MAAAC,CAAM,EAAIJ,EAG1CK,EAAWD,GAAO,KAAO,GAGzBE,EACJ,WAAWL,EAAK,YAAY,MAAM,EAAI,WAAWA,EAAK,eAAe,MAAM,GAC3EA,EAAK,YAAY,eAAiBA,EAAK,eAAe,aAExD,SACE,QAAC,OAAI,UAAU,aAEb,oBAAC,OAAI,UAAU,sCAAsC,MAAO,CAAE,MAAO,OAAQ,OAAQ,MAAO,EAC1F,mBAAC,OAAI,IAAKI,EAAU,IAAKH,EAAQ,MAAO,UAAU,yBAAyB,QAAQ,OAAO,EAC5F,KAGA,OAAC,OAAI,UAAU,uBACb,oBAAC,OAAI,UAAU,uCAEb,qBAAC,OAAI,UAAU,SACb,oBAAC,MAAG,UAAU,mFACX,SAAAA,EAAQ,MACX,EACCC,MACC,OAAC,KAAE,UAAU,2EAA4E,SAAAA,EAAa,KAExG,QAAC,KAAE,UAAU,2EAA2E,iBAAEJ,GAAS,GACrG,KAGA,QAAC,OAAI,UAAU,wBACb,oBAAC,OAAI,UAAU,qEACZ,SAAAR,EAAaU,EAAK,WAAW,EAChC,EACCK,MACC,OAAC,OAAI,UAAU,mFACZ,SAAAf,EAAaU,EAAK,cAAc,EACnC,GAEJ,GACF,EACF,GACF,CAEJ,EAKMM,EAED,CAAC,CAAE,MAAAC,CAAM,OAEV,OAAC,OAAI,UAAU,+BACb,oBAAC,OAAI,UAAU,oCACb,oBAAC,QAAK,UAAU,qEAAqE,iBAAK,KAC1F,OAAC,QAAK,UAAU,qEACb,SAAAjB,EAAaiB,CAAK,EACrB,GACF,EACF,EAmDSrB,EAA4B,CACvC,OAAQsB,GAAW,CACjB,MAAMC,EAAcD,EACd,CAAE,KAAAE,CAAK,EAAID,EAEjB,GAAI,CAACC,EACH,OAAO,KAGT,KAAM,CAAE,QAAAC,EAAS,MAAAC,EAAO,KAAAZ,EAAM,YAAAa,EAAa,OAAAC,EAAQ,OAAAC,EAAQ,WAAAC,CAAW,EAAIN,EAGpEO,EAAa,CAAE,GAAG,sBAAqB,GAAGD,CAAW,EAGrDE,EAAa,IAAM,CACnBJ,EACFA,EAAOC,EAAQF,CAAW,EACjBA,GACT,OAAO,KAAKA,EAAa,SAAU,qBAAqB,CAE5D,EAGA,OAAIF,GAAW,CAACC,GAASA,EAAM,SAAW,EACjC,QAIP,QAAC,OAAI,UAAU,wDAAwD,MAAO,CAAE,gBAAiB,SAAU,EAEzG,oBAAC,OAAI,UAAU,wDACZ,SAAAA,EAAM,IAAIf,MACT,OAACD,EAAA,CAA2B,KAAMC,GAAfA,EAAK,EAAgB,CACzC,EACH,KAGA,OAACS,EAAA,CAAY,MAAON,EAAK,YAAa,GAGpCa,GAAeC,OACf,OAAC,OAAI,UAAU,YACb,mBAAC,UACC,KAAK,SACL,QAASI,EACT,UAAU,0GACV,MAAO,CAAE,gBAAiB,SAAU,EAEnC,SAAAD,EAAW,SACd,EACF,GAEJ,CAEJ,CACF",
4
+ "sourcesContent": ["/**\n * \u8D2D\u7269\u8F66\u5361\u7247\u6E32\u67D3\u5668\n * \u663E\u793A\u8D2D\u7269\u8F66\u5185\u5BB9\u3001\u4EF7\u683C\u6C47\u603B\u548C\u7ED3\u8D26\u6309\u94AE\n * \u57FA\u4E8E\u540E\u7AEF\u8FD4\u56DE\u7684\u8D2D\u7269\u8F66\u6570\u636E\u7ED3\u6784\n */\n\nimport React from 'react'\nimport type { MessageRenderer, CartContent, CartLine, CartAmount } from '../../types'\nimport { CURRENCY_SYMBOLS, DEFAULT_COMMON_TEXT } from '../../constants.js'\n\n/**\n * \u683C\u5F0F\u5316\u91D1\u989D\n * @param amount \u91D1\u989D\u5BF9\u8C61\n * @returns \u683C\u5F0F\u5316\u540E\u7684\u91D1\u989D\u5B57\u7B26\u4E32\uFF08\u5982 \"$99.99\"\uFF09\n */\nfunction formatAmount(amount: CartAmount): string {\n const { amount: value, currencyCode } = amount\n\n const symbol = CURRENCY_SYMBOLS[currencyCode] || currencyCode\n const numValue = parseFloat(value)\n\n return `${symbol}${numValue.toFixed(2)}`\n}\n\n/**\n * \u8D2D\u7269\u8F66\u5546\u54C1\u884C\u7EC4\u4EF6\n */\nconst CartLineItem: React.FC<{\n line: CartLine\n}> = ({ line }) => {\n const { quantity, merchandise, cost } = line\n const { product, title: variantTitle, image } = merchandise\n\n // \u5546\u54C1\u56FE\u7247 URL\n const imageUrl = image?.url || ''\n\n // \u5224\u65AD\u662F\u5426\u6709\u6298\u6263\uFF08\u603B\u4EF7 < \u539F\u4EF7\uFF09\n const hasDiscount =\n parseFloat(cost.totalAmount.amount) < parseFloat(cost.subtotalAmount.amount) &&\n cost.totalAmount.currencyCode === cost.subtotalAmount.currencyCode\n\n return (\n <div className=\"flex gap-4\">\n {/* \u5546\u54C1\u56FE\u7247 */}\n <div className=\"shrink-0 overflow-hidden rounded-md\" style={{ width: '72px', height: '72px' }}>\n <img src={imageUrl} alt={product.title} className=\"size-full object-cover\" loading=\"lazy\" />\n </div>\n\n {/* \u5546\u54C1\u4FE1\u606F */}\n <div className=\"flex flex-1 flex-col\">\n <h4 className=\"line-clamp-2 text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]\">\n {product.title}\n </h4>\n <div className=\"flex items-end justify-between gap-2\">\n {/* \u5DE6\u4FA7\uFF1A\u6807\u9898\u3001\u53D8\u4F53\u3001\u6570\u91CF */}\n <div className=\"flex-1\">\n {variantTitle && (\n <p className=\"mt-0.5 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]\">{variantTitle}</p>\n )}\n <p className=\"mt-1 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]\">\u00D7{quantity}</p>\n </div>\n\n {/* \u53F3\u4FA7\uFF1A\u4EF7\u683C */}\n <div className=\"flex gap-1 text-right\">\n <div className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900\">\n {formatAmount(cost.totalAmount)}\n </div>\n {hasDiscount && (\n <div className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56] line-through\">\n {formatAmount(cost.subtotalAmount)}\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * \u4EF7\u683C\u6C47\u603B\u7EC4\u4EF6\uFF08\u7B80\u5316\u7248\uFF09\n */\nconst CartSummary: React.FC<{\n total: CartAmount\n}> = ({ total }) => {\n return (\n <div className=\"border-t border-gray-200 p-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900\">Total</span>\n <span className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-gray-900\">\n {formatAmount(total)}\n </span>\n </div>\n </div>\n )\n}\n\n/**\n * \u8D2D\u7269\u8F66\u5361\u7247\u6E32\u67D3\u5668\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u8D2D\u7269\u8F66\u5546\u54C1\u5217\u8868\n * - \u663E\u793A\u4EF7\u683C\u6C47\u603B\uFF08\u5C0F\u8BA1\u3001\u6298\u6263\u3001\u603B\u8BA1\uFF09\n * - \u663E\u793A\u6298\u6263\u7801\n * - \u63D0\u4F9B Checkout \u6309\u94AE\n * - \u7A7A\u8D2D\u7269\u8F66\u72B6\u6001\n *\n * \u5E03\u5C40\uFF1A\n * ```\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 \u8D2D\u7269\u8F66 (3 \u4EF6\u5546\u54C1) \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 [\u56FE] \u5546\u54C11 \u2502\n * \u2502 \u53D8\u4F53: Black \u2502\n * \u2502 \u6570\u91CF: 2 $199.98 \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 [\u56FE] \u5546\u54C12 \u2502\n * \u2502 \u53D8\u4F53: White \u2502\n * \u2502 \u6570\u91CF: 1 $99.99 \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 \u5C0F\u8BA1 $299.97 \u2502\n * \u2502 \u6298\u6263 [SPRING20] -$30.00 \u2502\n * \u2502 \u603B\u8BA1 $269.97 \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 [Checkout \u6309\u94AE] \u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * ```\n *\n * @example\n * ```tsx\n * const content: CartContent = {\n * type: 'cart',\n * data: {\n * isEmpty: false,\n * cartId: \"gid://...\",\n * totalQuantity: 3,\n * lines: [...],\n * cost: {...},\n * checkoutUrl: \"https://...\"\n * }\n * }\n * <CartCard.render(content, false, false) />\n * ```\n */\nexport const CartCard: MessageRenderer = {\n render: content => {\n const cartContent = content as CartContent\n const { data } = cartContent\n\n if (!data) {\n return null\n }\n\n const { isEmpty, lines, cost, checkoutUrl, onCart, cartId, commonText } = data\n\n // \u5408\u5E76\u9ED8\u8BA4\u6587\u6848\u548C\u81EA\u5B9A\u4E49\u6587\u6848\n const mergedText = { ...DEFAULT_COMMON_TEXT, ...commonText }\n\n // \u5904\u7406\u8D2D\u7269\u8F66\u6309\u94AE\u70B9\u51FB\n const handleCart = () => {\n if (onCart) {\n onCart(cartId, checkoutUrl)\n } else if (checkoutUrl) {\n window.open(checkoutUrl, '_blank', 'noopener,noreferrer')\n }\n }\n\n // \u7A7A\u8D2D\u7269\u8F66\u72B6\u6001 - \u4E0D\u5C55\u793A\u7EC4\u4EF6\n if (isEmpty || !lines || lines.length === 0) {\n return null\n }\n\n return (\n <div className=\"w-full max-w-md overflow-hidden rounded-2xl shadow-sm\" style={{ backgroundColor: '#F5F6F7' }}>\n {/* \u5546\u54C1\u5217\u8868 */}\n <div className=\"flex flex-col gap-4 overflow-y-auto p-3\">\n {lines.map(line => (\n <CartLineItem key={line.id} line={line} />\n ))}\n </div>\n\n {/* \u4EF7\u683C\u6C47\u603B */}\n <CartSummary total={cost.totalAmount} />\n\n {/* Checkout \u6309\u94AE */}\n {(checkoutUrl || onCart) && (\n <div className=\"px-4 pb-4\">\n <button\n type=\"button\"\n onClick={handleCart}\n className=\"w-full rounded-full py-[10px] text-center text-sm font-bold leading-[1.4] tracking-[-0.02em] text-white\"\n style={{ backgroundColor: '#1D1D1F' }}\n >\n {mergedText.viewMore}\n </button>\n </div>\n )}\n </div>\n )\n },\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,cAAAE,IAAA,eAAAC,EAAAH,GA6CQ,IAAAI,EAAA,6BArCRC,EAAsD,8BAOtD,SAASC,EAAaC,EAA4B,CAChD,KAAM,CAAE,OAAQC,EAAO,aAAAC,CAAa,EAAIF,EAElCG,EAAS,mBAAiBD,CAAY,GAAKA,EAC3CE,EAAW,WAAWH,CAAK,EAEjC,MAAO,GAAGE,CAAM,GAAGC,EAAS,QAAQ,CAAC,CAAC,EACxC,CAKA,MAAMC,EAED,CAAC,CAAE,KAAAC,CAAK,IAAM,CACjB,KAAM,CAAE,SAAAC,EAAU,YAAAC,EAAa,KAAAC,CAAK,EAAIH,EAClC,CAAE,QAAAI,EAAS,MAAOC,EAAc,MAAAC,CAAM,EAAIJ,EAG1CK,EAAWD,GAAO,KAAO,GAGzBE,EACJ,WAAWL,EAAK,YAAY,MAAM,EAAI,WAAWA,EAAK,eAAe,MAAM,GAC3EA,EAAK,YAAY,eAAiBA,EAAK,eAAe,aAExD,SACE,QAAC,OAAI,UAAU,aAEb,oBAAC,OAAI,UAAU,sCAAsC,MAAO,CAAE,MAAO,OAAQ,OAAQ,MAAO,EAC1F,mBAAC,OAAI,IAAKI,EAAU,IAAKH,EAAQ,MAAO,UAAU,yBAAyB,QAAQ,OAAO,EAC5F,KAGA,QAAC,OAAI,UAAU,uBACb,oBAAC,MAAG,UAAU,mFACX,SAAAA,EAAQ,MACX,KACA,QAAC,OAAI,UAAU,uCAEb,qBAAC,OAAI,UAAU,SACZ,UAAAC,MACC,OAAC,KAAE,UAAU,2EAA4E,SAAAA,EAAa,KAExG,QAAC,KAAE,UAAU,yEAAyE,iBAAEJ,GAAS,GACnG,KAGA,QAAC,OAAI,UAAU,wBACb,oBAAC,OAAI,UAAU,qEACZ,SAAAR,EAAaU,EAAK,WAAW,EAChC,EACCK,MACC,OAAC,OAAI,UAAU,mFACZ,SAAAf,EAAaU,EAAK,cAAc,EACnC,GAEJ,GACF,GACF,GACF,CAEJ,EAKMM,EAED,CAAC,CAAE,MAAAC,CAAM,OAEV,OAAC,OAAI,UAAU,+BACb,oBAAC,OAAI,UAAU,oCACb,oBAAC,QAAK,UAAU,qEAAqE,iBAAK,KAC1F,OAAC,QAAK,UAAU,qEACb,SAAAjB,EAAaiB,CAAK,EACrB,GACF,EACF,EAmDSrB,EAA4B,CACvC,OAAQsB,GAAW,CACjB,MAAMC,EAAcD,EACd,CAAE,KAAAE,CAAK,EAAID,EAEjB,GAAI,CAACC,EACH,OAAO,KAGT,KAAM,CAAE,QAAAC,EAAS,MAAAC,EAAO,KAAAZ,EAAM,YAAAa,EAAa,OAAAC,EAAQ,OAAAC,EAAQ,WAAAC,CAAW,EAAIN,EAGpEO,EAAa,CAAE,GAAG,sBAAqB,GAAGD,CAAW,EAGrDE,EAAa,IAAM,CACnBJ,EACFA,EAAOC,EAAQF,CAAW,EACjBA,GACT,OAAO,KAAKA,EAAa,SAAU,qBAAqB,CAE5D,EAGA,OAAIF,GAAW,CAACC,GAASA,EAAM,SAAW,EACjC,QAIP,QAAC,OAAI,UAAU,wDAAwD,MAAO,CAAE,gBAAiB,SAAU,EAEzG,oBAAC,OAAI,UAAU,0CACZ,SAAAA,EAAM,IAAIf,MACT,OAACD,EAAA,CAA2B,KAAMC,GAAfA,EAAK,EAAgB,CACzC,EACH,KAGA,OAACS,EAAA,CAAY,MAAON,EAAK,YAAa,GAGpCa,GAAeC,OACf,OAAC,OAAI,UAAU,YACb,mBAAC,UACC,KAAK,SACL,QAASI,EACT,UAAU,0GACV,MAAO,CAAE,gBAAiB,SAAU,EAEnC,SAAAD,EAAW,SACd,EACF,GAEJ,CAEJ,CACF",
6
6
  "names": ["CartCard_exports", "__export", "CartCard", "__toCommonJS", "import_jsx_runtime", "import_constants", "formatAmount", "amount", "value", "currencyCode", "symbol", "numValue", "CartLineItem", "line", "quantity", "merchandise", "cost", "product", "variantTitle", "image", "imageUrl", "hasDiscount", "CartSummary", "total", "content", "cartContent", "data", "isEmpty", "lines", "checkoutUrl", "onCart", "cartId", "commonText", "mergedText", "handleCart"]
7
7
  }
@@ -1,2 +1,2 @@
1
- "use strict";var l=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var d=(t,e)=>{for(var a in e)l(t,a,{get:e[a],enumerable:!0})},m=(t,e,a,c)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of k(e))!R.call(t,i)&&i!==a&&l(t,i,{get:()=>e[i],enumerable:!(c=u(e,i))||c.enumerable});return t};var x=t=>m(l({},"__esModule",{value:!0}),t);var Q={};d(Q,{QuickReplies:()=>y,createQuickRepliesRenderer:()=>r});module.exports=x(Q);var s=require("react/jsx-runtime");const r=t=>({render:(e,a,c)=>{const i=e,{replies:p}=i.data;if(!p||p.length===0)return null;const o=n=>{t?.(n)};return(0,s.jsx)("div",{className:"flex flex-wrap gap-2",children:p.map(n=>(0,s.jsxs)("button",{type:"button",onClick:()=>o(n),className:"livechat-quick-reply-button inline-flex items-center gap-1 rounded-[19px] px-3 py-[6px] text-sm leading-[140%] tracking-[-0.02em] transition-transform hover:scale-105 active:scale-95",children:[n.icon&&(0,s.jsx)("span",{className:"text-base",children:n.icon}),(0,s.jsx)("span",{children:n.label})]},n.id))})}}),y=r();
1
+ "use strict";var o=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var R=(t,e)=>{for(var a in e)o(t,a,{get:e[a],enumerable:!0})},m=(t,e,a,l)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of d(e))!k.call(t,n)&&n!==a&&o(t,n,{get:()=>e[n],enumerable:!(l=u(e,n))||l.enumerable});return t};var x=t=>m(o({},"__esModule",{value:!0}),t);var y={};R(y,{QuickReplies:()=>f,createQuickRepliesRenderer:()=>p});module.exports=x(y);var i=require("react/jsx-runtime");const p=t=>({render:(e,a,l)=>{const n=e,{replies:c}=n.data;if(!c||c.length===0)return null;const r=s=>{t?.(s)};return(0,i.jsx)("div",{className:"flex flex-wrap gap-2",children:c.map(s=>(0,i.jsxs)("button",{type:"button",onClick:()=>r(s),className:"livechat-quick-reply-button inline-flex font-bold items-center gap-1 rounded-[19px] px-3 py-[6px] text-sm leading-[140%] tracking-[-0.02em] transition-transform hover:scale-105 active:scale-95",children:[s.icon&&(0,i.jsx)("span",{className:"text-base",children:s.icon}),(0,i.jsx)("span",{className:"text-left",children:s.label})]},s.id))})}}),f=p();
2
2
  //# sourceMappingURL=QuickReplies.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/components/LiveChatWidget/components/MessageContent/QuickReplies.tsx"],
4
- "sourcesContent": ["/**\n * \u5FEB\u6377\u56DE\u590D\u6309\u94AE\u6E32\u67D3\u5668\n * \u663E\u793A\u53EF\u70B9\u51FB\u7684\u5FEB\u6377\u56DE\u590D\u9009\u9879\n * \u57FA\u4E8E specs/livechat-widget/data-model.md \u7684\u5FEB\u6377\u56DE\u590D\u6570\u636E\u6A21\u578B\n */\n\nimport React from 'react'\nimport type { MessageRenderer, QuickRepliesContent, QuickReply } from '../../types'\n\nexport interface QuickRepliesProps {\n /**\n * \u5FEB\u6377\u56DE\u590D\u70B9\u51FB\u56DE\u8C03\n */\n onReplyClick?: (reply: QuickReply) => void\n}\n\n/**\n * \u5FEB\u6377\u56DE\u590D\u6309\u94AE\u6E32\u67D3\u5668\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u591A\u4E2A\u5FEB\u6377\u56DE\u590D\u6309\u94AE\n * - \u652F\u6301\u56FE\u6807\uFF08\u53EF\u9009\uFF09\n * - \u70B9\u51FB\u540E\u53D1\u9001\u5BF9\u5E94\u7684\u6D88\u606F\n *\n * \u4F7F\u7528\u573A\u666F\uFF1A\n * - \u6B22\u8FCE\u6D88\u606F\u540E\u7684\u5E38\u89C1\u95EE\u9898\n * - \u5F15\u5BFC\u7528\u6237\u9009\u62E9\n * - \u5FEB\u901F\u64CD\u4F5C\u6309\u94AE\n *\n * \u5E03\u5C40\uFF1A\n * ```\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 \uD83D\uDED2 \u67E5\u4EF7\u683C \u2502 \u2502 \uD83D\uDCE6 \u67E5\u7269\u6D41 \u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 \uD83D\uDCDE \u8054\u7CFB\u5BA2\u670D\u2502 \u2502 \uD83D\uDCAC \u5176\u4ED6\u95EE\u9898\u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * ```\n *\n * @example\n * ```tsx\n * const content: QuickRepliesContent = {\n * type: 'quick_replies',\n * data: {\n * replies: [\n * { id: '1', label: '\u67E5\u4EF7\u683C', value: '\u6211\u60F3\u67E5\u8BE2\u5546\u54C1\u4EF7\u683C', icon: '\uD83D\uDED2' },\n * { id: '2', label: '\u67E5\u7269\u6D41', value: '\u6211\u60F3\u67E5\u8BE2\u7269\u6D41\u4FE1\u606F', icon: '\uD83D\uDCE6' }\n * ]\n * }\n * }\n * <QuickReplies.render(content, false, false) />\n * ```\n */\nexport const createQuickRepliesRenderer = (onReplyClick?: (reply: QuickReply) => void): MessageRenderer => ({\n render: (content, isUser, isSystem) => {\n const quickRepliesContent = content as QuickRepliesContent\n const { replies } = quickRepliesContent.data\n\n if (!replies || replies.length === 0) {\n return null\n }\n\n const handleClick = (reply: QuickReply) => {\n onReplyClick?.(reply)\n }\n\n return (\n <div className=\"flex flex-wrap gap-2\">\n {replies.map(reply => (\n <button\n key={reply.id}\n type=\"button\"\n onClick={() => handleClick(reply)}\n className=\"livechat-quick-reply-button inline-flex items-center gap-1 rounded-[19px] px-3 py-[6px] text-sm leading-[140%] tracking-[-0.02em] transition-transform hover:scale-105 active:scale-95\"\n >\n {/* \u56FE\u6807\uFF08\u53EF\u9009\uFF09 */}\n {reply.icon && <span className=\"text-base\">{reply.icon}</span>}\n\n {/* \u6807\u7B7E */}\n <span>{reply.label}</span>\n </button>\n ))}\n </div>\n )\n },\n})\n\n/**\n * \u9ED8\u8BA4\u5FEB\u6377\u56DE\u590D\u6E32\u67D3\u5668\uFF08\u65E0\u56DE\u8C03\uFF09\n */\nexport const QuickReplies: MessageRenderer = createQuickRepliesRenderer()\n"],
5
- "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,kBAAAE,EAAA,+BAAAC,IAAA,eAAAC,EAAAJ,GAqEU,IAAAK,EAAA,6BAhBH,MAAMF,EAA8BG,IAAiE,CAC1G,OAAQ,CAACC,EAASC,EAAQC,IAAa,CACrC,MAAMC,EAAsBH,EACtB,CAAE,QAAAI,CAAQ,EAAID,EAAoB,KAExC,GAAI,CAACC,GAAWA,EAAQ,SAAW,EACjC,OAAO,KAGT,MAAMC,EAAeC,GAAsB,CACzCP,IAAeO,CAAK,CACtB,EAEA,SACE,OAAC,OAAI,UAAU,uBACZ,SAAAF,EAAQ,IAAIE,MACX,QAAC,UAEC,KAAK,SACL,QAAS,IAAMD,EAAYC,CAAK,EAChC,UAAU,yLAGT,UAAAA,EAAM,SAAQ,OAAC,QAAK,UAAU,YAAa,SAAAA,EAAM,KAAK,KAGvD,OAAC,QAAM,SAAAA,EAAM,MAAM,IATdA,EAAM,EAUb,CACD,EACH,CAEJ,CACF,GAKaX,EAAgCC,EAA2B",
4
+ "sourcesContent": ["/**\n * \u5FEB\u6377\u56DE\u590D\u6309\u94AE\u6E32\u67D3\u5668\n * \u663E\u793A\u53EF\u70B9\u51FB\u7684\u5FEB\u6377\u56DE\u590D\u9009\u9879\n * \u57FA\u4E8E specs/livechat-widget/data-model.md \u7684\u5FEB\u6377\u56DE\u590D\u6570\u636E\u6A21\u578B\n */\n\nimport React from 'react'\nimport type { MessageRenderer, QuickRepliesContent, QuickReply } from '../../types'\n\nexport interface QuickRepliesProps {\n /**\n * \u5FEB\u6377\u56DE\u590D\u70B9\u51FB\u56DE\u8C03\n */\n onReplyClick?: (reply: QuickReply) => void\n}\n\n/**\n * \u5FEB\u6377\u56DE\u590D\u6309\u94AE\u6E32\u67D3\u5668\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u591A\u4E2A\u5FEB\u6377\u56DE\u590D\u6309\u94AE\n * - \u652F\u6301\u56FE\u6807\uFF08\u53EF\u9009\uFF09\n * - \u70B9\u51FB\u540E\u53D1\u9001\u5BF9\u5E94\u7684\u6D88\u606F\n *\n * \u4F7F\u7528\u573A\u666F\uFF1A\n * - \u6B22\u8FCE\u6D88\u606F\u540E\u7684\u5E38\u89C1\u95EE\u9898\n * - \u5F15\u5BFC\u7528\u6237\u9009\u62E9\n * - \u5FEB\u901F\u64CD\u4F5C\u6309\u94AE\n *\n * \u5E03\u5C40\uFF1A\n * ```\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 \uD83D\uDED2 \u67E5\u4EF7\u683C \u2502 \u2502 \uD83D\uDCE6 \u67E5\u7269\u6D41 \u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 \uD83D\uDCDE \u8054\u7CFB\u5BA2\u670D\u2502 \u2502 \uD83D\uDCAC \u5176\u4ED6\u95EE\u9898\u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * ```\n *\n * @example\n * ```tsx\n * const content: QuickRepliesContent = {\n * type: 'quick_replies',\n * data: {\n * replies: [\n * { id: '1', label: '\u67E5\u4EF7\u683C', value: '\u6211\u60F3\u67E5\u8BE2\u5546\u54C1\u4EF7\u683C', icon: '\uD83D\uDED2' },\n * { id: '2', label: '\u67E5\u7269\u6D41', value: '\u6211\u60F3\u67E5\u8BE2\u7269\u6D41\u4FE1\u606F', icon: '\uD83D\uDCE6' }\n * ]\n * }\n * }\n * <QuickReplies.render(content, false, false) />\n * ```\n */\nexport const createQuickRepliesRenderer = (onReplyClick?: (reply: QuickReply) => void): MessageRenderer => ({\n render: (content, isUser, isSystem) => {\n const quickRepliesContent = content as QuickRepliesContent\n const { replies } = quickRepliesContent.data\n\n if (!replies || replies.length === 0) {\n return null\n }\n\n const handleClick = (reply: QuickReply) => {\n onReplyClick?.(reply)\n }\n\n return (\n <div className=\"flex flex-wrap gap-2\">\n {replies.map(reply => (\n <button\n key={reply.id}\n type=\"button\"\n onClick={() => handleClick(reply)}\n className=\"livechat-quick-reply-button inline-flex font-bold items-center gap-1 rounded-[19px] px-3 py-[6px] text-sm leading-[140%] tracking-[-0.02em] transition-transform hover:scale-105 active:scale-95\"\n >\n {/* \u56FE\u6807\uFF08\u53EF\u9009\uFF09 */}\n {reply.icon && <span className=\"text-base\">{reply.icon}</span>}\n\n {/* \u6807\u7B7E */}\n <span className='text-left'>{reply.label}</span>\n </button>\n ))}\n </div>\n )\n },\n})\n\n/**\n * \u9ED8\u8BA4\u5FEB\u6377\u56DE\u590D\u6E32\u67D3\u5668\uFF08\u65E0\u56DE\u8C03\uFF09\n */\nexport const QuickReplies: MessageRenderer = createQuickRepliesRenderer()\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,kBAAAE,EAAA,+BAAAC,IAAA,eAAAC,EAAAJ,GAqEU,IAAAK,EAAA,6BAhBH,MAAMF,EAA8BG,IAAiE,CAC1G,OAAQ,CAACC,EAASC,EAAQC,IAAa,CACrC,MAAMC,EAAsBH,EACtB,CAAE,QAAAI,CAAQ,EAAID,EAAoB,KAExC,GAAI,CAACC,GAAWA,EAAQ,SAAW,EACjC,OAAO,KAGT,MAAMC,EAAeC,GAAsB,CACzCP,IAAeO,CAAK,CACtB,EAEA,SACE,OAAC,OAAI,UAAU,uBACZ,SAAAF,EAAQ,IAAIE,MACX,QAAC,UAEC,KAAK,SACL,QAAS,IAAMD,EAAYC,CAAK,EAChC,UAAU,mMAGT,UAAAA,EAAM,SAAQ,OAAC,QAAK,UAAU,YAAa,SAAAA,EAAM,KAAK,KAGvD,OAAC,QAAK,UAAU,YAAa,SAAAA,EAAM,MAAM,IATpCA,EAAM,EAUb,CACD,EACH,CAEJ,CACF,GAKaX,EAAgCC,EAA2B",
6
6
  "names": ["QuickReplies_exports", "__export", "QuickReplies", "createQuickRepliesRenderer", "__toCommonJS", "import_jsx_runtime", "onReplyClick", "content", "isUser", "isSystem", "quickRepliesContent", "replies", "handleClick", "reply"]
7
7
  }