@ai-group/chat-sdk 3.3.0 → 3.3.3
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.
- package/README.md +1 -1
- package/dist/cjs/components/XAdkChatbot/index.js +28 -21
- package/dist/cjs/components/XAdkChatbot/index.js.map +2 -2
- package/dist/cjs/components/XAdkChatbot/styles.js +4 -0
- package/dist/cjs/components/XAdkChatbot/styles.js.map +2 -2
- package/dist/cjs/components/XAdkProvider/compound/Messages.d.ts +3 -1
- package/dist/cjs/components/XAdkProvider/compound/Messages.js +2 -0
- package/dist/cjs/components/XAdkProvider/compound/Messages.js.map +2 -2
- package/dist/cjs/components/XAdkSender/index.js +2 -1
- package/dist/cjs/components/XAdkSender/index.js.map +2 -2
- package/dist/cjs/hooks/useADKChat.js +49 -6
- package/dist/cjs/hooks/useADKChat.js.map +2 -2
- package/dist/cjs/presets/xGroupAdk.js +2 -1
- package/dist/cjs/presets/xGroupAdk.js.map +2 -2
- package/dist/cjs/types/XAdkChatbot.d.ts +25 -4
- package/dist/cjs/types/XAdkChatbot.js.map +1 -1
- package/dist/cjs/types/XAdkSender.d.ts +2 -2
- package/dist/cjs/types/XAdkSender.js.map +1 -1
- package/dist/esm/components/XAdkChatbot/index.js +11 -8
- package/dist/esm/components/XAdkChatbot/index.js.map +1 -1
- package/dist/esm/components/XAdkChatbot/styles.js +1 -1
- package/dist/esm/components/XAdkChatbot/styles.js.map +1 -1
- package/dist/esm/components/XAdkProvider/compound/Messages.d.ts +3 -1
- package/dist/esm/components/XAdkProvider/compound/Messages.js +3 -1
- package/dist/esm/components/XAdkProvider/compound/Messages.js.map +1 -1
- package/dist/esm/components/XAdkSender/index.js +3 -2
- package/dist/esm/components/XAdkSender/index.js.map +1 -1
- package/dist/esm/hooks/useADKChat.js +42 -6
- package/dist/esm/hooks/useADKChat.js.map +1 -1
- package/dist/esm/presets/xGroupAdk.js +8 -7
- package/dist/esm/presets/xGroupAdk.js.map +1 -1
- package/dist/esm/types/XAdkChatbot.d.ts +25 -4
- package/dist/esm/types/XAdkChatbot.js.map +1 -1
- package/dist/esm/types/XAdkSender.d.ts +2 -2
- package/dist/esm/types/XAdkSender.js.map +1 -1
- package/dist/umd/chat-sdk.min.js +1 -1
- package/package.json +14 -12
package/README.md
CHANGED
|
@@ -157,7 +157,7 @@ const chatbot = new XAiWebSDK(container, {
|
|
|
157
157
|
|
|
158
158
|
- `options.file`: 要上传的 File 对象
|
|
159
159
|
- `options.onProgress`: 进度回调,参数 `{ percent: number }`
|
|
160
|
-
- `options.onSuccess`:
|
|
160
|
+
- `options.onSuccess`: 上传成功回调,参数类型为 `UploadFileResult | UploadFileResult[] | { data: UploadFileResult | UploadFileResult[] }`
|
|
161
161
|
- `options.onError`: 上传失败回调,参数为 Error 对象
|
|
162
162
|
|
|
163
163
|
**示例:**
|
|
@@ -62,6 +62,7 @@ var XAdkChatbot = ({
|
|
|
62
62
|
onCopy,
|
|
63
63
|
onShowLog,
|
|
64
64
|
actions,
|
|
65
|
+
actionsExtra,
|
|
65
66
|
className,
|
|
66
67
|
style,
|
|
67
68
|
// welcome = null,
|
|
@@ -319,6 +320,8 @@ var XAdkChatbot = ({
|
|
|
319
320
|
const lastBotMsg = msgs[msgs.length - 1];
|
|
320
321
|
const hasProcess = nodes.some((n) => n.type === "process");
|
|
321
322
|
const isGroupLoading = loading && isLastGroup;
|
|
323
|
+
const actionProps = { message: group, isLastBotMsg: isLastGroup };
|
|
324
|
+
const actionExtraNode = actionsExtra == null ? void 0 : actionsExtra(actionProps);
|
|
322
325
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: styles.botMsg, children: [
|
|
323
326
|
nodes.map((node, idx) => {
|
|
324
327
|
if (node.type === "process") {
|
|
@@ -355,28 +358,31 @@ var XAdkChatbot = ({
|
|
|
355
358
|
] }) }),
|
|
356
359
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px" }, children: allFiles.map((file, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_FileGallery.default, { file, onClick: onFileClick }, index)) })
|
|
357
360
|
] }),
|
|
358
|
-
!isGroupLoading && (actions
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
361
|
+
!isGroupLoading && ((actions || showRetry || showCopy || showLog || actionExtraNode) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: styles.metaFooter, children: [
|
|
362
|
+
actions ? actions(actionProps) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_antd.Flex, { gap: 16, className: styles.actionIcons, children: [
|
|
363
|
+
showRetry && isLastGroup && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Tooltip, { title: "重新生成", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.ReloadOutlined, { onClick: onRetry }) }),
|
|
364
|
+
showCopy && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Tooltip, { title: "复制内容", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
365
|
+
import_icons.CopyOutlined,
|
|
366
|
+
{
|
|
367
|
+
onClick: () => {
|
|
368
|
+
(0, import_copy_to_clipboard.default)(fullTextToCopy);
|
|
369
|
+
import_antd.message.success("复制成功");
|
|
370
|
+
onCopy == null ? void 0 : onCopy(fullTextToCopy);
|
|
371
|
+
}
|
|
367
372
|
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
|
|
373
|
+
) }),
|
|
374
|
+
showLog && (lastBotMsg == null ? void 0 : lastBotMsg.invocationId) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Tooltip, { title: "查看日志", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
375
|
+
import_icons.InfoCircleOutlined,
|
|
376
|
+
{
|
|
377
|
+
onClick: () => onShowLog == null ? void 0 : onShowLog(
|
|
378
|
+
lastBotMsg.invocationId,
|
|
379
|
+
lastBotMsg.timestamp
|
|
380
|
+
)
|
|
381
|
+
}
|
|
382
|
+
) })
|
|
383
|
+
] }),
|
|
384
|
+
actionExtraNode
|
|
385
|
+
] }))
|
|
380
386
|
] }, group.id);
|
|
381
387
|
},
|
|
382
388
|
[
|
|
@@ -393,6 +399,7 @@ var XAdkChatbot = ({
|
|
|
393
399
|
onCopy,
|
|
394
400
|
onShowLog,
|
|
395
401
|
actions,
|
|
402
|
+
actionsExtra,
|
|
396
403
|
onFileClick,
|
|
397
404
|
styles,
|
|
398
405
|
resolveToolKind,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/XAdkChatbot/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import React, { useMemo, useCallback, useEffect, useRef } from \"react\";\nimport { Button, Flex, Tooltip, message as antdMessage } from \"antd\";\nimport clsx from \"clsx\";\nimport {\n SwapRightOutlined,\n ReloadOutlined,\n CopyOutlined,\n InfoCircleOutlined,\n CheckCircleFilled,\n} from \"@ant-design/icons\";\nimport copy from \"copy-to-clipboard\";\nimport { useStyles } from \"./styles\";\nimport MarkdownRender from \"./components/MarkdownRender\";\nimport FunctionCallRender from \"./components/FunctionCallRender\";\nimport FileGallery from \"../FileGallery\";\nimport XAdkThoughtChain from \"@/components/XAdkThoughtChain\";\nimport { parseAgentMessage } from \"@/utils\";\nimport { mergeChatStrategies } from \"@/presets/xGroupAdk\";\nimport type { IMessage, XAdkChatbotProps, ChatGroup } from \"@/types\";\nimport type { ThoughtChainItemType } from \"@/types/XAdkThoughtChain\";\nimport { defaultToolKindResolver } from \"@/types/FunctionCallRender\";\n\nconst scrollThreshold = 10;\n\n/**\n * XAdkChatbot - 增强版聊天组件\n *\n * 新增功能:\n * - ✅ 自动消息分组 (enableGrouping)\n * - ✅ 自动解析思维链 (enableProcessParsing)\n * - ✅ 文件展示 (FileGallery)\n * - ✅ 操作栏 (重试/复制/日志)\n * - ✅ 欢迎页面 (agentName/agentIcon/description)\n */\nconst XAdkChatbot: React.FC<XAdkChatbotProps> = ({\n loading = false,\n prologue,\n suggestions,\n messages,\n showFnCallDetail,\n onConfirm,\n onSuggest,\n showRetry,\n showCopy,\n showLog,\n onRetry,\n onCopy,\n onShowLog,\n actions,\n className,\n style,\n // welcome = null,\n enableGrouping = true,\n enableProcessParsing = true,\n parseOptions,\n initialized = false,\n onFileClick,\n renderFunctionCall,\n toolKindResolver,\n strategies,\n preset,\n}) => {\n const styles = useStyles();\n const listRef = useRef<HTMLDivElement>(null);\n const lastScrollTopRef = useRef(0);\n const userHasScrolledRef = useRef(false);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const prevInitializedRef = useRef(false);\n const mergedStrategies = useMemo(\n () => mergeChatStrategies(preset, strategies),\n [preset, strategies],\n );\n const resolveToolKind = useCallback(\n (name?: string, msg?: IMessage) =>\n toolKindResolver?.(name) ??\n mergedStrategies.resolveToolKind?.({ name, msg }) ??\n defaultToolKindResolver(name),\n [toolKindResolver, mergedStrategies],\n );\n const parseProcessMessage =\n mergedStrategies.parseProcessMessage ?? parseAgentMessage;\n\n // 初始化完成时滚动到底部\n useEffect(() => {\n if (!initialized) return;\n if (!messages.length) return;\n if (prevInitializedRef.current) return; // 只在第一次 initialized+messages 就绪时触发\n prevInitializedRef.current = true;\n // 推迟到浏览器完成 paint 后执行,确保 DOM 高度已撑开\n const timer = setTimeout(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"auto\" });\n }, 0);\n return () => clearTimeout(timer);\n }, [initialized, messages]);\n\n // 流式输出时滚动到底部\n useEffect(() => {\n if (!loading) return;\n if (userHasScrolledRef.current) return;\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n }, [loading, messages]);\n\n // 处理滚动事件\n const handleScroll = useCallback(() => {\n const el = listRef.current;\n if (!el) return;\n const currentTop = el.scrollTop;\n\n if (currentTop < lastScrollTopRef.current) {\n userHasScrolledRef.current = true;\n }\n\n const isAtBottom =\n Math.abs(el.scrollHeight - currentTop - el.clientHeight) <=\n scrollThreshold;\n if (isAtBottom) {\n userHasScrolledRef.current = false;\n }\n\n lastScrollTopRef.current = currentTop;\n }, []);\n\n useEffect(() => {\n const listElement = listRef.current;\n if (!listElement) return;\n\n listElement.addEventListener(\"scroll\", handleScroll);\n return () => {\n listElement.removeEventListener(\"scroll\", handleScroll);\n };\n }, [handleScroll]);\n\n // ========== 消息分组逻辑 ==========\n const chatGroups = useMemo(() => {\n if (!enableGrouping) {\n // 不分组,每条消息独立\n return messages.map((msg) => ({\n id: msg.id,\n role: msg.role,\n msgs: [msg],\n invocationId: msg.invocationId,\n allFiles: msg.fileData || [],\n isLike: msg.isLike ?? 0,\n }));\n }\n\n const groups: ChatGroup[] = [];\n messages.forEach((msg) => {\n // 过滤 followup 消息\n if ((msg as any).role === \"followup\") return;\n\n const isRealUserQuery =\n msg.role === \"user\" && !msg.functionResponse && !msg.functionCall;\n const lastGroup = groups[groups.length - 1];\n const isLastGroupAgent = lastGroup?.role === \"bot\";\n\n if (isLastGroupAgent && !isRealUserQuery) {\n // 合并到上一个 bot 分组\n lastGroup.msgs.push(msg);\n if (msg.invocationId) lastGroup.invocationId = msg.invocationId;\n // 更新 isLike: 取最新的非0值,或保持当前值\n if (msg.isLike && msg.isLike !== 0) {\n lastGroup.isLike = msg.isLike;\n }\n } else {\n // 创建新分组\n groups.push({\n id: msg.id || `group-${groups.length}`,\n role: isRealUserQuery ? \"user\" : \"bot\",\n msgs: [msg],\n invocationId: msg.invocationId,\n allFiles: [],\n isLike: msg.isLike ?? 0,\n });\n }\n });\n\n // 合并文件\n groups.forEach((g) => {\n g.allFiles = g.msgs.reduce(\n (acc, m) => [...acc, ...(m.fileData || [])],\n [] as any[],\n );\n });\n\n return groups;\n }, [messages, enableGrouping]);\n\n // ========== 渲染 Bot 消息组 ==========\n const renderBotGroup = useCallback(\n (group: ChatGroup, isLastGroup: boolean, renderFunctionCall?: XAdkChatbotProps[\"renderFunctionCall\"]) => {\n const { msgs, allFiles } = group;\n\n if (!enableProcessParsing) {\n // 不解析 process,简单渲染\n return (\n <div key={group.id} className={styles.botMsg}>\n {msgs.map((msg, i) => {\n if (msg.text) {\n return (\n <MarkdownRender\n key={`${msg.id}-${i}`}\n text={msg.text}\n onFileClick={onFileClick}\n />\n );\n }\n if (msg.functionCall) {\n return (\n renderFunctionCall?.(msg) ?? (\n <FunctionCallRender\n key={`${msg.id}-${i}`}\n msg={msg}\n showDetail={showFnCallDetail}\n onConfirm={onConfirm}\n kind={resolveToolKind(msg.functionCall?.name, msg)}\n renderApproval={mergedStrategies.renderApproval}\n renderHandoff={mergedStrategies.renderHandoff}\n />\n )\n );\n }\n return null;\n })}\n </div>\n );\n }\n\n // ========== 解析 Process 内容 ==========\n\n // 1. 合并工具调用\n const mergedToolMap = new Map<string, IMessage>();\n msgs.forEach((msg) => {\n if (msg.functionCall) {\n const callId = msg.functionCall.id || \"\";\n if (!mergedToolMap.has(callId)) {\n mergedToolMap.set(callId, { ...msg });\n } else {\n mergedToolMap.set(callId, {\n ...(mergedToolMap.get(callId) ?? {}),\n ...msg,\n });\n }\n } else if (msg.functionResponse) {\n const callId = msg.functionResponse.id || \"\";\n if (mergedToolMap.has(callId)) {\n const tool = mergedToolMap.get(callId);\n if (tool) tool.functionResponse = msg.functionResponse;\n } else {\n mergedToolMap.set(callId, {\n ...msg,\n functionCall: {\n id: callId,\n name: msg.functionResponse.name || \"Unknown\",\n args: {},\n },\n });\n }\n }\n });\n\n // 2. 解析文本消息中的 process 内容\n type RenderNode =\n | { type: \"text\"; content: string; key: string }\n | { type: \"process\"; items: ThoughtChainItemType[]; key: string };\n\n const nodes: RenderNode[] = [];\n let currentProcessItems: ThoughtChainItemType[] = [];\n const processedToolIds = new Set<string>();\n\n const flushProcessItems = () => {\n if (currentProcessItems.length > 0) {\n nodes.push({\n type: \"process\",\n items: [...currentProcessItems],\n key: `process-${nodes.length}`,\n });\n currentProcessItems = [];\n }\n };\n\n msgs.forEach((msg) => {\n // 处理工具调用\n if (msg.functionCall) {\n const callId = msg.functionCall.id || \"\";\n if (!processedToolIds.has(callId)) {\n const mergedMsg = mergedToolMap.get(callId);\n if (mergedMsg) {\n currentProcessItems.push({\n type: \"tool\",\n key: `tool-${callId}`,\n content: \"\",\n msg: mergedMsg,\n });\n processedToolIds.add(callId);\n }\n }\n }\n // 处理文本消息\n else if (msg.text && !msg.functionResponse) {\n const parts = parseProcessMessage(msg.text, parseOptions as any);\n\n parts.forEach((part, partIdx) => {\n // process 内容放入 currentProcessItems\n if (\n [\n \"planning\",\n \"replanning\",\n \"reasoning\",\n \"action_log\",\n \"process_text\",\n ].includes(part.type)\n ) {\n const titleMap: Record<string, string> = {\n planning: \"任务规划\",\n replanning: \"重新规划\",\n reasoning: \"推理分析\",\n action_log: \"行动记录\",\n process_text: \"过程分析\",\n };\n\n currentProcessItems.push({\n type: \"text\",\n key: `${msg.id}-${partIdx}`,\n content: part.content,\n title: titleMap[part.type] || \"分析\",\n });\n }\n // 普通文本内容\n else {\n flushProcessItems();\n if (part.content.trim()) {\n const lastNode = nodes[nodes.length - 1];\n if (lastNode?.type === \"text\") {\n // 合并到上一个文本节点\n lastNode.content += \"\\n\\n\" + part.content;\n } else {\n nodes.push({\n type: \"text\",\n content: part.content,\n key: `text-${msg.id}-${partIdx}`,\n });\n }\n }\n }\n });\n }\n });\n\n // 最后flush一次\n flushProcessItems();\n\n // 3. 准备操作栏数据\n const fullTextToCopy = msgs\n .filter((m) => !m.functionCall && !m.functionResponse)\n .map((m) => m.text || \"\")\n .join(\"\");\n\n const lastBotMsg = msgs[msgs.length - 1];\n const hasProcess = nodes.some((n) => n.type === \"process\");\n const isGroupLoading = loading && isLastGroup;\n\n // 4. 渲染\n return (\n <div key={group.id} className={styles.botMsg}>\n {nodes.map((node, idx) => {\n if (node.type === \"process\") {\n return (\n <XAdkThoughtChain\n key={node.key}\n loading={isGroupLoading}\n title=\"思维链已完成\"\n items={node.items}\n showFnCallDetail={showFnCallDetail}\n onConfirm={onConfirm}\n defaultOpen={isGroupLoading}\n renderFunctionCall={renderFunctionCall}\n toolKindResolver={(name) => resolveToolKind(name)}\n strategies={mergedStrategies}\n />\n );\n }\n\n // 文本节点\n const showBadge =\n hasProcess && nodes.findIndex((n) => n.type === \"text\") === idx;\n return (\n <div key={node.key}>\n {showBadge && (\n <div className={styles.successBadge}>\n <CheckCircleFilled /> 已完成所有规划任务\n </div>\n )}\n <MarkdownRender text={node.content} onFileClick={onFileClick} />\n </div>\n );\n })}\n\n {/* 文件展示 */}\n {allFiles.length > 0 && (\n <div className={styles.fileSection}>\n <div className={styles.fileHeader}>\n <span>生成文件 ({allFiles.length})</span>\n </div>\n <div style={{ display: \"flex\", flexWrap: \"wrap\", gap: \"8px\" }}>\n {allFiles.map((file, index) => (\n <FileGallery key={index} file={file} onClick={onFileClick} />\n ))}\n </div>\n </div>\n )}\n\n {/* 操作栏 */}\n {!isGroupLoading &&\n (actions\n ? actions({ message: group, isLastBotMsg: isLastGroup })\n : (showRetry || showCopy || showLog) && (\n <div className={styles.metaFooter}>\n <Flex gap={16} className={styles.actionIcons}>\n {showRetry && isLastGroup && (\n <Tooltip title=\"重新生成\">\n <ReloadOutlined onClick={onRetry} />\n </Tooltip>\n )}\n {showCopy && (\n <Tooltip title=\"复制内容\">\n <CopyOutlined\n onClick={() => {\n copy(fullTextToCopy);\n antdMessage.success(\"复制成功\");\n onCopy?.(fullTextToCopy);\n }}\n />\n </Tooltip>\n )}\n {showLog && lastBotMsg?.invocationId && (\n <Tooltip title=\"查看日志\">\n <InfoCircleOutlined\n onClick={() =>\n onShowLog?.(\n lastBotMsg.invocationId!,\n lastBotMsg.timestamp,\n )\n }\n />\n </Tooltip>\n )}\n </Flex>\n </div>\n ))}\n </div>\n );\n },\n [\n enableProcessParsing,\n parseOptions,\n parseProcessMessage,\n showFnCallDetail,\n onConfirm,\n loading,\n showRetry,\n showCopy,\n showLog,\n onRetry,\n onCopy,\n onShowLog,\n actions,\n onFileClick,\n styles,\n resolveToolKind,\n mergedStrategies,\n ],\n );\n\n // ========== 渲染用户消息组 ==========\n const renderUserGroup = useCallback(\n (group: ChatGroup) => {\n return (\n <div key={group.id} className={styles.userMsg}>\n {group.msgs.map((m, i) => (\n <div key={m.id || i} className={styles.userContainer}>\n {/* 文件展示 */}\n {m.fileData && m.fileData.length > 0 && (\n <>\n {m.fileData.map((file, index) => (\n <FileGallery\n key={index}\n file={file}\n align=\"left\"\n style={{ marginBottom: \"16px\" }}\n onClick={onFileClick}\n />\n ))}\n </>\n )}\n {/* 文本展示 */}\n {m.text && <div className={styles.card}>{m.text}</div>}\n </div>\n ))}\n </div>\n );\n },\n [styles, onFileClick],\n );\n\n // ========== 欢迎页面 ==========\n // const isEmpty =\n // messages.length === 0 &&\n // !prologue &&\n // (!suggestions || suggestions.length === 0);\n\n // const renderWelcome = () => {\n // if (!isEmpty) return null;\n\n // return (\n // <>\n // {welcome ?? (\n // <div className={styles.welcomeWrapper}>\n // {agentIcon && (\n // <img src={agentIcon} alt=\"icon\" className={styles.welcomeIcon} />\n // )}\n // {agentName && (\n // <div className={styles.welcomeTitle}>{agentName}</div>\n // )}\n // {description && (\n // <div className={styles.welcomeDesc}>{description}</div>\n // )}\n // </div>\n // )}\n // </>\n // );\n // };\n\n // ========== 渲染建议问题 ==========\n const renderSuggestions = () => {\n if (!suggestions || suggestions.length === 0) return null;\n\n return (\n <div className={styles.suggestionWrapper}>\n {suggestions.map((item) => (\n <div key={item} className={styles.suggestion}>\n <div className={styles.suggestContent}>\n <Button\n type=\"text\"\n icon={<SwapRightOutlined />}\n iconPosition=\"end\"\n onClick={() => {\n if (!item) return;\n onSuggest?.(item);\n }}\n style={{\n whiteSpace: \"normal\",\n height: \"auto\",\n wordWrap: \"break-word\",\n textAlign: \"left\",\n padding: \"4px 15px\",\n lineHeight: \"1.5\",\n }}\n >\n {item}\n </Button>\n </div>\n </div>\n ))}\n </div>\n );\n };\n\n return (\n <div className={clsx(styles.wrapper, className)} style={style}>\n <div className={styles.list} ref={listRef}>\n {/* 开场白 */}\n {prologue && (\n <div className={styles.prologue}>\n <MarkdownRender text={prologue} onFileClick={onFileClick} />\n </div>\n )}\n\n {/* 欢迎页面 */}\n {/* {renderWelcome()} */}\n\n {/* 消息列表 */}\n {chatGroups.map((group, idx) =>\n group.role === \"user\"\n ? renderUserGroup(group)\n : renderBotGroup(group, idx === chatGroups.length - 1, renderFunctionCall),\n )}\n\n {/* 建议问题 */}\n {renderSuggestions()}\n\n <div ref={messagesEndRef} />\n </div>\n </div>\n );\n};\n\nexport default XAdkChatbot;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA+D;AAC/D,kBAA8D;AAC9D,kBAAiB;AACjB,mBAMO;AACP,+BAAiB;AACjB,oBAA0B;AAC1B,4BAA2B;AAC3B,gCAA+B;AAC/B,yBAAwB;AACxB,8BAA6B;AAC7B,mBAAkC;AAClC,uBAAoC;AAGpC,IAAAA,6BAAwC;
|
|
4
|
+
"sourcesContent": ["import React, { useMemo, useCallback, useEffect, useRef } from \"react\";\nimport { Button, Flex, Tooltip, message as antdMessage } from \"antd\";\nimport clsx from \"clsx\";\nimport {\n SwapRightOutlined,\n ReloadOutlined,\n CopyOutlined,\n InfoCircleOutlined,\n CheckCircleFilled,\n} from \"@ant-design/icons\";\nimport copy from \"copy-to-clipboard\";\nimport { useStyles } from \"./styles\";\nimport MarkdownRender from \"./components/MarkdownRender\";\nimport FunctionCallRender from \"./components/FunctionCallRender\";\nimport FileGallery from \"../FileGallery\";\nimport XAdkThoughtChain from \"@/components/XAdkThoughtChain\";\nimport { parseAgentMessage } from \"@/utils\";\nimport { mergeChatStrategies } from \"@/presets/xGroupAdk\";\nimport type { IMessage, XAdkChatbotProps, ChatGroup } from \"@/types\";\nimport type { ThoughtChainItemType } from \"@/types/XAdkThoughtChain\";\nimport { defaultToolKindResolver } from \"@/types/FunctionCallRender\";\n\nconst scrollThreshold = 10;\n\n/**\n * XAdkChatbot - 增强版聊天组件\n *\n * 新增功能:\n * - ✅ 自动消息分组 (enableGrouping)\n * - ✅ 自动解析思维链 (enableProcessParsing)\n * - ✅ 文件展示 (FileGallery)\n * - ✅ 操作栏 (重试/复制/日志)\n * - ✅ 欢迎页面 (agentName/agentIcon/description)\n */\nconst XAdkChatbot: React.FC<XAdkChatbotProps> = ({\n loading = false,\n prologue,\n suggestions,\n messages,\n showFnCallDetail,\n onConfirm,\n onSuggest,\n showRetry,\n showCopy,\n showLog,\n onRetry,\n onCopy,\n onShowLog,\n actions,\n actionsExtra,\n className,\n style,\n // welcome = null,\n enableGrouping = true,\n enableProcessParsing = true,\n parseOptions,\n initialized = false,\n onFileClick,\n renderFunctionCall,\n toolKindResolver,\n strategies,\n preset,\n}) => {\n const styles = useStyles();\n const listRef = useRef<HTMLDivElement>(null);\n const lastScrollTopRef = useRef(0);\n const userHasScrolledRef = useRef(false);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const prevInitializedRef = useRef(false);\n const mergedStrategies = useMemo(\n () => mergeChatStrategies(preset, strategies),\n [preset, strategies],\n );\n const resolveToolKind = useCallback(\n (name?: string, msg?: IMessage) =>\n toolKindResolver?.(name) ??\n mergedStrategies.resolveToolKind?.({ name, msg }) ??\n defaultToolKindResolver(name),\n [toolKindResolver, mergedStrategies],\n );\n const parseProcessMessage =\n mergedStrategies.parseProcessMessage ?? parseAgentMessage;\n\n // 初始化完成时滚动到底部\n useEffect(() => {\n if (!initialized) return;\n if (!messages.length) return;\n if (prevInitializedRef.current) return; // 只在第一次 initialized+messages 就绪时触发\n prevInitializedRef.current = true;\n // 推迟到浏览器完成 paint 后执行,确保 DOM 高度已撑开\n const timer = setTimeout(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"auto\" });\n }, 0);\n return () => clearTimeout(timer);\n }, [initialized, messages]);\n\n // 流式输出时滚动到底部\n useEffect(() => {\n if (!loading) return;\n if (userHasScrolledRef.current) return;\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n }, [loading, messages]);\n\n // 处理滚动事件\n const handleScroll = useCallback(() => {\n const el = listRef.current;\n if (!el) return;\n const currentTop = el.scrollTop;\n\n if (currentTop < lastScrollTopRef.current) {\n userHasScrolledRef.current = true;\n }\n\n const isAtBottom =\n Math.abs(el.scrollHeight - currentTop - el.clientHeight) <=\n scrollThreshold;\n if (isAtBottom) {\n userHasScrolledRef.current = false;\n }\n\n lastScrollTopRef.current = currentTop;\n }, []);\n\n useEffect(() => {\n const listElement = listRef.current;\n if (!listElement) return;\n\n listElement.addEventListener(\"scroll\", handleScroll);\n return () => {\n listElement.removeEventListener(\"scroll\", handleScroll);\n };\n }, [handleScroll]);\n\n // ========== 消息分组逻辑 ==========\n const chatGroups = useMemo(() => {\n if (!enableGrouping) {\n // 不分组,每条消息独立\n return messages.map((msg) => ({\n id: msg.id,\n role: msg.role,\n msgs: [msg],\n invocationId: msg.invocationId,\n allFiles: msg.fileData || [],\n isLike: msg.isLike ?? 0,\n }));\n }\n\n const groups: ChatGroup[] = [];\n messages.forEach((msg) => {\n // 过滤 followup 消息\n if ((msg as any).role === \"followup\") return;\n\n const isRealUserQuery =\n msg.role === \"user\" && !msg.functionResponse && !msg.functionCall;\n const lastGroup = groups[groups.length - 1];\n const isLastGroupAgent = lastGroup?.role === \"bot\";\n\n if (isLastGroupAgent && !isRealUserQuery) {\n // 合并到上一个 bot 分组\n lastGroup.msgs.push(msg);\n if (msg.invocationId) lastGroup.invocationId = msg.invocationId;\n // 更新 isLike: 取最新的非0值,或保持当前值\n if (msg.isLike && msg.isLike !== 0) {\n lastGroup.isLike = msg.isLike;\n }\n } else {\n // 创建新分组\n groups.push({\n id: msg.id || `group-${groups.length}`,\n role: isRealUserQuery ? \"user\" : \"bot\",\n msgs: [msg],\n invocationId: msg.invocationId,\n allFiles: [],\n isLike: msg.isLike ?? 0,\n });\n }\n });\n\n // 合并文件\n groups.forEach((g) => {\n g.allFiles = g.msgs.reduce(\n (acc, m) => [...acc, ...(m.fileData || [])],\n [] as any[],\n );\n });\n\n return groups;\n }, [messages, enableGrouping]);\n\n // ========== 渲染 Bot 消息组 ==========\n const renderBotGroup = useCallback(\n (group: ChatGroup, isLastGroup: boolean, renderFunctionCall?: XAdkChatbotProps[\"renderFunctionCall\"]) => {\n const { msgs, allFiles } = group;\n\n if (!enableProcessParsing) {\n // 不解析 process,简单渲染\n return (\n <div key={group.id} className={styles.botMsg}>\n {msgs.map((msg, i) => {\n if (msg.text) {\n return (\n <MarkdownRender\n key={`${msg.id}-${i}`}\n text={msg.text}\n onFileClick={onFileClick}\n />\n );\n }\n if (msg.functionCall) {\n return (\n renderFunctionCall?.(msg) ?? (\n <FunctionCallRender\n key={`${msg.id}-${i}`}\n msg={msg}\n showDetail={showFnCallDetail}\n onConfirm={onConfirm}\n kind={resolveToolKind(msg.functionCall?.name, msg)}\n renderApproval={mergedStrategies.renderApproval}\n renderHandoff={mergedStrategies.renderHandoff}\n />\n )\n );\n }\n return null;\n })}\n </div>\n );\n }\n\n // ========== 解析 Process 内容 ==========\n\n // 1. 合并工具调用\n const mergedToolMap = new Map<string, IMessage>();\n msgs.forEach((msg) => {\n if (msg.functionCall) {\n const callId = msg.functionCall.id || \"\";\n if (!mergedToolMap.has(callId)) {\n mergedToolMap.set(callId, { ...msg });\n } else {\n mergedToolMap.set(callId, {\n ...(mergedToolMap.get(callId) ?? {}),\n ...msg,\n });\n }\n } else if (msg.functionResponse) {\n const callId = msg.functionResponse.id || \"\";\n if (mergedToolMap.has(callId)) {\n const tool = mergedToolMap.get(callId);\n if (tool) tool.functionResponse = msg.functionResponse;\n } else {\n mergedToolMap.set(callId, {\n ...msg,\n functionCall: {\n id: callId,\n name: msg.functionResponse.name || \"Unknown\",\n args: {},\n },\n });\n }\n }\n });\n\n // 2. 解析文本消息中的 process 内容\n type RenderNode =\n | { type: \"text\"; content: string; key: string }\n | { type: \"process\"; items: ThoughtChainItemType[]; key: string };\n\n const nodes: RenderNode[] = [];\n let currentProcessItems: ThoughtChainItemType[] = [];\n const processedToolIds = new Set<string>();\n\n const flushProcessItems = () => {\n if (currentProcessItems.length > 0) {\n nodes.push({\n type: \"process\",\n items: [...currentProcessItems],\n key: `process-${nodes.length}`,\n });\n currentProcessItems = [];\n }\n };\n\n msgs.forEach((msg) => {\n // 处理工具调用\n if (msg.functionCall) {\n const callId = msg.functionCall.id || \"\";\n if (!processedToolIds.has(callId)) {\n const mergedMsg = mergedToolMap.get(callId);\n if (mergedMsg) {\n currentProcessItems.push({\n type: \"tool\",\n key: `tool-${callId}`,\n content: \"\",\n msg: mergedMsg,\n });\n processedToolIds.add(callId);\n }\n }\n }\n // 处理文本消息\n else if (msg.text && !msg.functionResponse) {\n const parts = parseProcessMessage(msg.text, parseOptions as any);\n\n parts.forEach((part, partIdx) => {\n // process 内容放入 currentProcessItems\n if (\n [\n \"planning\",\n \"replanning\",\n \"reasoning\",\n \"action_log\",\n \"process_text\",\n ].includes(part.type)\n ) {\n const titleMap: Record<string, string> = {\n planning: \"任务规划\",\n replanning: \"重新规划\",\n reasoning: \"推理分析\",\n action_log: \"行动记录\",\n process_text: \"过程分析\",\n };\n\n currentProcessItems.push({\n type: \"text\",\n key: `${msg.id}-${partIdx}`,\n content: part.content,\n title: titleMap[part.type] || \"分析\",\n });\n }\n // 普通文本内容\n else {\n flushProcessItems();\n if (part.content.trim()) {\n const lastNode = nodes[nodes.length - 1];\n if (lastNode?.type === \"text\") {\n // 合并到上一个文本节点\n lastNode.content += \"\\n\\n\" + part.content;\n } else {\n nodes.push({\n type: \"text\",\n content: part.content,\n key: `text-${msg.id}-${partIdx}`,\n });\n }\n }\n }\n });\n }\n });\n\n // 最后flush一次\n flushProcessItems();\n\n // 3. 准备操作栏数据\n const fullTextToCopy = msgs\n .filter((m) => !m.functionCall && !m.functionResponse)\n .map((m) => m.text || \"\")\n .join(\"\");\n\n const lastBotMsg = msgs[msgs.length - 1];\n const hasProcess = nodes.some((n) => n.type === \"process\");\n const isGroupLoading = loading && isLastGroup;\n const actionProps = { message: group, isLastBotMsg: isLastGroup };\n const actionExtraNode = actionsExtra?.(actionProps);\n\n // 4. 渲染\n return (\n <div key={group.id} className={styles.botMsg}>\n {nodes.map((node, idx) => {\n if (node.type === \"process\") {\n return (\n <XAdkThoughtChain\n key={node.key}\n loading={isGroupLoading}\n title=\"思维链已完成\"\n items={node.items}\n showFnCallDetail={showFnCallDetail}\n onConfirm={onConfirm}\n defaultOpen={isGroupLoading}\n renderFunctionCall={renderFunctionCall}\n toolKindResolver={(name) => resolveToolKind(name)}\n strategies={mergedStrategies}\n />\n );\n }\n\n // 文本节点\n const showBadge =\n hasProcess && nodes.findIndex((n) => n.type === \"text\") === idx;\n return (\n <div key={node.key}>\n {showBadge && (\n <div className={styles.successBadge}>\n <CheckCircleFilled /> 已完成所有规划任务\n </div>\n )}\n <MarkdownRender text={node.content} onFileClick={onFileClick} />\n </div>\n );\n })}\n\n {/* 文件展示 */}\n {allFiles.length > 0 && (\n <div className={styles.fileSection}>\n <div className={styles.fileHeader}>\n <span>生成文件 ({allFiles.length})</span>\n </div>\n <div style={{ display: \"flex\", flexWrap: \"wrap\", gap: \"8px\" }}>\n {allFiles.map((file, index) => (\n <FileGallery key={index} file={file} onClick={onFileClick} />\n ))}\n </div>\n </div>\n )}\n\n {/* 操作栏 */}\n {!isGroupLoading &&\n ((actions || showRetry || showCopy || showLog || actionExtraNode) && (\n <div className={styles.metaFooter}>\n {actions ? (\n actions(actionProps)\n ) : (\n <Flex gap={16} className={styles.actionIcons}>\n {showRetry && isLastGroup && (\n <Tooltip title=\"重新生成\">\n <ReloadOutlined onClick={onRetry} />\n </Tooltip>\n )}\n {showCopy && (\n <Tooltip title=\"复制内容\">\n <CopyOutlined\n onClick={() => {\n copy(fullTextToCopy);\n antdMessage.success(\"复制成功\");\n onCopy?.(fullTextToCopy);\n }}\n />\n </Tooltip>\n )}\n {showLog && lastBotMsg?.invocationId && (\n <Tooltip title=\"查看日志\">\n <InfoCircleOutlined\n onClick={() =>\n onShowLog?.(\n lastBotMsg.invocationId!,\n lastBotMsg.timestamp,\n )\n }\n />\n </Tooltip>\n )}\n </Flex>\n )}\n {actionExtraNode}\n </div>\n ))}\n </div>\n );\n },\n [\n enableProcessParsing,\n parseOptions,\n parseProcessMessage,\n showFnCallDetail,\n onConfirm,\n loading,\n showRetry,\n showCopy,\n showLog,\n onRetry,\n onCopy,\n onShowLog,\n actions,\n actionsExtra,\n onFileClick,\n styles,\n resolveToolKind,\n mergedStrategies,\n ],\n );\n\n // ========== 渲染用户消息组 ==========\n const renderUserGroup = useCallback(\n (group: ChatGroup) => {\n return (\n <div key={group.id} className={styles.userMsg}>\n {group.msgs.map((m, i) => (\n <div key={m.id || i} className={styles.userContainer}>\n {/* 文件展示 */}\n {m.fileData && m.fileData.length > 0 && (\n <>\n {m.fileData.map((file, index) => (\n <FileGallery\n key={index}\n file={file}\n align=\"left\"\n style={{ marginBottom: \"16px\" }}\n onClick={onFileClick}\n />\n ))}\n </>\n )}\n {/* 文本展示 */}\n {m.text && <div className={styles.card}>{m.text}</div>}\n </div>\n ))}\n </div>\n );\n },\n [styles, onFileClick],\n );\n\n // ========== 欢迎页面 ==========\n // const isEmpty =\n // messages.length === 0 &&\n // !prologue &&\n // (!suggestions || suggestions.length === 0);\n\n // const renderWelcome = () => {\n // if (!isEmpty) return null;\n\n // return (\n // <>\n // {welcome ?? (\n // <div className={styles.welcomeWrapper}>\n // {agentIcon && (\n // <img src={agentIcon} alt=\"icon\" className={styles.welcomeIcon} />\n // )}\n // {agentName && (\n // <div className={styles.welcomeTitle}>{agentName}</div>\n // )}\n // {description && (\n // <div className={styles.welcomeDesc}>{description}</div>\n // )}\n // </div>\n // )}\n // </>\n // );\n // };\n\n // ========== 渲染建议问题 ==========\n const renderSuggestions = () => {\n if (!suggestions || suggestions.length === 0) return null;\n\n return (\n <div className={styles.suggestionWrapper}>\n {suggestions.map((item) => (\n <div key={item} className={styles.suggestion}>\n <div className={styles.suggestContent}>\n <Button\n type=\"text\"\n icon={<SwapRightOutlined />}\n iconPosition=\"end\"\n onClick={() => {\n if (!item) return;\n onSuggest?.(item);\n }}\n style={{\n whiteSpace: \"normal\",\n height: \"auto\",\n wordWrap: \"break-word\",\n textAlign: \"left\",\n padding: \"4px 15px\",\n lineHeight: \"1.5\",\n }}\n >\n {item}\n </Button>\n </div>\n </div>\n ))}\n </div>\n );\n };\n\n return (\n <div className={clsx(styles.wrapper, className)} style={style}>\n <div className={styles.list} ref={listRef}>\n {/* 开场白 */}\n {prologue && (\n <div className={styles.prologue}>\n <MarkdownRender text={prologue} onFileClick={onFileClick} />\n </div>\n )}\n\n {/* 欢迎页面 */}\n {/* {renderWelcome()} */}\n\n {/* 消息列表 */}\n {chatGroups.map((group, idx) =>\n group.role === \"user\"\n ? renderUserGroup(group)\n : renderBotGroup(group, idx === chatGroups.length - 1, renderFunctionCall),\n )}\n\n {/* 建议问题 */}\n {renderSuggestions()}\n\n <div ref={messagesEndRef} />\n </div>\n </div>\n );\n};\n\nexport default XAdkChatbot;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_FunctionCallRender", "renderFunctionCall", "MarkdownRender", "FunctionCallRender", "XAdkThoughtChain", "FileGallery", "copy", "antdMessage", "clsx"]
|
|
7
7
|
}
|
|
@@ -151,6 +151,10 @@ var useStyles = (0, import_common.withBasicStyles)(() => ({
|
|
|
151
151
|
margin-bottom: 8px;
|
|
152
152
|
`,
|
|
153
153
|
metaFooter: import_css.css`
|
|
154
|
+
display: flex;
|
|
155
|
+
align-items: center;
|
|
156
|
+
flex-wrap: wrap;
|
|
157
|
+
gap: 16px;
|
|
154
158
|
margin-top: 12px;
|
|
155
159
|
padding-top: 12px;
|
|
156
160
|
border-top: 1px solid #f0f0f0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/XAdkChatbot/styles.tsx"],
|
|
4
|
-
"sourcesContent": ["import { css } from \"@emotion/css\";\nimport { withBasicStyles } from \"@/styles/common\";\n\nexport const useStyles = withBasicStyles(() => ({\n wrapper: css`\n padding: 0 16px;\n min-height: 300px;\n width: 100%;\n height: 100%;\n border-radius: 8px;\n display: flex;\n flex-direction: column;\n\n .x-markdown {\n --margin-ul-ol: 0 0 1em 1em;\n\n code {\n background: rgba(150, 150, 150, 0.2);\n border-radius: 3px;\n }\n\n .ant-highlightCode-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n color: var(--ant-highlight-code-color-text-title);\n background: var(--ant-highlight-code-color-bg-title);\n padding: var(--ant-padding-sm);\n border-top-left-radius: var(--ant-border-radius);\n border-top-right-radius: var(--ant-border-radius);\n }\n }\n `,\n\n list: css`\n flex: 1;\n margin: 16px 0;\n overflow-y: auto;\n /* Chrome, Safari, Edge, Opera */\n &::-webkit-scrollbar {\n display: none;\n }\n /* Firefox */\n scrollbar-width: none;\n /* IE 10+ */\n -ms-overflow-style: none;\n `,\n\n prologue: css`\n margin-bottom: 12px;\n `,\n\n suggestion: css`\n margin-bottom: 12px;\n `,\n\n suggestContent: css`\n display: inline-flex;\n border: 2px solid #e7e7e7;\n border-radius: 8px;\n `,\n\n userMsg: css`\n display: flex;\n justify-content: flex-end;\n margin-bottom: 24px;\n `,\n\n userContainer: css`\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n align-items: flex-end;\n `,\n\n card: css`\n display: inline-flex;\n padding: 12px 16px;\n background: #d5e3ff;\n border-radius: 12px;\n max-width: 100%;\n white-space: pre-line;\n word-break: break-all;\n `,\n\n botMsg: css`\n margin-bottom: 12px;\n `,\n\n // 新增样式\n welcomeWrapper: css`\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 40px 20px;\n text-align: center;\n `,\n\n welcomeIcon: css`\n width: 64px;\n height: 64px;\n border-radius: 12px;\n margin-bottom: 16px;\n `,\n\n welcomeTitle: css`\n font-size: 24px;\n font-weight: 600;\n color: rgba(0, 0, 0, 0.88);\n margin-bottom: 8px;\n `,\n\n welcomeDesc: css`\n font-size: 14px;\n color: rgba(0, 0, 0, 0.65);\n margin-bottom: 24px;\n `,\n\n suggestionWrapper: css`\n margin-top: 16px;\n `,\n\n successBadge: css`\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: #f6ffed;\n border: 1px solid #b7eb8f;\n border-radius: 4px;\n color: #52c41a;\n font-size: 13px;\n margin-bottom: 12px;\n `,\n\n fileSection: css`\n margin: 12px 0;\n `,\n\n fileHeader: css`\n font-size: 13px;\n color: rgba(0, 0, 0, 0.65);\n margin-bottom: 8px;\n `,\n\n metaFooter: css`\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid #f0f0f0;\n `,\n\n actionIcons: css`\n color: rgba(0, 0, 0, 0.45);\n\n & > span {\n cursor: pointer;\n transition: color 0.2s;\n\n &:hover {\n color: #1890ff;\n }\n }\n `,\n\n fnCall: css`\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 11px;\n border-radius: 24px;\n line-height: 16px;\n border: 1px solid #e7e7e7;\n cursor: pointer;\n `,\n\n confirm: css`\n padding: 12px;\n border-radius: 12px;\n border: 1px solid #e7e7e7;\n `,\n\n footer: css`\n /* senderWrap and tip moved to top-level keys so they are\n accessible as styles.senderWrap and styles.tip in the component */\n `,\n\n sender: css`\n width: 100%;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 8px 8px 12px;\n border: 1px solid #e7e7e7;\n border-radius: 24px;\n\n /* .input moved to top-level key so component can use styles.input */\n `,\n\n senderWrap: css`\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n `,\n\n tip: css`\n padding: 4px;\n font-size: 12px;\n text-align: center;\n color: rgba(0, 0, 0, 0.4);\n `,\n\n input: css`\n padding: 0;\n margin: 5px 0;\n line-height: 22px;\n font-size: 15px;\n border-radius: 0;\n border: none;\n\n &:focus {\n box-shadow: none;\n }\n `,\n}));\n\nexport default useStyles;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAoB;AACpB,oBAAgC;AAEzB,IAAM,gBAAY,+BAAgB,OAAO;AAAA,EAC9C,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BT,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcN,UAAU;AAAA;AAAA;AAAA,EAIV,YAAY;AAAA;AAAA;AAAA,EAIZ,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMhB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAMT,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOf,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUN,QAAQ;AAAA;AAAA;AAAA;AAAA,EAKR,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQhB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOb,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAMb,mBAAmB;AAAA;AAAA;AAAA,EAInB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAad,aAAa;AAAA;AAAA;AAAA,EAIb,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAMZ,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import { css } from \"@emotion/css\";\nimport { withBasicStyles } from \"@/styles/common\";\n\nexport const useStyles = withBasicStyles(() => ({\n wrapper: css`\n padding: 0 16px;\n min-height: 300px;\n width: 100%;\n height: 100%;\n border-radius: 8px;\n display: flex;\n flex-direction: column;\n\n .x-markdown {\n --margin-ul-ol: 0 0 1em 1em;\n\n code {\n background: rgba(150, 150, 150, 0.2);\n border-radius: 3px;\n }\n\n .ant-highlightCode-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n color: var(--ant-highlight-code-color-text-title);\n background: var(--ant-highlight-code-color-bg-title);\n padding: var(--ant-padding-sm);\n border-top-left-radius: var(--ant-border-radius);\n border-top-right-radius: var(--ant-border-radius);\n }\n }\n `,\n\n list: css`\n flex: 1;\n margin: 16px 0;\n overflow-y: auto;\n /* Chrome, Safari, Edge, Opera */\n &::-webkit-scrollbar {\n display: none;\n }\n /* Firefox */\n scrollbar-width: none;\n /* IE 10+ */\n -ms-overflow-style: none;\n `,\n\n prologue: css`\n margin-bottom: 12px;\n `,\n\n suggestion: css`\n margin-bottom: 12px;\n `,\n\n suggestContent: css`\n display: inline-flex;\n border: 2px solid #e7e7e7;\n border-radius: 8px;\n `,\n\n userMsg: css`\n display: flex;\n justify-content: flex-end;\n margin-bottom: 24px;\n `,\n\n userContainer: css`\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n align-items: flex-end;\n `,\n\n card: css`\n display: inline-flex;\n padding: 12px 16px;\n background: #d5e3ff;\n border-radius: 12px;\n max-width: 100%;\n white-space: pre-line;\n word-break: break-all;\n `,\n\n botMsg: css`\n margin-bottom: 12px;\n `,\n\n // 新增样式\n welcomeWrapper: css`\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 40px 20px;\n text-align: center;\n `,\n\n welcomeIcon: css`\n width: 64px;\n height: 64px;\n border-radius: 12px;\n margin-bottom: 16px;\n `,\n\n welcomeTitle: css`\n font-size: 24px;\n font-weight: 600;\n color: rgba(0, 0, 0, 0.88);\n margin-bottom: 8px;\n `,\n\n welcomeDesc: css`\n font-size: 14px;\n color: rgba(0, 0, 0, 0.65);\n margin-bottom: 24px;\n `,\n\n suggestionWrapper: css`\n margin-top: 16px;\n `,\n\n successBadge: css`\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: #f6ffed;\n border: 1px solid #b7eb8f;\n border-radius: 4px;\n color: #52c41a;\n font-size: 13px;\n margin-bottom: 12px;\n `,\n\n fileSection: css`\n margin: 12px 0;\n `,\n\n fileHeader: css`\n font-size: 13px;\n color: rgba(0, 0, 0, 0.65);\n margin-bottom: 8px;\n `,\n\n metaFooter: css`\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 16px;\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid #f0f0f0;\n `,\n\n actionIcons: css`\n color: rgba(0, 0, 0, 0.45);\n\n & > span {\n cursor: pointer;\n transition: color 0.2s;\n\n &:hover {\n color: #1890ff;\n }\n }\n `,\n\n fnCall: css`\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 11px;\n border-radius: 24px;\n line-height: 16px;\n border: 1px solid #e7e7e7;\n cursor: pointer;\n `,\n\n confirm: css`\n padding: 12px;\n border-radius: 12px;\n border: 1px solid #e7e7e7;\n `,\n\n footer: css`\n /* senderWrap and tip moved to top-level keys so they are\n accessible as styles.senderWrap and styles.tip in the component */\n `,\n\n sender: css`\n width: 100%;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 8px 8px 12px;\n border: 1px solid #e7e7e7;\n border-radius: 24px;\n\n /* .input moved to top-level key so component can use styles.input */\n `,\n\n senderWrap: css`\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n `,\n\n tip: css`\n padding: 4px;\n font-size: 12px;\n text-align: center;\n color: rgba(0, 0, 0, 0.4);\n `,\n\n input: css`\n padding: 0;\n margin: 5px 0;\n line-height: 22px;\n font-size: 15px;\n border-radius: 0;\n border: none;\n\n &:focus {\n box-shadow: none;\n }\n `,\n}));\n\nexport default useStyles;\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAoB;AACpB,oBAAgC;AAEzB,IAAM,gBAAY,+BAAgB,OAAO;AAAA,EAC9C,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BT,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcN,UAAU;AAAA;AAAA;AAAA,EAIV,YAAY;AAAA;AAAA;AAAA,EAIZ,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMhB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAMT,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOf,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUN,QAAQ;AAAA;AAAA;AAAA;AAAA,EAKR,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQhB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOb,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAMb,mBAAmB;AAAA;AAAA;AAAA,EAInB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAad,aAAa;AAAA;AAAA;AAAA,EAIb,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAMZ,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUZ,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAab,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWR,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAMT,QAAQ;AAAA;AAAA;AAAA;AAAA,EAKR,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYR,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOZ,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOL,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYT,EAAE;AAEF,IAAO,iBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import type { ActionProps } from "../../../types";
|
|
2
|
+
import type { ActionExtraProps, ActionProps } from "../../../types";
|
|
3
3
|
import type { ChatStrategies } from "../../../types";
|
|
4
4
|
export interface XAdkMessagesProps {
|
|
5
5
|
/** 自定义操作栏渲染 */
|
|
6
6
|
actions?: ActionProps;
|
|
7
|
+
/** 追加在操作栏之后的内容 */
|
|
8
|
+
actionsExtra?: ActionExtraProps;
|
|
7
9
|
/** 类名 */
|
|
8
10
|
className?: string;
|
|
9
11
|
/** 样式 */
|
|
@@ -43,6 +43,7 @@ var import_ChatActionContext = require("../context/ChatActionContext");
|
|
|
43
43
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
44
44
|
var Messages = ({
|
|
45
45
|
actions,
|
|
46
|
+
actionsExtra,
|
|
46
47
|
className,
|
|
47
48
|
style,
|
|
48
49
|
showFnCallDetail,
|
|
@@ -141,6 +142,7 @@ var Messages = ({
|
|
|
141
142
|
onSuggest: suggestChat,
|
|
142
143
|
onConfirm: confirmFnCall,
|
|
143
144
|
actions: actions ?? defaultActions,
|
|
145
|
+
actionsExtra,
|
|
144
146
|
enableGrouping: true,
|
|
145
147
|
enableProcessParsing: true,
|
|
146
148
|
initialized,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/components/XAdkProvider/compound/Messages.tsx"],
|
|
4
|
-
"sourcesContent": ["import React, { useCallback } from \"react\";\nimport { message, Tooltip, Flex } from \"antd\";\nimport {\n LikeFilled,\n LikeOutlined,\n ReloadOutlined,\n CopyOutlined,\n} from \"@ant-design/icons\";\nimport copy from \"copy-to-clipboard\";\nimport XAdkChatbot from \"../../XAdkChatbot\";\nimport DislikeFeedback from \"./DislikeFeedBack\";\nimport { useChatState } from \"../context/ChatStateContext\";\nimport { useChatActions } from \"../context/ChatActionContext\";\nimport type { ActionProps } from \"@/types\";\nimport type { ChatStrategies } from \"@/types\";\n\nexport interface XAdkMessagesProps {\n /** 自定义操作栏渲染 */\n actions?: ActionProps;\n /** 类名 */\n className?: string;\n /** 样式 */\n style?: React.CSSProperties;\n /** 是否显示函数调用详情 */\n showFnCallDetail?: boolean;\n /** 自定义工具调用渲染函数,返回 null 则降级使用默认渲染 */\n renderFunctionCall?: (msg: import(\"@/types\").IMessage) => React.ReactNode | null;\n /** 局部策略覆盖,优先级高于 Provider preset/strategies */\n strategies?: ChatStrategies;\n}\n\n/**\n * Messages - XAdkProvider 的消息展示组件\n *\n * 使用 XAdkChatbot 渲染消息列表\n */\nconst Messages: React.FC<XAdkMessagesProps> = ({\n actions,\n className,\n style,\n showFnCallDetail,\n renderFunctionCall,\n strategies: strategiesProp,\n}) => {\n const {\n messages,\n loading,\n prologue,\n suggestions,\n initialized,\n showFeedback,\n appInfo,\n onFileClick,\n strategies,\n } = useChatState();\n const { suggestChat, reChat, likeMessage, confirmFnCall } = useChatActions();\n\n const primaryColor = appInfo?.appTheme || \"#1677ff\";\n\n const defaultActions = useCallback<ActionProps>(\n ({ message: group, isLastBotMsg }) => {\n // 取该组最后一条 bot 消息的 invocationId 和 isLike 状态\n const lastMsg = group.msgs[group.msgs.length - 1];\n const invocationId = lastMsg?.invocationId;\n const isLikeVal = group.isLike ?? 0;\n\n const fullText = group.msgs\n .filter((m) => !m.functionCall && !m.functionResponse)\n .map((m) => m.text || \"\")\n .join(\"\");\n\n return (\n <Flex\n gap={16}\n align=\"center\"\n style={{ padding: \"4px 0\", color: \"#81858c\" }}\n >\n {isLastBotMsg && (\n <Tooltip title=\"重新生成\">\n <ReloadOutlined style={{ cursor: \"pointer\" }} onClick={reChat} />\n </Tooltip>\n )}\n <Tooltip title=\"复制内容\">\n <CopyOutlined\n style={{ cursor: \"pointer\" }}\n onClick={() => {\n copy(fullText);\n message.success(\"复制成功\");\n }}\n />\n </Tooltip>\n {showFeedback && invocationId && (\n <>\n <Tooltip title=\"赞\">\n {isLikeVal === 1 ? (\n <LikeFilled\n style={{ color: primaryColor, cursor: \"pointer\" }}\n />\n ) : (\n <LikeOutlined\n style={{ cursor: \"pointer\" }}\n onClick={async () => {\n if (isLikeVal !== 0) {\n message.warning(\"已收到您的反馈,无需重复提交\");\n return;\n }\n const ok = await likeMessage(invocationId, 1);\n if (ok) message.success(\"感谢您的反馈\");\n else message.error(\"反馈失败,请稍后再试\");\n }}\n />\n )}\n </Tooltip>\n <DislikeFeedback\n msg={group}\n activeColor={primaryColor}\n onSubmit={async (formData) => {\n const ok = await likeMessage(invocationId, -1, formData);\n if (ok) message.success(\"已收到您的反馈,我们会努力做得更好\");\n else message.error(\"反馈失败,请稍后再试\");\n return ok;\n }}\n />\n </>\n )}\n </Flex>\n );\n },\n [reChat, likeMessage, showFeedback, primaryColor],\n );\n\n return (\n <XAdkChatbot\n messages={messages}\n loading={loading}\n prologue={prologue}\n suggestions={suggestions}\n onSuggest={suggestChat}\n onConfirm={confirmFnCall}\n // 操作栏:使用外部传入的 actions,否则用内置默认 actions\n actions={actions ?? defaultActions}\n // 启用分组和解析\n enableGrouping={true}\n enableProcessParsing={true}\n initialized={initialized}\n showFnCallDetail={showFnCallDetail}\n // 文件点击回调\n onFileClick={onFileClick}\n // 样式\n className={className}\n style={style}\n // 自定义工具调用渲染\n renderFunctionCall={renderFunctionCall}\n strategies={{ ...strategies, ...strategiesProp }}\n />\n );\n};\n\nexport default Messages;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAmC;AACnC,kBAAuC;AACvC,mBAKO;AACP,+BAAiB;AACjB,yBAAwB;AACxB,6BAA4B;AAC5B,8BAA6B;AAC7B,+BAA+B;
|
|
4
|
+
"sourcesContent": ["import React, { useCallback } from \"react\";\nimport { message, Tooltip, Flex } from \"antd\";\nimport {\n LikeFilled,\n LikeOutlined,\n ReloadOutlined,\n CopyOutlined,\n} from \"@ant-design/icons\";\nimport copy from \"copy-to-clipboard\";\nimport XAdkChatbot from \"../../XAdkChatbot\";\nimport DislikeFeedback from \"./DislikeFeedBack\";\nimport { useChatState } from \"../context/ChatStateContext\";\nimport { useChatActions } from \"../context/ChatActionContext\";\nimport type { ActionExtraProps, ActionProps } from \"@/types\";\nimport type { ChatStrategies } from \"@/types\";\n\nexport interface XAdkMessagesProps {\n /** 自定义操作栏渲染 */\n actions?: ActionProps;\n /** 追加在操作栏之后的内容 */\n actionsExtra?: ActionExtraProps;\n /** 类名 */\n className?: string;\n /** 样式 */\n style?: React.CSSProperties;\n /** 是否显示函数调用详情 */\n showFnCallDetail?: boolean;\n /** 自定义工具调用渲染函数,返回 null 则降级使用默认渲染 */\n renderFunctionCall?: (msg: import(\"@/types\").IMessage) => React.ReactNode | null;\n /** 局部策略覆盖,优先级高于 Provider preset/strategies */\n strategies?: ChatStrategies;\n}\n\n/**\n * Messages - XAdkProvider 的消息展示组件\n *\n * 使用 XAdkChatbot 渲染消息列表\n */\nconst Messages: React.FC<XAdkMessagesProps> = ({\n actions,\n actionsExtra,\n className,\n style,\n showFnCallDetail,\n renderFunctionCall,\n strategies: strategiesProp,\n}) => {\n const {\n messages,\n loading,\n prologue,\n suggestions,\n initialized,\n showFeedback,\n appInfo,\n onFileClick,\n strategies,\n } = useChatState();\n const { suggestChat, reChat, likeMessage, confirmFnCall } = useChatActions();\n\n const primaryColor = appInfo?.appTheme || \"#1677ff\";\n\n const defaultActions = useCallback<ActionProps>(\n ({ message: group, isLastBotMsg }) => {\n // 取该组最后一条 bot 消息的 invocationId 和 isLike 状态\n const lastMsg = group.msgs[group.msgs.length - 1];\n const invocationId = lastMsg?.invocationId;\n const isLikeVal = group.isLike ?? 0;\n\n const fullText = group.msgs\n .filter((m) => !m.functionCall && !m.functionResponse)\n .map((m) => m.text || \"\")\n .join(\"\");\n\n return (\n <Flex\n gap={16}\n align=\"center\"\n style={{ padding: \"4px 0\", color: \"#81858c\" }}\n >\n {isLastBotMsg && (\n <Tooltip title=\"重新生成\">\n <ReloadOutlined style={{ cursor: \"pointer\" }} onClick={reChat} />\n </Tooltip>\n )}\n <Tooltip title=\"复制内容\">\n <CopyOutlined\n style={{ cursor: \"pointer\" }}\n onClick={() => {\n copy(fullText);\n message.success(\"复制成功\");\n }}\n />\n </Tooltip>\n {showFeedback && invocationId && (\n <>\n <Tooltip title=\"赞\">\n {isLikeVal === 1 ? (\n <LikeFilled\n style={{ color: primaryColor, cursor: \"pointer\" }}\n />\n ) : (\n <LikeOutlined\n style={{ cursor: \"pointer\" }}\n onClick={async () => {\n if (isLikeVal !== 0) {\n message.warning(\"已收到您的反馈,无需重复提交\");\n return;\n }\n const ok = await likeMessage(invocationId, 1);\n if (ok) message.success(\"感谢您的反馈\");\n else message.error(\"反馈失败,请稍后再试\");\n }}\n />\n )}\n </Tooltip>\n <DislikeFeedback\n msg={group}\n activeColor={primaryColor}\n onSubmit={async (formData) => {\n const ok = await likeMessage(invocationId, -1, formData);\n if (ok) message.success(\"已收到您的反馈,我们会努力做得更好\");\n else message.error(\"反馈失败,请稍后再试\");\n return ok;\n }}\n />\n </>\n )}\n </Flex>\n );\n },\n [reChat, likeMessage, showFeedback, primaryColor],\n );\n\n return (\n <XAdkChatbot\n messages={messages}\n loading={loading}\n prologue={prologue}\n suggestions={suggestions}\n onSuggest={suggestChat}\n onConfirm={confirmFnCall}\n // 操作栏:使用外部传入的 actions,否则用内置默认 actions\n actions={actions ?? defaultActions}\n actionsExtra={actionsExtra}\n // 启用分组和解析\n enableGrouping={true}\n enableProcessParsing={true}\n initialized={initialized}\n showFnCallDetail={showFnCallDetail}\n // 文件点击回调\n onFileClick={onFileClick}\n // 样式\n className={className}\n style={style}\n // 自定义工具调用渲染\n renderFunctionCall={renderFunctionCall}\n strategies={{ ...strategies, ...strategiesProp }}\n />\n );\n};\n\nexport default Messages;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["copy", "DislikeFeedback", "XAdkChatbot"]
|
|
7
7
|
}
|
|
@@ -86,7 +86,8 @@ var XAdkSender = ({
|
|
|
86
86
|
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
|
87
87
|
};
|
|
88
88
|
const normalizeUploadResponse = (response) => {
|
|
89
|
-
|
|
89
|
+
const data = (response == null ? void 0 : response.data) ?? response;
|
|
90
|
+
return (Array.isArray(data) ? data[0] : data) ?? {};
|
|
90
91
|
};
|
|
91
92
|
const checkFileType = (file) => {
|
|
92
93
|
if ((allowedFileTypes == null ? void 0 : allowedFileTypes.length) > 0) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/XAdkSender/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import React, { useState, useRef, useCallback } from \"react\";\nimport { Popconfirm, message, Tooltip, Input, Modal } from \"antd\";\nimport {\n ClearOutlined,\n LoadingOutlined,\n ArrowUpOutlined,\n PaperClipOutlined,\n} from \"@ant-design/icons\";\nimport {\n XAdkSenderProps,\n ServerFile,\n LocalFile,\n ActionsComponents,\n FileValidator,\n UploadFileResult,\n} from \"@/types\";\nimport { useStyles } from \"./styles\";\nimport FileGallery from \"../FileGallery\";\nimport { getExt } from \"@/utils/file\";\n\nconst XAdkSender: React.FC<XAdkSenderProps> = ({\n clearBtnShow = true,\n allowUpload = false,\n loading = false,\n disabled = false,\n uploadRequest = () => {},\n onClear,\n onChange,\n onSubmit,\n onStop,\n onFilesChange,\n onUploadSuccess,\n onUploadError,\n maxFileSize = 10,\n validators: customValidators = [],\n // allowedFileTypes = [\n // \"image/*\",\n // \"audio/*\",\n // \"video/*\",\n // \"application/pdf\",\n // \"text/plain\",\n // \".doc\",\n // \".docx\",\n // \".xls\",\n // \".xlsx\",\n // \".ppt\",\n // \".pptx\",\n // \"\",\n // ],\n allowedFileTypes = [],\n maxFiles = Infinity,\n suffix,\n header,\n prefix,\n footer,\n}) => {\n const styles = useStyles();\n const [value, setValue] = useState<string>(\"\");\n const [files, setFiles] = useState<LocalFile[]>([]);\n const [isDragOver, setIsDragOver] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // 生成唯一ID\n const generateId = () => {\n return Date.now().toString(36) + Math.random().toString(36).substr(2);\n };\n\n const normalizeUploadResponse = (response: any): UploadFileResult => {\n return response?.data ?? response ?? {};\n };\n\n // 检查文件类型\n const checkFileType = (file: File): boolean => {\n if (allowedFileTypes?.length > 0) {\n const ext = getExt(file);\n\n return allowedFileTypes.some((type) => {\n // 1️⃣ MIME 校验\n if (type.includes(\"/\")) {\n if (type.endsWith(\"/*\")) {\n return file.type.startsWith(type.split(\"/\")[0]);\n }\n return file.type === type;\n }\n\n if (type.startsWith(\".\")) {\n return ext === type.slice(1);\n }\n\n return false;\n });\n }\n return true;\n };\n\n // 检查文件大小\n const checkFileSize = (file: File): boolean => {\n const maxSize = maxFileSize * 1024 * 1024;\n return file.size <= maxSize;\n };\n\n // 重复文件检测\n const isDuplicateFile = (file: File, files: LocalFile[]) => {\n return files.some((f) => f.name === file.name && f.size === file.size);\n };\n\n // 内置校验器集合\n const builtInValidators: FileValidator[] = [\n // 数量校验(修复批量 bug)\n (_file, { files }) => {\n if (files.length >= maxFiles) {\n return `最多只能上传 ${maxFiles} 个文件`;\n }\n return null;\n },\n\n // 大小校验\n (file) => {\n const maxSize = maxFileSize * 1024 * 1024;\n if (file.size > maxSize) {\n return `文件大小不能超过 ${maxFileSize}MB`;\n }\n return null;\n },\n\n // 类型校验\n (file) => {\n if (!checkFileType(file)) {\n return \"不支持的文件类型\";\n }\n return null;\n },\n\n // ⭐ 新增:去重校验\n (file, { files }) => {\n if (isDuplicateFile(file, files)) {\n return \"文件已存在\";\n }\n return null;\n },\n ];\n\n const allValidators = [...builtInValidators, ...customValidators];\n\n // 验证文件\n const validateFile = (file: File): { valid: boolean; message?: string } => {\n for (const validator of allValidators) {\n const error = validator(file, { files });\n if (error) {\n return { valid: false, message: error };\n }\n }\n return { valid: true };\n };\n\n // 处理文件选择\n const handleFileSelect = useCallback(\n (selectedFiles: File[]) => {\n const fileArray = Array.from(selectedFiles);\n\n if (files.length + fileArray.length > maxFiles) {\n message.error(`最多只能上传 ${maxFiles} 个文件`);\n return;\n }\n\n const validFiles: LocalFile[] = [];\n\n fileArray.forEach((file) => {\n const validation = validateFile(file);\n\n if (validation.valid) {\n validFiles.push({\n id: generateId(),\n uid: generateId(),\n name: file.name,\n size: file.size,\n type: file.type,\n file,\n progress: 0,\n status: \"pending\",\n response: null,\n });\n } else {\n message.error(validation.message);\n }\n });\n\n if (validFiles.length > 0) {\n setFiles((prev) => {\n const next = [...prev, ...validFiles];\n onFilesChange?.(next);\n return next;\n });\n uploadFiles(validFiles);\n }\n },\n [files, maxFiles, maxFileSize, allowedFileTypes],\n );\n\n // 上传文件\n const uploadFiles = async (fileList: LocalFile[]) => {\n for (const localFile of fileList) {\n // 开始上传\n setFiles((prev) =>\n prev.map((f) =>\n f.id === localFile.id ? { ...f, status: \"uploading\" } : f,\n ),\n );\n\n try {\n await uploadRequest({\n file: localFile.file,\n onProgress: (e) => {\n setFiles((prev) =>\n prev.map((f) =>\n f.id === localFile.id ? { ...f, progress: e.percent } : f,\n ),\n );\n },\n onSuccess: (response) => {\n const responseData = normalizeUploadResponse(response);\n setFiles((prev) =>\n prev.map((f) =>\n f.id === localFile.id\n ? {\n ...f,\n status: \"success\",\n progress: 100,\n response,\n fileId: responseData.fileId || responseData.id,\n tempUrl:\n responseData.tempUrl ||\n responseData.fileUrl ||\n responseData.url,\n }\n : f,\n ),\n );\n\n onUploadSuccess?.({\n ...localFile,\n status: \"success\",\n response,\n fileId: responseData.fileId || responseData.id,\n tempUrl:\n responseData.tempUrl || responseData.fileUrl || responseData.url,\n });\n },\n onError: (error) => {\n setFiles((prev) =>\n prev.map((f) =>\n f.id === localFile.id\n ? {\n ...f,\n status: \"error\",\n errorMessage: error.message,\n }\n : f,\n ),\n );\n onUploadError?.(localFile, error);\n },\n });\n } catch (error) {\n console.error(\"上传处理错误:\", error);\n setFiles((prev) =>\n prev.map((f) =>\n f.id === localFile.id\n ? {\n ...f,\n status: \"error\",\n errorMessage:\n error instanceof Error ? error.message : \"上传处理失败\",\n }\n : f,\n ),\n );\n message.error(`${localFile.name} 上传失败`);\n }\n }\n };\n\n // 删除文件\n const handleRemoveFile = (id: string) => {\n setFiles((prev) => {\n const next = prev.filter((file) => file.id !== id);\n\n onFilesChange?.(next);\n\n return next;\n });\n };\n\n // 触发文件选择\n const triggerFileSelect = () => {\n if (fileInputRef.current) {\n fileInputRef.current.click();\n }\n };\n\n // 处理拖拽事件\n const handleDragOver = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (allowUpload) {\n setIsDragOver(true);\n }\n },\n [allowUpload],\n );\n\n const handleDragLeave = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (allowUpload) {\n setIsDragOver(false);\n }\n },\n [allowUpload],\n );\n\n const handleDrop = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setIsDragOver(false);\n\n if (\n allowUpload &&\n e.dataTransfer.files &&\n e.dataTransfer.files.length > 0\n ) {\n handleFileSelect(Array.from(e.dataTransfer.files));\n }\n },\n [allowUpload, handleFileSelect],\n );\n\n // 处理提交\n const handleSubmit = () => {\n if (!value.trim() && files.length === 0) {\n message.warning(\"请输入消息或选择文件\");\n return;\n }\n\n const successFiles = files.filter((file) => file.status === \"success\");\n const uploadingFiles = files.filter((file) => file.status === \"uploading\");\n\n if (uploadingFiles.length > 0) {\n Modal.confirm({\n title: \"文件上传中\",\n content: `还有 ${uploadingFiles.length} 个文件正在上传,是否继续发送?`,\n okText: \"发送已完成的\",\n cancelText: \"等待全部完成\",\n onOk: () => {\n const formattedFiles = formatFilesForServer(successFiles);\n if (onSubmit) {\n onSubmit({\n text: value,\n files: formattedFiles,\n });\n }\n\n setValue(\"\");\n setFiles((prev) => prev.filter((f) => f.status !== \"success\"));\n },\n onCancel: () => {\n message.info(\"请等待文件上传完成后再发送\");\n },\n });\n } else {\n const formattedFiles = formatFilesForServer(successFiles);\n\n if (onSubmit) {\n onSubmit({\n text: value,\n files: formattedFiles,\n });\n }\n\n setValue(\"\");\n setFiles((prev) => prev.filter((f) => f.status !== \"success\"));\n }\n };\n\n // 格式化文件列表为服务端格式\n const formatFilesForServer = (fileList: LocalFile[]): ServerFile[] => {\n return fileList\n .filter((file) => file.status === \"success\" && file.response)\n .map((file) => {\n const responseData = normalizeUploadResponse(file.response);\n\n return {\n fileName: responseData.fileName || file.name,\n fileId: responseData.fileId || responseData.id || \"\",\n tempUrl:\n responseData.tempUrl || responseData.fileUrl || responseData.url || \"\",\n type: responseData.fileType || responseData.type || getExt(file),\n size: responseData.fileSize || responseData.size || file.size,\n mimeType: responseData.mimeType || file.type,\n raw: {\n ...responseData,\n },\n };\n });\n };\n\n // 处理输入框变化\n const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n const newValue = e.target.value;\n setValue(newValue);\n onChange?.(newValue);\n };\n\n // 处理回车键\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n };\n\n // 渲染文件列表 - 使用新的 FileGallery 组件\n const renderFileList = () => {\n if (files.length === 0) return null;\n\n return (\n <div\n style={{\n display: \"flex\",\n flexWrap: \"wrap\",\n gap: \"8px\",\n padding: \"12px 0\",\n }}\n >\n {files.map((file) => (\n <FileGallery\n key={file.id}\n file={file}\n removable\n onRemove={handleRemoveFile}\n />\n ))}\n </div>\n );\n };\n\n const containerClass = `${styles.container} ${isDragOver ? \"drag-over\" : \"\"}`;\n const uploading = files.some((f) => f.status === \"uploading\");\n const isDisabled = disabled || uploading || loading;\n\n // 定义按钮组件\n const SendButton = (props: any) => (\n <Tooltip title={loading ? \"停止生成\" : \"发送消息\"}>\n <button\n className={`${styles.iconButton} ${styles.sendButton} ${loading ? \"stop\" : \"\"}`}\n onClick={loading ? onStop : handleSubmit}\n disabled={uploading || disabled}\n aria-label={loading ? \"停止生成\" : \"发送消息\"}\n {...props}\n >\n {loading ? <LoadingOutlined /> : <ArrowUpOutlined />}\n </button>\n </Tooltip>\n );\n\n const UploadButton = (props: any) => (\n <Tooltip title=\"上传文件\">\n <button\n className={`${styles.iconButton} ${styles.uploadButton}`}\n onClick={triggerFileSelect}\n disabled={isDisabled}\n aria-label=\"上传文件\"\n {...props}\n >\n <PaperClipOutlined />\n </button>\n </Tooltip>\n );\n\n const ClearButton = (props: any) => (\n <Popconfirm\n title=\"确定要清空聊天记录吗?\"\n onConfirm={onClear}\n placement=\"top\"\n okText=\"确定\"\n cancelText=\"取消\"\n disabled={isDisabled}\n >\n <button\n className={`${styles.iconButton} ${styles.clearButton}`}\n disabled={isDisabled}\n aria-label=\"清空聊天记录\"\n {...props}\n >\n <ClearOutlined />\n </button>\n </Popconfirm>\n );\n\n const actionsComponents: ActionsComponents = {\n SendButton,\n UploadButton,\n ClearButton,\n };\n\n // 渲染插槽内容\n const renderSlot = (\n slot:\n | React.ReactNode\n | false\n | ((\n oriNode: React.ReactNode,\n info: { components: ActionsComponents },\n ) => React.ReactNode | false)\n | undefined,\n oriNode: React.ReactNode,\n ): React.ReactNode | null => {\n if (slot === false) return null;\n if (slot === undefined) return oriNode;\n if (typeof slot === \"function\") {\n const result = slot(oriNode, { components: actionsComponents });\n return result === false ? null : result;\n }\n return slot;\n };\n\n // 默认的后缀内容(操作按钮组)\n const defaultSuffix = (\n <div className={styles.buttonGroup}>\n {allowUpload && <UploadButton />}\n <SendButton />\n </div>\n );\n\n return (\n <div\n ref={containerRef}\n className={containerClass}\n onDragOver={allowUpload ? handleDragOver : undefined}\n onDragLeave={allowUpload ? handleDragLeave : undefined}\n onDrop={allowUpload ? handleDrop : undefined}\n >\n {/* 头部插槽 */}\n {renderSlot(header, false)}\n\n {allowUpload && files.length > 0 && renderFileList()}\n\n {/* 隐藏的原生 file input */}\n {allowUpload && (\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n accept={allowedFileTypes.join(\",\")}\n style={{ display: \"none\" }}\n onChange={(e) => {\n if (e.target.files) {\n handleFileSelect(Array.from(e.target.files));\n e.target.value = \"\";\n }\n }}\n />\n )}\n\n <div className={styles.mainArea}>\n <div className={styles.senderWrap}>\n {/* 清除按钮 */}\n {clearBtnShow && <ClearButton />}\n\n <div className={styles.inputAndButtons}>\n {/* 前缀插槽 */}\n {renderSlot(prefix, false)}\n\n <div className={styles.textAreaWrapper}>\n <Input.TextArea\n className={styles.textArea}\n value={value}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n placeholder=\"请输入消息...\"\n disabled={isDisabled}\n autoSize={{ minRows: 1, maxRows: 4 }}\n style={{\n border: \"none\",\n boxShadow: \"none\",\n outline: \"none\",\n padding: 0,\n }}\n />\n </div>\n\n {/* 后缀插槽(默认显示操作按钮) */}\n {renderSlot(suffix, defaultSuffix)}\n </div>\n </div>\n\n {/* 底部插槽(默认显示提示文字) */}\n {renderSlot(\n footer,\n <div className={styles.tip}>\n 内容由AI生成,无法确保真实准确,仅供参考\n </div>,\n )}\n </div>\n </div>\n );\n};\n\nexport default XAdkSender;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAqD;AACrD,kBAA2D;AAC3D,mBAKO;AASP,oBAA0B;AAC1B,yBAAwB;AACxB,kBAAuB;
|
|
4
|
+
"sourcesContent": ["import React, { useState, useRef, useCallback } from \"react\";\nimport { Popconfirm, message, Tooltip, Input, Modal } from \"antd\";\nimport {\n ClearOutlined,\n LoadingOutlined,\n ArrowUpOutlined,\n PaperClipOutlined,\n} from \"@ant-design/icons\";\nimport {\n XAdkSenderProps,\n ServerFile,\n LocalFile,\n ActionsComponents,\n FileValidator,\n UploadFileResult,\n} from \"@/types\";\nimport { useStyles } from \"./styles\";\nimport FileGallery from \"../FileGallery\";\nimport { getExt } from \"@/utils/file\";\n\nconst XAdkSender: React.FC<XAdkSenderProps> = ({\n clearBtnShow = true,\n allowUpload = false,\n loading = false,\n disabled = false,\n uploadRequest = () => {},\n onClear,\n onChange,\n onSubmit,\n onStop,\n onFilesChange,\n onUploadSuccess,\n onUploadError,\n maxFileSize = 10,\n validators: customValidators = [],\n // allowedFileTypes = [\n // \"image/*\",\n // \"audio/*\",\n // \"video/*\",\n // \"application/pdf\",\n // \"text/plain\",\n // \".doc\",\n // \".docx\",\n // \".xls\",\n // \".xlsx\",\n // \".ppt\",\n // \".pptx\",\n // \"\",\n // ],\n allowedFileTypes = [],\n maxFiles = Infinity,\n suffix,\n header,\n prefix,\n footer,\n}) => {\n const styles = useStyles();\n const [value, setValue] = useState<string>(\"\");\n const [files, setFiles] = useState<LocalFile[]>([]);\n const [isDragOver, setIsDragOver] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // 生成唯一ID\n const generateId = () => {\n return Date.now().toString(36) + Math.random().toString(36).substr(2);\n };\n\n const normalizeUploadResponse = (response: any): UploadFileResult => {\n const data = response?.data ?? response;\n return (Array.isArray(data) ? data[0] : data) ?? {};\n };\n\n // 检查文件类型\n const checkFileType = (file: File): boolean => {\n if (allowedFileTypes?.length > 0) {\n const ext = getExt(file);\n\n return allowedFileTypes.some((type) => {\n // 1️⃣ MIME 校验\n if (type.includes(\"/\")) {\n if (type.endsWith(\"/*\")) {\n return file.type.startsWith(type.split(\"/\")[0]);\n }\n return file.type === type;\n }\n\n if (type.startsWith(\".\")) {\n return ext === type.slice(1);\n }\n\n return false;\n });\n }\n return true;\n };\n\n // 检查文件大小\n const checkFileSize = (file: File): boolean => {\n const maxSize = maxFileSize * 1024 * 1024;\n return file.size <= maxSize;\n };\n\n // 重复文件检测\n const isDuplicateFile = (file: File, files: LocalFile[]) => {\n return files.some((f) => f.name === file.name && f.size === file.size);\n };\n\n // 内置校验器集合\n const builtInValidators: FileValidator[] = [\n // 数量校验(修复批量 bug)\n (_file, { files }) => {\n if (files.length >= maxFiles) {\n return `最多只能上传 ${maxFiles} 个文件`;\n }\n return null;\n },\n\n // 大小校验\n (file) => {\n const maxSize = maxFileSize * 1024 * 1024;\n if (file.size > maxSize) {\n return `文件大小不能超过 ${maxFileSize}MB`;\n }\n return null;\n },\n\n // 类型校验\n (file) => {\n if (!checkFileType(file)) {\n return \"不支持的文件类型\";\n }\n return null;\n },\n\n // ⭐ 新增:去重校验\n (file, { files }) => {\n if (isDuplicateFile(file, files)) {\n return \"文件已存在\";\n }\n return null;\n },\n ];\n\n const allValidators = [...builtInValidators, ...customValidators];\n\n // 验证文件\n const validateFile = (file: File): { valid: boolean; message?: string } => {\n for (const validator of allValidators) {\n const error = validator(file, { files });\n if (error) {\n return { valid: false, message: error };\n }\n }\n return { valid: true };\n };\n\n // 处理文件选择\n const handleFileSelect = useCallback(\n (selectedFiles: File[]) => {\n const fileArray = Array.from(selectedFiles);\n\n if (files.length + fileArray.length > maxFiles) {\n message.error(`最多只能上传 ${maxFiles} 个文件`);\n return;\n }\n\n const validFiles: LocalFile[] = [];\n\n fileArray.forEach((file) => {\n const validation = validateFile(file);\n\n if (validation.valid) {\n validFiles.push({\n id: generateId(),\n uid: generateId(),\n name: file.name,\n size: file.size,\n type: file.type,\n file,\n progress: 0,\n status: \"pending\",\n response: null,\n });\n } else {\n message.error(validation.message);\n }\n });\n\n if (validFiles.length > 0) {\n setFiles((prev) => {\n const next = [...prev, ...validFiles];\n onFilesChange?.(next);\n return next;\n });\n uploadFiles(validFiles);\n }\n },\n [files, maxFiles, maxFileSize, allowedFileTypes],\n );\n\n // 上传文件\n const uploadFiles = async (fileList: LocalFile[]) => {\n for (const localFile of fileList) {\n // 开始上传\n setFiles((prev) =>\n prev.map((f) =>\n f.id === localFile.id ? { ...f, status: \"uploading\" } : f,\n ),\n );\n\n try {\n await uploadRequest({\n file: localFile.file,\n onProgress: (e) => {\n setFiles((prev) =>\n prev.map((f) =>\n f.id === localFile.id ? { ...f, progress: e.percent } : f,\n ),\n );\n },\n onSuccess: (response) => {\n const responseData = normalizeUploadResponse(response);\n setFiles((prev) =>\n prev.map((f) =>\n f.id === localFile.id\n ? {\n ...f,\n status: \"success\",\n progress: 100,\n response,\n fileId: responseData.fileId || responseData.id,\n tempUrl:\n responseData.tempUrl ||\n responseData.fileUrl ||\n responseData.url,\n }\n : f,\n ),\n );\n\n onUploadSuccess?.({\n ...localFile,\n status: \"success\",\n response,\n fileId: responseData.fileId || responseData.id,\n tempUrl:\n responseData.tempUrl || responseData.fileUrl || responseData.url,\n });\n },\n onError: (error) => {\n setFiles((prev) =>\n prev.map((f) =>\n f.id === localFile.id\n ? {\n ...f,\n status: \"error\",\n errorMessage: error.message,\n }\n : f,\n ),\n );\n onUploadError?.(localFile, error);\n },\n });\n } catch (error) {\n console.error(\"上传处理错误:\", error);\n setFiles((prev) =>\n prev.map((f) =>\n f.id === localFile.id\n ? {\n ...f,\n status: \"error\",\n errorMessage:\n error instanceof Error ? error.message : \"上传处理失败\",\n }\n : f,\n ),\n );\n message.error(`${localFile.name} 上传失败`);\n }\n }\n };\n\n // 删除文件\n const handleRemoveFile = (id: string) => {\n setFiles((prev) => {\n const next = prev.filter((file) => file.id !== id);\n\n onFilesChange?.(next);\n\n return next;\n });\n };\n\n // 触发文件选择\n const triggerFileSelect = () => {\n if (fileInputRef.current) {\n fileInputRef.current.click();\n }\n };\n\n // 处理拖拽事件\n const handleDragOver = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (allowUpload) {\n setIsDragOver(true);\n }\n },\n [allowUpload],\n );\n\n const handleDragLeave = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (allowUpload) {\n setIsDragOver(false);\n }\n },\n [allowUpload],\n );\n\n const handleDrop = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setIsDragOver(false);\n\n if (\n allowUpload &&\n e.dataTransfer.files &&\n e.dataTransfer.files.length > 0\n ) {\n handleFileSelect(Array.from(e.dataTransfer.files));\n }\n },\n [allowUpload, handleFileSelect],\n );\n\n // 处理提交\n const handleSubmit = () => {\n if (!value.trim() && files.length === 0) {\n message.warning(\"请输入消息或选择文件\");\n return;\n }\n\n const successFiles = files.filter((file) => file.status === \"success\");\n const uploadingFiles = files.filter((file) => file.status === \"uploading\");\n\n if (uploadingFiles.length > 0) {\n Modal.confirm({\n title: \"文件上传中\",\n content: `还有 ${uploadingFiles.length} 个文件正在上传,是否继续发送?`,\n okText: \"发送已完成的\",\n cancelText: \"等待全部完成\",\n onOk: () => {\n const formattedFiles = formatFilesForServer(successFiles);\n if (onSubmit) {\n onSubmit({\n text: value,\n files: formattedFiles,\n });\n }\n\n setValue(\"\");\n setFiles((prev) => prev.filter((f) => f.status !== \"success\"));\n },\n onCancel: () => {\n message.info(\"请等待文件上传完成后再发送\");\n },\n });\n } else {\n const formattedFiles = formatFilesForServer(successFiles);\n\n if (onSubmit) {\n onSubmit({\n text: value,\n files: formattedFiles,\n });\n }\n\n setValue(\"\");\n setFiles((prev) => prev.filter((f) => f.status !== \"success\"));\n }\n };\n\n // 格式化文件列表为服务端格式\n const formatFilesForServer = (fileList: LocalFile[]): ServerFile[] => {\n return fileList\n .filter((file) => file.status === \"success\" && file.response)\n .map((file) => {\n const responseData = normalizeUploadResponse(file.response);\n\n return {\n fileName: responseData.fileName || file.name,\n fileId: responseData.fileId || responseData.id || \"\",\n tempUrl:\n responseData.tempUrl || responseData.fileUrl || responseData.url || \"\",\n type: responseData.fileType || responseData.type || getExt(file),\n size: responseData.fileSize || responseData.size || file.size,\n mimeType: responseData.mimeType || file.type,\n raw: {\n ...responseData,\n },\n };\n });\n };\n\n // 处理输入框变化\n const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n const newValue = e.target.value;\n setValue(newValue);\n onChange?.(newValue);\n };\n\n // 处理回车键\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n };\n\n // 渲染文件列表 - 使用新的 FileGallery 组件\n const renderFileList = () => {\n if (files.length === 0) return null;\n\n return (\n <div\n style={{\n display: \"flex\",\n flexWrap: \"wrap\",\n gap: \"8px\",\n padding: \"12px 0\",\n }}\n >\n {files.map((file) => (\n <FileGallery\n key={file.id}\n file={file}\n removable\n onRemove={handleRemoveFile}\n />\n ))}\n </div>\n );\n };\n\n const containerClass = `${styles.container} ${isDragOver ? \"drag-over\" : \"\"}`;\n const uploading = files.some((f) => f.status === \"uploading\");\n const isDisabled = disabled || uploading || loading;\n\n // 定义按钮组件\n const SendButton = (props: any) => (\n <Tooltip title={loading ? \"停止生成\" : \"发送消息\"}>\n <button\n className={`${styles.iconButton} ${styles.sendButton} ${loading ? \"stop\" : \"\"}`}\n onClick={loading ? onStop : handleSubmit}\n disabled={uploading || disabled}\n aria-label={loading ? \"停止生成\" : \"发送消息\"}\n {...props}\n >\n {loading ? <LoadingOutlined /> : <ArrowUpOutlined />}\n </button>\n </Tooltip>\n );\n\n const UploadButton = (props: any) => (\n <Tooltip title=\"上传文件\">\n <button\n className={`${styles.iconButton} ${styles.uploadButton}`}\n onClick={triggerFileSelect}\n disabled={isDisabled}\n aria-label=\"上传文件\"\n {...props}\n >\n <PaperClipOutlined />\n </button>\n </Tooltip>\n );\n\n const ClearButton = (props: any) => (\n <Popconfirm\n title=\"确定要清空聊天记录吗?\"\n onConfirm={onClear}\n placement=\"top\"\n okText=\"确定\"\n cancelText=\"取消\"\n disabled={isDisabled}\n >\n <button\n className={`${styles.iconButton} ${styles.clearButton}`}\n disabled={isDisabled}\n aria-label=\"清空聊天记录\"\n {...props}\n >\n <ClearOutlined />\n </button>\n </Popconfirm>\n );\n\n const actionsComponents: ActionsComponents = {\n SendButton,\n UploadButton,\n ClearButton,\n };\n\n // 渲染插槽内容\n const renderSlot = (\n slot:\n | React.ReactNode\n | false\n | ((\n oriNode: React.ReactNode,\n info: { components: ActionsComponents },\n ) => React.ReactNode | false)\n | undefined,\n oriNode: React.ReactNode,\n ): React.ReactNode | null => {\n if (slot === false) return null;\n if (slot === undefined) return oriNode;\n if (typeof slot === \"function\") {\n const result = slot(oriNode, { components: actionsComponents });\n return result === false ? null : result;\n }\n return slot;\n };\n\n // 默认的后缀内容(操作按钮组)\n const defaultSuffix = (\n <div className={styles.buttonGroup}>\n {allowUpload && <UploadButton />}\n <SendButton />\n </div>\n );\n\n return (\n <div\n ref={containerRef}\n className={containerClass}\n onDragOver={allowUpload ? handleDragOver : undefined}\n onDragLeave={allowUpload ? handleDragLeave : undefined}\n onDrop={allowUpload ? handleDrop : undefined}\n >\n {/* 头部插槽 */}\n {renderSlot(header, false)}\n\n {allowUpload && files.length > 0 && renderFileList()}\n\n {/* 隐藏的原生 file input */}\n {allowUpload && (\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n accept={allowedFileTypes.join(\",\")}\n style={{ display: \"none\" }}\n onChange={(e) => {\n if (e.target.files) {\n handleFileSelect(Array.from(e.target.files));\n e.target.value = \"\";\n }\n }}\n />\n )}\n\n <div className={styles.mainArea}>\n <div className={styles.senderWrap}>\n {/* 清除按钮 */}\n {clearBtnShow && <ClearButton />}\n\n <div className={styles.inputAndButtons}>\n {/* 前缀插槽 */}\n {renderSlot(prefix, false)}\n\n <div className={styles.textAreaWrapper}>\n <Input.TextArea\n className={styles.textArea}\n value={value}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n placeholder=\"请输入消息...\"\n disabled={isDisabled}\n autoSize={{ minRows: 1, maxRows: 4 }}\n style={{\n border: \"none\",\n boxShadow: \"none\",\n outline: \"none\",\n padding: 0,\n }}\n />\n </div>\n\n {/* 后缀插槽(默认显示操作按钮) */}\n {renderSlot(suffix, defaultSuffix)}\n </div>\n </div>\n\n {/* 底部插槽(默认显示提示文字) */}\n {renderSlot(\n footer,\n <div className={styles.tip}>\n 内容由AI生成,无法确保真实准确,仅供参考\n </div>,\n )}\n </div>\n </div>\n );\n};\n\nexport default XAdkSender;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["files", "FileGallery"]
|
|
7
7
|
}
|