@ai-group/chat-sdk 3.4.5 → 3.5.0-alpha1
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/dist/cjs/components/FolderTree/styles.js +2 -1
- package/dist/cjs/components/FolderTree/styles.js.map +2 -2
- package/dist/cjs/components/XAdkChatbot/index.d.ts +2 -2
- package/dist/cjs/components/XAdkChatbot/index.js +433 -389
- package/dist/cjs/components/XAdkChatbot/index.js.map +2 -2
- package/dist/cjs/components/XAdkSender/index.js +22 -3
- package/dist/cjs/components/XAdkSender/index.js.map +2 -2
- package/dist/cjs/components/XAdkThoughtChain/styles.d.ts +0 -1
- package/dist/cjs/components/XAdkThoughtChain/styles.js +0 -9
- package/dist/cjs/components/XAdkThoughtChain/styles.js.map +2 -2
- package/dist/cjs/hooks/useADKChat.js +2 -0
- package/dist/cjs/hooks/useADKChat.js.map +2 -2
- package/dist/cjs/presets/xGroupAdk.js +0 -1
- package/dist/cjs/presets/xGroupAdk.js.map +2 -2
- package/dist/cjs/types/XAdkChatbot.d.ts +12 -0
- package/dist/cjs/types/XAdkChatbot.js.map +1 -1
- package/dist/cjs/types/XAdkSender.d.ts +6 -0
- package/dist/cjs/types/XAdkSender.js.map +1 -1
- package/dist/cjs/utils/umdEntry.d.ts +25 -1
- package/dist/cjs/utils/umdEntry.js +126 -41
- package/dist/cjs/utils/umdEntry.js.map +3 -3
- package/dist/esm/components/FolderTree/styles.js +1 -1
- package/dist/esm/components/FolderTree/styles.js.map +1 -1
- package/dist/esm/components/XAdkChatbot/index.d.ts +2 -2
- package/dist/esm/components/XAdkChatbot/index.js +69 -14
- package/dist/esm/components/XAdkChatbot/index.js.map +1 -1
- package/dist/esm/components/XAdkSender/index.js +28 -5
- package/dist/esm/components/XAdkSender/index.js.map +1 -1
- package/dist/esm/components/XAdkThoughtChain/styles.d.ts +0 -1
- package/dist/esm/components/XAdkThoughtChain/styles.js +19 -20
- package/dist/esm/components/XAdkThoughtChain/styles.js.map +1 -1
- package/dist/esm/hooks/useADKChat.js +10 -6
- package/dist/esm/hooks/useADKChat.js.map +1 -1
- package/dist/esm/presets/xGroupAdk.js +0 -1
- package/dist/esm/presets/xGroupAdk.js.map +1 -1
- package/dist/esm/types/XAdkChatbot.d.ts +12 -0
- package/dist/esm/types/XAdkChatbot.js.map +1 -1
- package/dist/esm/types/XAdkSender.d.ts +6 -0
- package/dist/esm/types/XAdkSender.js.map +1 -1
- package/dist/esm/utils/umdEntry.d.ts +25 -1
- package/dist/esm/utils/umdEntry.js +162 -70
- package/dist/esm/utils/umdEntry.js.map +1 -1
- package/dist/umd/chat-sdk.min.js +1 -1
- package/package.json +1 -1
|
@@ -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 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 (\n group: ChatGroup,\n isLastGroup: boolean,\n renderFunctionCall?: XAdkChatbotProps[\"renderFunctionCall\"],\n ) => {\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 ||\n showRetry ||\n showCopy ||\n showLog ||\n 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(\n group,\n idx === chatGroups.length - 1,\n renderFunctionCall,\n ),\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,
|
|
4
|
+
"sourcesContent": ["import React, {\n useMemo,\n useCallback,\n useEffect,\n useRef,\n useImperativeHandle,\n forwardRef,\n} 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 {\n IMessage,\n XAdkChatbotProps,\n XAdkChatbotHandle,\n ChatGroup,\n} 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 = forwardRef<XAdkChatbotHandle, XAdkChatbotProps>(\n (\n {\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 sessionId,\n onFileClick,\n renderFunctionCall,\n toolKindResolver,\n strategies,\n preset,\n },\n ref,\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 prevSessionIdRef = useRef(sessionId);\n\n // 暴露命令式 API\n const scrollToBottom = useCallback(\n (behavior: ScrollBehavior = \"auto\") => {\n messagesEndRef.current?.scrollIntoView({ behavior });\n },\n [],\n );\n useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);\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;\n prevInitializedRef.current = true;\n\n const el = listRef.current;\n if (!el) return;\n\n // 用 MutationObserver 监听 DOM 子树变化,等内容渲染稳定后置底\n // 每次 DOM 变化都重新计时,确保所有异步渲染(Markdown、代码高亮、思维链等)完成后再滚动\n let stableTimer: ReturnType<typeof setTimeout>;\n const doScroll = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"auto\" });\n };\n\n const observer = new MutationObserver(() => {\n clearTimeout(stableTimer);\n stableTimer = setTimeout(doScroll, 80);\n });\n\n observer.observe(el, { childList: true, subtree: true, characterData: true });\n\n // 立即执行一次,处理内容已经就绪的情况\n doScroll();\n // 兜底:最多等 500ms 后强制滚动并断开观察\n const maxTimer = setTimeout(() => {\n observer.disconnect();\n doScroll();\n }, 500);\n\n return () => {\n clearTimeout(stableTimer);\n clearTimeout(maxTimer);\n observer.disconnect();\n };\n }, [initialized, messages]);\n\n // 会话切换时重置滚动状态,允许重新触发初始化置底\n useEffect(() => {\n if (sessionId === undefined) return; // 未传 sessionId 不处理\n if (prevSessionIdRef.current === sessionId) return; // 同一会话不处理\n prevSessionIdRef.current = sessionId;\n prevInitializedRef.current = false;\n userHasScrolledRef.current = false;\n }, [sessionId]);\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 (\n group: ChatGroup,\n isLastGroup: boolean,\n renderFunctionCall?: XAdkChatbotProps[\"renderFunctionCall\"],\n ) => {\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 const lastProcessIdx = nodes.findLastIndex(\n (n) => n.type === \"process\",\n );\n return (\n <div key={group.id} className={styles.botMsg}>\n {nodes.map((node, idx) => {\n if (node.type === \"process\") {\n // 只有最后一个 process node 跟随 loading 状态,已完成的思维链不应显示 loading\n const isLastProcess = idx === lastProcessIdx;\n return (\n <XAdkThoughtChain\n key={node.key}\n loading={isLastProcess && isGroupLoading}\n title=\"思维链已完成\"\n items={node.items}\n showFnCallDetail={showFnCallDetail}\n onConfirm={onConfirm}\n defaultOpen={isLastProcess && 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 showRetry ||\n showCopy ||\n showLog ||\n 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(\n group,\n idx === chatGroups.length - 1,\n renderFunctionCall,\n ),\n )}\n\n {/* 建议问题 */}\n {renderSuggestions()}\n\n <div ref={messagesEndRef} />\n </div>\n </div>\n );\n },\n);\n\nXAdkChatbot.displayName = \"XAdkChatbot\";\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
|
}
|
|
@@ -46,6 +46,8 @@ var XAdkSender = (0, import_react.forwardRef)(({
|
|
|
46
46
|
draggable = true,
|
|
47
47
|
loading = false,
|
|
48
48
|
disabled = false,
|
|
49
|
+
value: controlledValue,
|
|
50
|
+
defaultValue = "",
|
|
49
51
|
uploadRequest = () => {
|
|
50
52
|
},
|
|
51
53
|
onClear,
|
|
@@ -80,7 +82,15 @@ var XAdkSender = (0, import_react.forwardRef)(({
|
|
|
80
82
|
footer
|
|
81
83
|
}, ref) => {
|
|
82
84
|
const styles = (0, import_styles.useStyles)();
|
|
83
|
-
const
|
|
85
|
+
const isControlled = controlledValue !== void 0;
|
|
86
|
+
const [internalValue, setInternalValue] = (0, import_react.useState)(defaultValue);
|
|
87
|
+
const value = isControlled ? controlledValue : internalValue;
|
|
88
|
+
const setValue = (newValue) => {
|
|
89
|
+
if (!isControlled) {
|
|
90
|
+
setInternalValue(newValue);
|
|
91
|
+
}
|
|
92
|
+
onChange == null ? void 0 : onChange(newValue);
|
|
93
|
+
};
|
|
84
94
|
const [files, setFiles] = (0, import_react.useState)([]);
|
|
85
95
|
const [isDragOver, setIsDragOver] = (0, import_react.useState)(false);
|
|
86
96
|
const containerRef = (0, import_react.useRef)(null);
|
|
@@ -369,7 +379,6 @@ var XAdkSender = (0, import_react.forwardRef)(({
|
|
|
369
379
|
const handleInputChange = (e) => {
|
|
370
380
|
const newValue = e.target.value;
|
|
371
381
|
setValue(newValue);
|
|
372
|
-
onChange == null ? void 0 : onChange(newValue);
|
|
373
382
|
};
|
|
374
383
|
const handleKeyDown = (e) => {
|
|
375
384
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
@@ -404,11 +413,15 @@ var XAdkSender = (0, import_react.forwardRef)(({
|
|
|
404
413
|
const containerClass = `${styles.container} ${isDragOver ? "drag-over" : ""}`;
|
|
405
414
|
const uploading = files.some((f) => f.status === "uploading");
|
|
406
415
|
const isDisabled = disabled || uploading || loading;
|
|
416
|
+
const hasContent = value.trim().length > 0;
|
|
417
|
+
const hasFiles = files.some((f) => f.status === "success");
|
|
418
|
+
const canSend = hasContent || hasFiles;
|
|
407
419
|
const SendButton = (props) => {
|
|
408
420
|
if (sendButtonRender) {
|
|
409
421
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: sendButtonRender({
|
|
410
422
|
loading,
|
|
411
423
|
disabled: uploading || disabled,
|
|
424
|
+
canSend,
|
|
412
425
|
onSend: handleSubmit,
|
|
413
426
|
onStop
|
|
414
427
|
}) });
|
|
@@ -484,7 +497,13 @@ var XAdkSender = (0, import_react.forwardRef)(({
|
|
|
484
497
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.PaperClipOutlined, {})
|
|
485
498
|
}
|
|
486
499
|
) }),
|
|
487
|
-
|
|
500
|
+
sendButtonRender ? sendButtonRender({
|
|
501
|
+
loading,
|
|
502
|
+
disabled: uploading || disabled,
|
|
503
|
+
canSend,
|
|
504
|
+
onSend: handleSubmit,
|
|
505
|
+
onStop
|
|
506
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Tooltip, { title: loading ? "停止生成" : "发送消息", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
488
507
|
"button",
|
|
489
508
|
{
|
|
490
509
|
className: `${styles.iconButton} ${styles.sendButton} ${loading ? "stop" : ""}`,
|
|
@@ -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, forwardRef, useImperativeHandle } 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 XAdkSenderHandle,\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 = forwardRef<XAdkSenderHandle, XAdkSenderProps>(({\n clearBtnShow = true,\n allowUpload = false,\n draggable = true,\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 sendButtonRender,\n suffix,\n header,\n prefix,\n footer,\n}, ref) => {\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 // 暴露 addFiles 方法供外层(如 DefaultLayout 拖拽上传)调用\n useImperativeHandle(ref, () => ({\n addFiles: (rawFiles: File[]) => {\n handleFileSelect(rawFiles);\n },\n }), [handleFileSelect]);\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 // 定义按钮组件(供外部 slot 函数通过 actionsComponents 使用)\n const SendButton = (props: any) => {\n if (sendButtonRender) {\n return (\n <>\n {sendButtonRender({\n loading,\n disabled: uploading || disabled,\n onSend: handleSubmit,\n onStop,\n })}\n </>\n );\n }\n return (\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 spin /> : <ArrowUpOutlined />}\n </button>\n </Tooltip>\n );\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 // 默认的后缀内容(操作按钮组) - 直接内联渲染避免组件引用变化导致 unmount/remount\n const defaultSuffix = (\n <div className={styles.buttonGroup}>\n {allowUpload && (\n <Tooltip title=\"上传文件\">\n <button\n className={`${styles.iconButton} ${styles.uploadButton}`}\n onClick={triggerFileSelect}\n disabled={isDisabled}\n aria-label=\"上传文件\"\n >\n <PaperClipOutlined />\n </button>\n </Tooltip>\n )}\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 >\n {loading ? <LoadingOutlined spin /> : <ArrowUpOutlined />}\n </button>\n </Tooltip>\n </div>\n );\n\n return (\n <div\n ref={containerRef}\n className={containerClass}\n onDragOver={allowUpload && draggable ? handleDragOver : undefined}\n onDragLeave={allowUpload && draggable ? handleDragLeave : undefined}\n onDrop={allowUpload && draggable ? 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 <div className={styles.inputAndButtons}>\n {/* 清除按钮 */}\n {clearBtnShow && <ClearButton />}\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\n {/* 底部插槽(默认显示提示文字) */}\n {renderSlot(\n footer,\n <div className={styles.tip}>\n 内容由AI生成,无法确保真实准确,仅供参考\n </div>,\n )}\n </div>\n </div>\n </div>\n );\n});\n\nXAdkSender.displayName = 'XAdkSender';\n\nimport XAdkSenderSwitch from \"./Switch\";\n\ntype XAdkSenderComponent = typeof XAdkSender & {\n Switch: typeof XAdkSenderSwitch;\n};\n\nconst CompoundSender = XAdkSender as XAdkSenderComponent;\nCompoundSender.Switch = XAdkSenderSwitch;\n\nexport default CompoundSender;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAsF;AACtF,kBAA2D;AAC3D,mBAKO;AAUP,oBAA0B;AAC1B,yBAAwB;AACxB,kBAAuB;AA+nBvB,oBAA6B;AAhNnB;AA7aV,IAAM,iBAAa,yBAA8C,CAAC;AAAA,EAChE,eAAe;AAAA,EACf,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,WAAW;AAAA,EACX,gBAAgB,MAAM;AAAA,EAAC;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,YAAY,mBAAmB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAehC,mBAAmB,CAAC;AAAA,EACpB,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAG,QAAQ;AACT,QAAM,aAAS,yBAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAiB,EAAE;AAC7C,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAsB,CAAC,CAAC;AAClD,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,KAAK;AAClD,QAAM,mBAAe,qBAAuB,IAAI;AAChD,QAAM,mBAAe,qBAAyB,IAAI;AAGlD,QAAM,aAAa,MAAM;AACvB,WAAO,KAAK,IAAI,EAAE,SAAS,EAAE,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC;AAAA,EACtE;AAEA,QAAM,0BAA0B,CAAC,aAAoC;AACnE,UAAM,QAAO,qCAAU,SAAQ;AAC/B,YAAQ,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI,SAAS,CAAC;AAAA,EACpD;AAGA,QAAM,gBAAgB,CAAC,SAAwB;AAC7C,SAAI,qDAAkB,UAAS,GAAG;AAChC,YAAM,UAAM,oBAAO,IAAI;AAEvB,aAAO,iBAAiB,KAAK,CAAC,SAAS;AAErC,YAAI,KAAK,SAAS,GAAG,GAAG;AACtB,cAAI,KAAK,SAAS,IAAI,GAAG;AACvB,mBAAO,KAAK,KAAK,WAAW,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA,UAChD;AACA,iBAAO,KAAK,SAAS;AAAA,QACvB;AAEA,YAAI,KAAK,WAAW,GAAG,GAAG;AACxB,iBAAO,QAAQ,KAAK,MAAM,CAAC;AAAA,QAC7B;AAEA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAGA,QAAM,gBAAgB,CAAC,SAAwB;AAC7C,UAAM,UAAU,cAAc,OAAO;AACrC,WAAO,KAAK,QAAQ;AAAA,EACtB;AAGA,QAAM,kBAAkB,CAAC,MAAYA,WAAuB;AAC1D,WAAOA,OAAM,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,QAAQ,EAAE,SAAS,KAAK,IAAI;AAAA,EACvE;AAGA,QAAM,oBAAqC;AAAA;AAAA,IAEzC,CAAC,OAAO,EAAE,OAAAA,OAAM,MAAM;AACpB,UAAIA,OAAM,UAAU,UAAU;AAC5B,eAAO,UAAU;AAAA,MACnB;AACA,aAAO;AAAA,IACT;AAAA;AAAA,IAGA,CAAC,SAAS;AACR,YAAM,UAAU,cAAc,OAAO;AACrC,UAAI,KAAK,OAAO,SAAS;AACvB,eAAO,YAAY;AAAA,MACrB;AACA,aAAO;AAAA,IACT;AAAA;AAAA,IAGA,CAAC,SAAS;AACR,UAAI,CAAC,cAAc,IAAI,GAAG;AACxB,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA;AAAA,IAGA,CAAC,MAAM,EAAE,OAAAA,OAAM,MAAM;AACnB,UAAI,gBAAgB,MAAMA,MAAK,GAAG;AAChC,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,gBAAgB,CAAC,GAAG,mBAAmB,GAAG,gBAAgB;AAGhE,QAAM,eAAe,CAAC,SAAqD;AACzE,eAAW,aAAa,eAAe;AACrC,YAAM,QAAQ,UAAU,MAAM,EAAE,MAAM,CAAC;AACvC,UAAI,OAAO;AACT,eAAO,EAAE,OAAO,OAAO,SAAS,MAAM;AAAA,MACxC;AAAA,IACF;AACA,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB;AAGA,QAAM,uBAAmB;AAAA,IACvB,CAAC,kBAA0B;AACzB,YAAM,YAAY,MAAM,KAAK,aAAa;AAE1C,UAAI,MAAM,SAAS,UAAU,SAAS,UAAU;AAC9C,4BAAQ,MAAM,UAAU,cAAc;AACtC;AAAA,MACF;AAEA,YAAM,aAA0B,CAAC;AAEjC,gBAAU,QAAQ,CAAC,SAAS;AAC1B,cAAM,aAAa,aAAa,IAAI;AAEpC,YAAI,WAAW,OAAO;AACpB,qBAAW,KAAK;AAAA,YACd,IAAI,WAAW;AAAA,YACf,KAAK,WAAW;AAAA,YAChB,MAAM,KAAK;AAAA,YACX,MAAM,KAAK;AAAA,YACX,MAAM,KAAK;AAAA,YACX;AAAA,YACA,UAAU;AAAA,YACV,QAAQ;AAAA,YACR,UAAU;AAAA,UACZ,CAAC;AAAA,QACH,OAAO;AACL,8BAAQ,MAAM,WAAW,OAAO;AAAA,QAClC;AAAA,MACF,CAAC;AAED,UAAI,WAAW,SAAS,GAAG;AACzB,iBAAS,CAAC,SAAS;AACjB,gBAAM,OAAO,CAAC,GAAG,MAAM,GAAG,UAAU;AACpC,yDAAgB;AAChB,iBAAO;AAAA,QACT,CAAC;AACD,oBAAY,UAAU;AAAA,MACxB;AAAA,IACF;AAAA,IACA,CAAC,OAAO,UAAU,aAAa,gBAAgB;AAAA,EACjD;AAGA,wCAAoB,KAAK,OAAO;AAAA,IAC9B,UAAU,CAAC,aAAqB;AAC9B,uBAAiB,QAAQ;AAAA,IAC3B;AAAA,EACF,IAAI,CAAC,gBAAgB,CAAC;AAGtB,QAAM,cAAc,OAAO,aAA0B;AACnD,eAAW,aAAa,UAAU;AAEhC;AAAA,QAAS,CAAC,SACR,KAAK;AAAA,UAAI,CAAC,MACR,EAAE,OAAO,UAAU,KAAK,EAAE,GAAG,GAAG,QAAQ,YAAY,IAAI;AAAA,QAC1D;AAAA,MACF;AAEA,UAAI;AACF,cAAM,cAAc;AAAA,UAClB,MAAM,UAAU;AAAA,UAChB,YAAY,CAAC,MAAM;AACjB;AAAA,cAAS,CAAC,SACR,KAAK;AAAA,gBAAI,CAAC,MACR,EAAE,OAAO,UAAU,KAAK,EAAE,GAAG,GAAG,UAAU,EAAE,QAAQ,IAAI;AAAA,cAC1D;AAAA,YACF;AAAA,UACF;AAAA,UACA,WAAW,CAAC,aAAa;AACvB,kBAAM,eAAe,wBAAwB,QAAQ;AACrD;AAAA,cAAS,CAAC,SACR,KAAK;AAAA,gBAAI,CAAC,MACR,EAAE,OAAO,UAAU,KACf;AAAA,kBACE,GAAG;AAAA,kBACH,QAAQ;AAAA,kBACR,UAAU;AAAA,kBACV;AAAA,kBACA,QAAQ,aAAa,UAAU,aAAa;AAAA,kBAC5C,SACE,aAAa,WACb,aAAa,WACb,aAAa;AAAA,gBACjB,IACA;AAAA,cACN;AAAA,YACF;AAEA,+DAAkB;AAAA,cAChB,GAAG;AAAA,cACH,QAAQ;AAAA,cACR;AAAA,cACA,QAAQ,aAAa,UAAU,aAAa;AAAA,cAC5C,SACE,aAAa,WAAW,aAAa,WAAW,aAAa;AAAA,YACjE;AAAA,UACF;AAAA,UACA,SAAS,CAAC,UAAU;AAClB;AAAA,cAAS,CAAC,SACR,KAAK;AAAA,gBAAI,CAAC,MACR,EAAE,OAAO,UAAU,KACf;AAAA,kBACE,GAAG;AAAA,kBACH,QAAQ;AAAA,kBACR,cAAc,MAAM;AAAA,gBACtB,IACA;AAAA,cACN;AAAA,YACF;AACA,2DAAgB,WAAW;AAAA,UAC7B;AAAA,QACF,CAAC;AAAA,MACH,SAAS,OAAP;AACA,gBAAQ,MAAM,WAAW,KAAK;AAC9B;AAAA,UAAS,CAAC,SACR,KAAK;AAAA,YAAI,CAAC,MACR,EAAE,OAAO,UAAU,KACf;AAAA,cACE,GAAG;AAAA,cACH,QAAQ;AAAA,cACR,cACE,iBAAiB,QAAQ,MAAM,UAAU;AAAA,YAC7C,IACA;AAAA,UACN;AAAA,QACF;AACA,4BAAQ,MAAM,GAAG,UAAU,WAAW;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AAGA,QAAM,mBAAmB,CAAC,OAAe;AACvC,aAAS,CAAC,SAAS;AACjB,YAAM,OAAO,KAAK,OAAO,CAAC,SAAS,KAAK,OAAO,EAAE;AAEjD,qDAAgB;AAEhB,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAGA,QAAM,oBAAoB,MAAM;AAC9B,QAAI,aAAa,SAAS;AACxB,mBAAa,QAAQ,MAAM;AAAA,IAC7B;AAAA,EACF;AAGA,QAAM,qBAAiB;AAAA,IACrB,CAAC,MAAuB;AACtB,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,UAAI,aAAa;AACf,sBAAc,IAAI;AAAA,MACpB;AAAA,IACF;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,sBAAkB;AAAA,IACtB,CAAC,MAAuB;AACtB,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,UAAI,aAAa;AACf,sBAAc,KAAK;AAAA,MACrB;AAAA,IACF;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,iBAAa;AAAA,IACjB,CAAC,MAAuB;AACtB,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,oBAAc,KAAK;AAEnB,UACE,eACA,EAAE,aAAa,SACf,EAAE,aAAa,MAAM,SAAS,GAC9B;AACA,yBAAiB,MAAM,KAAK,EAAE,aAAa,KAAK,CAAC;AAAA,MACnD;AAAA,IACF;AAAA,IACA,CAAC,aAAa,gBAAgB;AAAA,EAChC;AAGA,QAAM,eAAe,MAAM;AACzB,QAAI,CAAC,MAAM,KAAK,KAAK,MAAM,WAAW,GAAG;AACvC,0BAAQ,QAAQ,YAAY;AAC5B;AAAA,IACF;AAEA,UAAM,eAAe,MAAM,OAAO,CAAC,SAAS,KAAK,WAAW,SAAS;AACrE,UAAM,iBAAiB,MAAM,OAAO,CAAC,SAAS,KAAK,WAAW,WAAW;AAEzE,QAAI,eAAe,SAAS,GAAG;AAC7B,wBAAM,QAAQ;AAAA,QACZ,OAAO;AAAA,QACP,SAAS,MAAM,eAAe;AAAA,QAC9B,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,MAAM,MAAM;AACV,gBAAM,iBAAiB,qBAAqB,YAAY;AACxD,cAAI,UAAU;AACZ,qBAAS;AAAA,cACP,MAAM;AAAA,cACN,OAAO;AAAA,YACT,CAAC;AAAA,UACH;AAEA,mBAAS,EAAE;AACX,mBAAS,CAAC,SAAS,KAAK,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC;AAAA,QAC/D;AAAA,QACA,UAAU,MAAM;AACd,8BAAQ,KAAK,eAAe;AAAA,QAC9B;AAAA,MACF,CAAC;AAAA,IACH,OAAO;AACL,YAAM,iBAAiB,qBAAqB,YAAY;AAExD,UAAI,UAAU;AACZ,iBAAS;AAAA,UACP,MAAM;AAAA,UACN,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAEA,eAAS,EAAE;AACX,eAAS,CAAC,SAAS,KAAK,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC;AAAA,IAC/D;AAAA,EACF;AAGA,QAAM,uBAAuB,CAAC,aAAwC;AACpE,WAAO,SACJ,OAAO,CAAC,SAAS,KAAK,WAAW,aAAa,KAAK,QAAQ,EAC3D,IAAI,CAAC,SAAS;AACb,YAAM,eAAe,wBAAwB,KAAK,QAAQ;AAE1D,aAAO;AAAA,QACL,UAAU,aAAa,YAAY,KAAK;AAAA,QACxC,QAAQ,aAAa,UAAU,aAAa,MAAM;AAAA,QAClD,SACE,aAAa,WAAW,aAAa,WAAW,aAAa,OAAO;AAAA,QACtE,MAAM,aAAa,YAAY,aAAa,YAAQ,oBAAO,IAAI;AAAA,QAC/D,MAAM,aAAa,YAAY,aAAa,QAAQ,KAAK;AAAA,QACzD,UAAU,aAAa,YAAY,KAAK;AAAA,QACxC,KAAK;AAAA,UACH,GAAG;AAAA,QACL;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AAGA,QAAM,oBAAoB,CAAC,MAA8C;AACvE,UAAM,WAAW,EAAE,OAAO;AAC1B,aAAS,QAAQ;AACjB,yCAAW;AAAA,EACb;AAGA,QAAM,gBAAgB,CAAC,MAAgD;AACrE,QAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AACpC,QAAE,eAAe;AACjB,mBAAa;AAAA,IACf;AAAA,EACF;AAGA,QAAM,iBAAiB,MAAM;AAC3B,QAAI,MAAM,WAAW;AAAG,aAAO;AAE/B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,UACL,SAAS;AAAA,UACT,UAAU;AAAA,UACV,KAAK;AAAA,UACL,SAAS;AAAA,QACX;AAAA,QAEC,gBAAM,IAAI,CAAC,SACV;AAAA,UAAC,mBAAAC;AAAA,UAAA;AAAA,YAEC;AAAA,YACA,WAAS;AAAA,YACT,UAAU;AAAA;AAAA,UAHL,KAAK;AAAA,QAIZ,CACD;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,QAAM,iBAAiB,GAAG,OAAO,aAAa,aAAa,cAAc;AACzE,QAAM,YAAY,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,WAAW;AAC5D,QAAM,aAAa,YAAY,aAAa;AAG5C,QAAM,aAAa,CAAC,UAAe;AACjC,QAAI,kBAAkB;AACpB,aACE,2EACG,2BAAiB;AAAA,QAChB;AAAA,QACA,UAAU,aAAa;AAAA,QACvB,QAAQ;AAAA,QACR;AAAA,MACF,CAAC,GACH;AAAA,IAEJ;AACA,WACE,4CAAC,uBAAQ,OAAO,UAAU,SAAS,QACjC;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,GAAG,OAAO,cAAc,OAAO,cAAc,UAAU,SAAS;AAAA,QAC3E,SAAS,UAAU,SAAS;AAAA,QAC5B,UAAU,aAAa;AAAA,QACvB,cAAY,UAAU,SAAS;AAAA,QAC9B,GAAG;AAAA,QAEH,oBAAU,4CAAC,gCAAgB,MAAI,MAAC,IAAK,4CAAC,gCAAgB;AAAA;AAAA,IACzD,GACF;AAAA,EAEJ;AAEA,QAAM,eAAe,CAAC,UACpB,4CAAC,uBAAQ,OAAM,QACb;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,OAAO,cAAc,OAAO;AAAA,MAC1C,SAAS;AAAA,MACT,UAAU;AAAA,MACV,cAAW;AAAA,MACV,GAAG;AAAA,MAEJ,sDAAC,kCAAkB;AAAA;AAAA,EACrB,GACF;AAGF,QAAM,cAAc,CAAC,UACnB;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,WAAW;AAAA,MACX,WAAU;AAAA,MACV,QAAO;AAAA,MACP,YAAW;AAAA,MACX,UAAU;AAAA,MAEV;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,GAAG,OAAO,cAAc,OAAO;AAAA,UAC1C,UAAU;AAAA,UACV,cAAW;AAAA,UACV,GAAG;AAAA,UAEJ,sDAAC,8BAAc;AAAA;AAAA,MACjB;AAAA;AAAA,EACF;AAGF,QAAM,oBAAuC;AAAA,IAC3C;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAGA,QAAM,aAAa,CACjB,MAQA,YAC2B;AAC3B,QAAI,SAAS;AAAO,aAAO;AAC3B,QAAI,SAAS;AAAW,aAAO;AAC/B,QAAI,OAAO,SAAS,YAAY;AAC9B,YAAM,SAAS,KAAK,SAAS,EAAE,YAAY,kBAAkB,CAAC;AAC9D,aAAO,WAAW,QAAQ,OAAO;AAAA,IACnC;AACA,WAAO;AAAA,EACT;AAGA,QAAM,gBACJ,6CAAC,SAAI,WAAW,OAAO,aACpB;AAAA,mBACC,4CAAC,uBAAQ,OAAM,QACb;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,GAAG,OAAO,cAAc,OAAO;AAAA,QAC1C,SAAS;AAAA,QACT,UAAU;AAAA,QACV,cAAW;AAAA,QAEX,sDAAC,kCAAkB;AAAA;AAAA,IACrB,GACF;AAAA,IAEF,4CAAC,uBAAQ,OAAO,UAAU,SAAS,QACjC;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,GAAG,OAAO,cAAc,OAAO,cAAc,UAAU,SAAS;AAAA,QAC3E,SAAS,UAAU,SAAS;AAAA,QAC5B,UAAU,aAAa;AAAA,QACvB,cAAY,UAAU,SAAS;AAAA,QAE9B,oBAAU,4CAAC,gCAAgB,MAAI,MAAC,IAAK,4CAAC,gCAAgB;AAAA;AAAA,IACzD,GACF;AAAA,KACF;AAGF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAW;AAAA,MACX,YAAY,eAAe,YAAY,iBAAiB;AAAA,MACxD,aAAa,eAAe,YAAY,kBAAkB;AAAA,MAC1D,QAAQ,eAAe,YAAY,aAAa;AAAA,MAG/C;AAAA,mBAAW,QAAQ,KAAK;AAAA,QAExB,eAAe,MAAM,SAAS,KAAK,eAAe;AAAA,QAGlD,eACC;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,MAAK;AAAA,YACL,UAAQ;AAAA,YACR,QAAQ,iBAAiB,KAAK,GAAG;AAAA,YACjC,OAAO,EAAE,SAAS,OAAO;AAAA,YACzB,UAAU,CAAC,MAAM;AACf,kBAAI,EAAE,OAAO,OAAO;AAClB,iCAAiB,MAAM,KAAK,EAAE,OAAO,KAAK,CAAC;AAC3C,kBAAE,OAAO,QAAQ;AAAA,cACnB;AAAA,YACF;AAAA;AAAA,QACF;AAAA,QAGF,4CAAC,SAAI,WAAW,OAAO,UACrB,uDAAC,SAAI,WAAW,OAAO,YACrB;AAAA,uDAAC,SAAI,WAAW,OAAO,iBAEpB;AAAA,4BAAgB,4CAAC,eAAY;AAAA,YAE7B,WAAW,QAAQ,KAAK;AAAA,YAEzB,4CAAC,SAAI,WAAW,OAAO,iBACrB;AAAA,cAAC,kBAAM;AAAA,cAAN;AAAA,gBACC,WAAW,OAAO;AAAA,gBAClB;AAAA,gBACA,UAAU;AAAA,gBACV,WAAW;AAAA,gBACX,aAAY;AAAA,gBACZ,UAAU;AAAA,gBACV,UAAU,EAAE,SAAS,GAAG,SAAS,EAAE;AAAA,gBACnC,OAAO;AAAA,kBACL,QAAQ;AAAA,kBACR,WAAW;AAAA,kBACX,SAAS;AAAA,kBACT,SAAS;AAAA,gBACX;AAAA;AAAA,YACF,GACF;AAAA,YAGC,WAAW,QAAQ,aAAa;AAAA,aACnC;AAAA,UAGC;AAAA,YACC;AAAA,YACA,4CAAC,SAAI,WAAW,OAAO,KAAK,mCAE5B;AAAA,UACF;AAAA,WACF,GACF;AAAA;AAAA;AAAA,EACF;AAEJ,CAAC;AAED,WAAW,cAAc;AAQzB,IAAM,iBAAiB;AACvB,eAAe,SAAS,cAAAC;AAExB,IAAO,qBAAQ;",
|
|
4
|
+
"sourcesContent": ["import React, { useState, useRef, useCallback, forwardRef, useImperativeHandle } 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 XAdkSenderHandle,\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 = forwardRef<XAdkSenderHandle, XAdkSenderProps>(({\n clearBtnShow = true,\n allowUpload = false,\n draggable = true,\n loading = false,\n disabled = false,\n value: controlledValue,\n defaultValue = \"\",\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 sendButtonRender,\n suffix,\n header,\n prefix,\n footer,\n}, ref) => {\n const styles = useStyles();\n const isControlled = controlledValue !== undefined;\n const [internalValue, setInternalValue] = useState<string>(defaultValue);\n const value = isControlled ? controlledValue : internalValue;\n const setValue = (newValue: string) => {\n if (!isControlled) {\n setInternalValue(newValue);\n }\n onChange?.(newValue);\n };\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 // 暴露 addFiles 方法供外层(如 DefaultLayout 拖拽上传)调用\n useImperativeHandle(ref, () => ({\n addFiles: (rawFiles: File[]) => {\n handleFileSelect(rawFiles);\n },\n }), [handleFileSelect]);\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 };\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 const hasContent = value.trim().length > 0;\n const hasFiles = files.some((f) => f.status === \"success\");\n const canSend = hasContent || hasFiles;\n\n // 定义按钮组件(供外部 slot 函数通过 actionsComponents 使用)\n // 注意:sendButtonRender 的结果直接内联到 defaultSuffix 中,避免组件引用变化导致闪烁\n const SendButton = (props: any) => {\n if (sendButtonRender) {\n return (\n <>\n {sendButtonRender({\n loading,\n disabled: uploading || disabled,\n canSend,\n onSend: handleSubmit,\n onStop,\n })}\n </>\n );\n }\n return (\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 spin /> : <ArrowUpOutlined />}\n </button>\n </Tooltip>\n );\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 // 默认的后缀内容(操作按钮组) - 直接内联渲染避免组件引用变化导致 unmount/remount\n const defaultSuffix = (\n <div className={styles.buttonGroup}>\n {allowUpload && (\n <Tooltip title=\"上传文件\">\n <button\n className={`${styles.iconButton} ${styles.uploadButton}`}\n onClick={triggerFileSelect}\n disabled={isDisabled}\n aria-label=\"上传文件\"\n >\n <PaperClipOutlined />\n </button>\n </Tooltip>\n )}\n {sendButtonRender ? (\n sendButtonRender({\n loading,\n disabled: uploading || disabled,\n canSend,\n onSend: handleSubmit,\n onStop,\n })\n ) : (\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 >\n {loading ? <LoadingOutlined spin /> : <ArrowUpOutlined />}\n </button>\n </Tooltip>\n )}\n </div>\n );\n\n return (\n <div\n ref={containerRef}\n className={containerClass}\n onDragOver={allowUpload && draggable ? handleDragOver : undefined}\n onDragLeave={allowUpload && draggable ? handleDragLeave : undefined}\n onDrop={allowUpload && draggable ? 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 <div className={styles.inputAndButtons}>\n {/* 清除按钮 */}\n {clearBtnShow && <ClearButton />}\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\n {/* 底部插槽(默认显示提示文字) */}\n {renderSlot(\n footer,\n <div className={styles.tip}>\n 内容由AI生成,无法确保真实准确,仅供参考\n </div>,\n )}\n </div>\n </div>\n </div>\n );\n});\n\nXAdkSender.displayName = 'XAdkSender';\n\nimport XAdkSenderSwitch from \"./Switch\";\n\ntype XAdkSenderComponent = typeof XAdkSender & {\n Switch: typeof XAdkSenderSwitch;\n};\n\nconst CompoundSender = XAdkSender as XAdkSenderComponent;\nCompoundSender.Switch = XAdkSenderSwitch;\n\nexport default CompoundSender;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["files", "FileGallery", "XAdkSenderSwitch"]
|
|
7
7
|
}
|
|
@@ -74,16 +74,7 @@ var useStyles = (0, import_common.withBasicStyles)(() => ({
|
|
|
74
74
|
font-weight: 500;
|
|
75
75
|
`,
|
|
76
76
|
headerRight: import_css.css``,
|
|
77
|
-
stepCount: import_css.css`
|
|
78
|
-
font-size: 12px;
|
|
79
|
-
color: #8194ad;
|
|
80
|
-
background: #fff;
|
|
81
|
-
padding: 2px 6px;
|
|
82
|
-
border-radius: 4px;
|
|
83
|
-
border: 1px solid #e0e0e0;
|
|
84
|
-
`,
|
|
85
77
|
thinkingBody: import_css.css`
|
|
86
|
-
background: #fff;
|
|
87
78
|
padding: 0 16px;
|
|
88
79
|
animation: fadeIn 0.2s ease-in-out;
|
|
89
80
|
`,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/XAdkThoughtChain/styles.tsx"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable import/prefer-default-export */\nimport { css, keyframes } from \"@emotion/css\";\nimport { withBasicStyles, lightBlue, primaryBlue } from \"@/styles/common\";\n\nconst blinkAnimation = keyframes`\n 0% { background-position-x: -200%; background-position-y: 100%; }\n 25% { background-position-x: -100%; background-position-y: 100%; }\n 50% { background-position-x: 0%; background-position-y: 100%; }\n 75% { background-position-x: 100%; background-position-y: 100%; }\n 100% { background-position-x: 200%; background-position-y: 100%; }\n`;\n\nexport const useStyles = withBasicStyles(() => ({\n /* --- ThoughtChain --- */\n\n thoughtChainBlock: css`\n margin-bottom: 16px;\n border-radius: 8px;\n overflow: hidden;\n `,\n\n thoughtHeader: css`\n display: flex;\n align-items: center;\n padding: 10px 14px;\n cursor: pointer;\n user-select: none;\n transition: background 0.2s;\n &:hover {\n background: #f0f0f0;\n border-color: #ccc;\n }\n `,\n\n headerLeft: css`\n display: flex;\n align-items: center;\n gap: 8px;\n `,\n\n caret: css`\n font-size: 12px;\n color: #8194ad;\n transition: transform 0.2s;\n &.open {\n transform: rotate(90deg);\n }\n `,\n\n statusIcon: css`\n font-size: 14px;\n display: flex;\n align-items: center;\n `,\n\n headerTitle: css`\n font-size: 13px;\n color: #8194ad;\n font-weight: 500;\n `,\n\n headerRight: css``,\n\n
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,iBAA+B;AAC/B,oBAAwD;AAExD,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQhB,IAAM,gBAAY,+BAAgB,OAAO;AAAA;AAAA,EAG9C,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMnB,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaf,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAMZ,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASP,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAMZ,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAMb,aAAa;AAAA,EAEb,
|
|
4
|
+
"sourcesContent": ["/* eslint-disable import/prefer-default-export */\nimport { css, keyframes } from \"@emotion/css\";\nimport { withBasicStyles, lightBlue, primaryBlue } from \"@/styles/common\";\n\nconst blinkAnimation = keyframes`\n 0% { background-position-x: -200%; background-position-y: 100%; }\n 25% { background-position-x: -100%; background-position-y: 100%; }\n 50% { background-position-x: 0%; background-position-y: 100%; }\n 75% { background-position-x: 100%; background-position-y: 100%; }\n 100% { background-position-x: 200%; background-position-y: 100%; }\n`;\n\nexport const useStyles = withBasicStyles(() => ({\n /* --- ThoughtChain --- */\n\n thoughtChainBlock: css`\n margin-bottom: 16px;\n border-radius: 8px;\n overflow: hidden;\n `,\n\n thoughtHeader: css`\n display: flex;\n align-items: center;\n padding: 10px 14px;\n cursor: pointer;\n user-select: none;\n transition: background 0.2s;\n &:hover {\n background: #f0f0f0;\n border-color: #ccc;\n }\n `,\n\n headerLeft: css`\n display: flex;\n align-items: center;\n gap: 8px;\n `,\n\n caret: css`\n font-size: 12px;\n color: #8194ad;\n transition: transform 0.2s;\n &.open {\n transform: rotate(90deg);\n }\n `,\n\n statusIcon: css`\n font-size: 14px;\n display: flex;\n align-items: center;\n `,\n\n headerTitle: css`\n font-size: 13px;\n color: #8194ad;\n font-weight: 500;\n `,\n\n headerRight: css``,\n\n thinkingBody: css`\n padding: 0 16px;\n animation: fadeIn 0.2s ease-in-out;\n `,\n\n /* --- Timeline --- */\n\n timeline: css`\n width: 100%;\n `,\n\n timelineItem: css`\n display: flex;\n gap: 12px;\n position: relative;\n `,\n\n timelineLeft: css`\n display: flex;\n flex-direction: column;\n align-items: center;\n width: 20px;\n flex-shrink: 0;\n `,\n\n timelineIcon: css`\n color: #8194ad;\n font-size: 16px;\n margin-top: 6px;\n `,\n\n timelineIconTool: css`\n color: #faad14;\n font-size: 16px;\n margin-top: 6px;\n `,\n\n timelineConnector: css`\n flex: 1;\n width: 1px;\n background-color: #e5e5e5;\n margin: 4px 0;\n min-height: 12px;\n `,\n\n /* Hide connector on last item */\n timelineItemLastConnector: css`\n display: none;\n `,\n\n timelineRight: css`\n flex: 1;\n padding-bottom: 8px;\n min-width: 0;\n `,\n\n collapsibleHeader: css`\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 5px 8px;\n margin: 0 -8px 4px -8px;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s;\n user-select: none;\n\n &:hover {\n background: #f5f5f5;\n }\n `,\n\n titleText: css`\n font-size: 13px;\n font-weight: 600;\n color: #8194ad;\n `,\n\n toolTitleGroup: css`\n display: flex;\n align-items: center;\n gap: 8px;\n overflow: hidden;\n `,\n\n toolName: css`\n font-size: 13px;\n font-weight: 600;\n color: #8194ad;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n `,\n\n toolStatusTag: css`\n font-size: 11px;\n color: #52c41a;\n background: #f6ffed;\n border: 1px solid #b7eb8f;\n padding: 0 6px;\n border-radius: 4px;\n flex-shrink: 0;\n `,\n\n arrowIcon: css`\n font-size: 10px;\n color: #bbb;\n margin-left: 8px;\n `,\n\n stepContent: css`\n font-size: 13px;\n color: #8194ad;\n line-height: 1.6;\n padding-left: 4px;\n `,\n\n statusInfo: css`\n font-size: 12px;\n color: #8194ad;\n margin-bottom: 4px;\n font-style: italic;\n `,\n\n blink: css`\n color: #8194ad;\n background-image: linear-gradient(\n 90deg,\n transparent,\n rgba(0, 0, 0, 0.88),\n transparent\n );\n background-clip: text;\n -webkit-background-clip: text;\n background-size: 50%;\n background-repeat: no-repeat;\n animation-name: ${blinkAnimation};\n animation-duration: 1s;\n animation-iteration-count: infinite;\n animation-timing-function: linear;\n animation-fill-mode: forwards;\n `,\n}));\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,iBAA+B;AAC/B,oBAAwD;AAExD,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQhB,IAAM,gBAAY,+BAAgB,OAAO;AAAA;AAAA,EAG9C,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMnB,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaf,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAMZ,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASP,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAMZ,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAMb,aAAa;AAAA,EAEb,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,UAAU;AAAA;AAAA;AAAA,EAIV,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,EAMd,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQd,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,EAMd,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMlB,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASnB,2BAA2B;AAAA;AAAA;AAAA,EAI3B,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,EAMf,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBnB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA,EAMX,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOhB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASV,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUf,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA,EAMX,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOb,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOZ,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAYa;AAAA;AAAA;AAAA;AAAA;AAAA;AAMtB,EAAE;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -664,6 +664,7 @@ function useADKChat({
|
|
|
664
664
|
return;
|
|
665
665
|
}
|
|
666
666
|
stopChat();
|
|
667
|
+
setInitialized(false);
|
|
667
668
|
setCurrentSessionId(sessionId);
|
|
668
669
|
const { data, result } = await (0, import_api.fetchSessionDetail)({
|
|
669
670
|
url,
|
|
@@ -681,6 +682,7 @@ function useADKChat({
|
|
|
681
682
|
);
|
|
682
683
|
}
|
|
683
684
|
}
|
|
685
|
+
setInitialized(true);
|
|
684
686
|
} else {
|
|
685
687
|
setCurrentSessionId((0, import_uuid.v4)());
|
|
686
688
|
}
|