@anker-in/campaign-ui 0.3.3 → 0.3.5

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 (224) 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 +3 -3
  46. package/dist/cjs/components/LiveChatWidget/components/MessageList.js.map +3 -3
  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 +36 -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 +213 -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/index.d.ts +2 -0
  68. package/dist/cjs/components/index.js +1 -1
  69. package/dist/cjs/components/index.js.map +3 -3
  70. package/dist/cjs/stories/LiveChatWidget.stories.d.ts +1 -79
  71. package/dist/cjs/stories/LiveChatWidget.stories.js +3 -49
  72. package/dist/cjs/stories/LiveChatWidget.stories.js.map +3 -3
  73. package/dist/esm/components/LiveChatWidget/LiveChatWidget.d.ts +21 -1
  74. package/dist/esm/components/LiveChatWidget/LiveChatWidget.js +1 -1
  75. package/dist/esm/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
  76. package/dist/esm/components/LiveChatWidget/api/chat.d.ts +23 -2
  77. package/dist/esm/components/LiveChatWidget/api/chat.js +2 -2
  78. package/dist/esm/components/LiveChatWidget/api/chat.js.map +3 -3
  79. package/dist/esm/components/LiveChatWidget/components/ChatHeader.js +1 -1
  80. package/dist/esm/components/LiveChatWidget/components/ChatHeader.js.map +2 -2
  81. package/dist/esm/components/LiveChatWidget/components/ChatInput.d.ts +5 -0
  82. package/dist/esm/components/LiveChatWidget/components/ChatInput.js +1 -1
  83. package/dist/esm/components/LiveChatWidget/components/ChatInput.js.map +3 -3
  84. package/dist/esm/components/LiveChatWidget/components/ChatMessage.js +2 -2
  85. package/dist/esm/components/LiveChatWidget/components/ChatMessage.js.map +3 -3
  86. package/dist/esm/components/LiveChatWidget/components/ChatWindow.d.ts +5 -0
  87. package/dist/esm/components/LiveChatWidget/components/ChatWindow.js +1 -1
  88. package/dist/esm/components/LiveChatWidget/components/ChatWindow.js.map +3 -3
  89. package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.d.ts +51 -0
  90. package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.js +33 -0
  91. package/dist/esm/components/LiveChatWidget/components/ComplianceDialog.js.map +7 -0
  92. package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js +1 -1
  93. package/dist/esm/components/LiveChatWidget/components/MessageContent/CartCard.js.map +3 -3
  94. package/dist/esm/components/LiveChatWidget/components/MessageContent/ErrorBlock.js +1 -1
  95. package/dist/esm/components/LiveChatWidget/components/MessageContent/ErrorBlock.js.map +2 -2
  96. package/dist/esm/components/LiveChatWidget/components/MessageContent/FAQList.js +1 -1
  97. package/dist/esm/components/LiveChatWidget/components/MessageContent/FAQList.js.map +3 -3
  98. package/dist/esm/components/LiveChatWidget/components/MessageContent/PolicyBlock.js +2 -2
  99. package/dist/esm/components/LiveChatWidget/components/MessageContent/PolicyBlock.js.map +3 -3
  100. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.d.ts +17 -24
  101. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js +1 -4
  102. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductCard.js.map +3 -3
  103. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.d.ts +7 -1
  104. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js +1 -1
  105. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductComparison.js.map +3 -3
  106. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js +1 -1
  107. package/dist/esm/components/LiveChatWidget/components/MessageContent/ProductList.js.map +3 -3
  108. package/dist/esm/components/LiveChatWidget/components/MessageContent/PromotionList.d.ts +4 -1
  109. package/dist/esm/components/LiveChatWidget/components/MessageContent/PromotionList.js +1 -1
  110. package/dist/esm/components/LiveChatWidget/components/MessageContent/PromotionList.js.map +3 -3
  111. package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js +1 -1
  112. package/dist/esm/components/LiveChatWidget/components/MessageContent/QuickReplies.js.map +2 -2
  113. package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js +1 -1
  114. package/dist/esm/components/LiveChatWidget/components/MessageContent/TextBlock.js.map +3 -3
  115. package/dist/esm/components/LiveChatWidget/components/MessageContent.js +1 -1
  116. package/dist/esm/components/LiveChatWidget/components/MessageContent.js.map +2 -2
  117. package/dist/esm/components/LiveChatWidget/components/MessageList.js +3 -3
  118. package/dist/esm/components/LiveChatWidget/components/MessageList.js.map +3 -3
  119. package/dist/esm/components/LiveChatWidget/constants.d.ts +5 -0
  120. package/dist/esm/components/LiveChatWidget/constants.js +1 -1
  121. package/dist/esm/components/LiveChatWidget/constants.js.map +3 -3
  122. package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.d.ts +9 -0
  123. package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.js +1 -1
  124. package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.js.map +3 -3
  125. package/dist/esm/components/LiveChatWidget/hooks/useChatState.d.ts +36 -2
  126. package/dist/esm/components/LiveChatWidget/hooks/useChatState.js +1 -1
  127. package/dist/esm/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
  128. package/dist/esm/components/LiveChatWidget/index.d.ts +1 -1
  129. package/dist/esm/components/LiveChatWidget/index.js +1 -1
  130. package/dist/esm/components/LiveChatWidget/index.js.map +2 -2
  131. package/dist/esm/components/LiveChatWidget/types.d.ts +213 -3
  132. package/dist/esm/components/LiveChatWidget/utils/fetcher.d.ts +42 -0
  133. package/dist/esm/components/LiveChatWidget/utils/fetcher.js +2 -0
  134. package/dist/esm/components/LiveChatWidget/utils/fetcher.js.map +7 -0
  135. package/dist/esm/components/chat/markdown.js +1 -1
  136. package/dist/esm/components/chat/markdown.js.map +2 -2
  137. package/dist/esm/components/index.d.ts +2 -0
  138. package/dist/esm/components/index.js +1 -1
  139. package/dist/esm/components/index.js.map +3 -3
  140. package/dist/esm/stories/LiveChatWidget.stories.d.ts +1 -79
  141. package/dist/esm/stories/LiveChatWidget.stories.js +3 -49
  142. package/dist/esm/stories/LiveChatWidget.stories.js.map +3 -3
  143. package/dist/index.d.mts +1305 -0
  144. package/dist/index.d.ts +1305 -0
  145. package/dist/index.js +26656 -0
  146. package/dist/index.js.map +1 -0
  147. package/dist/index.mjs +26641 -0
  148. package/dist/index.mjs.map +1 -0
  149. package/package.json +8 -1
  150. package/src/components/LiveChatWidget/LiveChatWidget.tsx +907 -0
  151. package/src/components/LiveChatWidget/api/chat.ts +175 -0
  152. package/src/components/LiveChatWidget/components/ChatBubble.tsx +152 -0
  153. package/src/components/LiveChatWidget/components/ChatHeader.tsx +150 -0
  154. package/src/components/LiveChatWidget/components/ChatInput.tsx +253 -0
  155. package/src/components/LiveChatWidget/components/ChatMessage.tsx +190 -0
  156. package/src/components/LiveChatWidget/components/ChatWindow.tsx +363 -0
  157. package/src/components/LiveChatWidget/components/ComplianceDialog.tsx +216 -0
  158. package/src/components/LiveChatWidget/components/MessageContent/CartCard.tsx +202 -0
  159. package/src/components/LiveChatWidget/components/MessageContent/ErrorBlock.tsx +75 -0
  160. package/src/components/LiveChatWidget/components/MessageContent/FAQList.tsx +128 -0
  161. package/src/components/LiveChatWidget/components/MessageContent/PolicyBlock.tsx +152 -0
  162. package/src/components/LiveChatWidget/components/MessageContent/ProductCard.tsx +227 -0
  163. package/src/components/LiveChatWidget/components/MessageContent/ProductComparison.tsx +377 -0
  164. package/src/components/LiveChatWidget/components/MessageContent/ProductList.tsx +293 -0
  165. package/src/components/LiveChatWidget/components/MessageContent/PromotionList.tsx +170 -0
  166. package/src/components/LiveChatWidget/components/MessageContent/QuickReplies.tsx +91 -0
  167. package/src/components/LiveChatWidget/components/MessageContent/TextBlock.tsx +110 -0
  168. package/src/components/LiveChatWidget/components/MessageContent/ThinkingBlock.tsx +53 -0
  169. package/src/components/LiveChatWidget/components/MessageContent/index.ts +16 -0
  170. package/src/components/LiveChatWidget/components/MessageContent.tsx +113 -0
  171. package/src/components/LiveChatWidget/components/MessageList.tsx +256 -0
  172. package/src/components/LiveChatWidget/components/ScrollAnchor.tsx +75 -0
  173. package/src/components/LiveChatWidget/constants.ts +36 -0
  174. package/src/components/LiveChatWidget/hooks/useChatAPI.ts +146 -0
  175. package/src/components/LiveChatWidget/hooks/useChatState.ts +1091 -0
  176. package/src/components/LiveChatWidget/hooks/useSession.ts +123 -0
  177. package/src/components/LiveChatWidget/index.tsx +63 -0
  178. package/src/components/LiveChatWidget/types.ts +1012 -0
  179. package/src/components/LiveChatWidget/utils/cartTransformers.ts +72 -0
  180. package/src/components/LiveChatWidget/utils/fetcher.ts +131 -0
  181. package/src/components/LiveChatWidget/utils/messageRenderers.ts +120 -0
  182. package/src/components/LiveChatWidget/utils/productTransformers.ts +149 -0
  183. package/src/components/LiveChatWidget/utils/userId.ts +140 -0
  184. package/src/components/LiveChatWidget/utils/validation.ts +99 -0
  185. package/src/components/chat/markdown.tsx +1 -1
  186. package/src/components/index.ts +23 -0
  187. package/src/stories/LiveChatWidget.stories.tsx +317 -0
  188. package/src/styles/livechat.css +346 -0
  189. package/dist/cjs/components/credits/context/hooks/useFunctionMemberPrice.d.ts +0 -7
  190. package/dist/cjs/components/credits/context/hooks/useFunctionMemberPrice.js +0 -2
  191. package/dist/cjs/components/credits/context/hooks/useFunctionMemberPrice.js.map +0 -7
  192. package/dist/cjs/components/credits/context/utils/atobID.d.ts +0 -1
  193. package/dist/cjs/components/credits/context/utils/atobID.js +0 -2
  194. package/dist/cjs/components/credits/context/utils/atobID.js.map +0 -7
  195. package/dist/cjs/components/credits/context/utils/functionDiscountCalculate.d.ts +0 -5
  196. package/dist/cjs/components/credits/context/utils/functionDiscountCalculate.js +0 -2
  197. package/dist/cjs/components/credits/context/utils/functionDiscountCalculate.js.map +0 -7
  198. package/dist/cjs/components/credits/context/utils/getFunctionMemberPrice.d.ts +0 -8
  199. package/dist/cjs/components/credits/context/utils/getFunctionMemberPrice.js +0 -2
  200. package/dist/cjs/components/credits/context/utils/getFunctionMemberPrice.js.map +0 -7
  201. package/dist/cjs/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.d.ts +0 -9
  202. package/dist/cjs/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js +0 -2
  203. package/dist/cjs/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js.map +0 -7
  204. package/dist/cjs/components/credits/context/utils/variantGetCoupon.d.ts +0 -6
  205. package/dist/cjs/components/credits/context/utils/variantGetCoupon.js +0 -2
  206. package/dist/cjs/components/credits/context/utils/variantGetCoupon.js.map +0 -7
  207. package/dist/esm/components/credits/context/hooks/useFunctionMemberPrice.d.ts +0 -7
  208. package/dist/esm/components/credits/context/hooks/useFunctionMemberPrice.js +0 -2
  209. package/dist/esm/components/credits/context/hooks/useFunctionMemberPrice.js.map +0 -7
  210. package/dist/esm/components/credits/context/utils/atobID.d.ts +0 -1
  211. package/dist/esm/components/credits/context/utils/atobID.js +0 -2
  212. package/dist/esm/components/credits/context/utils/atobID.js.map +0 -7
  213. package/dist/esm/components/credits/context/utils/functionDiscountCalculate.d.ts +0 -5
  214. package/dist/esm/components/credits/context/utils/functionDiscountCalculate.js +0 -2
  215. package/dist/esm/components/credits/context/utils/functionDiscountCalculate.js.map +0 -7
  216. package/dist/esm/components/credits/context/utils/getFunctionMemberPrice.d.ts +0 -8
  217. package/dist/esm/components/credits/context/utils/getFunctionMemberPrice.js +0 -2
  218. package/dist/esm/components/credits/context/utils/getFunctionMemberPrice.js.map +0 -7
  219. package/dist/esm/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.d.ts +0 -9
  220. package/dist/esm/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js +0 -2
  221. package/dist/esm/components/credits/context/utils/getFunctionMemberPriceDiscountConfig.js.map +0 -7
  222. package/dist/esm/components/credits/context/utils/variantGetCoupon.d.ts +0 -6
  223. package/dist/esm/components/credits/context/utils/variantGetCoupon.js +0 -2
  224. package/dist/esm/components/credits/context/utils/variantGetCoupon.js.map +0 -7
