@anker-in/campaign-ui 0.3.2 → 0.3.4

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 (229) hide show
  1. package/dist/cjs/components/LiveChatWidget/LiveChatWidget.d.ts +21 -1
  2. package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js +1 -1
  3. package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
  4. package/dist/cjs/components/LiveChatWidget/api/chat.d.ts +23 -2
  5. package/dist/cjs/components/LiveChatWidget/api/chat.js +2 -2
  6. package/dist/cjs/components/LiveChatWidget/api/chat.js.map +3 -3
  7. package/dist/cjs/components/LiveChatWidget/components/ChatHeader.js +1 -1
  8. package/dist/cjs/components/LiveChatWidget/components/ChatHeader.js.map +2 -2
  9. package/dist/cjs/components/LiveChatWidget/components/ChatInput.d.ts +5 -0
  10. package/dist/cjs/components/LiveChatWidget/components/ChatInput.js +1 -1
  11. package/dist/cjs/components/LiveChatWidget/components/ChatInput.js.map +3 -3
  12. package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js +2 -2
  13. package/dist/cjs/components/LiveChatWidget/components/ChatMessage.js.map +3 -3
  14. package/dist/cjs/components/LiveChatWidget/components/ChatWindow.d.ts +5 -0
  15. package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js +1 -1
  16. package/dist/cjs/components/LiveChatWidget/components/ChatWindow.js.map +3 -3
  17. package/dist/cjs/components/LiveChatWidget/components/ComplianceDialog.d.ts +51 -0
  18. package/dist/cjs/components/LiveChatWidget/components/ComplianceDialog.js +33 -0
  19. package/dist/cjs/components/LiveChatWidget/components/ComplianceDialog.js.map +7 -0
  20. package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
  21. package/dist/cjs/components/LiveChatWidget/components/MessageContent/CartCard.js.map +3 -3
  22. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ErrorBlock.js +1 -1
  23. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ErrorBlock.js.map +2 -2
  24. package/dist/cjs/components/LiveChatWidget/components/MessageContent/FAQList.js +1 -1
  25. package/dist/cjs/components/LiveChatWidget/components/MessageContent/FAQList.js.map +3 -3
  26. package/dist/cjs/components/LiveChatWidget/components/MessageContent/PolicyBlock.js +2 -2
  27. package/dist/cjs/components/LiveChatWidget/components/MessageContent/PolicyBlock.js.map +3 -3
  28. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +17 -24
  29. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -4
  30. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
  31. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.d.ts +7 -1
  32. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
  33. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
  34. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
  35. package/dist/cjs/components/LiveChatWidget/components/MessageContent/ProductList.js.map +3 -3
  36. package/dist/cjs/components/LiveChatWidget/components/MessageContent/PromotionList.d.ts +4 -1
  37. package/dist/cjs/components/LiveChatWidget/components/MessageContent/PromotionList.js +1 -1
  38. package/dist/cjs/components/LiveChatWidget/components/MessageContent/PromotionList.js.map +3 -3
  39. package/dist/cjs/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
  40. package/dist/cjs/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
  41. package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
  42. package/dist/cjs/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +3 -3
  43. package/dist/cjs/components/LiveChatWidget/components/MessageContent.js +1 -1
  44. package/dist/cjs/components/LiveChatWidget/components/MessageContent.js.map +2 -2
  45. package/dist/cjs/components/LiveChatWidget/components/MessageList.js +2 -2
  46. package/dist/cjs/components/LiveChatWidget/components/MessageList.js.map +2 -2
  47. package/dist/cjs/components/LiveChatWidget/constants.d.ts +5 -0
  48. package/dist/cjs/components/LiveChatWidget/constants.js +1 -1
  49. package/dist/cjs/components/LiveChatWidget/constants.js.map +3 -3
  50. package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.d.ts +9 -0
  51. package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.js +1 -1
  52. package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.js.map +3 -3
  53. package/dist/cjs/components/LiveChatWidget/hooks/useChatState.d.ts +35 -2
  54. package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js +1 -1
  55. package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
  56. package/dist/cjs/components/LiveChatWidget/index.d.ts +1 -1
  57. package/dist/cjs/components/LiveChatWidget/index.js +1 -1
  58. package/dist/cjs/components/LiveChatWidget/index.js.map +2 -2
  59. package/dist/cjs/components/LiveChatWidget/types.d.ts +212 -3
  60. package/dist/cjs/components/LiveChatWidget/types.js +1 -1
  61. package/dist/cjs/components/LiveChatWidget/types.js.map +1 -1
  62. package/dist/cjs/components/LiveChatWidget/utils/fetcher.d.ts +42 -0
  63. package/dist/cjs/components/LiveChatWidget/utils/fetcher.js +2 -0
  64. package/dist/cjs/components/LiveChatWidget/utils/fetcher.js.map +7 -0
  65. package/dist/cjs/components/chat/markdown.js +1 -1
  66. package/dist/cjs/components/chat/markdown.js.map +2 -2
  67. package/dist/cjs/components/credits/creditsBanner/index.js +2 -2
  68. package/dist/cjs/components/credits/creditsBanner/index.js.map +2 -2
  69. package/dist/cjs/components/index.d.ts +2 -0
  70. package/dist/cjs/components/index.js +1 -1
  71. package/dist/cjs/components/index.js.map +3 -3
  72. package/dist/cjs/stories/LiveChatWidget.stories.d.ts +1 -79
  73. package/dist/cjs/stories/LiveChatWidget.stories.js +8 -47
  74. package/dist/cjs/stories/LiveChatWidget.stories.js.map +3 -3
  75. package/dist/esm/components/LiveChatWidget/LiveChatWidget.d.ts +21 -1
  76. package/dist/esm/components/LiveChatWidget/LiveChatWidget.js +1 -1
  77. package/dist/esm/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
  78. package/dist/esm/components/LiveChatWidget/api/chat.d.ts +23 -2
  79. package/dist/esm/components/LiveChatWidget/api/chat.js +2 -2
  80. package/dist/esm/components/LiveChatWidget/api/chat.js.map +3 -3
  81. package/dist/esm/components/LiveChatWidget/components/ChatHeader.js +1 -1
  82. package/dist/esm/components/LiveChatWidget/components/ChatHeader.js.map +2 -2
  83. package/dist/esm/components/LiveChatWidget/components/ChatInput.d.ts +5 -0
  84. package/dist/esm/components/LiveChatWidget/components/ChatInput.js +1 -1
  85. package/dist/esm/components/LiveChatWidget/components/ChatInput.js.map +3 -3
  86. package/dist/esm/components/LiveChatWidget/components/ChatMessage.js +2 -2
  87. package/dist/esm/components/LiveChatWidget/components/ChatMessage.js.map +3 -3
  88. package/dist/esm/components/LiveChatWidget/components/ChatWindow.d.ts +5 -0
  89. package/dist/esm/components/LiveChatWidget/components/ChatWindow.js +1 -1
  90. package/dist/esm/components/LiveChatWidget/components/ChatWindow.js.map +3 -3
  91. package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.d.ts +51 -0
  92. package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.js +33 -0
  93. package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.js.map +7 -0
  94. package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
  95. package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js.map +3 -3
  96. package/dist/esm/components/LiveChatWidget/components/MessageContent/ErrorBlock.js +1 -1
  97. package/dist/esm/components/LiveChatWidget/components/MessageContent/ErrorBlock.js.map +2 -2
  98. package/dist/esm/components/LiveChatWidget/components/MessageContent/FAQList.js +1 -1
  99. package/dist/esm/components/LiveChatWidget/components/MessageContent/FAQList.js.map +3 -3
  100. package/dist/esm/components/LiveChatWidget/components/MessageContent/PolicyBlock.js +2 -2
  101. package/dist/esm/components/LiveChatWidget/components/MessageContent/PolicyBlock.js.map +3 -3
  102. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +17 -24
  103. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -4
  104. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
  105. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.d.ts +7 -1
  106. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
  107. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
  108. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
  109. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js.map +3 -3
  110. package/dist/esm/components/LiveChatWidget/components/MessageContent/PromotionList.d.ts +4 -1
  111. package/dist/esm/components/LiveChatWidget/components/MessageContent/PromotionList.js +1 -1
  112. package/dist/esm/components/LiveChatWidget/components/MessageContent/PromotionList.js.map +3 -3
  113. package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
  114. package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
  115. package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
  116. package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +3 -3
  117. package/dist/esm/components/LiveChatWidget/components/MessageContent.js +1 -1
  118. package/dist/esm/components/LiveChatWidget/components/MessageContent.js.map +2 -2
  119. package/dist/esm/components/LiveChatWidget/components/MessageList.js +2 -2
  120. package/dist/esm/components/LiveChatWidget/components/MessageList.js.map +2 -2
  121. package/dist/esm/components/LiveChatWidget/constants.d.ts +5 -0
  122. package/dist/esm/components/LiveChatWidget/constants.js +1 -1
  123. package/dist/esm/components/LiveChatWidget/constants.js.map +3 -3
  124. package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.d.ts +9 -0
  125. package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.js +1 -1
  126. package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.js.map +3 -3
  127. package/dist/esm/components/LiveChatWidget/hooks/useChatState.d.ts +35 -2
  128. package/dist/esm/components/LiveChatWidget/hooks/useChatState.js +1 -1
  129. package/dist/esm/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
  130. package/dist/esm/components/LiveChatWidget/index.d.ts +1 -1
  131. package/dist/esm/components/LiveChatWidget/index.js +1 -1
  132. package/dist/esm/components/LiveChatWidget/index.js.map +2 -2
  133. package/dist/esm/components/LiveChatWidget/types.d.ts +212 -3
  134. package/dist/esm/components/LiveChatWidget/utils/fetcher.d.ts +42 -0
  135. package/dist/esm/components/LiveChatWidget/utils/fetcher.js +2 -0
  136. package/dist/esm/components/LiveChatWidget/utils/fetcher.js.map +7 -0
  137. package/dist/esm/components/chat/markdown.js +1 -1
  138. package/dist/esm/components/chat/markdown.js.map +2 -2
  139. package/dist/esm/components/credits/creditsBanner/index.js +2 -2
  140. package/dist/esm/components/credits/creditsBanner/index.js.map +2 -2
  141. package/dist/esm/components/index.d.ts +2 -0
  142. package/dist/esm/components/index.js +1 -1
  143. package/dist/esm/components/index.js.map +3 -3
  144. package/dist/esm/stories/LiveChatWidget.stories.d.ts +1 -79
  145. package/dist/esm/stories/LiveChatWidget.stories.js +8 -47
  146. package/dist/esm/stories/LiveChatWidget.stories.js.map +3 -3
  147. package/dist/index.d.mts +1305 -0
  148. package/dist/index.d.ts +1305 -0
  149. package/dist/index.js +26656 -0
  150. package/dist/index.js.map +1 -0
  151. package/dist/index.mjs +26641 -0
  152. package/dist/index.mjs.map +1 -0
  153. package/package.json +8 -1
  154. package/src/components/LiveChatWidget/LiveChatWidget.tsx +887 -0
  155. package/src/components/LiveChatWidget/api/chat.ts +175 -0
  156. package/src/components/LiveChatWidget/components/ChatBubble.tsx +152 -0
  157. package/src/components/LiveChatWidget/components/ChatHeader.tsx +150 -0
  158. package/src/components/LiveChatWidget/components/ChatInput.tsx +253 -0
  159. package/src/components/LiveChatWidget/components/ChatMessage.tsx +190 -0
  160. package/src/components/LiveChatWidget/components/ChatWindow.tsx +363 -0
  161. package/src/components/LiveChatWidget/components/ComplianceDialog.tsx +216 -0
  162. package/src/components/LiveChatWidget/components/MessageContent/CartCard.tsx +202 -0
  163. package/src/components/LiveChatWidget/components/MessageContent/ErrorBlock.tsx +75 -0
  164. package/src/components/LiveChatWidget/components/MessageContent/FAQList.tsx +128 -0
  165. package/src/components/LiveChatWidget/components/MessageContent/PolicyBlock.tsx +152 -0
  166. package/src/components/LiveChatWidget/components/MessageContent/ProductCard.tsx +227 -0
  167. package/src/components/LiveChatWidget/components/MessageContent/ProductComparison.tsx +377 -0
  168. package/src/components/LiveChatWidget/components/MessageContent/ProductList.tsx +293 -0
  169. package/src/components/LiveChatWidget/components/MessageContent/PromotionList.tsx +170 -0
  170. package/src/components/LiveChatWidget/components/MessageContent/QuickReplies.tsx +91 -0
  171. package/src/components/LiveChatWidget/components/MessageContent/TextBlock.tsx +110 -0
  172. package/src/components/LiveChatWidget/components/MessageContent/ThinkingBlock.tsx +53 -0
  173. package/src/components/LiveChatWidget/components/MessageContent/index.ts +16 -0
  174. package/src/components/LiveChatWidget/components/MessageContent.tsx +113 -0
  175. package/src/components/LiveChatWidget/components/MessageList.tsx +261 -0
  176. package/src/components/LiveChatWidget/components/ScrollAnchor.tsx +75 -0
  177. package/src/components/LiveChatWidget/constants.ts +36 -0
  178. package/src/components/LiveChatWidget/hooks/useChatAPI.ts +146 -0
  179. package/src/components/LiveChatWidget/hooks/useChatState.ts +1090 -0
  180. package/src/components/LiveChatWidget/hooks/useSession.ts +123 -0
  181. package/src/components/LiveChatWidget/index.tsx +63 -0
  182. package/src/components/LiveChatWidget/types.ts +1011 -0
  183. package/src/components/LiveChatWidget/utils/cartTransformers.ts +72 -0
  184. package/src/components/LiveChatWidget/utils/fetcher.ts +131 -0
  185. package/src/components/LiveChatWidget/utils/messageRenderers.ts +120 -0
  186. package/src/components/LiveChatWidget/utils/productTransformers.ts +149 -0
  187. package/src/components/LiveChatWidget/utils/userId.ts +140 -0
  188. package/src/components/LiveChatWidget/utils/validation.ts +99 -0
  189. package/src/components/chat/markdown.tsx +1 -1
  190. package/src/components/credits/creditsBanner/index.tsx +5 -5
  191. package/src/components/index.ts +23 -0
  192. package/src/stories/LiveChatWidget.stories.tsx +322 -0
  193. package/src/styles/livechat.css +317 -0
  194. package/dist/cjs/components/credits/context/hooks/useFunctionMemberPrice.d.ts +0 -7
  195. package/dist/cjs/components/credits/context/hooks/useFunctionMemberPrice.js +0 -2
  196. package/dist/cjs/components/credits/context/hooks/useFunctionMemberPrice.js.map +0 -7
  197. package/dist/cjs/components/credits/context/utils/atobID.d.ts +0 -1
  198. package/dist/cjs/components/credits/context/utils/atobID.js +0 -2
  199. package/dist/cjs/components/credits/context/utils/atobID.js.map +0 -7
  200. package/dist/cjs/components/credits/context/utils/functionDiscountCalculate.d.ts +0 -5
  201. package/dist/cjs/components/credits/context/utils/functionDiscountCalculate.js +0 -2
  202. package/dist/cjs/components/credits/context/utils/functionDiscountCalculate.js.map +0 -7
  203. package/dist/cjs/components/credits/context/utils/getFunctionMemberPrice.d.ts +0 -8
  204. package/dist/cjs/components/credits/context/utils/getFunctionMemberPrice.js +0 -2
  205. package/dist/cjs/components/credits/context/utils/getFunctionMemberPrice.js.map +0 -7
  206. package/dist/cjs/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.d.ts +0 -9
  207. package/dist/cjs/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js +0 -2
  208. package/dist/cjs/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js.map +0 -7
  209. package/dist/cjs/components/credits/context/utils/variantGetCoupon.d.ts +0 -6
  210. package/dist/cjs/components/credits/context/utils/variantGetCoupon.js +0 -2
  211. package/dist/cjs/components/credits/context/utils/variantGetCoupon.js.map +0 -7
  212. package/dist/esm/components/credits/context/hooks/useFunctionMemberPrice.d.ts +0 -7
  213. package/dist/esm/components/credits/context/hooks/useFunctionMemberPrice.js +0 -2
  214. package/dist/esm/components/credits/context/hooks/useFunctionMemberPrice.js.map +0 -7
  215. package/dist/esm/components/credits/context/utils/atobID.d.ts +0 -1
  216. package/dist/esm/components/credits/context/utils/atobID.js +0 -2
  217. package/dist/esm/components/credits/context/utils/atobID.js.map +0 -7
  218. package/dist/esm/components/credits/context/utils/functionDiscountCalculate.d.ts +0 -5
  219. package/dist/esm/components/credits/context/utils/functionDiscountCalculate.js +0 -2
  220. package/dist/esm/components/credits/context/utils/functionDiscountCalculate.js.map +0 -7
  221. package/dist/esm/components/credits/context/utils/getFunctionMemberPrice.d.ts +0 -8
  222. package/dist/esm/components/credits/context/utils/getFunctionMemberPrice.js +0 -2
  223. package/dist/esm/components/credits/context/utils/getFunctionMemberPrice.js.map +0 -7
  224. package/dist/esm/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.d.ts +0 -9
  225. package/dist/esm/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js +0 -2
  226. package/dist/esm/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js.map +0 -7
  227. package/dist/esm/components/credits/context/utils/variantGetCoupon.d.ts +0 -6
  228. package/dist/esm/components/credits/context/utils/variantGetCoupon.js +0 -2
  229. package/dist/esm/components/credits/context/utils/variantGetCoupon.js.map +0 -7
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 思考状态消息渲染器
3
+ * 显示 AI 助手正在思考的动画
4
+ * 基于 specs/livechat-widget/plan.md 的思考状态设计
5
+ */
6
+
7
+ import React from 'react'
8
+ import type { MessageRenderer, ThinkingContent } from '../../types'
9
+
10
+ /**
11
+ * 思考状态渲染器
12
+ *
13
+ * 功能:
14
+ * - 显示"正在思考..."动画
15
+ * - 三个跳动的圆点
16
+ * - 黄色背景提示
17
+ *
18
+ * 动画效果:
19
+ * - 使用 CSS 动画(livechat.css 中定义)
20
+ * - 三个圆点依次跳动
21
+ * - 循环播放
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * const content: ThinkingContent = {
26
+ * type: 'thinking',
27
+ * data: {
28
+ * status: '正在查询商品信息...'
29
+ * }
30
+ * }
31
+ * <ThinkingBlock.render(content, false, false) />
32
+ * ```
33
+ */
34
+ export const ThinkingBlock: MessageRenderer = {
35
+ render: (content, isUser, isSystem) => {
36
+ const thinkingContent = content as ThinkingContent
37
+ const status = thinkingContent.data.status || ''
38
+
39
+ return (
40
+ <div className="flex items-center gap-2 py-1">
41
+ {/* 思考动画 - 三个跳动的点 */}
42
+ <div className="livechat-thinking-dots flex gap-1">
43
+ <span className="size-2 rounded-full" style={{ backgroundColor: 'rgba(0, 93, 142, 1)' }} />
44
+ <span className="size-2 rounded-full" style={{ backgroundColor: 'rgba(0, 93, 142, 0.6)' }} />
45
+ <span className="size-2 rounded-full" style={{ backgroundColor: 'rgba(0, 93, 142, 0.2)' }} />
46
+ </div>
47
+
48
+ {/* 状态文本(可选) */}
49
+ {/* {status && <span className="text-sm text-gray-500">{status}</span>} */}
50
+ </div>
51
+ )
52
+ },
53
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 消息内容渲染器统一导出
3
+ */
4
+
5
+ export { MessageContent } from '../MessageContent'
6
+ export { TextBlock } from './TextBlock'
7
+ export { ProductCard } from './ProductCard'
8
+ export { ProductList } from './ProductList'
9
+ export { ProductComparison, ProductComparisonRenderer } from './ProductComparison'
10
+ export { PolicyBlock } from './PolicyBlock'
11
+ export { QuickReplies, createQuickRepliesRenderer } from './QuickReplies'
12
+ export { ThinkingBlock } from './ThinkingBlock'
13
+ export { ErrorBlock } from './ErrorBlock'
14
+ export { FAQList, FAQListRenderer } from './FAQList.js'
15
+ export { PromotionList, PromotionListRenderer } from './PromotionList.js'
16
+ export { CartCard } from './CartCard.js'
@@ -0,0 +1,113 @@
1
+ /**
2
+ * 消息内容容器组件
3
+ * 根据消息内容类型分发到对应的渲染器
4
+ * 基于 specs/livechat-widget/plan.md 的消息内容渲染设计
5
+ */
6
+
7
+ import React from 'react'
8
+ import type { MessageContent as MessageContentType, MessageRenderer } from '../types'
9
+ import { MessageRendererRegistry } from '../utils/messageRenderers'
10
+
11
+ export interface MessageContentProps {
12
+ /**
13
+ * 消息内容
14
+ */
15
+ content: MessageContentType
16
+
17
+ /**
18
+ * 是否为用户消息
19
+ */
20
+ isUser: boolean
21
+
22
+ /**
23
+ * 是否为系统消息
24
+ */
25
+ isSystem: boolean
26
+
27
+ /**
28
+ * 自定义渲染器注册表
29
+ */
30
+ rendererRegistry?: MessageRendererRegistry
31
+
32
+ /**
33
+ * 默认渲染器(当找不到对应渲染器时使用)
34
+ */
35
+ defaultRenderer?: MessageRenderer
36
+
37
+ /**
38
+ * 自定义样式类名
39
+ */
40
+ className?: string
41
+
42
+ /**
43
+ * 商品添加到购物车回调
44
+ */
45
+ onAddToCart?: (product: any) => void
46
+ }
47
+
48
+ /**
49
+ * 默认渲染器 - 显示未知类型提示
50
+ */
51
+ const defaultFallbackRenderer: MessageRenderer = {
52
+ render: content => <div className="text-sm italic text-gray-500">未知消息类型: {content.type}</div>,
53
+ }
54
+
55
+ /**
56
+ * 消息内容容器组件
57
+ *
58
+ * 功能:
59
+ * - 根据 content.type 分发到对应的渲染器
60
+ * - 支持自定义渲染器注册表
61
+ * - 提供默认兜底渲染器
62
+ *
63
+ * 渲染流程:
64
+ * 1. 检查自定义渲染器注册表是否有对应类型
65
+ * 2. 如果有,使用自定义渲染器
66
+ * 3. 如果没有,使用默认渲染器
67
+ * 4. 如果默认渲染器也没有,显示未知类型提示
68
+ *
69
+ * @example
70
+ * ```tsx
71
+ * <MessageContent
72
+ * content={content}
73
+ * isUser={message.role === 'user'}
74
+ * isSystem={message.role === 'system'}
75
+ * rendererRegistry={customRegistry}
76
+ * />
77
+ * ```
78
+ */
79
+ export const MessageContent: React.FC<MessageContentProps> = ({
80
+ content,
81
+ isUser,
82
+ isSystem,
83
+ rendererRegistry,
84
+ defaultRenderer,
85
+ className = '',
86
+ onAddToCart,
87
+ }) => {
88
+ // 动态注入 onAddToCart 到 product_card、product_list 和 product_comparison 类型的内容中
89
+ let processedContent = content
90
+ if (
91
+ (content.type === 'product_card' || content.type === 'product_list' || content.type === 'product_comparison') &&
92
+ onAddToCart
93
+ ) {
94
+ processedContent = {
95
+ ...content,
96
+ data: {
97
+ ...(content as any).data,
98
+ onAddToCart,
99
+ },
100
+ }
101
+ }
102
+
103
+ // 尝试从注册表获取渲染器
104
+ const customRenderer = rendererRegistry?.get(processedContent.type)
105
+
106
+ // 确定最终使用的渲染器
107
+ const renderer = customRenderer || defaultRenderer || defaultFallbackRenderer
108
+
109
+ // 渲染内容
110
+ const rendered = renderer.render(processedContent, isUser, isSystem)
111
+
112
+ return <div className={className}>{rendered}</div>
113
+ }
@@ -0,0 +1,261 @@
1
+ /**
2
+ * 消息列表组件
3
+ * 显示所有聊天消息,支持滚动
4
+ * 基于 specs/livechat-widget/plan.md 的消息列表设计
5
+ */
6
+
7
+ import React, { useRef, useEffect, useState, useCallback } from 'react'
8
+ import type { Message, MessageRenderer } from '../types'
9
+ import { ChatMessage } from './ChatMessage'
10
+ import { ScrollAnchor } from './ScrollAnchor'
11
+ import { MessageRendererRegistry } from '../utils/messageRenderers'
12
+
13
+ export interface MessageListProps {
14
+ /**
15
+ * 消息列表
16
+ */
17
+ messages: Message[]
18
+
19
+ /**
20
+ * 自定义渲染器注册表
21
+ */
22
+ rendererRegistry?: MessageRendererRegistry
23
+
24
+ /**
25
+ * 默认渲染器
26
+ */
27
+ defaultRenderer?: MessageRenderer
28
+
29
+ /**
30
+ * 是否显示时间戳
31
+ * @default true
32
+ */
33
+ showTimestamp?: boolean
34
+
35
+ /**
36
+ * 是否自动滚动到底部
37
+ * @default true
38
+ */
39
+ autoScroll?: boolean
40
+
41
+ /**
42
+ * 是否正在加载历史消息
43
+ * @default false
44
+ */
45
+ isLoadingHistory?: boolean
46
+
47
+ /**
48
+ * 空状态占位内容
49
+ */
50
+ emptyPlaceholder?: React.ReactNode
51
+
52
+ /**
53
+ * 自定义样式类名
54
+ */
55
+ className?: string
56
+
57
+ /**
58
+ * 商品添加到购物车回调
59
+ */
60
+ onAddToCart?: (product: any) => void
61
+ }
62
+
63
+ /**
64
+ * 默认空状态占位
65
+ */
66
+ const DefaultEmptyPlaceholder: React.FC = () => (
67
+ <div className="flex h-full items-center justify-center text-gray-400">
68
+ <p className="text-sm">No messages yet</p>
69
+ </div>
70
+ )
71
+
72
+ /**
73
+ * 加载指示器
74
+ */
75
+ const LoadingIndicator: React.FC = () => (
76
+ <div className="flex justify-center py-4">
77
+ <div className="flex gap-1">
78
+ <div className="size-2 animate-bounce rounded-full bg-gray-400" />
79
+ <div className="size-2 animate-bounce rounded-full bg-gray-400" style={{ animationDelay: '0.1s' }} />
80
+ <div className="size-2 animate-bounce rounded-full bg-gray-400" style={{ animationDelay: '0.2s' }} />
81
+ </div>
82
+ </div>
83
+ )
84
+
85
+ /**
86
+ * 消息列表组件
87
+ *
88
+ * 功能:
89
+ * - 显示所有消息(用户、助手、系统)
90
+ * - 自动滚动到最新消息
91
+ * - 加载历史消息时显示指示器
92
+ * - 空状态占位
93
+ *
94
+ * 样式:
95
+ * - 使用自定义滚动条样式(livechat.css)
96
+ * - 内容间距合理
97
+ * - 支持响应式布局
98
+ *
99
+ * @example
100
+ * ```tsx
101
+ * <MessageList
102
+ * messages={messages}
103
+ * rendererRegistry={customRegistry}
104
+ * autoScroll={true}
105
+ * isLoadingHistory={isLoading}
106
+ * />
107
+ * ```
108
+ */
109
+ export const MessageList: React.FC<MessageListProps> = ({
110
+ messages,
111
+ rendererRegistry,
112
+ defaultRenderer,
113
+ showTimestamp = true,
114
+ autoScroll = true,
115
+ isLoadingHistory = false,
116
+ emptyPlaceholder,
117
+ className = '',
118
+ onAddToCart,
119
+ }) => {
120
+ const listRef = useRef<HTMLDivElement>(null)
121
+ const [showScrollButton, setShowScrollButton] = useState(false)
122
+
123
+ // 检查是否接近底部
124
+ const isNearBottom = useCallback((threshold = 100) => {
125
+ const container = listRef.current
126
+ if (!container) return true
127
+
128
+ const { scrollTop, scrollHeight, clientHeight } = container
129
+ return scrollHeight - scrollTop - clientHeight < threshold
130
+ }, [])
131
+
132
+ // 平滑滚动到底部
133
+ const scrollToBottom = useCallback(() => {
134
+ const container = listRef.current
135
+ if (!container) return
136
+
137
+ container.scrollTo({
138
+ top: container.scrollHeight,
139
+ behavior: 'smooth',
140
+ })
141
+ }, [])
142
+
143
+ // 监听滚动事件,控制按钮显示
144
+ useEffect(() => {
145
+ const container = listRef.current
146
+ if (!container) return
147
+
148
+ const handleScroll = () => {
149
+ setShowScrollButton(!isNearBottom())
150
+ }
151
+
152
+ // 初始检查
153
+ handleScroll()
154
+
155
+ container.addEventListener('scroll', handleScroll, { passive: true })
156
+ return () => container.removeEventListener('scroll', handleScroll)
157
+ }, [isNearBottom])
158
+
159
+ // 当消息列表变化时,重新检查按钮状态(但不在自动滚动时)
160
+ useEffect(() => {
161
+ if (autoScroll) return // 自动滚动会在另一个 effect 中处理
162
+
163
+ const timeoutId = setTimeout(() => {
164
+ setShowScrollButton(!isNearBottom())
165
+ }, 150)
166
+
167
+ return () => clearTimeout(timeoutId)
168
+ }, [messages, autoScroll, isNearBottom])
169
+
170
+ // 监听消息列表变化,自动滚动
171
+ useEffect(() => {
172
+ if (!autoScroll || !listRef.current) return
173
+
174
+ // 延迟滚动以确保 DOM 已更新
175
+ const timeoutId = setTimeout(() => {
176
+ if (listRef.current) {
177
+ listRef.current.scrollTop = listRef.current.scrollHeight
178
+ // 自动滚动到底部后,隐藏按钮
179
+ setShowScrollButton(false)
180
+ }
181
+ }, 100)
182
+
183
+ return () => clearTimeout(timeoutId)
184
+ }, [messages, autoScroll])
185
+
186
+ // 空状态
187
+ if (messages.length === 0 && !isLoadingHistory) {
188
+ return (
189
+ <div className={`flex-1 overflow-hidden ${className}`}>{emptyPlaceholder || <DefaultEmptyPlaceholder />}</div>
190
+ )
191
+ }
192
+
193
+ return (
194
+ <div className="relative flex-1 overflow-hidden">
195
+ <div
196
+ ref={listRef}
197
+ className={`
198
+ livechat-message-list absolute inset-0 overflow-y-auto p-4
199
+ ${className}
200
+ `}
201
+ >
202
+ {/* 加载历史消息指示器 */}
203
+ {isLoadingHistory && <LoadingIndicator />}
204
+
205
+ {/* 消息列表 */}
206
+ <div className="flex flex-col gap-1">
207
+ {messages.map(message => (
208
+ <ChatMessage
209
+ key={message.id}
210
+ message={message}
211
+ rendererRegistry={rendererRegistry}
212
+ defaultRenderer={defaultRenderer}
213
+ showTimestamp={showTimestamp}
214
+ onAddToCart={onAddToCart}
215
+ />
216
+ ))}
217
+ </div>
218
+
219
+ {/* 滚动锚点 */}
220
+ {autoScroll && <ScrollAnchor dependencies={[messages]} />}
221
+ </div>
222
+
223
+ {/* 回到底部按钮 */}
224
+ <button
225
+ onClick={scrollToBottom}
226
+ className={`flex -translate-x-1/2 items-center justify-center ${showScrollButton ? '' : 'pointer-events-none'}`}
227
+ style={{
228
+ position: 'absolute',
229
+ bottom: '24px',
230
+ left: '50%',
231
+ zIndex: 10,
232
+ width: '40px',
233
+ height: '40px',
234
+ borderRadius: '50%',
235
+ backgroundColor: 'rgba(255, 255, 255, 0.95)',
236
+ boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
237
+ transition: 'all 300ms ease-in-out',
238
+ opacity: showScrollButton ? 1 : 0,
239
+ cursor: 'pointer',
240
+ border: 'none',
241
+ }}
242
+ aria-label="Scroll to bottom"
243
+ aria-hidden={!showScrollButton}
244
+ >
245
+ <svg
246
+ width="20"
247
+ height="20"
248
+ viewBox="0 0 24 24"
249
+ fill="none"
250
+ stroke="currentColor"
251
+ strokeWidth="2"
252
+ strokeLinecap="round"
253
+ strokeLinejoin="round"
254
+ className="text-gray-700"
255
+ >
256
+ <polyline points="6 9 12 15 18 9" />
257
+ </svg>
258
+ </button>
259
+ </div>
260
+ )
261
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * 滚动锚点组件
3
+ * 用于自动滚动到消息列表底部
4
+ * 基于 specs/livechat-widget/plan.md 的滚动行为设计
5
+ */
6
+
7
+ import React, { useEffect, useRef } from 'react'
8
+
9
+ export interface ScrollAnchorProps {
10
+ /**
11
+ * 是否自动滚动
12
+ * @default true
13
+ */
14
+ autoScroll?: boolean
15
+
16
+ /**
17
+ * 滚动行为
18
+ * @default "smooth"
19
+ */
20
+ behavior?: ScrollBehavior
21
+
22
+ /**
23
+ * 依赖项(当依赖项变化时触发滚动)
24
+ */
25
+ dependencies?: any[]
26
+ }
27
+
28
+ /**
29
+ * 滚动锚点组件
30
+ *
31
+ * 功能:
32
+ * - 放置在消息列表底部
33
+ * - 当新消息到达时自动滚动到此位置
34
+ * - 支持平滑滚动动画
35
+ *
36
+ * 使用场景:
37
+ * - 接收新消息时自动滚动
38
+ * - 发送消息后自动滚动
39
+ * - 加载历史消息后保持位置
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * <MessageList>
44
+ * {messages.map(msg => <ChatMessage key={msg.id} message={msg} />)}
45
+ * <ScrollAnchor dependencies={[messages]} />
46
+ * </MessageList>
47
+ * ```
48
+ */
49
+ export const ScrollAnchor: React.FC<ScrollAnchorProps> = ({
50
+ autoScroll = true,
51
+ behavior = 'smooth',
52
+ dependencies = [],
53
+ }) => {
54
+ const anchorRef = useRef<HTMLDivElement>(null)
55
+
56
+ useEffect(() => {
57
+ if (!autoScroll) return
58
+
59
+ const anchor = anchorRef.current
60
+ if (!anchor) return
61
+
62
+ // 使用 setTimeout 确保 DOM 已更新
63
+ const timeoutId = setTimeout(() => {
64
+ anchor.scrollIntoView({
65
+ behavior,
66
+ block: 'end',
67
+ inline: 'nearest',
68
+ })
69
+ }, 100)
70
+
71
+ return () => clearTimeout(timeoutId)
72
+ }, [autoScroll, behavior, ...dependencies])
73
+
74
+ return <div ref={anchorRef} style={{ height: 0 }} />
75
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * LiveChat 组件常量定义
3
+ */
4
+
5
+ import type { CommonText } from './types'
6
+
7
+ /**
8
+ * 货币符号映射表
9
+ * 用于将货币代码转换为对应的符号
10
+ */
11
+ export const CURRENCY_SYMBOLS: Record<string, string> = {
12
+ USD: "$",
13
+ CAD: "CA$",
14
+ GBP: "£",
15
+ EUR: "€",
16
+ AUD: "$",
17
+ SGD: "$",
18
+ NZD: "NZ$",
19
+ AED: "AED ",
20
+ VND: "₫",
21
+ TWD: "NT$",
22
+ PLN: "zł",
23
+ RON: "Lei"
24
+ }
25
+
26
+ /**
27
+ * 默认文案配置
28
+ */
29
+ export const DEFAULT_COMMON_TEXT: Required<CommonText> = {
30
+ learnMore: 'Learn More',
31
+ showLess: 'Show Less',
32
+ addToCart: 'Add to Cart',
33
+ viewMore: 'View More',
34
+ off: 'OFF',
35
+ total: 'Total',
36
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * 聊天 API 调用 Hook
3
+ * 封装 SSE 流式消息接收、历史消息加载、新会话创建
4
+ * 基于 specs/livechat-widget/plan.md 的 API 调用策略
5
+ */
6
+
7
+ import { useCallback, useRef } from 'react'
8
+ import type { ChatStreamRequest, SSEEvent, NewSessionRequest, NewSessionResponse } from '../types'
9
+ import { sendMessage, createNewSession, type RecaptchaConfig } from '../api/chat'
10
+
11
+ export interface UseChatAPIOptions {
12
+ /**
13
+ * API 基础 URL
14
+ */
15
+ apiBaseUrl: string
16
+
17
+ /**
18
+ * 自定义请求头
19
+ */
20
+ headers?: Record<string, string>
21
+
22
+ /**
23
+ * reCAPTCHA 配置
24
+ */
25
+ recaptchaConfig?: RecaptchaConfig
26
+
27
+ /**
28
+ * 错误处理回调
29
+ */
30
+ onError?: (error: Error) => void
31
+ }
32
+
33
+ export interface UseChatAPIReturn {
34
+ /**
35
+ * 发送消息并接收 SSE 流式响应
36
+ * @param request 请求参数
37
+ * @param onEvent SSE 事件回调
38
+ * @returns Promise<void>
39
+ */
40
+ sendMessageStream: (request: ChatStreamRequest, onEvent: (event: SSEEvent) => void) => Promise<void>
41
+
42
+ /**
43
+ * 创建新会话或恢复现有会话
44
+ * @param request 请求参数
45
+ * @returns 新会话信息,可能包含历史消息
46
+ */
47
+ createSession: (request: NewSessionRequest) => Promise<NewSessionResponse>
48
+
49
+ /**
50
+ * 中断当前的 SSE 流
51
+ */
52
+ abortStream: () => void
53
+
54
+ /**
55
+ * 是否正在发送消息
56
+ */
57
+ isSending: boolean
58
+ }
59
+
60
+ /**
61
+ * 聊天 API 调用 Hook
62
+ *
63
+ * 功能:
64
+ * 1. sendMessageStream: 发送消息并接收 SSE 流式响应
65
+ * 2. createSession: 创建新会话或恢复现有会话
66
+ * 3. abortStream: 中断当前流
67
+ *
68
+ * @param options Hook 配置选项
69
+ * @returns API 调用工具对象
70
+ */
71
+ export function useChatAPI(options: UseChatAPIOptions): UseChatAPIReturn {
72
+ const { apiBaseUrl, headers, recaptchaConfig, onError } = options
73
+
74
+ // 用于中断当前请求的 AbortController
75
+ const abortControllerRef = useRef<AbortController | null>(null)
76
+
77
+ // 发送状态标记
78
+ const isSendingRef = useRef(false)
79
+
80
+ /**
81
+ * 发送消息并接收 SSE 流式响应
82
+ */
83
+ const sendMessageStream = useCallback(
84
+ async (request: ChatStreamRequest, onEvent: (event: SSEEvent) => void) => {
85
+ // 如果正在发送,先中断之前的请求
86
+ if (abortControllerRef.current) {
87
+ abortControllerRef.current.abort()
88
+ }
89
+
90
+ // 创建新的 AbortController
91
+ abortControllerRef.current = new AbortController()
92
+ isSendingRef.current = true
93
+
94
+ try {
95
+ await sendMessage(apiBaseUrl, request, onEvent, headers, recaptchaConfig)
96
+ } catch (error) {
97
+ // 忽略手动中断的错误
98
+ if (error instanceof Error && error.name === 'AbortError') {
99
+ return
100
+ }
101
+
102
+ console.error('[useChatAPI] Stream error:', error)
103
+ onError?.(error as Error)
104
+ throw error
105
+ } finally {
106
+ isSendingRef.current = false
107
+ abortControllerRef.current = null
108
+ }
109
+ },
110
+ [apiBaseUrl, headers, recaptchaConfig, onError]
111
+ )
112
+
113
+ /**
114
+ * 创建新会话或恢复现有会话
115
+ */
116
+ const createSession = useCallback(
117
+ async (request: NewSessionRequest): Promise<NewSessionResponse> => {
118
+ try {
119
+ return await createNewSession(apiBaseUrl, request, headers, recaptchaConfig)
120
+ } catch (error) {
121
+ console.error('[useChatAPI] Create session error:', error)
122
+ onError?.(error as Error)
123
+ throw error
124
+ }
125
+ },
126
+ [apiBaseUrl, headers, recaptchaConfig, onError]
127
+ )
128
+
129
+ /**
130
+ * 中断当前的 SSE 流
131
+ */
132
+ const abortStream = useCallback(() => {
133
+ if (abortControllerRef.current) {
134
+ abortControllerRef.current.abort()
135
+ abortControllerRef.current = null
136
+ isSendingRef.current = false
137
+ }
138
+ }, [])
139
+
140
+ return {
141
+ sendMessageStream,
142
+ createSession,
143
+ abortStream,
144
+ isSending: isSendingRef.current,
145
+ }
146
+ }