@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,175 @@
1
+ /**
2
+ * LiveChat API 层
3
+ * 处理 SSE 流式通信和 HTTP 请求
4
+ * 基于 specs/livechat-widget/contracts/*.yaml
5
+ */
6
+
7
+ import type { ChatStreamRequest, SSEEvent, NewSessionRequest, NewSessionResponse, ErrorResponse } from '../types'
8
+ import { fetcher } from '../utils/fetcher'
9
+
10
+ /**
11
+ * Recaptcha 配置
12
+ */
13
+ export interface RecaptchaConfig {
14
+ /**
15
+ * 是否启用 reCAPTCHA
16
+ */
17
+ needRecaptcha?: boolean
18
+ /**
19
+ * reCAPTCHA site key
20
+ */
21
+ recaptchaSitekey?: string
22
+ /**
23
+ * reCAPTCHA action 名称
24
+ */
25
+ recaptchaAction?: string
26
+ }
27
+
28
+ // ============================================================================
29
+ // SSE 流式消息处理 (T006)
30
+ // ============================================================================
31
+
32
+ /**
33
+ * 发送消息并接收 SSE 流式响应
34
+ * @param apiBaseUrl API 基础 URL
35
+ * @param request 请求参数
36
+ * @param onEvent SSE 事件回调
37
+ * @param customHeaders 自定义请求头
38
+ * @param recaptchaConfig reCAPTCHA 配置
39
+ */
40
+ export async function sendMessage(
41
+ apiBaseUrl: string,
42
+ request: ChatStreamRequest,
43
+ onEvent: (event: SSEEvent) => void,
44
+ customHeaders?: Record<string, string>,
45
+ recaptchaConfig?: RecaptchaConfig
46
+ ): Promise<void> {
47
+ // 使用 fetcher 处理 reCAPTCHA 和自定义 headers
48
+ const response = await fetcher({
49
+ url: `${apiBaseUrl}/api/chat/stream`,
50
+ method: 'POST',
51
+ headers: {
52
+ Accept: 'text/event-stream',
53
+ ...customHeaders,
54
+ },
55
+ body: request,
56
+ needRecaptcha: recaptchaConfig?.needRecaptcha,
57
+ recaptchaSitekey: recaptchaConfig?.recaptchaSitekey,
58
+ recaptchaAction: recaptchaConfig?.recaptchaAction || 'send_message',
59
+ })
60
+
61
+ if (!response.ok) {
62
+ const errorData = await response.json().catch(() => null)
63
+ const error = new Error(errorData?.message || `HTTP ${response.status}`) as Error & { type?: string }
64
+ error.type = errorData?.type
65
+ throw error
66
+ }
67
+
68
+ const reader = response.body?.getReader()
69
+ if (!reader) {
70
+ throw new Error('Response body is not readable')
71
+ }
72
+
73
+ const decoder = new TextDecoder()
74
+ let buffer = ''
75
+ let currentEvent: string | null = null
76
+
77
+ try {
78
+ while (true) {
79
+ const { done, value } = await reader.read()
80
+ if (done) break
81
+
82
+ // 解码并追加到缓冲区
83
+ buffer += decoder.decode(value, { stream: true })
84
+
85
+ // 按行分割
86
+ const lines = buffer.split('\n')
87
+
88
+ // 保留最后一个不完整的行
89
+ buffer = lines.pop() || ''
90
+
91
+ for (const line of lines) {
92
+ const trimmed = line.trim()
93
+
94
+ if (trimmed === '') {
95
+ // 空行表示事件边界,重置当前事件类型
96
+ currentEvent = null
97
+ continue
98
+ }
99
+
100
+ if (trimmed.startsWith('event:')) {
101
+ // 提取事件类型
102
+ currentEvent = trimmed.substring(6).trim()
103
+ } else if (trimmed.startsWith('data:')) {
104
+ // 提取数据并解析 JSON
105
+ const dataStr = trimmed.substring(5).trim()
106
+ try {
107
+ const data = JSON.parse(dataStr)
108
+ onEvent({
109
+ event: (currentEvent as any) || 'message',
110
+ data,
111
+ })
112
+ } catch (err) {
113
+ console.error('[LiveChat API] Failed to parse SSE data:', dataStr, err)
114
+ onEvent({
115
+ event: 'error',
116
+ data: {
117
+ message: 'Failed to parse SSE data',
118
+ code: 'PARSE_ERROR',
119
+ },
120
+ })
121
+ }
122
+ }
123
+ }
124
+ }
125
+ } catch (error) {
126
+ console.error('[LiveChat API] SSE stream error:', error)
127
+ throw error
128
+ } finally {
129
+ reader.releaseLock()
130
+ }
131
+ }
132
+
133
+ // ============================================================================
134
+ // 创建/恢复会话 (T009)
135
+ // ============================================================================
136
+
137
+ /**
138
+ * 创建新会话或恢复现有会话
139
+ *
140
+ * 使用场景:
141
+ * 1. 创建新会话:不传 session_id,返回新的 sessionId
142
+ * 2. 恢复会话:传入 session_id,验证会话有效性并返回历史消息(resumed: true, messages: [...])
143
+ *
144
+ * @param apiBaseUrl API 基础 URL
145
+ * @param request 请求参数
146
+ * @param customHeaders 自定义请求头
147
+ * @param recaptchaConfig reCAPTCHA 配置
148
+ * @returns 会话信息,可能包含历史消息
149
+ */
150
+ export async function createNewSession(
151
+ apiBaseUrl: string,
152
+ request: NewSessionRequest,
153
+ customHeaders?: Record<string, string>,
154
+ recaptchaConfig?: RecaptchaConfig
155
+ ): Promise<NewSessionResponse> {
156
+ // 使用 fetcher 处理 reCAPTCHA 和自定义 headers
157
+ const response = await fetcher({
158
+ url: `${apiBaseUrl}/api/chat/new-session`,
159
+ method: 'POST',
160
+ headers: customHeaders,
161
+ body: request,
162
+ needRecaptcha: recaptchaConfig?.needRecaptcha,
163
+ recaptchaSitekey: recaptchaConfig?.recaptchaSitekey,
164
+ recaptchaAction: recaptchaConfig?.recaptchaAction || 'new_session',
165
+ })
166
+
167
+ if (!response.ok) {
168
+ const errorData = await response.json().catch(() => null)
169
+ const error = new Error(errorData?.message || `HTTP ${response.status}`) as Error & { type?: string }
170
+ error.type = errorData?.type
171
+ throw error
172
+ }
173
+
174
+ return response.json()
175
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * 聊天悬浮按钮组件
3
+ * 显示在页面角落的气泡按钮,点击后打开聊天窗口
4
+ * 基于 specs/livechat-widget/plan.md 的气泡按钮设计
5
+ */
6
+
7
+ import React from 'react'
8
+ import type { BubblePosition } from '../types'
9
+
10
+ export interface ChatBubbleProps {
11
+ /**
12
+ * 气泡按钮位置
13
+ * @default { bottom: "1.5rem", right: "1.5rem" }
14
+ */
15
+ position?: BubblePosition
16
+
17
+ /**
18
+ * 点击按钮的回调
19
+ */
20
+ onClick?: () => void
21
+
22
+ /**
23
+ * 自定义图标
24
+ */
25
+ icon?: React.ReactNode
26
+
27
+ /**
28
+ * 自定义图标图片 URL
29
+ * 如果提供,将使用图片替代默认的 SVG 图标
30
+ */
31
+ iconImageUrl?: string
32
+
33
+ /**
34
+ * 是否显示气泡
35
+ * @default true
36
+ */
37
+ visible?: boolean
38
+
39
+ /**
40
+ * 自定义样式类名
41
+ */
42
+ className?: string
43
+ }
44
+
45
+ /**
46
+ * 获取气泡按钮的位置样式
47
+ * 如果没有传入 position,使用默认位置(右下角)
48
+ */
49
+ function getPositionStyles(position?: BubblePosition): React.CSSProperties {
50
+ // 默认位置:右下角
51
+ const defaultPosition: React.CSSProperties = {
52
+ bottom: '1.5rem',
53
+ right: '1.5rem',
54
+ }
55
+
56
+ // 如果传入了自定义位置,使用自定义位置
57
+ return position ? (position as React.CSSProperties) : defaultPosition
58
+ }
59
+
60
+ /**
61
+ * 默认聊天图标 (简单的消息气泡 SVG)
62
+ */
63
+ const DefaultChatIcon: React.FC = () => (
64
+ <svg
65
+ width="24"
66
+ height="24"
67
+ viewBox="0 0 24 24"
68
+ fill="none"
69
+ stroke="currentColor"
70
+ strokeWidth="2"
71
+ strokeLinecap="round"
72
+ strokeLinejoin="round"
73
+ >
74
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
75
+ </svg>
76
+ )
77
+
78
+ /**
79
+ * 聊天悬浮按钮组件
80
+ *
81
+ * 功能:
82
+ * - 可配置位置(预设或自定义)
83
+ * - 支持自定义图标(React 节点或图片 URL)
84
+ * - 悬停和点击动画效果
85
+ *
86
+ * 样式:
87
+ * - 使用 livechat.css 中定义的 CSS 变量
88
+ * - 悬停时放大 1.05 倍
89
+ * - 点击时缩小 0.95 倍
90
+ *
91
+ * @example
92
+ * ```tsx
93
+ * // 使用默认图标
94
+ * <ChatBubble onClick={() => setIsOpen(true)} />
95
+ *
96
+ * // 使用自定义位置
97
+ * <ChatBubble
98
+ * position={{ bottom: "20px", right: "30px" }}
99
+ * onClick={() => setIsOpen(true)}
100
+ * />
101
+ *
102
+ * // 使用图片作为图标
103
+ * <ChatBubble
104
+ * iconImageUrl="https://example.com/icon.png"
105
+ * onClick={() => setIsOpen(true)}
106
+ * />
107
+ * ```
108
+ */
109
+ export const ChatBubble: React.FC<ChatBubbleProps> = ({
110
+ position,
111
+ onClick,
112
+ icon,
113
+ iconImageUrl,
114
+ visible = true,
115
+ className = '',
116
+ }) => {
117
+ if (!visible) return null
118
+
119
+ const positionStyles = getPositionStyles(position)
120
+
121
+ // 渲染图标内容
122
+ const renderIcon = () => {
123
+ // 优先使用图片 URL
124
+ if (iconImageUrl) {
125
+ return (
126
+ <img src={iconImageUrl} alt="Chat" className="size-full object-cover" style={{ borderRadius: 'inherit' }} />
127
+ )
128
+ }
129
+ // 其次使用自定义图标
130
+ if (icon) {
131
+ return icon
132
+ }
133
+ // 最后使用默认图标
134
+ return <DefaultChatIcon />
135
+ }
136
+
137
+ return (
138
+ <button
139
+ type="button"
140
+ className={`livechat-bubble ${className}`}
141
+ style={{
142
+ ...positionStyles,
143
+ // 如果传入了图片,移除默认背景色
144
+ ...(iconImageUrl && { background: 'transparent' }),
145
+ }}
146
+ onClick={onClick}
147
+ aria-label="打开聊天窗口"
148
+ >
149
+ <div className="flex size-full items-center justify-center">{renderIcon()}</div>
150
+ </button>
151
+ )
152
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * 聊天窗口头部组件
3
+ * 显示标题、Logo、关闭按钮和新会话按钮
4
+ * 基于 specs/livechat-widget/plan.md 的头部设计
5
+ */
6
+
7
+ import React from 'react'
8
+
9
+ export interface ChatHeaderProps {
10
+ /**
11
+ * 头部标题
12
+ */
13
+ title?: string
14
+
15
+ /**
16
+ * Logo URL
17
+ */
18
+ logoUrl?: string
19
+
20
+ /**
21
+ * 关闭按钮点击回调
22
+ */
23
+ onClose?: () => void
24
+
25
+ /**
26
+ * 新会话按钮点击回调
27
+ */
28
+ onNewSession?: () => void
29
+
30
+ /**
31
+ * 是否显示新会话按钮
32
+ * @default true
33
+ */
34
+ showNewSessionButton?: boolean
35
+
36
+ /**
37
+ * 自定义样式类名
38
+ */
39
+ className?: string
40
+ }
41
+
42
+ /**
43
+ * 默认关闭图标 (X)
44
+ */
45
+ const CloseIcon: React.FC = () => (
46
+ <svg
47
+ width="20"
48
+ height="20"
49
+ viewBox="0 0 24 24"
50
+ fill="none"
51
+ stroke="currentColor"
52
+ strokeWidth="2"
53
+ strokeLinecap="round"
54
+ strokeLinejoin="round"
55
+ >
56
+ <line x1="18" y1="6" x2="6" y2="18" />
57
+ <line x1="6" y1="6" x2="18" y2="18" />
58
+ </svg>
59
+ )
60
+
61
+ /**
62
+ * 新会话图标 (加号)
63
+ */
64
+ const NewSessionIcon: React.FC = () => (
65
+ <svg
66
+ width="18"
67
+ height="18"
68
+ viewBox="0 0 24 24"
69
+ fill="none"
70
+ stroke="currentColor"
71
+ strokeWidth="2"
72
+ strokeLinecap="round"
73
+ strokeLinejoin="round"
74
+ >
75
+ <line x1="12" y1="5" x2="12" y2="19" />
76
+ <line x1="5" y1="12" x2="19" y2="12" />
77
+ </svg>
78
+ )
79
+
80
+ /**
81
+ * 聊天窗口头部组件
82
+ *
83
+ * 功能:
84
+ * - 显示标题和 Logo
85
+ * - 关闭按钮(移动端隐藏,桌面端显示)
86
+ * - 新会话按钮(清空当前对话)
87
+ *
88
+ * 布局:
89
+ * - Logo (可选) + 标题 | 新会话按钮 + 关闭按钮
90
+ * - 固定高度 64px
91
+ * - 边框分隔下方内容
92
+ *
93
+ * @example
94
+ * ```tsx
95
+ * <ChatHeader
96
+ * title="AI 助手"
97
+ * logoUrl="/logo.png"
98
+ * onClose={() => setIsOpen(false)}
99
+ * onNewSession={() => createNewSession()}
100
+ * />
101
+ * ```
102
+ */
103
+ export const ChatHeader: React.FC<ChatHeaderProps> = ({
104
+ title = '',
105
+ logoUrl,
106
+ onClose,
107
+ onNewSession,
108
+ showNewSessionButton = true,
109
+ className = '',
110
+ }) => {
111
+ return (
112
+ <div
113
+ className={`flex items-center justify-between border-b border-[#DADCE0] bg-white px-4 pb-3 pt-0 tablet:pt-4 ${className}`}
114
+ >
115
+ {/* 左侧:Logo + 标题 */}
116
+ <div className="flex items-center gap-3">
117
+ {logoUrl && <img src={logoUrl} alt="Logo" className="size-8 rounded-full object-cover" />}
118
+ <h2 className="text-lg font-semibold text-gray-900">{title}</h2>
119
+ </div>
120
+
121
+ {/* 右侧:新会话按钮 + 关闭按钮 */}
122
+ <div className="flex items-center gap-2">
123
+ {/* 新会话按钮 */}
124
+ {showNewSessionButton && onNewSession && (
125
+ <button
126
+ type="button"
127
+ onClick={onNewSession}
128
+ className="rounded-lg p-2 text-gray-600"
129
+ aria-label="开始新会话"
130
+ title="开始新会话"
131
+ >
132
+ <NewSessionIcon />
133
+ </button>
134
+ )}
135
+
136
+ {/* 关闭按钮 */}
137
+ {onClose && (
138
+ <button
139
+ type="button"
140
+ onClick={onClose}
141
+ className="tablet:block rounded-lg text-gray-600"
142
+ aria-label="Close chat"
143
+ >
144
+ <CloseIcon />
145
+ </button>
146
+ )}
147
+ </div>
148
+ </div>
149
+ )
150
+ }