@ai-group/chat-sdk 3.3.5 → 3.3.7

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.
@@ -410,7 +410,7 @@ var XAdkSender = (0, import_react.forwardRef)(({
410
410
  disabled: uploading || disabled,
411
411
  "aria-label": loading ? "停止生成" : "发送消息",
412
412
  ...props,
413
- children: loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.LoadingOutlined, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.ArrowUpOutlined, {})
413
+ children: loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.LoadingOutlined, { spin: true }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.ArrowUpOutlined, {})
414
414
  }
415
415
  ) });
416
416
  const UploadButton = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Tooltip, { title: "上传文件", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -462,8 +462,26 @@ var XAdkSender = (0, import_react.forwardRef)(({
462
462
  return slot;
463
463
  };
464
464
  const defaultSuffix = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: styles.buttonGroup, children: [
465
- allowUpload && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UploadButton, {}),
466
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SendButton, {})
465
+ allowUpload && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Tooltip, { title: "上传文件", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
466
+ "button",
467
+ {
468
+ className: `${styles.iconButton} ${styles.uploadButton}`,
469
+ onClick: triggerFileSelect,
470
+ disabled: isDisabled,
471
+ "aria-label": "上传文件",
472
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.PaperClipOutlined, {})
473
+ }
474
+ ) }),
475
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Tooltip, { title: loading ? "停止生成" : "发送消息", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
476
+ "button",
477
+ {
478
+ className: `${styles.iconButton} ${styles.sendButton} ${loading ? "stop" : ""}`,
479
+ onClick: loading ? onStop : handleSubmit,
480
+ disabled: uploading || disabled,
481
+ "aria-label": loading ? "停止生成" : "发送消息",
482
+ children: loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.LoadingOutlined, { spin: true }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.ArrowUpOutlined, {})
483
+ }
484
+ ) })
467
485
  ] });
468
486
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
469
487
  "div",
@@ -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 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 // 定义按钮组件\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 && 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 {/* 清除按钮 */}\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\nXAdkSender.displayName = 'XAdkSender';\n\nexport default XAdkSender;\n"],
5
- "mappings": "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",
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 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 <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 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 {/* 清除按钮 */}\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\nXAdkSender.displayName = 'XAdkSender';\n\nexport default XAdkSender;\n"],
5
+ "mappings": "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",
6
6
  "names": ["files", "FileGallery"]
7
7
  }