@@ -0,0 +1,363 @@
1
+ /**
2
+ * 聊天窗口容器组件
3
+ * 包含头部、消息列表、输入框的完整聊天界面
4
+ * 基于 specs/livechat-widget/plan.md 的窗口设计
5
+ */
6
+
7
+ import React, { useState, useRef, useEffect, useCallback } from 'react'
8
+ import type { Message, MessageRenderer } from '../types'
9
+ import { ChatHeader } from './ChatHeader'
10
+ import { MessageList } from './MessageList'
11
+ import { ChatInput } from './ChatInput'
12
+ import { MessageRendererRegistry } from '../utils/messageRenderers'
13
+
14
+ export interface ChatWindowProps {
15
+ /**
16
+ * 消息列表
17
+ */
18
+ messages: Message[]
19
+
20
+ /**
21
+ * 输入框当前值
22
+ */
23
+ inputValue: string
24
+
25
+ /**
26
+ * 输入框值变化回调
27
+ */
28
+ onInputChange: (value: string) => void
29
+
30
+ /**
31
+ * 发送消息回调
32
+ */
33
+ onSend: () => void
34
+
35
+ /**
36
+ * 关闭窗口回调
37
+ */
38
+ onClose?: () => void
39
+
40
+ /**
41
+ * 新会话回调
42
+ */
43
+ onNewSession?: () => void
44
+
45
+ /**
46
+ * 头部标题
47
+ */
48
+ title?: string
49
+
50
+ /**
51
+ * Logo URL
52
+ */
53
+ logoUrl?: string
54
+
55
+ /**
56
+ * 是否正在发送消息
57
+ * @default false
58
+ */
59
+ isSending?: boolean
60
+
61
+ /**
62
+ * 是否正在加载历史消息
63
+ * @default false
64
+ */
65
+ isLoadingHistory?: boolean
66
+
67
+ /**
68
+ * 是否显示时间戳
69
+ * @default true
70
+ */
71
+ showTimestamp?: boolean
72
+
73
+ /**
74
+ * 是否自动滚动
75
+ * @default true
76
+ */
77
+ autoScroll?: boolean
78
+
79
+ /**
80
+ * 自定义渲染器注册表
81
+ */
82
+ rendererRegistry?: MessageRendererRegistry
83
+
84
+ /**
85
+ * 默认渲染器
86
+ */
87
+ defaultRenderer?: MessageRenderer
88
+
89
+ /**
90
+ * 输入框占位符
91
+ */
92
+ inputPlaceholder?: string
93
+
94
+ /**
95
+ * 自定义样式类名
96
+ */
97
+ className?: string
98
+
99
+ /**
100
+ * 商品添加到购物车回调
101
+ */
102
+ onAddToCart?: (product: any) => void
103
+
104
+ /**
105
+ * 是否显示新会话按钮
106
+ * @default true
107
+ */
108
+ showNewSessionButton?: boolean
109
+
110
+ /**
111
+ * 输入框底部提示文本
112
+ * 不传入则不显示
113
+ */
114
+ bottomTips?: string
115
+ }
116
+
117
+ /**
118
+ * 聊天窗口容器组件
119
+ *
120
+ * 功能:
121
+ * - 组合头部、消息列表、输入框
122
+ * - 响应式布局(移动端全屏,桌面端固定尺寸)
123
+ * - 动画进入/退出效果
124
+ *
125
+ * 布局结构:
126
+ * ```
127
+ * ┌─────────────────────┐
128
+ * │ ChatHeader │ (固定顶部)
129
+ * ├─────────────────────┤
130
+ * │ MessageList │ (可滚动区域)
131
+ * │ (flex-1) │
132
+ * ├─────────────────────┤
133
+ * │ ChatInput │ (固定底部)
134
+ * └─────────────────────┘
135
+ * ```
136
+ *
137
+ * 响应式设计:
138
+ * - 移动端 (< 768px): 全屏显示
139
+ * - 平板及以上 (>= 768px): 固定尺寸弹窗
140
+ *
141
+ * @example
142
+ * ```tsx
143
+ * <ChatWindow
144
+ * messages={messages}
145
+ * inputValue={inputValue}
146
+ * onInputChange={setInputValue}
147
+ * onSend={handleSend}
148
+ * onClose={() => setIsOpen(false)}
149
+ * onNewSession={handleNewSession}
150
+ * isSending={isStreaming}
151
+ * title="AI 助手"
152
+ * logoUrl="/logo.png"
153
+ * />
154
+ * ```
155
+ */
156
+ export const ChatWindow: React.FC<ChatWindowProps> = ({
157
+ messages,
158
+ inputValue,
159
+ onInputChange,
160
+ onSend,
161
+ onClose,
162
+ onNewSession,
163
+ title,
164
+ logoUrl,
165
+ isSending = false,
166
+ isLoadingHistory = false,
167
+ showTimestamp = true,
168
+ autoScroll = true,
169
+ rendererRegistry,
170
+ defaultRenderer,
171
+ inputPlaceholder,
172
+ className = '',
173
+ onAddToCart,
174
+ showNewSessionButton = true,
175
+ bottomTips,
176
+ }) => {
177
+ // 常量:高度限制比例
178
+ const MIN_HEIGHT_RATIO = 0.4 // 最小40%
179
+ const MAX_HEIGHT_RATIO = 0.8 // 最大80%
180
+ const DEFAULT_HEIGHT = 680 // 默认680px
181
+
182
+ // 获取当前允许的高度范围
183
+ const getHeightConstraints = useCallback(() => {
184
+ if (typeof window === 'undefined') {
185
+ return { minHeight: DEFAULT_HEIGHT, maxHeight: DEFAULT_HEIGHT }
186
+ }
187
+ return {
188
+ minHeight: window.innerHeight * MIN_HEIGHT_RATIO,
189
+ maxHeight: window.innerHeight * MAX_HEIGHT_RATIO,
190
+ }
191
+ }, [MIN_HEIGHT_RATIO, MAX_HEIGHT_RATIO, DEFAULT_HEIGHT])
192
+
193
+ // 将高度限制在允许范围内
194
+ const clampHeight = useCallback(
195
+ (height: number) => {
196
+ const { minHeight, maxHeight } = getHeightConstraints()
197
+ return Math.max(minHeight, Math.min(maxHeight, height))
198
+ },
199
+ [getHeightConstraints]
200
+ )
201
+
202
+ // 计算初始高度:默认680px,但需要在30%-80%范围内
203
+ const getInitialHeight = () => {
204
+ if (typeof window === 'undefined') return DEFAULT_HEIGHT
205
+ return clampHeight(DEFAULT_HEIGHT)
206
+ }
207
+
208
+ // 移动端高度调节状态(默认680px或屏幕80%,取较小值)
209
+ const [mobileHeight, setMobileHeight] = useState(getInitialHeight)
210
+ const [isDragging, setIsDragging] = useState(false)
211
+ const dragStartY = useRef(0)
212
+ const dragStartHeight = useRef(getInitialHeight())
213
+ const windowRef = useRef<HTMLDivElement>(null)
214
+
215
+ // 更新高度并同步CSS变量
216
+ const updateHeight = useCallback(
217
+ (newHeight: number) => {
218
+ const clampedHeight = clampHeight(newHeight)
219
+ setMobileHeight(clampedHeight)
220
+ // 同步更新CSS变量,供 livechat.css 使用
221
+ if (windowRef.current) {
222
+ windowRef.current.style.setProperty('--livechat-mobile-height', `${clampedHeight}px`)
223
+ }
224
+ return clampedHeight
225
+ },
226
+ [clampHeight]
227
+ )
228
+
229
+ // 检测是否为移动端(初始化时立即检测)
230
+ const [isMobile, setIsMobile] = useState(() => {
231
+ if (typeof window !== 'undefined') {
232
+ return window.innerWidth < 768
233
+ }
234
+ return false
235
+ })
236
+
237
+ useEffect(() => {
238
+ const handleResize = () => {
239
+ const newIsMobile = window.innerWidth < 768
240
+ setIsMobile(newIsMobile)
241
+
242
+ // 如果是移动端,检查当前高度是否在允许范围内
243
+ if (newIsMobile) {
244
+ const clampedHeight = clampHeight(mobileHeight)
245
+ if (clampedHeight !== mobileHeight) {
246
+ updateHeight(clampedHeight)
247
+ }
248
+ }
249
+ }
250
+
251
+ window.addEventListener('resize', handleResize)
252
+ return () => window.removeEventListener('resize', handleResize)
253
+ }, [mobileHeight, clampHeight, updateHeight])
254
+
255
+ // 拖拽开始
256
+ const handleDragStart = (e: React.MouseEvent | React.TouchEvent) => {
257
+ if (!isMobile) return
258
+
259
+ setIsDragging(true)
260
+ const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY
261
+ dragStartY.current = clientY
262
+ dragStartHeight.current = mobileHeight
263
+
264
+ // 阻止默认行为
265
+ e.preventDefault()
266
+ }
267
+
268
+ // 拖拽中
269
+ useEffect(() => {
270
+ if (!isDragging) return
271
+
272
+ const handleDragMove = (e: MouseEvent | TouchEvent) => {
273
+ const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY
274
+ const deltaY = dragStartY.current - clientY // 向上拖动为正值
275
+ const newHeight = dragStartHeight.current + deltaY
276
+
277
+ updateHeight(newHeight)
278
+ }
279
+
280
+ const handleDragEnd = () => {
281
+ setIsDragging(false)
282
+ }
283
+
284
+ document.addEventListener('mousemove', handleDragMove)
285
+ document.addEventListener('mouseup', handleDragEnd)
286
+ document.addEventListener('touchmove', handleDragMove, { passive: false })
287
+ document.addEventListener('touchend', handleDragEnd)
288
+
289
+ return () => {
290
+ document.removeEventListener('mousemove', handleDragMove)
291
+ document.removeEventListener('mouseup', handleDragEnd)
292
+ document.removeEventListener('touchmove', handleDragMove)
293
+ document.removeEventListener('touchend', handleDragEnd)
294
+ }
295
+ }, [isDragging, updateHeight])
296
+
297
+ return (
298
+ <div
299
+ ref={windowRef}
300
+ className={`livechat-window flex flex-col overflow-hidden bg-white ${className}`}
301
+ style={
302
+ {
303
+ borderRadius: '16px',
304
+ borderBottomLeftRadius: isMobile && '0px',
305
+ borderBottomRightRadius: isMobile && '0px',
306
+ '--livechat-mobile-height': `${mobileHeight}px`,
307
+ } as React.CSSProperties
308
+ }
309
+ >
310
+ {/* 移动端拖拽手柄 */}
311
+ {isMobile && (
312
+ <div
313
+ className="flex cursor-ns-resize items-center justify-center py-2"
314
+ style={{
315
+ touchAction: 'none',
316
+ backgroundColor: '#ffffff',
317
+ }}
318
+ onMouseDown={handleDragStart}
319
+ onTouchStart={handleDragStart}
320
+ >
321
+ <div
322
+ style={{
323
+ width: '48px',
324
+ height: '6px',
325
+ borderRadius: '999px',
326
+ backgroundColor: '#DADCE0',
327
+ }}
328
+ />
329
+ </div>
330
+ )}
331
+
332
+ {/* 头部 */}
333
+ <ChatHeader
334
+ title={title}
335
+ logoUrl={logoUrl}
336
+ onClose={onClose}
337
+ onNewSession={onNewSession}
338
+ showNewSessionButton={showNewSessionButton}
339
+ />
340
+
341
+ {/* 消息列表(可滚动区域) */}
342
+ <MessageList
343
+ messages={messages}
344
+ rendererRegistry={rendererRegistry}
345
+ defaultRenderer={defaultRenderer}
346
+ showTimestamp={showTimestamp}
347
+ autoScroll={autoScroll}
348
+ isLoadingHistory={isLoadingHistory}
349
+ onAddToCart={onAddToCart}
350
+ />
351
+
352
+ {/* 输入框 */}
353
+ <ChatInput
354
+ value={inputValue}
355
+ onChange={onInputChange}
356
+ onSend={onSend}
357
+ placeholder={inputPlaceholder}
358
+ disabled={isSending}
359
+ bottomTips={bottomTips}
360
+ />
361
+ </div>
362
+ )
363
+ }
@@ -0,0 +1,216 @@
1
+ /**
2
+ * 法规协议弹窗组件
3
+ * 在用户首次点击聊天气泡时显示,用户同意后才打开聊天窗口
4
+ */
5
+
6
+ import React, { useState, useCallback } from 'react'
7
+ import * as Dialog from '@radix-ui/react-dialog'
8
+ import type { ComplianceDialogConfig } from '../types'
9
+
10
+ export interface ComplianceDialogProps {
11
+ /**
12
+ * 是否打开弹窗
13
+ */
14
+ open: boolean
15
+
16
+ /**
17
+ * 弹窗配置
18
+ */
19
+ config: ComplianceDialogConfig
20
+
21
+ /**
22
+ * 用户点击同意按钮的回调
23
+ */
24
+ onAgree: () => void
25
+
26
+ /**
27
+ * 用户关闭弹窗的回调(点击遮罩或关闭按钮)
28
+ */
29
+ onClose: () => void
30
+ }
31
+
32
+ /**
33
+ * 法规协议弹窗组件
34
+ *
35
+ * 功能:
36
+ * - 显示法规协议内容
37
+ * - 必须勾选复选框才能点击同意按钮
38
+ * - 支持在文本中嵌入链接
39
+ * - 响应式设计,移动端友好
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * <ComplianceDialog
44
+ * open={showCompliance}
45
+ * config={{
46
+ * title: "Hi! I'm your eufy AI assistant.",
47
+ * content: "AI-generated responses can be inaccurate...",
48
+ * checkboxText: "By starting to use \"Live Chat\", you agree to Anker's {link}.",
49
+ * linkText: "LIVE CHAT PRIVACY NOTICE",
50
+ * linkUrl: "https://www.anker.com/privacy",
51
+ * agreeButtonText: "Agree"
52
+ * }}
53
+ * onAgree={() => setShowCompliance(false)}
54
+ * onClose={() => setShowCompliance(false)}
55
+ * />
56
+ * ```
57
+ */
58
+ export const ComplianceDialog: React.FC<ComplianceDialogProps> = ({
59
+ open,
60
+ config,
61
+ onAgree,
62
+ onClose,
63
+ }) => {
64
+ const [isChecked, setIsChecked] = useState(true)
65
+
66
+ const handleAgree = useCallback(() => {
67
+ if (isChecked) {
68
+ onAgree()
69
+ }
70
+ }, [isChecked, onAgree])
71
+
72
+ const handleOpenChange = useCallback((open: boolean) => {
73
+ if (!open) {
74
+ onClose()
75
+ }
76
+ }, [onClose])
77
+
78
+ // 处理点击事件,防止点击链接时触发 checkbox
79
+ const handleCheckboxLabelClick = useCallback((e: React.MouseEvent) => {
80
+ // 如果点击的是链接,阻止冒泡
81
+ if ((e.target as HTMLElement).tagName === 'A') {
82
+ e.stopPropagation()
83
+ }
84
+ }, [])
85
+
86
+ return (
87
+ <Dialog.Root open={open} onOpenChange={handleOpenChange}>
88
+ <Dialog.Portal>
89
+ {/* 弹窗内容 - 无遮罩层(与聊天窗口一致) */}
90
+ <Dialog.Content
91
+ className="livechat-compliance-dialog"
92
+ onInteractOutside={(e) => e.preventDefault()}
93
+ onEscapeKeyDown={(e) => e.preventDefault()}
94
+ >
95
+ {/* Header - 包含关闭按钮(右上角) */}
96
+ <div className="flex items-center justify-end px-4 pt-4 pb-2 flex-shrink-0 max-md:px-6 max-sm:px-5">
97
+ <Dialog.Close asChild>
98
+ <button
99
+ type="button"
100
+ className="
101
+ w-6 h-6
102
+ flex items-center justify-center
103
+ border-0 bg-transparent text-gray-500
104
+ cursor-pointer
105
+ "
106
+ aria-label="关闭"
107
+ >
108
+ <svg
109
+ width="20"
110
+ height="20"
111
+ className="text-[#3C3C3B]"
112
+ viewBox="0 0 20 20"
113
+ fill="none"
114
+ stroke="currentColor"
115
+ strokeWidth="2"
116
+ strokeLinecap="round"
117
+ strokeLinejoin="round"
118
+ >
119
+ <line x1="15" y1="5" x2="5" y2="15" />
120
+ <line x1="5" y1="5" x2="15" y2="15" />
121
+ </svg>
122
+ </button>
123
+ </Dialog.Close>
124
+ </div>
125
+
126
+ {/* Body - 可滚动内容区域 */}
127
+ <div className="flex-1 overflow-y-auto px-4 pb-4 max-md:px-6 max-md:pb-6 max-sm:px-5 max-sm:pb-5">
128
+ {/* 标题 */}
129
+ <Dialog.Title
130
+ className="
131
+ text-[18px] font-bold text-[#080A0F]
132
+ mb-1 leading-[1.4] tracking-tight
133
+ "
134
+ >
135
+ {config.title}
136
+ </Dialog.Title>
137
+
138
+ {/* 内容 */}
139
+ <div
140
+ className="
141
+ text-[16px] font-bold leading-[1.4] text-[#080A0F]
142
+ tracking-tight
143
+ mb-2
144
+ "
145
+ dangerouslySetInnerHTML={{ __html: config.content }}
146
+ />
147
+
148
+ {/* 勾选框 */}
149
+ <label
150
+ className="
151
+ flex items-center gap-2 mb-4
152
+ cursor-pointer select-none
153
+ "
154
+ onClick={handleCheckboxLabelClick}
155
+ >
156
+ <input
157
+ type="checkbox"
158
+ className="absolute opacity-0 w-0 h-0"
159
+ checked={isChecked}
160
+ onChange={e => setIsChecked(e.target.checked)}
161
+ />
162
+ <span
163
+ className={`
164
+ flex-shrink-0 w-4 h-4 mt-0.5
165
+ border-[1px] rounded-[2px] flex items-center justify-center
166
+ transition-all duration-200 ease-in-out
167
+ ${isChecked ? 'bg-[#080A0F] border-[#080A0F] text-white' : 'bg-white border-[#2A2C32]'}
168
+ `}
169
+ >
170
+ {isChecked && (
171
+ <svg
172
+ width="12"
173
+ height="10"
174
+ viewBox="0 0 12 10"
175
+ fill="none"
176
+ stroke="currentColor"
177
+ strokeWidth="2"
178
+ strokeLinecap="round"
179
+ strokeLinejoin="round"
180
+ >
181
+ <polyline points="1.5 5 4.5 8 10.5 2" />
182
+ </svg>
183
+ )}
184
+ </span>
185
+ <span
186
+ className="
187
+ flex-1 text-[14px] leading-[1.4] text-[#080A0F]
188
+ tracking-tight font-bold
189
+ [&_a]:underline
190
+ "
191
+ dangerouslySetInnerHTML={{ __html: config.checkboxText }}
192
+ />
193
+ </label>
194
+
195
+ {/* 同意按钮 */}
196
+ <button
197
+ type="button"
198
+ className={`
199
+ w-full px-5 py-[10px]
200
+ border-0 rounded-[19px]
201
+ text-[14px] leading-[1.2]
202
+ tracking-tighter font-bold
203
+ transition-all duration-200 ease-in-out text-white
204
+ ${isChecked ? 'bg-[#080A0F] cursor-pointer ' : 'bg-[#767880] cursor-not-allowed'}
205
+ `}
206
+ onClick={handleAgree}
207
+ disabled={!isChecked}
208
+ >
209
+ {config.agreeButtonText || 'Agree'}
210
+ </button>
211
+ </div>
212
+ </Dialog.Content>
213
+ </Dialog.Portal>
214
+ </Dialog.Root>
215
+ )
216
+ }