@anker-in/campaign-ui 0.2.11-beta.30 → 0.2.11-beta.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js +1 -1
  2. package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
  3. package/dist/cjs/components/LiveChatWidget/components/ChatInput.js +1 -1
  4. package/dist/cjs/components/LiveChatWidget/components/ChatInput.js.map +3 -3
  5. package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js +2 -2
  6. package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js.map +3 -3
  7. package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js +1 -1
  8. package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js.map +2 -2
  9. package/dist/cjs/components/LiveChatWidget/components/MessageContent/FAQList.js +1 -1
  10. package/dist/cjs/components/LiveChatWidget/components/MessageContent/FAQList.js.map +2 -2
  11. package/dist/cjs/components/LiveChatWidget/components/MessageContent/PolicyBlock.js +1 -1
  12. package/dist/cjs/components/LiveChatWidget/components/MessageContent/PolicyBlock.js.map +2 -2
  13. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +3 -1
  14. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -1
  15. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
  16. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.d.ts +1 -0
  17. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
  18. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
  19. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
  20. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js.map +2 -2
  21. package/dist/cjs/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
  22. package/dist/cjs/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
  23. package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
  24. package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +2 -2
  25. package/dist/cjs/components/LiveChatWidget/constants.js +1 -1
  26. package/dist/cjs/components/LiveChatWidget/constants.js.map +2 -2
  27. package/dist/cjs/components/LiveChatWidget/hooks/useChatState.d.ts +5 -1
  28. package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js +1 -1
  29. package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
  30. package/dist/cjs/components/LiveChatWidget/types.d.ts +26 -0
  31. package/dist/cjs/components/LiveChatWidget/types.js.map +1 -1
  32. package/dist/cjs/stories/LiveChatWidget.stories.d.ts +8 -0
  33. package/dist/cjs/stories/LiveChatWidget.stories.js +21 -4
  34. package/dist/cjs/stories/LiveChatWidget.stories.js.map +3 -3
  35. package/dist/esm/components/LiveChatWidget/LiveChatWidget.js +1 -1
  36. package/dist/esm/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
  37. package/dist/esm/components/LiveChatWidget/components/ChatInput.js +1 -1
  38. package/dist/esm/components/LiveChatWidget/components/ChatInput.js.map +3 -3
  39. package/dist/esm/components/LiveChatWidget/components/ChatMessage.js +2 -2
  40. package/dist/esm/components/LiveChatWidget/components/ChatMessage.js.map +3 -3
  41. package/dist/esm/components/LiveChatWidget/components/ChatWindow.js +1 -1
  42. package/dist/esm/components/LiveChatWidget/components/ChatWindow.js.map +2 -2
  43. package/dist/esm/components/LiveChatWidget/components/MessageContent/FAQList.js +1 -1
  44. package/dist/esm/components/LiveChatWidget/components/MessageContent/FAQList.js.map +2 -2
  45. package/dist/esm/components/LiveChatWidget/components/MessageContent/PolicyBlock.js +1 -1
  46. package/dist/esm/components/LiveChatWidget/components/MessageContent/PolicyBlock.js.map +2 -2
  47. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +3 -1
  48. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -1
  49. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
  50. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.d.ts +1 -0
  51. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
  52. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
  53. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
  54. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js.map +2 -2
  55. package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
  56. package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
  57. package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
  58. package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +2 -2
  59. package/dist/esm/components/LiveChatWidget/constants.js +1 -1
  60. package/dist/esm/components/LiveChatWidget/constants.js.map +2 -2
  61. package/dist/esm/components/LiveChatWidget/hooks/useChatState.d.ts +5 -1
  62. package/dist/esm/components/LiveChatWidget/hooks/useChatState.js +1 -1
  63. package/dist/esm/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
  64. package/dist/esm/components/LiveChatWidget/types.d.ts +26 -0
  65. package/dist/esm/stories/LiveChatWidget.stories.d.ts +8 -0
  66. package/dist/esm/stories/LiveChatWidget.stories.js +24 -7
  67. package/dist/esm/stories/LiveChatWidget.stories.js.map +3 -3
  68. package/package.json +1 -1
  69. package/src/components/LiveChatWidget/LiveChatWidget.tsx +97 -48
  70. package/src/components/LiveChatWidget/components/ChatInput.tsx +26 -1
  71. package/src/components/LiveChatWidget/components/ChatMessage.tsx +11 -4
  72. package/src/components/LiveChatWidget/components/ChatWindow.tsx +2 -0
  73. package/src/components/LiveChatWidget/components/MessageContent/FAQList.tsx +1 -1
  74. package/src/components/LiveChatWidget/components/MessageContent/PolicyBlock.tsx +1 -1
  75. package/src/components/LiveChatWidget/components/MessageContent/ProductCard.tsx +12 -4
  76. package/src/components/LiveChatWidget/components/MessageContent/ProductComparison.tsx +23 -6
  77. package/src/components/LiveChatWidget/components/MessageContent/ProductList.tsx +1 -1
  78. package/src/components/LiveChatWidget/components/MessageContent/QuickReplies.tsx +1 -1
  79. package/src/components/LiveChatWidget/components/MessageContent/TextBlock.tsx +2 -2
  80. package/src/components/LiveChatWidget/constants.ts +12 -5
  81. package/src/components/LiveChatWidget/hooks/useChatState.ts +141 -10
  82. package/src/components/LiveChatWidget/types.ts +27 -0
  83. package/src/stories/LiveChatWidget.stories.tsx +182 -0
  84. package/src/styles/livechat.css +1 -2