@@ -5,7 +5,7 @@ declare function useADKChat({ url, token, config, type, enabled, // ← 新增:
5
5
  strategies, onError, onMessage, onSuccess, onStream, }: DebugOptions): {
6
6
  appInfo: ChatConfig | null;
7
7
  startChat: ({ text, files, functionResponse, }: SendContent) => Promise<void>;
8
- reChat: () => void;
8
+ reChat: () => Promise<void>;
9
9
  stopChat: () => void;
10
10
  clearChat: () => void;
11
11
  suggestChat: (text: string) => void;
@@ -21,6 +21,7 @@ strategies, onError, onMessage, onSuccess, onStream, }: DebugOptions): {
21
21
  prologue: string;
22
22
  suggestions: string[];
23
23
  loading: boolean;
24
+ sessionLoading: boolean;
24
25
  messages: IMessage[];
25
26
  insertSuggestedQuestions: (suggested_questions?: string[]) => void;
26
27
  insertPrologue: (prologue: string) => void;
@@ -34,12 +34,13 @@ var combineTextParts = (parts) => {
34
34
  const result = [];
35
35
  let combinedTextPart;
36
36
  for (const part of parts) {
37
- if (part.text) {
37
+ const isPureText = part.text && !part.fileData;
38
+ if (isPureText) {
38
39
  if (!combinedTextPart || combinedTextPart.thought !== part.thought) {
39
40
  combinedTextPart = { ...part };
40
41
  result.push(combinedTextPart);
41
42
  } else {
42
- combinedTextPart.text += part.text;
43
+ combinedTextPart.text = (combinedTextPart.text || "") + (part.text || "");
43
44
  }
44
45
  } else {
45
46
  combinedTextPart = void 0;
@@ -62,6 +63,8 @@ function useADKChat({
62
63
  onStream
63
64
  }) {
64
65
  const [loading, setLoading] = (0, import_react.useState)(false);
66
+ const [sessionLoading, setSessionLoading] = (0, import_react.useState)(false);
67
+ const sendingRef = (0, import_react.useRef)(false);
65
68
  const ctrl = (0, import_react.useRef)(null);
66
69
  const { appNo, showFirstSession } = config || {};
67
70
  const [appInfo, setAppInfo] = (0, import_react.useState)(null);
@@ -257,11 +260,6 @@ function useADKChat({
257
260
  storeEvents(part, event);
258
261
  insertMessage(newMessage);
259
262
  } else {
260
- if (event.partial === false) {
261
- storeEvents(part, event);
262
- textMsgRef.current = null;
263
- return;
264
- }
265
263
  if (renderedContent) {
266
264
  textMsgRef.current = {
267
265
  ...textMsgRef.current,
@@ -273,6 +271,10 @@ function useADKChat({
273
271
  text: (textMsgRef.current.text || "") + newChunk
274
272
  };
275
273
  updateMessage(textMsgRef.current);
274
+ if (event.partial === false) {
275
+ storeEvents(part, event);
276
+ textMsgRef.current = null;
277
+ }
276
278
  }
277
279
  } else {
278
280
  textMsgRef.current = null;
@@ -414,10 +416,11 @@ function useADKChat({
414
416
  files = [],
415
417
  functionResponse
416
418
  }) => {
417
- if (loading)
419
+ if (loading || sendingRef.current)
418
420
  return;
419
421
  if (!text.trim() && !(files == null ? void 0 : files.length) && !functionResponse)
420
422
  return;
423
+ sendingRef.current = true;
421
424
  setSuggestedQuestions([]);
422
425
  setMessages((prev) => {
423
426
  let temp = [...prev];
@@ -451,67 +454,67 @@ function useADKChat({
451
454
  }
452
455
  return temp;
453
456
  });
454
- await sendMessageRequest({
455
- stream: true,
456
- sessionId: currentSessionId,
457
- content: {
458
- role: "user",
459
- parts: functionResponse ? [{ functionResponse }] : [{ text }]
460
- },
461
- files: files.map((file) => {
462
- var _a, _b, _c, _d, _e, _f;
463
- return {
464
- fileName: file.fileName || ((_a = file.response) == null ? void 0 : _a.fileName),
465
- fileId: file.fileId || ((_b = file.response) == null ? void 0 : _b.fileId),
466
- tempUrl: file.tempUrl || ((_c = file.response) == null ? void 0 : _c.fileUrl) || ((_d = file.response) == null ? void 0 : _d.tempUrl),
467
- type: file.type || ((_e = file.response) == null ? void 0 : _e.fileType),
468
- mimeType: file.mimeType || ((_f = file.response) == null ? void 0 : _f.mimeType)
469
- };
470
- }),
471
- ...stateDeltaRef.current !== void 0 && { stateDelta: stateDeltaRef.current }
472
- });
457
+ try {
458
+ await sendMessageRequest({
459
+ stream: true,
460
+ sessionId: currentSessionId,
461
+ content: {
462
+ role: "user",
463
+ parts: functionResponse ? [{ functionResponse }] : [{ text }]
464
+ },
465
+ files: files.map((file) => {
466
+ var _a, _b, _c, _d, _e, _f;
467
+ return {
468
+ fileName: file.fileName || ((_a = file.response) == null ? void 0 : _a.fileName),
469
+ fileId: file.fileId || ((_b = file.response) == null ? void 0 : _b.fileId),
470
+ tempUrl: file.tempUrl || ((_c = file.response) == null ? void 0 : _c.fileUrl) || ((_d = file.response) == null ? void 0 : _d.tempUrl),
471
+ type: file.type || ((_e = file.response) == null ? void 0 : _e.fileType),
472
+ mimeType: file.mimeType || ((_f = file.response) == null ? void 0 : _f.mimeType)
473
+ };
474
+ }),
475
+ ...stateDeltaRef.current !== void 0 && { stateDelta: stateDeltaRef.current }
476
+ });
477
+ } finally {
478
+ sendingRef.current = false;
479
+ }
473
480
  initAppConversations();
474
481
  };
475
- const reChat = () => {
476
- if (loading)
482
+ const reChat = async () => {
483
+ if (loading || sendingRef.current)
477
484
  return;
478
485
  if (messages.length === 0)
479
486
  return;
480
- setMessages((prev) => {
481
- const newMessages = [...prev];
482
- const lastAIIndex = newMessages.findLastIndex(
483
- (msg) => msg.role === "bot"
484
- );
485
- if (lastAIIndex !== -1) {
486
- newMessages.splice(lastAIIndex, 1);
487
- }
488
- setSuggestedQuestions([]);
489
- const lastUserMessage = newMessages.findLast(
490
- (msg) => msg.role === "user"
491
- );
492
- if (lastUserMessage) {
493
- const { text = "", fileData = [] } = lastUserMessage;
494
- sendMessageRequest({
495
- stream: true,
496
- sessionId: currentSessionId,
497
- content: {
498
- role: "user",
499
- parts: [
500
- {
501
- text
502
- }
503
- ]
504
- },
505
- files: fileData == null ? void 0 : fileData.map((file) => ({
506
- fileName: file.displayName,
507
- tempUrl: file.fileUri,
508
- mimeType: file.mimeType
509
- })),
510
- ...stateDeltaRef.current !== void 0 && { stateDelta: stateDeltaRef.current }
511
- });
512
- }
513
- return newMessages;
514
- });
487
+ sendingRef.current = true;
488
+ setSuggestedQuestions([]);
489
+ const lastUserIndex = messages.findLastIndex(
490
+ (msg) => msg.role === "user" && !msg.functionResponse
491
+ );
492
+ if (lastUserIndex === -1) {
493
+ sendingRef.current = false;
494
+ return;
495
+ }
496
+ const newMessages = messages.slice(0, lastUserIndex + 1);
497
+ setMessages(newMessages);
498
+ const lastUserMessage = newMessages[lastUserIndex];
499
+ const { text = "", fileData = [] } = lastUserMessage;
500
+ try {
501
+ await sendMessageRequest({
502
+ stream: true,
503
+ sessionId: currentSessionId,
504
+ content: {
505
+ role: "user",
506
+ parts: [{ text }]
507
+ },
508
+ files: fileData == null ? void 0 : fileData.map((file) => ({
509
+ fileName: file.displayName,
510
+ tempUrl: file.fileUri,
511
+ mimeType: file.mimeType
512
+ })),
513
+ ...stateDeltaRef.current !== void 0 && { stateDelta: stateDeltaRef.current }
514
+ });
515
+ } finally {
516
+ sendingRef.current = false;
517
+ }
515
518
  };
516
519
  const confirmFnCall = (fnCall, confirmed) => {
517
520
  var _a;
@@ -671,7 +674,7 @@ function useADKChat({
671
674
  const initAppConversations = async (fetchDetail = false) => {
672
675
  var _a, _b;
673
676
  try {
674
- setLoading(true);
677
+ setSessionLoading(true);
675
678
  const {
676
679
  data = [],
677
680
  pagination,
@@ -735,7 +738,7 @@ function useADKChat({
735
738
  message: "获取会话列表失败"
736
739
  });
737
740
  } finally {
738
- setLoading(false);
741
+ setSessionLoading(false);
739
742
  }
740
743
  };
741
744
  const deleteSession = async (sessionId) => {
@@ -778,7 +781,7 @@ function useADKChat({
778
781
  return false;
779
782
  };
780
783
  const updateSession = async (sessionId, title) => {
781
- if (!sessionId && !title)
784
+ if (!sessionId || !title)
782
785
  return;
783
786
  const { result } = await (0, import_api.updateSessionTitle)({
784
787
  url,
@@ -795,7 +798,7 @@ function useADKChat({
795
798
  };
796
799
  const initConfig = async () => {
797
800
  try {
798
- setLoading(true);
801
+ setSessionLoading(true);
799
802
  const { data, result } = await (0, import_api.fetchAppConfig)({ url, appNo });
800
803
  if (result.code === import_constants.API_SUCCESS_CODE) {
801
804
  setAppInfo(data);
@@ -815,7 +818,7 @@ function useADKChat({
815
818
  message: "获取应用配置失败"
816
819
  });
817
820
  } finally {
818
- setLoading(false);
821
+ setSessionLoading(false);
819
822
  }
820
823
  };
821
824
  (0, import_react.useEffect)(() => {
@@ -843,6 +846,7 @@ function useADKChat({
843
846
  prologue,
844
847
  suggestions: suggestedQuestions,
845
848
  loading,
849
+ sessionLoading,
846
850
  messages: mergedMessages,
847
851
  insertSuggestedQuestions,
848
852
  insertPrologue,