@@ -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\uFF08\u63D0\u4F9B sitekey \u5373\u81EA\u52A8\u542F\u7528\uFF09\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * recaptchaSitekey=\"6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14\"\n * recaptchaAction=\"livechat\"\n * />\n *\n * // \u4F7F\u7528\u81EA\u5B9A\u4E49 headers \u548C reCAPTCHA\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * headers={{\n * \"Authorization\": \"Bearer your-token\",\n * \"X-Custom-Header\": \"value\"\n * }}\n * recaptchaSitekey=\"your-site-key\"\n * />\n * ```\n */\nexport const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({\n apiBaseUrl,\n headers,\n recaptchaSitekey,\n recaptchaAction,\n site,\n channelCode,\n loginUserId,\n cartId,\n accessToken,\n position,\n welcomeMessage,\n quickReplies,\n customRenderers,\n logoUrl,\n title,\n chatBubbleIcon,\n open,\n onOpenChange,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n showNewSessionButton,\n commonText,\n}) => {\n // \u5408\u5E76\u9ED8\u8BA4\u6587\u6848\u548C\u81EA\u5B9A\u4E49\u6587\u6848\n const mergedText: Required<CommonText> = React.useMemo(\n () => ({\n ...DEFAULT_COMMON_TEXT,\n ...commonText,\n }),\n [commonText]\n )\n\n // \u72B6\u6001\u7BA1\u7406\n const chatState = useChatState({\n welcomeMessage,\n site,\n open,\n onOpenChange,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n })\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: !!recaptchaSitekey, // \u6839\u636E sitekey \u81EA\u52A8\u5224\u65AD\u662F\u5426\u542F\u7528\n recaptchaSitekey,\n recaptchaAction,\n },\n onError,\n })\n\n // \u4F7F\u7528 ref \u5B58\u50A8\u6700\u65B0\u7684 handleSendMessage\uFF0C\u907F\u514D\u5FAA\u73AF\u4F9D\u8D56\n const handleSendMessageRef = React.useRef<(_message?: string) => Promise<void>>(async (_message?: string) => {})\n\n // \u6D88\u606F\u6E32\u67D3\u5668\u6CE8\u518C\u8868\n const rendererRegistry = React.useMemo(() => {\n const registry = new MessageRendererRegistry()\n\n // \u6CE8\u518C\u9ED8\u8BA4\u6E32\u67D3\u5668\n registry.register('text', TextBlock)\n registry.register('product_card', ProductCard)\n registry.register('product_list', ProductList)\n registry.register('product_comparison', ProductComparisonRenderer)\n registry.register('policy', PolicyBlock)\n registry.register('thinking', ThinkingBlock)\n registry.register('error', ErrorBlock)\n registry.register('faq_list', FAQListRenderer)\n registry.register('promotion_list', PromotionListRenderer)\n registry.register('cart', CartCard)\n\n // \u6CE8\u518C\u5FEB\u6377\u56DE\u590D\u6E32\u67D3\u5668\uFF08\u5E26\u56DE\u8C03\uFF09\n const quickRepliesRenderer = createQuickRepliesRenderer((reply: QuickReply) => {\n // \u4F7F\u7528 ref \u8C03\u7528\u6700\u65B0\u7684 handleSendMessage\n handleSendMessageRef.current(reply.value)\n })\n registry.register('quick_replies', quickRepliesRenderer)\n\n // \u6CE8\u518C\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n if (customRenderers) {\n registry.registerMany(customRenderers)\n }\n\n return registry\n }, [customRenderers])\n\n /**\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 channel_code: channelCode,\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 // \u6DFB\u52A0\u9519\u8BEF\u6D88\u606F\u5230\u754C\u9762\n addMessage({\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: 'Failed to create session. Please refresh the page and try again.',\n code: 'SESSION_CREATE_ERROR',\n },\n },\n ],\n timestamp: Date.now(),\n })\n }\n }, [\n userId,\n site,\n channelCode,\n loginUserId,\n createSession,\n saveSession,\n clearMessages,\n welcomeMessage,\n quickReplies,\n addMessage,\n onError,\n ])\n\n /**\n * \u6062\u590D\u4F1A\u8BDD\u548C\u5386\u53F2\u6D88\u606F\uFF08\u4F7F\u7528\u65B0\u7684 API v2.0.0\uFF09\n */\n const handleResumeSession = useCallback(\n async (existingSessionId: string) => {\n try {\n const response = await createSession({\n user_id: userId,\n session_id: existingSessionId,\n site: site,\n channel_code: channelCode,\n real_user_id: loginUserId,\n })\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\u548C\u7A7A\u5185\u5BB9\u6D88\u606F\uFF09\n const normalizedMessages = response.messages\n .filter((msg: any) => msg != null)\n .map(normalizeMessage)\n .filter((msg: Message) => msg.content && msg.content.length > 0 && !(msg.content.length === 1 && msg.content[0].type === 'text' && !msg.content[0].text))\n 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\n // \u6DFB\u52A0\u9519\u8BEF\u6D88\u606F\u5230\u754C\u9762\n addMessage({\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: 'Failed to resume session. Creating a new session...',\n code: 'SESSION_RESUME_ERROR',\n },\n },\n ],\n timestamp: Date.now(),\n })\n\n // \u6062\u590D\u5931\u8D25\uFF0C\u6E05\u7A7A\u4F1A\u8BDD\u5E76\u521B\u5EFA\u65B0\u7684\n clearSession()\n handleCreateNewSession()\n }\n },\n [\n userId,\n site,\n channelCode,\n loginUserId,\n createSession,\n setMessages,\n clearSession,\n clearMessages,\n normalizeMessage,\n handleCreateNewSession,\n welcomeMessage,\n quickReplies,\n addMessage,\n ]\n )\n\n /**\n * \u53D1\u9001\u6D88\u606F\n */\n const handleSendMessage = useCallback(\n async (message?: string) => {\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 // \u7ACB\u5373\u6DFB\u52A0\u601D\u8003\u72B6\u6001\u6D88\u606F\uFF0C\u63D0\u5347\u7528\u6237\u4F53\u9A8C\n const thinkingMessage = {\n id: `thinking-${Date.now()}`,\n role: 'assistant' as const,\n content: [{ type: 'thinking' as const, data: { status: 'thinking' } }],\n timestamp: Date.now(),\n }\n addMessage(thinkingMessage)\n\n // \u89E6\u53D1\u6D88\u606F\u53D1\u9001\u56DE\u8C03\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 channel_code: channelCode,\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 // \u79FB\u9664\u521A\u624D\u6DFB\u52A0\u7684 thinking \u6D88\u606F\uFF0C\u5E76\u6DFB\u52A0\u9519\u8BEF\u6D88\u606F\n const messagesWithoutThinking = messages.filter(msg => msg.id !== thinkingMessage.id)\n setMessages([\n ...messagesWithoutThinking,\n {\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: 'Failed to send message. Please check your network connection and try again.',\n code: 'NETWORK_ERROR',\n },\n },\n ],\n timestamp: Date.now(),\n },\n ])\n }\n },\n [\n inputValue,\n userId,\n sessionId,\n site,\n channelCode,\n loginUserId,\n cartId,\n accessToken,\n messages,\n setInputValue,\n addMessage,\n setMessages,\n createSession,\n sendMessageStream,\n handleSSEEvent,\n saveSession,\n clearSession,\n onMessageSend,\n onError,\n ]\n )\n\n // \u66F4\u65B0 ref \u4EE5\u4FDD\u6301\u6700\u65B0\u7684 handleSendMessage\n React.useEffect(() => {\n handleSendMessageRef.current = handleSendMessage\n }, [handleSendMessage])\n\n 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
- "mappings": "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",
6
- "names": ["Fragment", "jsx", "jsxs", "React", "useEffect", "useCallback", "Dialog", "DEFAULT_COMMON_TEXT", "ChatBubble", "ChatWindow", "useChatState", "useChatAPI", "MessageRendererRegistry", "sanitizeInput", "transformProducts", "transformCartData", "TextBlock", "ProductCard", "ProductList", "ProductComparisonRenderer", "PolicyBlock", "createQuickRepliesRenderer", "ThinkingBlock", "ErrorBlock", "FAQListRenderer", "PromotionListRenderer", "CartCard", "LiveChatWidget", "apiBaseUrl", "headers", "recaptchaSitekey", "recaptchaAction", "site", "channelCode", "loginUserId", "cartId", "accessToken", "position", "welcomeMessage", "quickReplies", "customRenderers", "logoUrl", "title", "chatBubbleIcon", "open", "onOpenChange", "onOpen", "onClose", "onMessageSend", "onError", "onTextMessage", "onProductList", "onPromotionList", "onAddToCart", "onCart", "showNewSessionButton", "commonText", "mergedText", "chatState", "messages", "isOpen", "userId", "sessionId", "inputValue", "isStreaming", "openChat", "closeChat", "setInputValue", "addMessage", "setMessages", "clearMessages", "handleSSEEvent", "saveSession", "clearSession", "sendMessageStream", "createSession", "handleSendMessageRef", "_message", "rendererRegistry", "registry", "quickRepliesRenderer", "reply", "currentSessionId", "handleResumeSession", "handleCreateNewSession", "normalizeMessage", "message", "contentBlocks", "structuredData", "block", "transformedData", "response", "messageText", "welcomeContent", "questions", "quickRepliesFromBackend", "question", "index", "error", "existingSessionId", "normalizedMessages", "msg", "handleSendMessage", "textToSend", "sanitized", "userMessage", "thinkingMessage", "event", "messagesWithoutThinking"]
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\uFF08\u63D0\u4F9B sitekey \u5373\u81EA\u52A8\u542F\u7528\uFF09\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * recaptchaSitekey=\"6LfS4J4pAAAAACX1e_WrxutmxxzCK7FU4WzVqL14\"\n * recaptchaAction=\"livechat\"\n * />\n *\n * // \u4F7F\u7528\u81EA\u5B9A\u4E49 headers \u548C reCAPTCHA\n * <LiveChatWidget\n * apiBaseUrl=\"https://beta-api-livechat.anker.com\"\n * site=\"www.eufy.com\"\n * headers={{\n * \"Authorization\": \"Bearer your-token\",\n * \"X-Custom-Header\": \"value\"\n * }}\n * recaptchaSitekey=\"your-site-key\"\n * />\n * ```\n */\nexport const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({\n apiBaseUrl,\n headers,\n recaptchaSitekey,\n recaptchaAction,\n site,\n channelCode,\n loginUserId,\n cartId,\n accessToken,\n position,\n welcomeMessage,\n quickReplies,\n customRenderers,\n logoUrl,\n title,\n chatBubbleIcon,\n open,\n onOpenChange,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n showNewSessionButton,\n commonText,\n productCardRender,\n}) => {\n // \u5408\u5E76\u9ED8\u8BA4\u6587\u6848\u548C\u81EA\u5B9A\u4E49\u6587\u6848\n const mergedText: Required<CommonText> = React.useMemo(\n () => ({\n ...DEFAULT_COMMON_TEXT,\n ...commonText,\n }),\n [commonText]\n )\n\n // \u72B6\u6001\u7BA1\u7406\n const chatState = useChatState({\n welcomeMessage,\n site,\n open,\n onOpenChange,\n onOpen,\n onClose,\n onMessageSend,\n onError,\n onTextMessage,\n onProductList,\n onPromotionList,\n onAddToCart,\n onCart,\n productCardRender,\n })\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: !!recaptchaSitekey, // \u6839\u636E sitekey \u81EA\u52A8\u5224\u65AD\u662F\u5426\u542F\u7528\n recaptchaSitekey,\n recaptchaAction,\n },\n onError,\n })\n\n // \u4F7F\u7528 ref \u5B58\u50A8\u6700\u65B0\u7684 handleSendMessage\uFF0C\u907F\u514D\u5FAA\u73AF\u4F9D\u8D56\n const handleSendMessageRef = React.useRef<(_message?: string) => Promise<void>>(async (_message?: string) => {})\n\n // \u6D88\u606F\u6E32\u67D3\u5668\u6CE8\u518C\u8868\n const rendererRegistry = React.useMemo(() => {\n const registry = new MessageRendererRegistry()\n\n // \u6CE8\u518C\u9ED8\u8BA4\u6E32\u67D3\u5668\n registry.register('text', TextBlock)\n registry.register('product_card', ProductCard)\n registry.register('product_list', ProductList)\n registry.register('product_comparison', ProductComparisonRenderer)\n registry.register('policy', PolicyBlock)\n registry.register('thinking', ThinkingBlock)\n registry.register('error', ErrorBlock)\n registry.register('faq_list', FAQListRenderer)\n registry.register('promotion_list', PromotionListRenderer)\n registry.register('cart', CartCard)\n\n // \u6CE8\u518C\u5FEB\u6377\u56DE\u590D\u6E32\u67D3\u5668\uFF08\u5E26\u56DE\u8C03\uFF09\n const quickRepliesRenderer = createQuickRepliesRenderer((reply: QuickReply) => {\n // \u4F7F\u7528 ref \u8C03\u7528\u6700\u65B0\u7684 handleSendMessage\n handleSendMessageRef.current(reply.value)\n })\n registry.register('quick_replies', quickRepliesRenderer)\n\n // \u6CE8\u518C\u81EA\u5B9A\u4E49\u6E32\u67D3\u5668\n if (customRenderers) {\n registry.registerMany(customRenderers)\n }\n\n return registry\n }, [customRenderers])\n\n /**\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 channel_code: channelCode,\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 // \u6DFB\u52A0\u9519\u8BEF\u6D88\u606F\u5230\u754C\u9762\n addMessage({\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: 'Failed to create session. Please refresh the page and try again.',\n code: 'SESSION_CREATE_ERROR',\n },\n },\n ],\n timestamp: Date.now(),\n })\n }\n }, [\n userId,\n site,\n channelCode,\n loginUserId,\n createSession,\n saveSession,\n clearMessages,\n welcomeMessage,\n quickReplies,\n addMessage,\n onError,\n ])\n\n /**\n * \u6062\u590D\u4F1A\u8BDD\u548C\u5386\u53F2\u6D88\u606F\uFF08\u4F7F\u7528\u65B0\u7684 API v2.0.0\uFF09\n */\n const handleResumeSession = useCallback(\n async (existingSessionId: string) => {\n try {\n const response = await createSession({\n user_id: userId,\n session_id: existingSessionId,\n site: site,\n channel_code: channelCode,\n real_user_id: loginUserId,\n })\n\n if (response.success && response.resumed) {\n // \u4F1A\u8BDD\u6062\u590D\u6210\u529F\n\n // \u51C6\u5907\u6B22\u8FCE\u6D88\u606F\uFF08\u65E0\u8BBA\u662F\u5426\u6709\u5386\u53F2\u6D88\u606F\u90FD\u9700\u8981\uFF09\n const messageText = response.welcomeMessage || welcomeMessage\n const welcomeContent: MessageContent[] = messageText ? [{ type: 'text', text: messageText }] : []\n\n // \u6DFB\u52A0\u5FEB\u6377\u56DE\u590D\u5230\u6B22\u8FCE\u6D88\u606F\n const questions = response.quickQuestions\n if (questions && questions.length > 0) {\n const quickRepliesFromBackend = questions.map((question, index) => ({\n id: `quick-${index}`,\n label: question,\n value: question,\n }))\n\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickRepliesFromBackend,\n },\n })\n } else if (quickReplies && quickReplies.length > 0) {\n welcomeContent.push({\n type: 'quick_replies',\n data: {\n replies: quickReplies,\n },\n })\n }\n\n if (response.messages && response.messages.length > 0) {\n // \u6709\u5386\u53F2\u6D88\u606F\uFF0C\u89C4\u8303\u5316\u5E76\u52A0\u8F7D\uFF08\u8FC7\u6EE4\u6389 null/undefined \u503C\u548C\u7A7A\u5185\u5BB9\u6D88\u606F\uFF09\n const normalizedMessages = response.messages\n .filter((msg: any) => msg != null)\n .map(normalizeMessage)\n .filter((msg: Message) => msg.content && msg.content.length > 0 && !(msg.content.length === 1 && msg.content[0].type === 'text' && !msg.content[0].text))\n\n // \u5982\u679C\u6709\u6B22\u8FCE\u6D88\u606F\uFF0C\u5C06\u5176\u6DFB\u52A0\u5230\u5386\u53F2\u6D88\u606F\u7684\u5F00\u5934\n if (welcomeContent.length > 0) {\n const welcomeMsg: Message = {\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: welcomeContent,\n timestamp: Date.now(),\n }\n setMessages([welcomeMsg, ...normalizedMessages])\n } else {\n setMessages(normalizedMessages)\n }\n } else {\n // \u6CA1\u6709\u5386\u53F2\u6D88\u606F\uFF0C\u4EC5\u663E\u793A\u6B22\u8FCE\u6D88\u606F\n clearMessages()\n\n if (welcomeContent.length > 0) {\n addMessage({\n id: `welcome-${Date.now()}`,\n role: 'assistant',\n content: welcomeContent,\n timestamp: Date.now(),\n })\n }\n }\n } else if (!response.resumed) {\n // \u4F1A\u8BDD\u65E0\u6548\u6216\u8FC7\u671F\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n clearSession()\n handleCreateNewSession()\n }\n } catch (error) {\n console.error('[LiveChatWidget] Failed to resume session:', error)\n\n // \u6DFB\u52A0\u9519\u8BEF\u6D88\u606F\u5230\u754C\u9762\n addMessage({\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: 'Failed to resume session. Creating a new session...',\n code: 'SESSION_RESUME_ERROR',\n },\n },\n ],\n timestamp: Date.now(),\n })\n\n // \u6062\u590D\u5931\u8D25\uFF0C\u6E05\u7A7A\u4F1A\u8BDD\u5E76\u521B\u5EFA\u65B0\u7684\n clearSession()\n handleCreateNewSession()\n }\n },\n [\n userId,\n site,\n channelCode,\n loginUserId,\n createSession,\n setMessages,\n clearSession,\n clearMessages,\n normalizeMessage,\n handleCreateNewSession,\n welcomeMessage,\n quickReplies,\n addMessage,\n ]\n )\n\n /**\n * \u53D1\u9001\u6D88\u606F\n */\n const handleSendMessage = useCallback(\n async (message?: string, isRetry: boolean = false) => {\n const textToSend = message || inputValue.trim()\n\n if (!textToSend) return\n\n // \u6E05\u7A7A\u8F93\u5165\u6846\uFF08\u4EC5\u5728\u975E\u91CD\u8BD5\u65F6\uFF09\n if (!message && !isRetry) {\n setInputValue('')\n }\n\n // \u8F93\u5165\u9A8C\u8BC1\n const sanitized = sanitizeInput(textToSend)\n if (!sanitized) {\n onError?.(new Error('Invalid message'))\n return\n }\n\n // \u6DFB\u52A0\u7528\u6237\u6D88\u606F\u5230\u754C\u9762\uFF08\u4EC5\u5728\u975E\u91CD\u8BD5\u65F6\uFF09\n if (!isRetry) {\n const userMessage = {\n id: `user-${Date.now()}`,\n role: 'user' as const,\n content: [{ type: 'text' as const, text: sanitized }],\n timestamp: Date.now(),\n }\n addMessage(userMessage)\n }\n\n // \u7ACB\u5373\u6DFB\u52A0\u601D\u8003\u72B6\u6001\u6D88\u606F\uFF0C\u63D0\u5347\u7528\u6237\u4F53\u9A8C\n const thinkingMessage = {\n id: `thinking-${Date.now()}`,\n role: 'assistant' as const,\n content: [{ type: 'thinking' as const, data: { status: 'thinking' } }],\n timestamp: Date.now(),\n }\n addMessage(thinkingMessage)\n\n // \u89E6\u53D1\u6D88\u606F\u53D1\u9001\u56DE\u8C03\uFF08\u4EC5\u5728\u975E\u91CD\u8BD5\u65F6\uFF09\n if (!isRetry) {\n onMessageSend?.(sanitized)\n }\n\n // \u6807\u8BB0\u662F\u5426\u68C0\u6D4B\u5230\u4F1A\u8BDD\u8FC7\u671F\n let sessionExpiredDetected = false\n\n try {\n // \u786E\u4FDD\u6709 sessionId\uFF0C\u5982\u679C\u6CA1\u6709\u5219\u5148\u521B\u5EFA\u4F1A\u8BDD\n let currentSessionId = sessionId\n if (!currentSessionId) {\n // \u6CA1\u6709\u4F1A\u8BDD\uFF0C\u521B\u5EFA\u65B0\u4F1A\u8BDD\n const response = await createSession({\n user_id: userId,\n site: site,\n channel_code: channelCode,\n real_user_id: loginUserId,\n })\n if (response.success) {\n currentSessionId = response.sessionId\n saveSession(currentSessionId)\n } else {\n throw new Error('Failed to create session')\n }\n }\n\n // \u6784\u5EFA\u8BF7\u6C42\u53C2\u6570\uFF08session_id \u73B0\u5728\u662F\u5FC5\u586B\u7684\uFF09\n const requestPayload: ChatStreamRequest = {\n message: sanitized,\n user_id: userId,\n session_id: currentSessionId,\n context: {\n cartId: cartId,\n accessToken: accessToken,\n real_user_id: loginUserId,\n },\n }\n\n // \u53D1\u9001\u6D88\u606F\u5230\u540E\u7AEF\n await sendMessageStream(requestPayload, event => {\n // \u5904\u7406 SSE \u4E8B\u4EF6\n handleSSEEvent(event)\n\n // \u7279\u6B8A\u5904\u7406\uFF1A\u4F1A\u8BDD\u8FC7\u671F\uFF08error \u4E8B\u4EF6\u4E14 type \u4E3A validation_error\uFF09\n if (event.event === 'error' && event.data.type === 'validation_error') {\n sessionExpiredDetected = true\n clearSession()\n }\n })\n\n // \u5982\u679C\u68C0\u6D4B\u5230\u4F1A\u8BDD\u8FC7\u671F\u4E14\u4E0D\u662F\u91CD\u8BD5\uFF0C\u81EA\u52A8\u521B\u5EFA\u65B0\u4F1A\u8BDD\u5E76\u91CD\u8BD5\n if (sessionExpiredDetected && !isRetry) {\n console.log('[LiveChatWidget] Session expired (validation_error), creating new session and retrying...')\n\n // \u79FB\u9664 thinking \u6D88\u606F\n const messagesWithoutThinking = messages.filter(msg => msg.id !== thinkingMessage.id)\n setMessages(messagesWithoutThinking)\n\n // \u521B\u5EFA\u65B0\u4F1A\u8BDD\n const response = await createSession({\n user_id: userId,\n site: site,\n channel_code: channelCode,\n real_user_id: loginUserId,\n })\n\n if (response.success) {\n saveSession(response.sessionId)\n // \u91CD\u8BD5\u53D1\u9001\u6D88\u606F\n await handleSendMessage(sanitized, true)\n } else {\n throw new Error('Failed to recreate session after expiration')\n }\n }\n } catch (error) {\n console.error('[LiveChatWidget] Failed to send message:', error)\n onError?.(error as Error)\n\n // \u79FB\u9664\u521A\u624D\u6DFB\u52A0\u7684 thinking \u6D88\u606F\uFF0C\u5E76\u6DFB\u52A0\u9519\u8BEF\u6D88\u606F\n const messagesWithoutThinking = messages.filter(msg => msg.id !== thinkingMessage.id)\n setMessages([\n ...messagesWithoutThinking,\n {\n id: `error-${Date.now()}`,\n role: 'system',\n content: [\n {\n type: 'error',\n data: {\n message: sessionExpiredDetected\n ? 'Your session has expired. We tried to reconnect but failed. Please try again.'\n : 'Failed to send message. Please check your network connection and try again.',\n code: sessionExpiredDetected ? 'SESSION_EXPIRED' : 'NETWORK_ERROR',\n },\n },\n ],\n timestamp: Date.now(),\n },\n ])\n }\n },\n [\n inputValue,\n userId,\n sessionId,\n site,\n channelCode,\n loginUserId,\n cartId,\n accessToken,\n messages,\n setInputValue,\n addMessage,\n setMessages,\n createSession,\n sendMessageStream,\n handleSSEEvent,\n saveSession,\n clearSession,\n onMessageSend,\n onError,\n ]\n )\n\n // \u66F4\u65B0 ref \u4EE5\u4FDD\u6301\u6700\u65B0\u7684 handleSendMessage\n React.useEffect(() => {\n handleSendMessageRef.current = handleSendMessage\n }, [handleSendMessage])\n\n 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
+ "mappings": "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",
6
+ "names": ["Fragment", "jsx", "jsxs", "React", "useEffect", "useCallback", "Dialog", "DEFAULT_COMMON_TEXT", "ChatBubble", "ChatWindow", "useChatState", "useChatAPI", "MessageRendererRegistry", "sanitizeInput", "transformProducts", "transformCartData", "TextBlock", "ProductCard", "ProductList", "ProductComparisonRenderer", "PolicyBlock", "createQuickRepliesRenderer", "ThinkingBlock", "ErrorBlock", "FAQListRenderer", "PromotionListRenderer", "CartCard", "LiveChatWidget", "apiBaseUrl", "headers", "recaptchaSitekey", "recaptchaAction", "site", "channelCode", "loginUserId", "cartId", "accessToken", "position", "welcomeMessage", "quickReplies", "customRenderers", "logoUrl", "title", "chatBubbleIcon", "open", "onOpenChange", "onOpen", "onClose", "onMessageSend", "onError", "onTextMessage", "onProductList", "onPromotionList", "onAddToCart", "onCart", "showNewSessionButton", "commonText", "productCardRender", "mergedText", "chatState", "messages", "isOpen", "userId", "sessionId", "inputValue", "isStreaming", "openChat", "closeChat", "setInputValue", "addMessage", "setMessages", "clearMessages", "handleSSEEvent", "saveSession", "clearSession", "sendMessageStream", "createSession", "handleSendMessageRef", "_message", "rendererRegistry", "registry", "quickRepliesRenderer", "reply", "currentSessionId", "handleResumeSession", "handleCreateNewSession", "normalizeMessage", "message", "contentBlocks", "structuredData", "block", "transformedData", "response", "messageText", "welcomeContent", "questions", "quickRepliesFromBackend", "question", "index", "error", "existingSessionId", "normalizedMessages", "msg", "welcomeMsg", "handleSendMessage", "isRetry", "textToSend", "sanitized", "userMessage", "thinkingMessage", "sessionExpiredDetected", "event", "messagesWithoutThinking"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{jsx as n,jsxs as l}from"react/jsx-runtime";import{useRef as w,useEffect as b}from"react";const y=()=>l("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[n("path",{d:"M10.2069 15.6997V4.69971",stroke:"white","stroke-width":"2","stroke-linecap":"round"}),n("path",{d:"M15.3995 8.50341L10.9506 4.05446C10.534 3.6379 9.85866 3.63791 9.4421 4.05446L5.00005 8.49651",stroke:"white","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"})]}),v=({value:t,onChange:d,onSend:i,placeholder:g="",disabled:a=!1,autoFocus:h=!1,maxLength:o=5e3,className:p=""})=>{const c=w(null);b(()=>{const e=c.current;if(!e)return;e.style.height="auto";const m=Math.min(e.scrollHeight,120);e.style.height=`${m}px`},[t]);const u=e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),t.trim()&&!a&&i())},x=e=>{const r=e.target.value;r.length<=o&&d(r)},f=()=>{t.trim()&&!a&&i()},s=t.trim().length>0&&!a;return n("div",{className:`flex items-end gap-2 bg-white px-4 py-3 ${p}`,children:n("div",{className:"flex flex-1 items-center gap-2 rounded-2xl",style:{background:"linear-gradient(to right, #7687F3, #7687F3, #4DA8F5, #A3DFCE)",padding:"2px"},children:l("div",{className:"flex flex-1 items-center gap-2 bg-white px-3 py-2",style:{borderRadius:"14px"},children:[n("textarea",{ref:c,value:t,onChange:x,onKeyDown:u,placeholder:g,disabled:a,autoFocus:h,rows:1,className:"flex-1 resize-none bg-transparent text-sm text-gray-900 outline-none placeholder:text-gray-400 disabled:cursor-not-allowed disabled:opacity-50",style:{resize:"none"}}),t.length>o*.8&&l("span",{className:"text-xs text-gray-400",children:[t.length,"/",o]}),n("button",{type:"button",onClick:f,disabled:!s,className:`shrink-0 rounded-full p-2 transition-all ${s?" active:scale-95":"cursor-not-allowed "}`,style:s?{background:"linear-gradient(to bottom, #A3DFCE, #4DA8F5, #7687F3)"}:{background:"#BEBEBE"},"aria-label":"Send message",children:n(y,{})})]})})})};export{v as ChatInput};
1
+ import{jsx as n,jsxs as l}from"react/jsx-runtime";import{useRef as h,useEffect as k}from"react";const E=()=>l("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[n("path",{d:"M10.2069 15.6997V4.69971",stroke:"white","stroke-width":"2","stroke-linecap":"round"}),n("path",{d:"M15.3995 8.50341L10.9506 4.05446C10.534 3.6379 9.85866 3.63791 9.4421 4.05446L5.00005 8.49651",stroke:"white","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"})]}),R=({value:t,onChange:g,onSend:c,placeholder:p="",disabled:o=!1,autoFocus:u=!1,maxLength:a=5e3,className:f=""})=>{const d=h(null),s=h(!1);k(()=>{const e=d.current;if(!e)return;e.style.height="auto";const y=Math.min(e.scrollHeight,120);e.style.height=`${y}px`},[t]);const m=()=>{s.current=!0},x=()=>{s.current=!1},b=e=>{if(e.key==="Enter"&&!e.shiftKey){if(s.current)return;e.preventDefault(),t.trim()&&!o&&c()}},w=e=>{const i=e.target.value;i.length<=a&&g(i)},C=()=>{t.trim()&&!o&&c()},r=t.trim().length>0&&!o;return n("div",{className:`flex items-end gap-2 bg-white px-4 py-3 ${f}`,children:n("div",{className:"flex flex-1 items-center gap-2 rounded-2xl",style:{background:"linear-gradient(to right, #7687F3, #7687F3, #4DA8F5, #A3DFCE)",padding:"2px"},children:l("div",{className:"flex flex-1 items-center gap-2 bg-white px-3 py-2",style:{borderRadius:"14px"},children:[n("textarea",{ref:d,value:t,onChange:w,onKeyDown:b,onCompositionStart:m,onCompositionEnd:x,placeholder:p,disabled:o,autoFocus:u,rows:1,className:"flex-1 resize-none bg-transparent font-bold text-sm text-gray-900 outline-none placeholder:text-gray-400 disabled:cursor-not-allowed disabled:opacity-50",style:{resize:"none"}}),t.length>a*.8&&l("span",{className:"text-xs text-gray-400",children:[t.length,"/",a]}),n("button",{type:"button",onClick:C,disabled:!r,className:`shrink-0 rounded-full p-2 transition-all ${r?" active:scale-95":"cursor-not-allowed "}`,style:r?{background:"linear-gradient(to bottom, #A3DFCE, #4DA8F5, #7687F3)"}:{background:"#BEBEBE"},"aria-label":"Send message",children:n(E,{})})]})})})};export{R as ChatInput};
2
2
  //# sourceMappingURL=ChatInput.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/components/LiveChatWidget/components/ChatInput.tsx"],
4
- "sourcesContent": ["/**\n * \u804A\u5929\u8F93\u5165\u6846\u7EC4\u4EF6\n * \u63D0\u4F9B\u6587\u672C\u8F93\u5165\u548C\u53D1\u9001\u6309\u94AE\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u8F93\u5165\u6846\u8BBE\u8BA1\n */\n\nimport React, { useRef, useEffect } from 'react'\n\nexport interface ChatInputProps {\n /**\n * \u8F93\u5165\u6846\u5F53\u524D\u503C\n */\n value: string\n\n /**\n * \u503C\u53D8\u5316\u56DE\u8C03\n */\n onChange: (value: string) => void\n\n /**\n * \u53D1\u9001\u6D88\u606F\u56DE\u8C03\n */\n onSend: () => void\n\n /**\n * \u5360\u4F4D\u7B26\u6587\u672C\n * @default \"\u8F93\u5165\u6D88\u606F...\"\n */\n placeholder?: string\n\n /**\n * \u662F\u5426\u7981\u7528\u8F93\u5165\n * @default false\n */\n disabled?: boolean\n\n /**\n * \u662F\u5426\u81EA\u52A8\u805A\u7126\n * @default false\n */\n autoFocus?: boolean\n\n /**\n * \u6700\u5927\u5B57\u7B26\u6570\n * @default 5000\n */\n maxLength?: number\n\n /**\n * \u81EA\u5B9A\u4E49\u6837\u5F0F\u7C7B\u540D\n */\n className?: string\n}\n\n/**\n * \u53D1\u9001\u56FE\u6807 (\u5411\u4E0A\u7BAD\u5934)\n */\nconst SendIcon: React.FC = () => (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10.2069 15.6997V4.69971\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\" />\n <path\n d=\"M15.3995 8.50341L10.9506 4.05446C10.534 3.6379 9.85866 3.63791 9.4421 4.05446L5.00005 8.49651\"\n stroke=\"white\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n)\n\n/**\n * \u804A\u5929\u8F93\u5165\u6846\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u591A\u884C\u6587\u672C\u8F93\u5165\uFF08\u81EA\u52A8\u589E\u957F\uFF09\n * - Enter \u53D1\u9001\uFF0CShift+Enter \u6362\u884C\n * - \u5B57\u7B26\u6570\u9650\u5236\uFF085000\uFF09\n * - \u7981\u7528\u72B6\u6001\uFF08\u53D1\u9001\u4E2D\uFF09\n * - \u53D1\u9001\u6309\u94AE\uFF08\u7A7A\u5185\u5BB9\u65F6\u7981\u7528\uFF09\n *\n * \u6837\u5F0F\uFF1A\n * - \u56FA\u5B9A\u5E95\u90E8\n * - \u8FB9\u6846\u5206\u9694\u4E0A\u65B9\u5185\u5BB9\n * - \u81EA\u9002\u5E94\u9AD8\u5EA6\uFF081-5 \u884C\uFF09\n *\n * @example\n * ```tsx\n * <ChatInput\n * value={inputValue}\n * onChange={setInputValue}\n * onSend={handleSend}\n * disabled={isStreaming}\n * />\n * ```\n */\nexport const ChatInput: React.FC<ChatInputProps> = ({\n value,\n onChange,\n onSend,\n placeholder = '',\n disabled = false,\n autoFocus = false,\n maxLength = 5000,\n className = '',\n}) => {\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n\n /**\n * \u81EA\u52A8\u8C03\u6574 textarea \u9AD8\u5EA6\n */\n useEffect(() => {\n const textarea = textareaRef.current\n if (!textarea) return\n\n // \u91CD\u7F6E\u9AD8\u5EA6\u4EE5\u83B7\u53D6\u6B63\u786E\u7684 scrollHeight\n textarea.style.height = 'auto'\n\n // \u8BBE\u7F6E\u9AD8\u5EA6\uFF0C\u6700\u5927 5 \u884C\uFF08\u7EA6 120px\uFF09\n const maxHeight = 120\n const newHeight = Math.min(textarea.scrollHeight, maxHeight)\n textarea.style.height = `${newHeight}px`\n }, [value])\n\n /**\n * \u5904\u7406\u952E\u76D8\u4E8B\u4EF6\n * Enter \u53D1\u9001\uFF0CShift+Enter \u6362\u884C\n */\n const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault()\n if (value.trim() && !disabled) {\n onSend()\n }\n }\n }\n\n /**\n * \u5904\u7406\u8F93\u5165\u53D8\u5316\n */\n const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {\n const newValue = event.target.value\n\n // \u5B57\u7B26\u6570\u9650\u5236\n if (newValue.length <= maxLength) {\n onChange(newValue)\n }\n }\n\n /**\n * \u5904\u7406\u53D1\u9001\u6309\u94AE\u70B9\u51FB\n */\n const handleSendClick = () => {\n if (value.trim() && !disabled) {\n onSend()\n }\n }\n\n const canSend = value.trim().length > 0 && !disabled\n\n return (\n <div className={`flex items-end gap-2 bg-white px-4 py-3 ${className}`}>\n {/* \u8F93\u5165\u6846\u5BB9\u5668 - \u5E26\u6E10\u53D8\u8FB9\u6846 */}\n <div\n className=\"flex flex-1 items-center gap-2 rounded-2xl\"\n style={{\n background: 'linear-gradient(to right, #7687F3, #7687F3, #4DA8F5, #A3DFCE)',\n padding: '2px',\n }}\n >\n {/* \u5185\u90E8\u767D\u8272\u80CC\u666F */}\n <div className=\"flex flex-1 items-center gap-2 bg-white px-3 py-2\" style={{ borderRadius: '14px' }}>\n {/* \u6587\u672C\u8F93\u5165\u6846 */}\n <textarea\n ref={textareaRef}\n value={value}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n autoFocus={autoFocus}\n rows={1}\n className=\"flex-1 resize-none bg-transparent text-sm text-gray-900 outline-none placeholder:text-gray-400 disabled:cursor-not-allowed disabled:opacity-50\"\n style={{\n resize: 'none',\n }}\n />\n\n {/* \u5B57\u7B26\u6570\u63D0\u793A (\u63A5\u8FD1\u4E0A\u9650\u65F6\u663E\u793A) */}\n {value.length > maxLength * 0.8 && (\n <span className=\"text-xs text-gray-400\">\n {value.length}/{maxLength}\n </span>\n )}\n\n {/* \u53D1\u9001\u6309\u94AE */}\n <button\n type=\"button\"\n onClick={handleSendClick}\n disabled={!canSend}\n className={`shrink-0 rounded-full p-2 transition-all ${canSend ? ' active:scale-95' : 'cursor-not-allowed '}`}\n style={\n canSend\n ? {\n background: 'linear-gradient(to bottom, #A3DFCE, #4DA8F5, #7687F3)',\n }\n : { background: '#BEBEBE' }\n }\n aria-label=\"Send message\"\n >\n <SendIcon />\n </button>\n </div>\n </div>\n </div>\n )\n}\n"],
5
- "mappings": "AA0DE,OACE,OAAAA,EADF,QAAAC,MAAA,oBApDF,OAAgB,UAAAC,EAAQ,aAAAC,MAAiB,QAmDzC,MAAMC,EAAqB,IACzBH,EAAC,OAAI,MAAM,KAAK,OAAO,KAAK,QAAQ,YAAY,KAAK,OAAO,MAAM,6BAChE,UAAAD,EAAC,QAAK,EAAE,2BAA2B,OAAO,QAAQ,eAAa,IAAI,iBAAe,QAAQ,EAC1FA,EAAC,QACC,EAAE,gGACF,OAAO,QACP,eAAa,IACb,iBAAe,QACf,kBAAgB,QAClB,GACF,EA4BWK,EAAsC,CAAC,CAClD,MAAAC,EACA,SAAAC,EACA,OAAAC,EACA,YAAAC,EAAc,GACd,SAAAC,EAAW,GACX,UAAAC,EAAY,GACZ,UAAAC,EAAY,IACZ,UAAAC,EAAY,EACd,IAAM,CACJ,MAAMC,EAAcZ,EAA4B,IAAI,EAKpDC,EAAU,IAAM,CACd,MAAMY,EAAWD,EAAY,QAC7B,GAAI,CAACC,EAAU,OAGfA,EAAS,MAAM,OAAS,OAIxB,MAAMC,EAAY,KAAK,IAAID,EAAS,aADlB,GACyC,EAC3DA,EAAS,MAAM,OAAS,GAAGC,CAAS,IACtC,EAAG,CAACV,CAAK,CAAC,EAMV,MAAMW,EAAiBC,GAAoD,CACrEA,EAAM,MAAQ,SAAW,CAACA,EAAM,WAClCA,EAAM,eAAe,EACjBZ,EAAM,KAAK,GAAK,CAACI,GACnBF,EAAO,EAGb,EAKMW,EAAgBD,GAAkD,CACtE,MAAME,EAAWF,EAAM,OAAO,MAG1BE,EAAS,QAAUR,GACrBL,EAASa,CAAQ,CAErB,EAKMC,EAAkB,IAAM,CACxBf,EAAM,KAAK,GAAK,CAACI,GACnBF,EAAO,CAEX,EAEMc,EAAUhB,EAAM,KAAK,EAAE,OAAS,GAAK,CAACI,EAE5C,OACEV,EAAC,OAAI,UAAW,2CAA2Ca,CAAS,GAElE,SAAAb,EAAC,OACC,UAAU,6CACV,MAAO,CACL,WAAY,gEACZ,QAAS,KACX,EAGA,SAAAC,EAAC,OAAI,UAAU,oDAAoD,MAAO,CAAE,aAAc,MAAO,EAE/F,UAAAD,EAAC,YACC,IAAKc,EACL,MAAOR,EACP,SAAUa,EACV,UAAWF,EACX,YAAaR,EACb,SAAUC,EACV,UAAWC,EACX,KAAM,EACN,UAAU,iJACV,MAAO,CACL,OAAQ,MACV,EACF,EAGCL,EAAM,OAASM,EAAY,IAC1BX,EAAC,QAAK,UAAU,wBACb,UAAAK,EAAM,OAAO,IAAEM,GAClB,EAIFZ,EAAC,UACC,KAAK,SACL,QAASqB,EACT,SAAU,CAACC,EACX,UAAW,4CAA4CA,EAAU,mBAAqB,qBAAqB,GAC3G,MACEA,EACI,CACE,WAAY,uDACd,EACA,CAAE,WAAY,SAAU,EAE9B,aAAW,eAEX,SAAAtB,EAACI,EAAA,EAAS,EACZ,GACF,EACF,EACF,CAEJ",
6
- "names": ["jsx", "jsxs", "useRef", "useEffect", "SendIcon", "ChatInput", "value", "onChange", "onSend", "placeholder", "disabled", "autoFocus", "maxLength", "className", "textareaRef", "textarea", "newHeight", "handleKeyDown", "event", "handleChange", "newValue", "handleSendClick", "canSend"]
4
+ "sourcesContent": ["/**\n * \u804A\u5929\u8F93\u5165\u6846\u7EC4\u4EF6\n * \u63D0\u4F9B\u6587\u672C\u8F93\u5165\u548C\u53D1\u9001\u6309\u94AE\n * \u57FA\u4E8E specs/livechat-widget/plan.md \u7684\u8F93\u5165\u6846\u8BBE\u8BA1\n */\n\nimport React, { useRef, useEffect } from 'react'\n\nexport interface ChatInputProps {\n /**\n * \u8F93\u5165\u6846\u5F53\u524D\u503C\n */\n value: string\n\n /**\n * \u503C\u53D8\u5316\u56DE\u8C03\n */\n onChange: (value: string) => void\n\n /**\n * \u53D1\u9001\u6D88\u606F\u56DE\u8C03\n */\n onSend: () => void\n\n /**\n * \u5360\u4F4D\u7B26\u6587\u672C\n * @default \"\u8F93\u5165\u6D88\u606F...\"\n */\n placeholder?: string\n\n /**\n * \u662F\u5426\u7981\u7528\u8F93\u5165\n * @default false\n */\n disabled?: boolean\n\n /**\n * \u662F\u5426\u81EA\u52A8\u805A\u7126\n * @default false\n */\n autoFocus?: boolean\n\n /**\n * \u6700\u5927\u5B57\u7B26\u6570\n * @default 5000\n */\n maxLength?: number\n\n /**\n * \u81EA\u5B9A\u4E49\u6837\u5F0F\u7C7B\u540D\n */\n className?: string\n}\n\n/**\n * \u53D1\u9001\u56FE\u6807 (\u5411\u4E0A\u7BAD\u5934)\n */\nconst SendIcon: React.FC = () => (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M10.2069 15.6997V4.69971\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\" />\n <path\n d=\"M15.3995 8.50341L10.9506 4.05446C10.534 3.6379 9.85866 3.63791 9.4421 4.05446L5.00005 8.49651\"\n stroke=\"white\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n)\n\n/**\n * \u804A\u5929\u8F93\u5165\u6846\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u591A\u884C\u6587\u672C\u8F93\u5165\uFF08\u81EA\u52A8\u589E\u957F\uFF09\n * - Enter \u53D1\u9001\uFF0CShift+Enter \u6362\u884C\n * - \u5B57\u7B26\u6570\u9650\u5236\uFF085000\uFF09\n * - \u7981\u7528\u72B6\u6001\uFF08\u53D1\u9001\u4E2D\uFF09\n * - \u53D1\u9001\u6309\u94AE\uFF08\u7A7A\u5185\u5BB9\u65F6\u7981\u7528\uFF09\n *\n * \u6837\u5F0F\uFF1A\n * - \u56FA\u5B9A\u5E95\u90E8\n * - \u8FB9\u6846\u5206\u9694\u4E0A\u65B9\u5185\u5BB9\n * - \u81EA\u9002\u5E94\u9AD8\u5EA6\uFF081-5 \u884C\uFF09\n *\n * @example\n * ```tsx\n * <ChatInput\n * value={inputValue}\n * onChange={setInputValue}\n * onSend={handleSend}\n * disabled={isStreaming}\n * />\n * ```\n */\nexport const ChatInput: React.FC<ChatInputProps> = ({\n value,\n onChange,\n onSend,\n placeholder = '',\n disabled = false,\n autoFocus = false,\n maxLength = 5000,\n className = '',\n}) => {\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n // \u8DDF\u8E2A\u8F93\u5165\u6CD5\u72B6\u6001\uFF08\u662F\u5426\u6B63\u5728\u4F7F\u7528\u62FC\u97F3\u8F93\u5165\u7B49\uFF09\n const isComposingRef = useRef(false)\n\n /**\n * \u81EA\u52A8\u8C03\u6574 textarea \u9AD8\u5EA6\n */\n useEffect(() => {\n const textarea = textareaRef.current\n if (!textarea) return\n\n // \u91CD\u7F6E\u9AD8\u5EA6\u4EE5\u83B7\u53D6\u6B63\u786E\u7684 scrollHeight\n textarea.style.height = 'auto'\n\n // \u8BBE\u7F6E\u9AD8\u5EA6\uFF0C\u6700\u5927 5 \u884C\uFF08\u7EA6 120px\uFF09\n const maxHeight = 120\n const newHeight = Math.min(textarea.scrollHeight, maxHeight)\n textarea.style.height = `${newHeight}px`\n }, [value])\n\n /**\n * \u5904\u7406\u8F93\u5165\u6CD5\u5F00\u59CB\n */\n const handleCompositionStart = () => {\n isComposingRef.current = true\n }\n\n /**\n * \u5904\u7406\u8F93\u5165\u6CD5\u7ED3\u675F\n */\n const handleCompositionEnd = () => {\n isComposingRef.current = false\n }\n\n /**\n * \u5904\u7406\u952E\u76D8\u4E8B\u4EF6\n * Enter \u53D1\u9001\uFF0CShift+Enter \u6362\u884C\n * \u6CE8\u610F\uFF1A\u8F93\u5165\u6CD5\u6FC0\u6D3B\u65F6\uFF08\u5982\u62FC\u97F3\u8F93\u5165\u4E2D\uFF09\uFF0C\u56DE\u8F66\u952E\u4E0D\u5E94\u53D1\u9001\u6D88\u606F\n */\n const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n // \u5982\u679C\u6B63\u5728\u4F7F\u7528\u8F93\u5165\u6CD5\uFF08\u5982\u62FC\u97F3\u8F93\u5165\uFF09\uFF0C\u5219\u4E0D\u53D1\u9001\u6D88\u606F\n // \u8BA9\u8F93\u5165\u6CD5\u5148\u5B8C\u6210\u5B57\u7B26\u9009\u62E9\n if (isComposingRef.current) {\n return\n }\n\n event.preventDefault()\n if (value.trim() && !disabled) {\n onSend()\n }\n }\n }\n\n /**\n * \u5904\u7406\u8F93\u5165\u53D8\u5316\n */\n const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {\n const newValue = event.target.value\n\n // \u5B57\u7B26\u6570\u9650\u5236\n if (newValue.length <= maxLength) {\n onChange(newValue)\n }\n }\n\n /**\n * \u5904\u7406\u53D1\u9001\u6309\u94AE\u70B9\u51FB\n */\n const handleSendClick = () => {\n if (value.trim() && !disabled) {\n onSend()\n }\n }\n\n const canSend = value.trim().length > 0 && !disabled\n\n return (\n <div className={`flex items-end gap-2 bg-white px-4 py-3 ${className}`}>\n {/* \u8F93\u5165\u6846\u5BB9\u5668 - \u5E26\u6E10\u53D8\u8FB9\u6846 */}\n <div\n className=\"flex flex-1 items-center gap-2 rounded-2xl\"\n style={{\n background: 'linear-gradient(to right, #7687F3, #7687F3, #4DA8F5, #A3DFCE)',\n padding: '2px',\n }}\n >\n {/* \u5185\u90E8\u767D\u8272\u80CC\u666F */}\n <div className=\"flex flex-1 items-center gap-2 bg-white px-3 py-2\" style={{ borderRadius: '14px' }}>\n {/* \u6587\u672C\u8F93\u5165\u6846 */}\n <textarea\n ref={textareaRef}\n value={value}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n onCompositionStart={handleCompositionStart}\n onCompositionEnd={handleCompositionEnd}\n placeholder={placeholder}\n disabled={disabled}\n autoFocus={autoFocus}\n rows={1}\n className=\"flex-1 resize-none bg-transparent font-bold text-sm text-gray-900 outline-none placeholder:text-gray-400 disabled:cursor-not-allowed disabled:opacity-50\"\n style={{\n resize: 'none',\n }}\n />\n\n {/* \u5B57\u7B26\u6570\u63D0\u793A (\u63A5\u8FD1\u4E0A\u9650\u65F6\u663E\u793A) */}\n {value.length > maxLength * 0.8 && (\n <span className=\"text-xs text-gray-400\">\n {value.length}/{maxLength}\n </span>\n )}\n\n {/* \u53D1\u9001\u6309\u94AE */}\n <button\n type=\"button\"\n onClick={handleSendClick}\n disabled={!canSend}\n className={`shrink-0 rounded-full p-2 transition-all ${canSend ? ' active:scale-95' : 'cursor-not-allowed '}`}\n style={\n canSend\n ? {\n background: 'linear-gradient(to bottom, #A3DFCE, #4DA8F5, #7687F3)',\n }\n : { background: '#BEBEBE' }\n }\n aria-label=\"Send message\"\n >\n <SendIcon />\n </button>\n </div>\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "AA0DE,OACE,OAAAA,EADF,QAAAC,MAAA,oBApDF,OAAgB,UAAAC,EAAQ,aAAAC,MAAiB,QAmDzC,MAAMC,EAAqB,IACzBH,EAAC,OAAI,MAAM,KAAK,OAAO,KAAK,QAAQ,YAAY,KAAK,OAAO,MAAM,6BAChE,UAAAD,EAAC,QAAK,EAAE,2BAA2B,OAAO,QAAQ,eAAa,IAAI,iBAAe,QAAQ,EAC1FA,EAAC,QACC,EAAE,gGACF,OAAO,QACP,eAAa,IACb,iBAAe,QACf,kBAAgB,QAClB,GACF,EA4BWK,EAAsC,CAAC,CAClD,MAAAC,EACA,SAAAC,EACA,OAAAC,EACA,YAAAC,EAAc,GACd,SAAAC,EAAW,GACX,UAAAC,EAAY,GACZ,UAAAC,EAAY,IACZ,UAAAC,EAAY,EACd,IAAM,CACJ,MAAMC,EAAcZ,EAA4B,IAAI,EAE9Ca,EAAiBb,EAAO,EAAK,EAKnCC,EAAU,IAAM,CACd,MAAMa,EAAWF,EAAY,QAC7B,GAAI,CAACE,EAAU,OAGfA,EAAS,MAAM,OAAS,OAIxB,MAAMC,EAAY,KAAK,IAAID,EAAS,aADlB,GACyC,EAC3DA,EAAS,MAAM,OAAS,GAAGC,CAAS,IACtC,EAAG,CAACX,CAAK,CAAC,EAKV,MAAMY,EAAyB,IAAM,CACnCH,EAAe,QAAU,EAC3B,EAKMI,EAAuB,IAAM,CACjCJ,EAAe,QAAU,EAC3B,EAOMK,EAAiBC,GAAoD,CACzE,GAAIA,EAAM,MAAQ,SAAW,CAACA,EAAM,SAAU,CAG5C,GAAIN,EAAe,QACjB,OAGFM,EAAM,eAAe,EACjBf,EAAM,KAAK,GAAK,CAACI,GACnBF,EAAO,CAEX,CACF,EAKMc,EAAgBD,GAAkD,CACtE,MAAME,EAAWF,EAAM,OAAO,MAG1BE,EAAS,QAAUX,GACrBL,EAASgB,CAAQ,CAErB,EAKMC,EAAkB,IAAM,CACxBlB,EAAM,KAAK,GAAK,CAACI,GACnBF,EAAO,CAEX,EAEMiB,EAAUnB,EAAM,KAAK,EAAE,OAAS,GAAK,CAACI,EAE5C,OACEV,EAAC,OAAI,UAAW,2CAA2Ca,CAAS,GAElE,SAAAb,EAAC,OACC,UAAU,6CACV,MAAO,CACL,WAAY,gEACZ,QAAS,KACX,EAGA,SAAAC,EAAC,OAAI,UAAU,oDAAoD,MAAO,CAAE,aAAc,MAAO,EAE/F,UAAAD,EAAC,YACC,IAAKc,EACL,MAAOR,EACP,SAAUgB,EACV,UAAWF,EACX,mBAAoBF,EACpB,iBAAkBC,EAClB,YAAaV,EACb,SAAUC,EACV,UAAWC,EACX,KAAM,EACN,UAAU,2JACV,MAAO,CACL,OAAQ,MACV,EACF,EAGCL,EAAM,OAASM,EAAY,IAC1BX,EAAC,QAAK,UAAU,wBACb,UAAAK,EAAM,OAAO,IAAEM,GAClB,EAIFZ,EAAC,UACC,KAAK,SACL,QAASwB,EACT,SAAU,CAACC,EACX,UAAW,4CAA4CA,EAAU,mBAAqB,qBAAqB,GAC3G,MACEA,EACI,CACE,WAAY,uDACd,EACA,CAAE,WAAY,SAAU,EAE9B,aAAW,eAEX,SAAAzB,EAACI,EAAA,EAAS,EACZ,GACF,EACF,EACF,CAEJ",
6
+ "names": ["jsx", "jsxs", "useRef", "useEffect", "SendIcon", "ChatInput", "value", "onChange", "onSend", "placeholder", "disabled", "autoFocus", "maxLength", "className", "textareaRef", "isComposingRef", "textarea", "newHeight", "handleCompositionStart", "handleCompositionEnd", "handleKeyDown", "event", "handleChange", "newValue", "handleSendClick", "canSend"]
7
7
  }
@@ -1,5 +1,5 @@
1
- import{jsx as t,jsxs as x}from"react/jsx-runtime";import{MessageContent as l}from"./MessageContent";function g(e){const r=new Date(e),o=r.getHours().toString().padStart(2,"0"),i=r.getMinutes().toString().padStart(2,"0");return`${o}:${i}`}const N=({message:e,rendererRegistry:r,defaultRenderer:o,showTimestamp:i=!0,className:d="",onAddToCart:c})=>{const n=e.role==="user",v=e.role==="assistant",u=e.role==="system",m=e.role==="tool";if(u)return t("div",{className:`flex justify-center py-2 ${d}`,children:t("div",{className:"",children:e.content.map((s,a)=>t(l,{content:s,isUser:!1,isSystem:!0,rendererRegistry:r,defaultRenderer:o},a))})});if(m)return null;const p=["product_list","product_comparison","faq_list","promotion_list","cart"],f=e.content.filter(s=>!p.includes(s.type)),y=e.content.filter(s=>p.includes(s.type));return t("div",{className:`flex ${n?"justify-end":"justify-start"} py-2 ${d}`,children:x("div",{className:"flex w-fit max-w-full flex-col gap-2",children:[f.length>0&&t("div",{className:`w-full min-w-0 overflow-hidden rounded-2xl p-3 ${n?"text-white":"text-[#1D1D1F]"}`,style:{backgroundColor:n?"#005D8E":"#F5F6F7"},children:t("div",{className:"flex w-full min-w-0 flex-col gap-2",children:f.map((s,a)=>t(l,{content:s,isUser:n,isSystem:!1,rendererRegistry:r,defaultRenderer:o,onAddToCart:c},a))})}),y.map((s,a)=>t("div",{className:"w-full",children:t(l,{content:s,isUser:n,isSystem:!1,rendererRegistry:r,defaultRenderer:o,onAddToCart:c})},`standalone-${a}`)),i&&t("span",{className:`
1
+ import{jsx as s,jsxs as C}from"react/jsx-runtime";import{MessageContent as d}from"./MessageContent";import u from"classnames";function v(t){const r=new Date(t),o=r.getHours().toString().padStart(2,"0"),i=r.getMinutes().toString().padStart(2,"0");return`${o}:${i}`}const $=({message:t,rendererRegistry:r,defaultRenderer:o,showTimestamp:i=!0,className:c="",onAddToCart:p})=>{const n=t.role==="user",R=t.role==="assistant",y=t.role==="system",g=t.role==="tool";if(y)return s("div",{className:`flex justify-center py-2 ${c}`,children:s("div",{className:"",children:t.content.map((e,a)=>s(d,{content:e,isUser:!1,isSystem:!0,rendererRegistry:r,defaultRenderer:o},a))})});if(g)return null;const f=["product_list","product_comparison","faq_list","promotion_list","cart"],l=t.content.filter(e=>!f.includes(e.type)),x=t.content.filter(e=>f.includes(e.type)),m=l.some(e=>e.type==="product_card");return s("div",{className:`flex ${n?"justify-end":"justify-start"} py-2 ${c}`,children:C("div",{className:"flex w-fit max-w-full flex-col gap-2",children:[l.length>0&&s("div",{className:u("w-fit min-w-0 overflow-hidden rounded-2xl ",n?"text-white":"text-[#1D1D1F]",m?"p-4":"p-3"),style:{backgroundColor:n?"#005D8E":"#F5F6F7"},children:s("div",{className:u("flex w-full min-w-0 flex-col",m?"gap-0":"gap-2"),children:l.map((e,a)=>s(d,{content:e,isUser:n,isSystem:!1,rendererRegistry:r,defaultRenderer:o,onAddToCart:p},a))})}),x.map((e,a)=>s("div",{className:"w-full",children:s(d,{content:e,isUser:n,isSystem:!1,rendererRegistry:r,defaultRenderer:o,onAddToCart:p})},`standalone-${a}`)),i&&s("span",{className:`
2
2
  px-2 text-xs text-gray-400
3
3
  ${n?"text-right":"text-left"}
4
- `,children:g(e.timestamp)})]})})};export{N as ChatMessage};
4
+ `,children:v(t.timestamp)})]})})};export{$ as ChatMessage};
5
5
  //# sourceMappingURL=ChatMessage.js.map
@@ -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=\"\">\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 // \u6CE8\u610F\uFF1Aproduct_card \u4E0D\u5728\u6B64\u5217\u8868\u4E2D\uFF0C\u5B83\u4F1A\u5728\u6C14\u6CE1\u5185\u6E32\u67D3\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\u5305\u542B\u6587\u672C\u548C\u4EA7\u54C1\u5361\u7247\uFF0C\u6309\u539F\u59CB\u987A\u5E8F\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 {/* \u6309\u539F\u59CB\u987A\u5E8F\u6E32\u67D3\u6C14\u6CE1\u5185\u7684\u6240\u6709\u5185\u5BB9\uFF08text \u548C product_card \u4EA4\u66FF\uFF09 */}\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": "AAkGY,cAAAA,EA8BN,QAAAC,MA9BM,oBA1FZ,OAAS,kBAAAC,MAAsB,mBAyC/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,MAAMC,EAA0C,CAAC,CACtD,QAAAC,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,OACEjB,EAAC,OAAI,UAAW,4BAA4Ba,CAAS,GACnD,SAAAb,EAAC,OAAI,UAAU,GACZ,SAAAS,EAAQ,QAAQ,IAAI,CAACU,EAASC,IAC7BpB,EAACE,EAAA,CAEC,QAASiB,EACT,OAAQ,GACR,SAAU,GACV,iBAAkBT,EAClB,gBAAiBC,GALZS,CAMP,CACD,EACH,EACF,EAKJ,GAAIF,EACF,OAAO,KAKT,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,OACEvB,EAAC,OAAI,UAAW,QAAQe,EAAS,cAAgB,eAAe,SAASF,CAAS,GAChF,SAAAZ,EAAC,OAAI,UAAU,uCAEZ,UAAAqB,EAAc,OAAS,GACtBtB,EAAC,OACC,UAAW,kDAAkDe,EAAS,aAAe,gBAAgB,GACrG,MAAO,CACL,gBAAiBA,EAAS,UAAY,SACxC,EAGA,SAAAf,EAAC,OAAI,UAAU,qCACZ,SAAAsB,EAAc,IAAI,CAACH,EAASC,IAC3BpB,EAACE,EAAA,CAEC,QAASiB,EACT,OAAQJ,EACR,SAAU,GACV,iBAAkBL,EAClB,gBAAiBC,EACjB,YAAaG,GANRM,CAOP,CACD,EACH,EACF,EAIDI,EAAkB,IAAI,CAACL,EAASC,IAC/BpB,EAAC,OAAgC,UAAU,SACzC,SAAAA,EAACE,EAAA,CACC,QAASiB,EACT,OAAQJ,EACR,SAAU,GACV,iBAAkBL,EAClB,gBAAiBC,EACjB,YAAaG,EACf,GARQ,cAAcM,CAAK,EAS7B,CACD,EAGAR,GACCZ,EAAC,QACC,UAAW;AAAA;AAAA,gBAEPe,EAAS,aAAe,WAAW;AAAA,cAGtC,SAAAZ,EAAgBM,EAAQ,SAAS,EACpC,GAEJ,EACF,CAEJ",
6
- "names": ["jsx", "jsxs", "MessageContent", "formatTimestamp", "timestamp", "date", "hours", "minutes", "ChatMessage", "message", "rendererRegistry", "defaultRenderer", "showTimestamp", "className", "onAddToCart", "isUser", "isAssistant", "isSystem", "isTool", "content", "index", "structuredContentTypes", "bubbleContent", "c", "standaloneContent"]
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'\nimport cn from 'classnames'\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=\"\">\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 // \u6CE8\u610F\uFF1Aproduct_card \u4E0D\u5728\u6B64\u5217\u8868\u4E2D\uFF0C\u5B83\u4F1A\u5728\u6C14\u6CE1\u5185\u6E32\u67D3\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 const isBubbleProductCard = bubbleContent.some(c => c.type === 'product_card')\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\u5305\u542B\u6587\u672C\u548C\u4EA7\u54C1\u5361\u7247\uFF0C\u6309\u539F\u59CB\u987A\u5E8F\uFF09 */}\n {bubbleContent.length > 0 && (\n <div\n className={cn(\n 'w-fit min-w-0 overflow-hidden rounded-2xl ',\n isUser ? 'text-white' : 'text-[#1D1D1F]',\n isBubbleProductCard ? 'p-4' : 'p-3'\n )}\n style={{\n backgroundColor: isUser ? '#005D8E' : '#F5F6F7',\n }}\n >\n {/* \u6309\u539F\u59CB\u987A\u5E8F\u6E32\u67D3\u6C14\u6CE1\u5185\u7684\u6240\u6709\u5185\u5BB9\uFF08text \u548C product_card \u4EA4\u66FF\uFF09 */}\n <div className={cn(\n 'flex w-full min-w-0 flex-col',\n isBubbleProductCard ?'gap-0':'gap-2'\n )}>\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": "AAkGY,cAAAA,EA8BN,QAAAC,MA9BM,oBA1FZ,OAAS,kBAAAC,MAAsB,mBAE/B,OAAOC,MAAQ,aAuCf,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,MAAMC,EAA0C,CAAC,CACtD,QAAAC,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,OACElB,EAAC,OAAI,UAAW,4BAA4Bc,CAAS,GACnD,SAAAd,EAAC,OAAI,UAAU,GACZ,SAAAU,EAAQ,QAAQ,IAAI,CAACU,EAASC,IAC7BrB,EAACE,EAAA,CAEC,QAASkB,EACT,OAAQ,GACR,SAAU,GACV,iBAAkBT,EAClB,gBAAiBC,GALZS,CAMP,CACD,EACH,EACF,EAKJ,GAAIF,EACF,OAAO,KAKT,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,EACvFE,EAAsBH,EAAc,KAAKC,GAAKA,EAAE,OAAS,cAAc,EAE7E,OACExB,EAAC,OAAI,UAAW,QAAQgB,EAAS,cAAgB,eAAe,SAASF,CAAS,GAChF,SAAAb,EAAC,OAAI,UAAU,uCAEZ,UAAAsB,EAAc,OAAS,GACtBvB,EAAC,OACC,UAAWG,EACT,6CACAa,EAAS,aAAe,iBACxBU,EAAsB,MAAQ,KAChC,EACA,MAAO,CACL,gBAAiBV,EAAS,UAAY,SACxC,EAGA,SAAAhB,EAAC,OAAI,UAAWG,EACd,+BACAuB,EAAqB,QAAQ,OAC/B,EACG,SAAAH,EAAc,IAAI,CAACH,EAASC,IAC3BrB,EAACE,EAAA,CAEC,QAASkB,EACT,OAAQJ,EACR,SAAU,GACV,iBAAkBL,EAClB,gBAAiBC,EACjB,YAAaG,GANRM,CAOP,CACD,EACH,EACF,EAIDI,EAAkB,IAAI,CAACL,EAASC,IAC/BrB,EAAC,OAAgC,UAAU,SACzC,SAAAA,EAACE,EAAA,CACC,QAASkB,EACT,OAAQJ,EACR,SAAU,GACV,iBAAkBL,EAClB,gBAAiBC,EACjB,YAAaG,EACf,GARQ,cAAcM,CAAK,EAS7B,CACD,EAGAR,GACCb,EAAC,QACC,UAAW;AAAA;AAAA,gBAEPgB,EAAS,aAAe,WAAW;AAAA,cAGtC,SAAAZ,EAAgBM,EAAQ,SAAS,EACpC,GAEJ,EACF,CAEJ",
6
+ "names": ["jsx", "jsxs", "MessageContent", "cn", "formatTimestamp", "timestamp", "date", "hours", "minutes", "ChatMessage", "message", "rendererRegistry", "defaultRenderer", "showTimestamp", "className", "onAddToCart", "isUser", "isAssistant", "isSystem", "isTool", "content", "index", "structuredContentTypes", "bubbleContent", "c", "standaloneContent", "isBubbleProductCard"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{jsx as r,jsxs as B}from"react/jsx-runtime";import{useState as d,useRef as c,useEffect as w,useCallback as u}from"react";import{ChatHeader as k}from"./ChatHeader";import{MessageList as X}from"./MessageList";import{ChatInput as $}from"./ChatInput";const oe=({messages:E,inputValue:T,onInputChange:I,onSend:R,onClose:M,onNewSession:b,title:L,logoUrl:y,isSending:A=!1,isLoadingHistory:C=!1,showTimestamp:_=!0,autoScroll:D=!0,rendererRegistry:S,defaultRenderer:x,inputPlaceholder:G,className:N="",onAddToCart:U,showNewSessionButton:Y=!0})=>{const l=u(()=>typeof window>"u"?{minHeight:680,maxHeight:680}:{minHeight:window.innerHeight*.4,maxHeight:window.innerHeight*.8},[.4,.8,680]),o=u(e=>{const{minHeight:t,maxHeight:n}=l();return Math.max(t,Math.min(n,e))},[l]),g=()=>typeof window>"u"?680:o(680),[i,F]=d(g),[h,m]=d(!1),f=c(0),H=c(g()),a=c(null),s=u(e=>{const t=o(e);return F(t),a.current&&a.current.style.setProperty("--livechat-mobile-height",`${t}px`),t},[o]),[v,O]=d(()=>typeof window<"u"?window.innerWidth<768:!1);w(()=>{const e=()=>{const t=window.innerWidth<768;if(O(t),t){const n=o(i);n!==i&&s(n)}};return window.addEventListener("resize",e),()=>window.removeEventListener("resize",e)},[i,o,s]);const p=e=>{if(!v)return;m(!0);const t="touches"in e?e.touches[0].clientY:e.clientY;f.current=t,H.current=i,e.preventDefault()};return w(()=>{if(!h)return;const e=n=>{const P="touches"in n?n.touches[0].clientY:n.clientY,W=f.current-P,z=H.current+W;s(z)},t=()=>{m(!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)}},[h,s]),B("div",{ref:a,className:`livechat-window flex flex-col overflow-hidden bg-white ${N}`,style:{borderRadius:"16px","--livechat-mobile-height":`${i}px`},children:[v&&r("div",{className:"flex cursor-ns-resize items-center justify-center py-2",style:{touchAction:"none",backgroundColor:"#ffffff"},onMouseDown:p,onTouchStart:p,children:r("div",{style:{width:"48px",height:"6px",borderRadius:"999px",backgroundColor:"#DADCE0"}})}),r(k,{title:L,logoUrl:y,onClose:M,onNewSession:b,showNewSessionButton:Y}),r(X,{messages:E,rendererRegistry:S,defaultRenderer:x,showTimestamp:_,autoScroll:D,isLoadingHistory:C,onAddToCart:U}),r($,{value:T,onChange:I,onSend:R,placeholder:G,disabled:A})]})};export{oe as ChatWindow};
1
+ import{jsx as i,jsxs as $}from"react/jsx-runtime";import{useState as c,useRef as u,useEffect as w,useCallback as l}from"react";import{ChatHeader as B}from"./ChatHeader";import{MessageList as k}from"./MessageList";import{ChatInput as X}from"./ChatInput";const oe=({messages:E,inputValue:T,onInputChange:R,onSend:I,onClose:M,onNewSession:b,title:L,logoUrl:y,isSending:A=!1,isLoadingHistory:C=!1,showTimestamp:_=!0,autoScroll:x=!0,rendererRegistry:D,defaultRenderer:S,inputPlaceholder:G,className:N="",onAddToCart:U,showNewSessionButton:Y=!0})=>{const g=l(()=>typeof window>"u"?{minHeight:680,maxHeight:680}:{minHeight:window.innerHeight*.4,maxHeight:window.innerHeight*.8},[.4,.8,680]),o=l(e=>{const{minHeight:t,maxHeight:n}=g();return Math.max(t,Math.min(n,e))},[g]),h=()=>typeof window>"u"?680:o(680),[r,F]=c(h),[m,f]=c(!1),H=u(0),v=u(h()),d=u(null),s=l(e=>{const t=o(e);return F(t),d.current&&d.current.style.setProperty("--livechat-mobile-height",`${t}px`),t},[o]),[a,O]=c(()=>typeof window<"u"?window.innerWidth<768:!1);w(()=>{const e=()=>{const t=window.innerWidth<768;if(O(t),t){const n=o(r);n!==r&&s(n)}};return window.addEventListener("resize",e),()=>window.removeEventListener("resize",e)},[r,o,s]);const p=e=>{if(!a)return;f(!0);const t="touches"in e?e.touches[0].clientY:e.clientY;H.current=t,v.current=r,e.preventDefault()};return w(()=>{if(!m)return;const e=n=>{const P="touches"in n?n.touches[0].clientY:n.clientY,W=H.current-P,z=v.current+W;s(z)},t=()=>{f(!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)}},[m,s]),$("div",{ref:d,className:`livechat-window flex flex-col overflow-hidden bg-white ${N}`,style:{borderRadius:"16px",borderBottomLeftRadius:a&&"0px",borderBottomRightRadius:a&&"0px","--livechat-mobile-height":`${r}px`},children:[a&&i("div",{className:"flex cursor-ns-resize items-center justify-center py-2",style:{touchAction:"none",backgroundColor:"#ffffff"},onMouseDown:p,onTouchStart:p,children:i("div",{style:{width:"48px",height:"6px",borderRadius:"999px",backgroundColor:"#DADCE0"}})}),i(B,{title:L,logoUrl:y,onClose:M,onNewSession:b,showNewSessionButton:Y}),i(k,{messages:E,rendererRegistry:D,defaultRenderer:S,showTimestamp:_,autoScroll:x,isLoadingHistory:C,onAddToCart:U}),i(X,{value:T,onChange:R,onSend:I,placeholder:G,disabled:A})]})};export{oe as ChatWindow};
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, 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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",
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 borderBottomLeftRadius: isMobile && '0px',\n borderBottomRightRadius: isMobile && '0px',\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
6
  "names": ["jsx", "jsxs", "useState", "useRef", "useEffect", "useCallback", "ChatHeader", "MessageList", "ChatInput", "ChatWindow", "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
- import{jsx as e,jsxs as o}from"react/jsx-runtime";import{useState as g}from"react";import x from"react-markdown";const N={render:n=>n.type!=="faq_list"?null:e(v,{content:n})},v=({content:n,onQuestionClick:d})=>{const{found:i,count:f,total:b,results:r}=n.data,[l,c]=g(new Set);if(!i||r.length===0)return e("div",{className:"rounded-lg border border-gray-200 bg-white p-4",children:e("p",{className:"text-sm text-gray-500",children:"\u672A\u627E\u5230\u76F8\u5173\u95EE\u9898"})});const p=t=>{c(a=>{const s=new Set(a);return s.has(t)?s.delete(t):s.add(t),s})},u=t=>{d?.(t)};return e("div",{className:"space-y-2",children:e("div",{className:"space-y-2",children:r.map(t=>{const a=l.has(t.id);return o("div",{className:"overflow-hidden rounded-2xl bg-[#F5F6F7] transition-all",children:[o("button",{onClick:()=>p(t.id),className:"flex w-full items-center justify-between gap-3 p-4 text-left transition-colors ",children:[e("div",{className:"flex-1",children:e("div",{className:"flex items-center gap-2",children:e("span",{className:"text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]",children:t.question})})}),e("svg",{className:`size-5 shrink-0 text-[#080A0F] transition-transform ${a?"rotate-180":""}`,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:e("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),a&&o("div",{className:" bg-[#F5F6F7] px-4 py-3 pt-0",children:[e("div",{className:"prose prose-sm max-w-none border-t border-gray-100 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]",style:{paddingTop:"12px"},children:e(x,{children:t.answer})}),t.relatedQuestions&&t.relatedQuestions.length>0&&o("div",{className:"mt-4 border-t border-gray-200 pt-3",children:[e("p",{className:"mb-2 text-xs font-medium text-gray-500",children:"You may also want to know:"}),e("div",{className:"space-y-1",children:t.relatedQuestions.map((s,m)=>e("button",{onClick:()=>u(s),className:"block w-full text-left text-xs text-blue-600 hover:text-blue-700 hover:underline",children:s},m))})]})]})]},t.id)})})})};export{v as FAQList,N as FAQListRenderer};
1
+ import{jsx as e,jsxs as o}from"react/jsx-runtime";import{useState as g}from"react";import x from"react-markdown";const N={render:n=>n.type!=="faq_list"?null:e(v,{content:n})},v=({content:n,onQuestionClick:d})=>{const{found:i,count:f,total:b,results:r}=n.data,[l,c]=g(new Set);if(!i||r.length===0)return e("div",{className:"rounded-lg border border-gray-200 bg-white p-4",children:e("p",{className:"text-sm text-gray-500",children:"\u672A\u627E\u5230\u76F8\u5173\u95EE\u9898"})});const p=t=>{c(a=>{const s=new Set(a);return s.has(t)?s.delete(t):s.add(t),s})},u=t=>{d?.(t)};return e("div",{className:"space-y-2",children:e("div",{className:"space-y-2",children:r.map(t=>{const a=l.has(t.id);return o("div",{className:"overflow-hidden rounded-2xl bg-[#F5F6F7] transition-all",children:[o("button",{onClick:()=>p(t.id),className:"flex w-full items-center justify-between gap-3 p-4 text-left transition-colors ",children:[e("div",{className:"flex-1",children:e("div",{className:"flex items-center gap-2",children:e("span",{className:"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]",children:t.question})})}),e("svg",{className:`size-5 shrink-0 text-[#080A0F] transition-transform ${a?"rotate-180":""}`,fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:e("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),a&&o("div",{className:" bg-[#F5F6F7] px-4 py-3 pt-0",children:[e("div",{className:"prose prose-sm max-w-none border-t border-gray-100 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]",style:{paddingTop:"12px"},children:e(x,{children:t.answer})}),t.relatedQuestions&&t.relatedQuestions.length>0&&o("div",{className:"mt-4 border-t border-gray-200 pt-3",children:[e("p",{className:"mb-2 text-xs font-medium text-gray-500",children:"You may also want to know:"}),e("div",{className:"space-y-1",children:t.relatedQuestions.map((s,m)=>e("button",{onClick:()=>u(s),className:"block w-full text-left text-xs text-blue-600 hover:text-blue-700 hover:underline",children:s},m))})]})]})]},t.id)})})})};export{v as FAQList,N as FAQListRenderer};
2
2
  //# sourceMappingURL=FAQList.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/components/LiveChatWidget/components/MessageContent/FAQList.tsx"],
4
- "sourcesContent": ["/**\n * FAQ \u5217\u8868\u7EC4\u4EF6\n * \u663E\u793A\u5E38\u89C1\u95EE\u9898\u5217\u8868\uFF0C\u652F\u6301\u6298\u53E0/\u5C55\u5F00\n */\n\nimport React, { useState } from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport type { FAQListContent, FAQItem, MessageRenderer, MessageContent } from '../../types'\n\nexport interface FAQListProps {\n content: FAQListContent\n onQuestionClick?: (question: string) => void\n}\n\n/**\n * FAQ \u5217\u8868\u6E32\u67D3\u5668\n * \u7528\u4E8E\u6CE8\u518C\u5230 MessageRendererRegistry\n */\nexport const FAQListRenderer: MessageRenderer = {\n render: (content: MessageContent) => {\n if (content.type !== 'faq_list') {\n return null\n }\n return <FAQList content={content as FAQListContent} />\n },\n}\n\nexport const FAQList: React.FC<FAQListProps> = ({ content, onQuestionClick }) => {\n const { found, count, total, results } = content.data\n const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set())\n\n // \u5982\u679C\u6CA1\u6709\u627E\u5230\u7ED3\u679C\n if (!found || results.length === 0) {\n return (\n <div className=\"rounded-lg border border-gray-200 bg-white p-4\">\n <p className=\"text-sm text-gray-500\">\u672A\u627E\u5230\u76F8\u5173\u95EE\u9898</p>\n </div>\n )\n }\n\n // \u5207\u6362\u5C55\u5F00/\u6298\u53E0\n const toggleExpand = (id: string) => {\n setExpandedIds(prev => {\n const newSet = new Set(prev)\n if (newSet.has(id)) {\n newSet.delete(id)\n } else {\n newSet.add(id)\n }\n return newSet\n })\n }\n\n // \u5904\u7406\u76F8\u5173\u95EE\u9898\u70B9\u51FB\n const handleRelatedQuestionClick = (question: string) => {\n onQuestionClick?.(question)\n }\n\n return (\n <div className=\"space-y-2\">\n {/* FAQ \u5217\u8868 */}\n <div className=\"space-y-2\">\n {results.map(item => {\n const isExpanded = expandedIds.has(item.id)\n\n return (\n <div key={item.id} className=\"overflow-hidden rounded-2xl bg-[#F5F6F7] transition-all\">\n {/* \u95EE\u9898\u6807\u9898 - \u53EF\u70B9\u51FB\u5C55\u5F00/\u6298\u53E0 */}\n <button\n onClick={() => toggleExpand(item.id)}\n className=\"flex w-full items-center justify-between gap-3 p-4 text-left transition-colors \"\n >\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]\">\n {item.question}\n </span>\n </div>\n </div>\n\n {/* \u5C55\u5F00/\u6298\u53E0\u56FE\u6807 */}\n <svg\n className={`size-5 shrink-0 text-[#080A0F] transition-transform ${isExpanded ? 'rotate-180' : ''}`}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n\n {/* \u7B54\u6848\u5185\u5BB9 - \u5C55\u5F00\u65F6\u663E\u793A */}\n {isExpanded && (\n <div className=\" bg-[#F5F6F7] px-4 py-3 pt-0\">\n <div\n className=\"prose prose-sm max-w-none border-t border-gray-100 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]\"\n style={{ paddingTop: '12px' }}\n >\n <ReactMarkdown>{item.answer}</ReactMarkdown>\n </div>\n\n {/* \u76F8\u5173\u95EE\u9898 */}\n {item.relatedQuestions && item.relatedQuestions.length > 0 && (\n <div className=\"mt-4 border-t border-gray-200 pt-3\">\n <p className=\"mb-2 text-xs font-medium text-gray-500\">You may also want to know:</p>\n <div className=\"space-y-1\">\n {item.relatedQuestions.map((q, idx) => (\n <button\n key={idx}\n onClick={() => handleRelatedQuestionClick(q)}\n className=\"block w-full text-left text-xs text-blue-600 hover:text-blue-700 hover:underline\"\n >\n {q}\n </button>\n ))}\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n )\n })}\n </div>\n </div>\n )\n}\n"],
5
- "mappings": "AAuBW,cAAAA,EA6CG,QAAAC,MA7CH,oBAlBX,OAAgB,YAAAC,MAAgB,QAChC,OAAOC,MAAmB,iBAYnB,MAAMC,EAAmC,CAC9C,OAASC,GACHA,EAAQ,OAAS,WACZ,KAEFL,EAACM,EAAA,CAAQ,QAASD,EAA2B,CAExD,EAEaC,EAAkC,CAAC,CAAE,QAAAD,EAAS,gBAAAE,CAAgB,IAAM,CAC/E,KAAM,CAAE,MAAAC,EAAO,MAAAC,EAAO,MAAAC,EAAO,QAAAC,CAAQ,EAAIN,EAAQ,KAC3C,CAACO,EAAaC,CAAc,EAAIX,EAAsB,IAAI,GAAK,EAGrE,GAAI,CAACM,GAASG,EAAQ,SAAW,EAC/B,OACEX,EAAC,OAAI,UAAU,iDACb,SAAAA,EAAC,KAAE,UAAU,wBAAwB,sDAAO,EAC9C,EAKJ,MAAMc,EAAgBC,GAAe,CACnCF,EAAeG,GAAQ,CACrB,MAAMC,EAAS,IAAI,IAAID,CAAI,EAC3B,OAAIC,EAAO,IAAIF,CAAE,EACfE,EAAO,OAAOF,CAAE,EAEhBE,EAAO,IAAIF,CAAE,EAERE,CACT,CAAC,CACH,EAGMC,EAA8BC,GAAqB,CACvDZ,IAAkBY,CAAQ,CAC5B,EAEA,OACEnB,EAAC,OAAI,UAAU,YAEb,SAAAA,EAAC,OAAI,UAAU,YACZ,SAAAW,EAAQ,IAAIS,GAAQ,CACnB,MAAMC,EAAaT,EAAY,IAAIQ,EAAK,EAAE,EAE1C,OACEnB,EAAC,OAAkB,UAAU,2DAE3B,UAAAA,EAAC,UACC,QAAS,IAAMa,EAAaM,EAAK,EAAE,EACnC,UAAU,kFAEV,UAAApB,EAAC,OAAI,UAAU,SACb,SAAAA,EAAC,OAAI,UAAU,0BACb,SAAAA,EAAC,QAAK,UAAU,oEACb,SAAAoB,EAAK,SACR,EACF,EACF,EAGApB,EAAC,OACC,UAAW,uDAAuDqB,EAAa,aAAe,EAAE,GAChG,KAAK,OACL,QAAQ,YACR,OAAO,eAEP,SAAArB,EAAC,QAAK,cAAc,QAAQ,eAAe,QAAQ,YAAa,EAAG,EAAE,iBAAiB,EACxF,GACF,EAGCqB,GACCpB,EAAC,OAAI,UAAU,+BACb,UAAAD,EAAC,OACC,UAAU,wHACV,MAAO,CAAE,WAAY,MAAO,EAE5B,SAAAA,EAACG,EAAA,CAAe,SAAAiB,EAAK,OAAO,EAC9B,EAGCA,EAAK,kBAAoBA,EAAK,iBAAiB,OAAS,GACvDnB,EAAC,OAAI,UAAU,qCACb,UAAAD,EAAC,KAAE,UAAU,yCAAyC,sCAA0B,EAChFA,EAAC,OAAI,UAAU,YACZ,SAAAoB,EAAK,iBAAiB,IAAI,CAACE,EAAGC,IAC7BvB,EAAC,UAEC,QAAS,IAAMkB,EAA2BI,CAAC,EAC3C,UAAU,mFAET,SAAAA,GAJIC,CAKP,CACD,EACH,GACF,GAEJ,IApDMH,EAAK,EAsDf,CAEJ,CAAC,EACH,EACF,CAEJ",
4
+ "sourcesContent": ["/**\n * FAQ \u5217\u8868\u7EC4\u4EF6\n * \u663E\u793A\u5E38\u89C1\u95EE\u9898\u5217\u8868\uFF0C\u652F\u6301\u6298\u53E0/\u5C55\u5F00\n */\n\nimport React, { useState } from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport type { FAQListContent, FAQItem, MessageRenderer, MessageContent } from '../../types'\n\nexport interface FAQListProps {\n content: FAQListContent\n onQuestionClick?: (question: string) => void\n}\n\n/**\n * FAQ \u5217\u8868\u6E32\u67D3\u5668\n * \u7528\u4E8E\u6CE8\u518C\u5230 MessageRendererRegistry\n */\nexport const FAQListRenderer: MessageRenderer = {\n render: (content: MessageContent) => {\n if (content.type !== 'faq_list') {\n return null\n }\n return <FAQList content={content as FAQListContent} />\n },\n}\n\nexport const FAQList: React.FC<FAQListProps> = ({ content, onQuestionClick }) => {\n const { found, count, total, results } = content.data\n const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set())\n\n // \u5982\u679C\u6CA1\u6709\u627E\u5230\u7ED3\u679C\n if (!found || results.length === 0) {\n return (\n <div className=\"rounded-lg border border-gray-200 bg-white p-4\">\n <p className=\"text-sm text-gray-500\">\u672A\u627E\u5230\u76F8\u5173\u95EE\u9898</p>\n </div>\n )\n }\n\n // \u5207\u6362\u5C55\u5F00/\u6298\u53E0\n const toggleExpand = (id: string) => {\n setExpandedIds(prev => {\n const newSet = new Set(prev)\n if (newSet.has(id)) {\n newSet.delete(id)\n } else {\n newSet.add(id)\n }\n return newSet\n })\n }\n\n // \u5904\u7406\u76F8\u5173\u95EE\u9898\u70B9\u51FB\n const handleRelatedQuestionClick = (question: string) => {\n onQuestionClick?.(question)\n }\n\n return (\n <div className=\"space-y-2\">\n {/* FAQ \u5217\u8868 */}\n <div className=\"space-y-2\">\n {results.map(item => {\n const isExpanded = expandedIds.has(item.id)\n\n return (\n <div key={item.id} className=\"overflow-hidden rounded-2xl bg-[#F5F6F7] transition-all\">\n {/* \u95EE\u9898\u6807\u9898 - \u53EF\u70B9\u51FB\u5C55\u5F00/\u6298\u53E0 */}\n <button\n onClick={() => toggleExpand(item.id)}\n className=\"flex w-full items-center justify-between gap-3 p-4 text-left transition-colors \"\n >\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-base font-bold leading-[1.4] tracking-[-0.02em] text-[#080A0F]\">\n {item.question}\n </span>\n </div>\n </div>\n\n {/* \u5C55\u5F00/\u6298\u53E0\u56FE\u6807 */}\n <svg\n className={`size-5 shrink-0 text-[#080A0F] transition-transform ${isExpanded ? 'rotate-180' : ''}`}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n\n {/* \u7B54\u6848\u5185\u5BB9 - \u5C55\u5F00\u65F6\u663E\u793A */}\n {isExpanded && (\n <div className=\" bg-[#F5F6F7] px-4 py-3 pt-0\">\n <div\n className=\"prose prose-sm max-w-none border-t border-gray-100 text-sm font-bold leading-[1.4] tracking-[-0.02em] text-[#4A4C56]\"\n style={{ paddingTop: '12px' }}\n >\n <ReactMarkdown>{item.answer}</ReactMarkdown>\n </div>\n\n {/* \u76F8\u5173\u95EE\u9898 */}\n {item.relatedQuestions && item.relatedQuestions.length > 0 && (\n <div className=\"mt-4 border-t border-gray-200 pt-3\">\n <p className=\"mb-2 text-xs font-medium text-gray-500\">You may also want to know:</p>\n <div className=\"space-y-1\">\n {item.relatedQuestions.map((q, idx) => (\n <button\n key={idx}\n onClick={() => handleRelatedQuestionClick(q)}\n className=\"block w-full text-left text-xs text-blue-600 hover:text-blue-700 hover:underline\"\n >\n {q}\n </button>\n ))}\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n )\n })}\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "AAuBW,cAAAA,EA6CG,QAAAC,MA7CH,oBAlBX,OAAgB,YAAAC,MAAgB,QAChC,OAAOC,MAAmB,iBAYnB,MAAMC,EAAmC,CAC9C,OAASC,GACHA,EAAQ,OAAS,WACZ,KAEFL,EAACM,EAAA,CAAQ,QAASD,EAA2B,CAExD,EAEaC,EAAkC,CAAC,CAAE,QAAAD,EAAS,gBAAAE,CAAgB,IAAM,CAC/E,KAAM,CAAE,MAAAC,EAAO,MAAAC,EAAO,MAAAC,EAAO,QAAAC,CAAQ,EAAIN,EAAQ,KAC3C,CAACO,EAAaC,CAAc,EAAIX,EAAsB,IAAI,GAAK,EAGrE,GAAI,CAACM,GAASG,EAAQ,SAAW,EAC/B,OACEX,EAAC,OAAI,UAAU,iDACb,SAAAA,EAAC,KAAE,UAAU,wBAAwB,sDAAO,EAC9C,EAKJ,MAAMc,EAAgBC,GAAe,CACnCF,EAAeG,GAAQ,CACrB,MAAMC,EAAS,IAAI,IAAID,CAAI,EAC3B,OAAIC,EAAO,IAAIF,CAAE,EACfE,EAAO,OAAOF,CAAE,EAEhBE,EAAO,IAAIF,CAAE,EAERE,CACT,CAAC,CACH,EAGMC,EAA8BC,GAAqB,CACvDZ,IAAkBY,CAAQ,CAC5B,EAEA,OACEnB,EAAC,OAAI,UAAU,YAEb,SAAAA,EAAC,OAAI,UAAU,YACZ,SAAAW,EAAQ,IAAIS,GAAQ,CACnB,MAAMC,EAAaT,EAAY,IAAIQ,EAAK,EAAE,EAE1C,OACEnB,EAAC,OAAkB,UAAU,2DAE3B,UAAAA,EAAC,UACC,QAAS,IAAMa,EAAaM,EAAK,EAAE,EACnC,UAAU,kFAEV,UAAApB,EAAC,OAAI,UAAU,SACb,SAAAA,EAAC,OAAI,UAAU,0BACb,SAAAA,EAAC,QAAK,UAAU,sEACb,SAAAoB,EAAK,SACR,EACF,EACF,EAGApB,EAAC,OACC,UAAW,uDAAuDqB,EAAa,aAAe,EAAE,GAChG,KAAK,OACL,QAAQ,YACR,OAAO,eAEP,SAAArB,EAAC,QAAK,cAAc,QAAQ,eAAe,QAAQ,YAAa,EAAG,EAAE,iBAAiB,EACxF,GACF,EAGCqB,GACCpB,EAAC,OAAI,UAAU,+BACb,UAAAD,EAAC,OACC,UAAU,wHACV,MAAO,CAAE,WAAY,MAAO,EAE5B,SAAAA,EAACG,EAAA,CAAe,SAAAiB,EAAK,OAAO,EAC9B,EAGCA,EAAK,kBAAoBA,EAAK,iBAAiB,OAAS,GACvDnB,EAAC,OAAI,UAAU,qCACb,UAAAD,EAAC,KAAE,UAAU,yCAAyC,sCAA0B,EAChFA,EAAC,OAAI,UAAU,YACZ,SAAAoB,EAAK,iBAAiB,IAAI,CAACE,EAAGC,IAC7BvB,EAAC,UAEC,QAAS,IAAMkB,EAA2BI,CAAC,EAC3C,UAAU,mFAET,SAAAA,GAJIC,CAKP,CACD,EACH,GACF,GAEJ,IApDMH,EAAK,EAsDf,CAEJ,CAAC,EACH,EACF,CAEJ",
6
6
  "names": ["jsx", "jsxs", "useState", "ReactMarkdown", "FAQListRenderer", "content", "FAQList", "onQuestionClick", "found", "count", "total", "results", "expandedIds", "setExpandedIds", "toggleExpand", "id", "prev", "newSet", "handleRelatedQuestionClick", "question", "item", "isExpanded", "q", "idx"]
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import{jsx as e,jsxs as s}from"react/jsx-runtime";import{useState as c}from"react";import m from"react-markdown";const g={render:(a,d,l)=>{const n=a,{title:i,content:r}=n.data;if(!i||!r)return null;const o=r.length>200;return e(p,{title:i,content:r,isLong:o})}},p=({title:a,content:d,isLong:l})=>{const[n,i]=c(!l);return s("div",{className:"overflow-hidden rounded-lg border border-blue-200 bg-blue-50",children:[s("div",{className:"flex items-center gap-2 border-b border-blue-200 bg-blue-100 px-3 py-2",children:[s("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:"shrink-0 text-blue-700",children:[e("path",{d:"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"}),e("polyline",{points:"14 2 14 8 20 8"}),e("line",{x1:"16",y1:"13",x2:"8",y2:"13"}),e("line",{x1:"16",y1:"17",x2:"8",y2:"17"}),e("polyline",{points:"10 9 9 9 8 9"})]}),e("h3",{className:"text-sm font-semibold text-blue-900",children:a})]}),s("div",{className:"px-3 py-2",children:[e("div",{className:`
2
2
  text-sm leading-relaxed text-blue-900
3
3
  ${!n&&l?"line-clamp-3":""}
4
- `,children:e(m,{components:{p:({node:o,...t})=>e("p",{...t,className:"mb-2 last:mb-0"}),ul:({node:o,...t})=>e("ul",{...t,className:"mb-2 ml-4 list-disc"}),ol:({node:o,...t})=>e("ol",{...t,className:"mb-2 ml-4 list-decimal"}),li:({node:o,...t})=>e("li",{...t,className:"mb-1"}),strong:({node:o,...t})=>e("strong",{...t,className:"font-semibold"})},children:d})}),l&&s("button",{type:"button",onClick:()=>{i(o=>!o)},className:"mt-2 flex items-center gap-1 text-xs font-medium text-blue-700 hover:text-blue-800",children:[e("span",{children:n?"\u6536\u8D77":"\u5C55\u5F00"}),e("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:`transition-transform ${n?"rotate-180":""}`,children:e("polyline",{points:"6 9 12 15 18 9"})})]})]})]})};export{g as PolicyBlock};
4
+ `,children:e(m,{components:{p:({node:o,...t})=>e("p",{...t,className:"mb-2 last:mb-0"}),ul:({node:o,...t})=>e("ul",{...t,className:"mb-2 ml-4 list-disc"}),ol:({node:o,...t})=>e("ol",{...t,className:"mb-2 ml-4 list-decimal"}),li:({node:o,...t})=>e("li",{...t,className:"mb-1"}),strong:({node:o,...t})=>e("strong",{...t,className:"font-bold"})},children:d})}),l&&s("button",{type:"button",onClick:()=>{i(o=>!o)},className:"mt-2 flex items-center gap-1 text-xs font-medium text-blue-700 hover:text-blue-800",children:[e("span",{children:n?"\u6536\u8D77":"\u5C55\u5F00"}),e("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:`transition-transform ${n?"rotate-180":""}`,children:e("polyline",{points:"6 9 12 15 18 9"})})]})]})]})};export{g as PolicyBlock};
5
5
  //# sourceMappingURL=PolicyBlock.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/components/LiveChatWidget/components/MessageContent/PolicyBlock.tsx"],
4
- "sourcesContent": ["/**\n * \u653F\u7B56\u8BF4\u660E\u6E32\u67D3\u5668\n * \u663E\u793A\u9000\u8D27\u3001\u4FDD\u4FEE\u7B49\u653F\u7B56\u4FE1\u606F\n * \u57FA\u4E8E specs/livechat-widget/data-model.md \u7684\u653F\u7B56\u6570\u636E\u6A21\u578B\n */\n\nimport React, { useState } from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport type { MessageRenderer, PolicyContent } from '../../types'\n\n/**\n * \u653F\u7B56\u8BF4\u660E\u6E32\u67D3\u5668\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u653F\u7B56\u6807\u9898\u548C\u5185\u5BB9\n * - \u652F\u6301 Markdown \u683C\u5F0F\n * - \u53EF\u6298\u53E0/\u5C55\u5F00\uFF08\u5185\u5BB9\u8F83\u957F\u65F6\uFF09\n *\n * \u653F\u7B56\u7C7B\u578B\uFF1A\n * - \u9000\u8D27\u653F\u7B56\n * - \u4FDD\u4FEE\u653F\u7B56\n * - \u8FD0\u8D39\u653F\u7B56\n * - \u9690\u79C1\u653F\u7B56\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\u2510\n * \u2502 \uD83D\uDCCB \u653F\u7B56\u6807\u9898 \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\u2524\n * \u2502 \u653F\u7B56\u5185\u5BB9... \u2502\n * \u2502 (\u652F\u6301 Markdown) \u2502\n * \u2502 \u2502\n * \u2502 [\u5C55\u5F00/\u6536\u8D77] \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\u2518\n * ```\n *\n * @example\n * ```tsx\n * const content: PolicyContent = {\n * type: 'policy',\n * data: {\n * title: '\u9000\u8D27\u653F\u7B56',\n * content: '\u6211\u4EEC\u63D0\u4F9B 30 \u5929\u65E0\u7406\u7531\u9000\u8D27...'\n * }\n * }\n * <PolicyBlock.render(content, false, false) />\n * ```\n */\nexport const PolicyBlock: MessageRenderer = {\n render: (content, isUser, isSystem) => {\n const policyContent = content as PolicyContent\n const { title, content: policyText } = policyContent.data\n\n if (!title || !policyText) {\n return null\n }\n\n // \u5224\u65AD\u5185\u5BB9\u662F\u5426\u8F83\u957F\uFF08\u8D85\u8FC7 200 \u5B57\u7B26\uFF09\n const isLongContent = policyText.length > 200\n\n return <PolicyCard title={title} content={policyText} isLong={isLongContent} />\n },\n}\n\n/**\n * \u653F\u7B56\u5361\u7247\u7EC4\u4EF6\uFF08\u652F\u6301\u6298\u53E0\uFF09\n */\nconst PolicyCard: React.FC<{\n title: string\n content: string\n isLong: boolean\n}> = ({ title, content, isLong }) => {\n const [isExpanded, setIsExpanded] = useState(!isLong)\n\n const toggleExpand = () => {\n setIsExpanded(prev => !prev)\n }\n\n return (\n <div className=\"overflow-hidden rounded-lg border border-blue-200 bg-blue-50\">\n {/* \u6807\u9898 */}\n <div className=\"flex items-center gap-2 border-b border-blue-200 bg-blue-100 px-3 py-2\">\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 className=\"shrink-0 text-blue-700\"\n >\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n <polyline points=\"14 2 14 8 20 8\" />\n <line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\" />\n <line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\" />\n <polyline points=\"10 9 9 9 8 9\" />\n </svg>\n <h3 className=\"text-sm font-semibold text-blue-900\">{title}</h3>\n </div>\n\n {/* \u5185\u5BB9 */}\n <div className=\"px-3 py-2\">\n <div\n className={`\n text-sm leading-relaxed text-blue-900\n ${!isExpanded && isLong ? 'line-clamp-3' : ''}\n `}\n >\n <ReactMarkdown\n components={{\n p: ({ node, ...props }) => <p {...props} className=\"mb-2 last:mb-0\" />,\n ul: ({ node, ...props }) => <ul {...props} className=\"mb-2 ml-4 list-disc\" />,\n ol: ({ node, ...props }) => <ol {...props} className=\"mb-2 ml-4 list-decimal\" />,\n li: ({ node, ...props }) => <li {...props} className=\"mb-1\" />,\n strong: ({ node, ...props }) => <strong {...props} className=\"font-semibold\" />,\n }}\n >\n {content}\n </ReactMarkdown>\n </div>\n\n {/* \u5C55\u5F00/\u6536\u8D77\u6309\u94AE\uFF08\u4EC5\u957F\u5185\u5BB9\u663E\u793A\uFF09 */}\n {isLong && (\n <button\n type=\"button\"\n onClick={toggleExpand}\n className=\"mt-2 flex items-center gap-1 text-xs font-medium text-blue-700 hover:text-blue-800\"\n >\n <span>{isExpanded ? '\u6536\u8D77' : '\u5C55\u5F00'}</span>\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={`transition-transform ${isExpanded ? 'rotate-180' : ''}`}\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n </button>\n )}\n </div>\n </div>\n )\n}\n"],
5
- "mappings": "AA4DW,cAAAA,EAsBH,QAAAC,MAtBG,oBAtDX,OAAgB,YAAAC,MAAgB,QAChC,OAAOC,MAAmB,iBAyCnB,MAAMC,EAA+B,CAC1C,OAAQ,CAACC,EAASC,EAAQC,IAAa,CACrC,MAAMC,EAAgBH,EAChB,CAAE,MAAAI,EAAO,QAASC,CAAW,EAAIF,EAAc,KAErD,GAAI,CAACC,GAAS,CAACC,EACb,OAAO,KAIT,MAAMC,EAAgBD,EAAW,OAAS,IAE1C,OAAOV,EAACY,EAAA,CAAW,MAAOH,EAAO,QAASC,EAAY,OAAQC,EAAe,CAC/E,CACF,EAKMC,EAID,CAAC,CAAE,MAAAH,EAAO,QAAAJ,EAAS,OAAAQ,CAAO,IAAM,CACnC,KAAM,CAACC,EAAYC,CAAa,EAAIb,EAAS,CAACW,CAAM,EAMpD,OACEZ,EAAC,OAAI,UAAU,+DAEb,UAAAA,EAAC,OAAI,UAAU,yEACb,UAAAA,EAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QACf,UAAU,yBAEV,UAAAD,EAAC,QAAK,EAAE,6DAA6D,EACrEA,EAAC,YAAS,OAAO,iBAAiB,EAClCA,EAAC,QAAK,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,EACrCA,EAAC,QAAK,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,EACrCA,EAAC,YAAS,OAAO,eAAe,GAClC,EACAA,EAAC,MAAG,UAAU,sCAAuC,SAAAS,EAAM,GAC7D,EAGAR,EAAC,OAAI,UAAU,YACb,UAAAD,EAAC,OACC,UAAW;AAAA;AAAA,cAEP,CAACc,GAAcD,EAAS,eAAiB,EAAE;AAAA,YAG/C,SAAAb,EAACG,EAAA,CACC,WAAY,CACV,EAAG,CAAC,CAAE,KAAAa,EAAM,GAAGC,CAAM,IAAMjB,EAAC,KAAG,GAAGiB,EAAO,UAAU,iBAAiB,EACpE,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,IAAMjB,EAAC,MAAI,GAAGiB,EAAO,UAAU,sBAAsB,EAC3E,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,IAAMjB,EAAC,MAAI,GAAGiB,EAAO,UAAU,yBAAyB,EAC9E,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,IAAMjB,EAAC,MAAI,GAAGiB,EAAO,UAAU,OAAO,EAC5D,OAAQ,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,IAAMjB,EAAC,UAAQ,GAAGiB,EAAO,UAAU,gBAAgB,CAC/E,EAEC,SAAAZ,EACH,EACF,EAGCQ,GACCZ,EAAC,UACC,KAAK,SACL,QArDW,IAAM,CACzBc,EAAcG,GAAQ,CAACA,CAAI,CAC7B,EAoDU,UAAU,qFAEV,UAAAlB,EAAC,QAAM,SAAAc,EAAa,eAAO,eAAK,EAChCd,EAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QACf,UAAW,wBAAwBc,EAAa,aAAe,EAAE,GAEjE,SAAAd,EAAC,YAAS,OAAO,iBAAiB,EACpC,GACF,GAEJ,GACF,CAEJ",
4
+ "sourcesContent": ["/**\n * \u653F\u7B56\u8BF4\u660E\u6E32\u67D3\u5668\n * \u663E\u793A\u9000\u8D27\u3001\u4FDD\u4FEE\u7B49\u653F\u7B56\u4FE1\u606F\n * \u57FA\u4E8E specs/livechat-widget/data-model.md \u7684\u653F\u7B56\u6570\u636E\u6A21\u578B\n */\n\nimport React, { useState } from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport type { MessageRenderer, PolicyContent } from '../../types'\n\n/**\n * \u653F\u7B56\u8BF4\u660E\u6E32\u67D3\u5668\n *\n * \u529F\u80FD\uFF1A\n * - \u663E\u793A\u653F\u7B56\u6807\u9898\u548C\u5185\u5BB9\n * - \u652F\u6301 Markdown \u683C\u5F0F\n * - \u53EF\u6298\u53E0/\u5C55\u5F00\uFF08\u5185\u5BB9\u8F83\u957F\u65F6\uFF09\n *\n * \u653F\u7B56\u7C7B\u578B\uFF1A\n * - \u9000\u8D27\u653F\u7B56\n * - \u4FDD\u4FEE\u653F\u7B56\n * - \u8FD0\u8D39\u653F\u7B56\n * - \u9690\u79C1\u653F\u7B56\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\u2510\n * \u2502 \uD83D\uDCCB \u653F\u7B56\u6807\u9898 \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\u2524\n * \u2502 \u653F\u7B56\u5185\u5BB9... \u2502\n * \u2502 (\u652F\u6301 Markdown) \u2502\n * \u2502 \u2502\n * \u2502 [\u5C55\u5F00/\u6536\u8D77] \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\u2518\n * ```\n *\n * @example\n * ```tsx\n * const content: PolicyContent = {\n * type: 'policy',\n * data: {\n * title: '\u9000\u8D27\u653F\u7B56',\n * content: '\u6211\u4EEC\u63D0\u4F9B 30 \u5929\u65E0\u7406\u7531\u9000\u8D27...'\n * }\n * }\n * <PolicyBlock.render(content, false, false) />\n * ```\n */\nexport const PolicyBlock: MessageRenderer = {\n render: (content, isUser, isSystem) => {\n const policyContent = content as PolicyContent\n const { title, content: policyText } = policyContent.data\n\n if (!title || !policyText) {\n return null\n }\n\n // \u5224\u65AD\u5185\u5BB9\u662F\u5426\u8F83\u957F\uFF08\u8D85\u8FC7 200 \u5B57\u7B26\uFF09\n const isLongContent = policyText.length > 200\n\n return <PolicyCard title={title} content={policyText} isLong={isLongContent} />\n },\n}\n\n/**\n * \u653F\u7B56\u5361\u7247\u7EC4\u4EF6\uFF08\u652F\u6301\u6298\u53E0\uFF09\n */\nconst PolicyCard: React.FC<{\n title: string\n content: string\n isLong: boolean\n}> = ({ title, content, isLong }) => {\n const [isExpanded, setIsExpanded] = useState(!isLong)\n\n const toggleExpand = () => {\n setIsExpanded(prev => !prev)\n }\n\n return (\n <div className=\"overflow-hidden rounded-lg border border-blue-200 bg-blue-50\">\n {/* \u6807\u9898 */}\n <div className=\"flex items-center gap-2 border-b border-blue-200 bg-blue-100 px-3 py-2\">\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 className=\"shrink-0 text-blue-700\"\n >\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n <polyline points=\"14 2 14 8 20 8\" />\n <line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\" />\n <line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\" />\n <polyline points=\"10 9 9 9 8 9\" />\n </svg>\n <h3 className=\"text-sm font-semibold text-blue-900\">{title}</h3>\n </div>\n\n {/* \u5185\u5BB9 */}\n <div className=\"px-3 py-2\">\n <div\n className={`\n text-sm leading-relaxed text-blue-900\n ${!isExpanded && isLong ? 'line-clamp-3' : ''}\n `}\n >\n <ReactMarkdown\n components={{\n p: ({ node, ...props }) => <p {...props} className=\"mb-2 last:mb-0\" />,\n ul: ({ node, ...props }) => <ul {...props} className=\"mb-2 ml-4 list-disc\" />,\n ol: ({ node, ...props }) => <ol {...props} className=\"mb-2 ml-4 list-decimal\" />,\n li: ({ node, ...props }) => <li {...props} className=\"mb-1\" />,\n strong: ({ node, ...props }) => <strong {...props} className=\"font-bold\" />,\n }}\n >\n {content}\n </ReactMarkdown>\n </div>\n\n {/* \u5C55\u5F00/\u6536\u8D77\u6309\u94AE\uFF08\u4EC5\u957F\u5185\u5BB9\u663E\u793A\uFF09 */}\n {isLong && (\n <button\n type=\"button\"\n onClick={toggleExpand}\n className=\"mt-2 flex items-center gap-1 text-xs font-medium text-blue-700 hover:text-blue-800\"\n >\n <span>{isExpanded ? '\u6536\u8D77' : '\u5C55\u5F00'}</span>\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={`transition-transform ${isExpanded ? 'rotate-180' : ''}`}\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n </button>\n )}\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "AA4DW,cAAAA,EAsBH,QAAAC,MAtBG,oBAtDX,OAAgB,YAAAC,MAAgB,QAChC,OAAOC,MAAmB,iBAyCnB,MAAMC,EAA+B,CAC1C,OAAQ,CAACC,EAASC,EAAQC,IAAa,CACrC,MAAMC,EAAgBH,EAChB,CAAE,MAAAI,EAAO,QAASC,CAAW,EAAIF,EAAc,KAErD,GAAI,CAACC,GAAS,CAACC,EACb,OAAO,KAIT,MAAMC,EAAgBD,EAAW,OAAS,IAE1C,OAAOV,EAACY,EAAA,CAAW,MAAOH,EAAO,QAASC,EAAY,OAAQC,EAAe,CAC/E,CACF,EAKMC,EAID,CAAC,CAAE,MAAAH,EAAO,QAAAJ,EAAS,OAAAQ,CAAO,IAAM,CACnC,KAAM,CAACC,EAAYC,CAAa,EAAIb,EAAS,CAACW,CAAM,EAMpD,OACEZ,EAAC,OAAI,UAAU,+DAEb,UAAAA,EAAC,OAAI,UAAU,yEACb,UAAAA,EAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QACf,UAAU,yBAEV,UAAAD,EAAC,QAAK,EAAE,6DAA6D,EACrEA,EAAC,YAAS,OAAO,iBAAiB,EAClCA,EAAC,QAAK,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,EACrCA,EAAC,QAAK,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,EACrCA,EAAC,YAAS,OAAO,eAAe,GAClC,EACAA,EAAC,MAAG,UAAU,sCAAuC,SAAAS,EAAM,GAC7D,EAGAR,EAAC,OAAI,UAAU,YACb,UAAAD,EAAC,OACC,UAAW;AAAA;AAAA,cAEP,CAACc,GAAcD,EAAS,eAAiB,EAAE;AAAA,YAG/C,SAAAb,EAACG,EAAA,CACC,WAAY,CACV,EAAG,CAAC,CAAE,KAAAa,EAAM,GAAGC,CAAM,IAAMjB,EAAC,KAAG,GAAGiB,EAAO,UAAU,iBAAiB,EACpE,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,IAAMjB,EAAC,MAAI,GAAGiB,EAAO,UAAU,sBAAsB,EAC3E,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,IAAMjB,EAAC,MAAI,GAAGiB,EAAO,UAAU,yBAAyB,EAC9E,GAAI,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,IAAMjB,EAAC,MAAI,GAAGiB,EAAO,UAAU,OAAO,EAC5D,OAAQ,CAAC,CAAE,KAAAD,EAAM,GAAGC,CAAM,IAAMjB,EAAC,UAAQ,GAAGiB,EAAO,UAAU,YAAY,CAC3E,EAEC,SAAAZ,EACH,EACF,EAGCQ,GACCZ,EAAC,UACC,KAAK,SACL,QArDW,IAAM,CACzBc,EAAcG,GAAQ,CAACA,CAAI,CAC7B,EAoDU,UAAU,qFAEV,UAAAlB,EAAC,QAAM,SAAAc,EAAa,eAAO,eAAK,EAChCd,EAAC,OACC,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,IACZ,cAAc,QACd,eAAe,QACf,UAAW,wBAAwBc,EAAa,aAAe,EAAE,GAEjE,SAAAd,EAAC,YAAS,OAAO,iBAAiB,EACpC,GACF,GAEJ,GACF,CAEJ",
6
6
  "names": ["jsx", "jsxs", "useState", "ReactMarkdown", "PolicyBlock", "content", "isUser", "isSystem", "policyContent", "title", "policyText", "isLongContent", "PolicyCard", "isLong", "isExpanded", "setIsExpanded", "node", "props", "prev"]
7
7
  }