@ai-group/chat-sdk 3.7.5 → 3.8.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +36 -0
  2. package/dist/cjs/components/DislikeFeedback.js.map +2 -2
  3. package/dist/cjs/components/FileGallery/index.js +2 -1
  4. package/dist/cjs/components/FileGallery/index.js.map +3 -3
  5. package/dist/cjs/components/FolderTree/DirectoryTree.js.map +2 -2
  6. package/dist/cjs/components/Sources/components/CarouselCard.js.map +2 -2
  7. package/dist/cjs/components/XAdkChatbot/index.js +94 -18
  8. package/dist/cjs/components/XAdkChatbot/index.js.map +3 -3
  9. package/dist/cjs/components/XAdkChatbot/styles.d.ts +3 -0
  10. package/dist/cjs/components/XAdkChatbot/styles.js +29 -0
  11. package/dist/cjs/components/XAdkChatbot/styles.js.map +2 -2
  12. package/dist/cjs/components/XAdkChatbot/utils.d.ts +14 -0
  13. package/dist/cjs/components/XAdkChatbot/utils.js +79 -0
  14. package/dist/cjs/components/XAdkChatbot/utils.js.map +7 -0
  15. package/dist/cjs/components/XAdkProvider/compound/DislikeFeedBack.js.map +2 -2
  16. package/dist/cjs/components/XAdkProvider/compound/Messages.js +2 -0
  17. package/dist/cjs/components/XAdkProvider/compound/Messages.js.map +2 -2
  18. package/dist/cjs/components/XAdkProvider/compound/Sidebar.js.map +2 -2
  19. package/dist/cjs/components/XAdkProvider/context/ChatStateContext.d.ts +2 -0
  20. package/dist/cjs/components/XAdkProvider/context/ChatStateContext.js.map +2 -2
  21. package/dist/cjs/components/XAdkProvider/index.js +3 -0
  22. package/dist/cjs/components/XAdkProvider/index.js.map +2 -2
  23. package/dist/cjs/components/XAdkSender/InputTags.js.map +2 -2
  24. package/dist/cjs/components/XAdkThoughtChain/index.js +23 -28
  25. package/dist/cjs/components/XAdkThoughtChain/index.js.map +2 -2
  26. package/dist/cjs/components/XAdkThoughtChain/styles.d.ts +3 -1
  27. package/dist/cjs/components/XAdkThoughtChain/styles.js +63 -29
  28. package/dist/cjs/components/XAdkThoughtChain/styles.js.map +2 -2
  29. package/dist/cjs/components/XAiChatbot/index.js.map +2 -2
  30. package/dist/cjs/components/XAiConversations/index.js +2 -1
  31. package/dist/cjs/components/XAiConversations/index.js.map +3 -3
  32. package/dist/cjs/components/XAiProvider/index.js.map +2 -2
  33. package/dist/cjs/components/XAiSender/index.js.map +2 -2
  34. package/dist/cjs/hooks/useADKChat.d.ts +6 -1
  35. package/dist/cjs/hooks/useADKChat.js +15 -1
  36. package/dist/cjs/hooks/useADKChat.js.map +2 -2
  37. package/dist/cjs/hooks/useADKMessageQueue.d.ts +22 -0
  38. package/dist/cjs/hooks/useADKMessageQueue.js +105 -0
  39. package/dist/cjs/hooks/useADKMessageQueue.js.map +7 -0
  40. package/dist/cjs/index.d.ts +3 -1
  41. package/dist/cjs/index.js +3 -0
  42. package/dist/cjs/index.js.map +2 -2
  43. package/dist/cjs/react/index.d.ts +2 -0
  44. package/dist/cjs/react/index.js +3 -0
  45. package/dist/cjs/react/index.js.map +2 -2
  46. package/dist/cjs/types/XAdkChatbot.d.ts +13 -0
  47. package/dist/cjs/types/XAdkChatbot.js.map +1 -1
  48. package/dist/cjs/types/XAdkProvider.d.ts +7 -0
  49. package/dist/cjs/types/XAdkProvider.js.map +2 -2
  50. package/dist/cjs/types/XAdkThoughtChain.d.ts +2 -0
  51. package/dist/cjs/types/XAdkThoughtChain.js.map +1 -1
  52. package/dist/cjs/types/antdx.d.ts +2 -2
  53. package/dist/cjs/types/antdx.js.map +1 -1
  54. package/dist/esm/components/DislikeFeedback.js.map +1 -1
  55. package/dist/esm/components/FileGallery/index.js +2 -1
  56. package/dist/esm/components/FileGallery/index.js.map +1 -1
  57. package/dist/esm/components/FolderTree/DirectoryTree.js.map +1 -1
  58. package/dist/esm/components/Sources/components/CarouselCard.js.map +1 -1
  59. package/dist/esm/components/XAdkChatbot/index.js +89 -14
  60. package/dist/esm/components/XAdkChatbot/index.js.map +1 -1
  61. package/dist/esm/components/XAdkChatbot/styles.d.ts +3 -0
  62. package/dist/esm/components/XAdkChatbot/styles.js +25 -22
  63. package/dist/esm/components/XAdkChatbot/styles.js.map +1 -1
  64. package/dist/esm/components/XAdkChatbot/utils.d.ts +14 -0
  65. package/dist/esm/components/XAdkChatbot/utils.js +66 -0
  66. package/dist/esm/components/XAdkChatbot/utils.js.map +1 -0
  67. package/dist/esm/components/XAdkProvider/compound/DislikeFeedBack.js.map +1 -1
  68. package/dist/esm/components/XAdkProvider/compound/Messages.js +5 -1
  69. package/dist/esm/components/XAdkProvider/compound/Messages.js.map +1 -1
  70. package/dist/esm/components/XAdkProvider/compound/Sidebar.js.map +1 -1
  71. package/dist/esm/components/XAdkProvider/context/ChatStateContext.d.ts +2 -0
  72. package/dist/esm/components/XAdkProvider/context/ChatStateContext.js.map +1 -1
  73. package/dist/esm/components/XAdkProvider/index.js +4 -2
  74. package/dist/esm/components/XAdkProvider/index.js.map +1 -1
  75. package/dist/esm/components/XAdkSender/InputTags.js.map +1 -1
  76. package/dist/esm/components/XAdkThoughtChain/index.js +52 -78
  77. package/dist/esm/components/XAdkThoughtChain/index.js.map +1 -1
  78. package/dist/esm/components/XAdkThoughtChain/styles.d.ts +3 -1
  79. package/dist/esm/components/XAdkThoughtChain/styles.js +28 -26
  80. package/dist/esm/components/XAdkThoughtChain/styles.js.map +1 -1
  81. package/dist/esm/components/XAiChatbot/index.js.map +1 -1
  82. package/dist/esm/components/XAiConversations/index.js +2 -1
  83. package/dist/esm/components/XAiConversations/index.js.map +1 -1
  84. package/dist/esm/components/XAiProvider/index.js.map +1 -1
  85. package/dist/esm/components/XAiSender/index.js.map +1 -1
  86. package/dist/esm/hooks/useADKChat.d.ts +6 -1
  87. package/dist/esm/hooks/useADKChat.js +18 -1
  88. package/dist/esm/hooks/useADKChat.js.map +1 -1
  89. package/dist/esm/hooks/useADKMessageQueue.d.ts +22 -0
  90. package/dist/esm/hooks/useADKMessageQueue.js +157 -0
  91. package/dist/esm/hooks/useADKMessageQueue.js.map +1 -0
  92. package/dist/esm/index.d.ts +3 -1
  93. package/dist/esm/index.js +2 -1
  94. package/dist/esm/index.js.map +1 -1
  95. package/dist/esm/react/index.d.ts +2 -0
  96. package/dist/esm/react/index.js +1 -0
  97. package/dist/esm/react/index.js.map +1 -1
  98. package/dist/esm/types/XAdkChatbot.d.ts +13 -0
  99. package/dist/esm/types/XAdkChatbot.js.map +1 -1
  100. package/dist/esm/types/XAdkProvider.d.ts +7 -0
  101. package/dist/esm/types/XAdkProvider.js.map +1 -1
  102. package/dist/esm/types/XAdkThoughtChain.d.ts +2 -0
  103. package/dist/esm/types/XAdkThoughtChain.js.map +1 -1
  104. package/dist/esm/types/antdx.d.ts +2 -2
  105. package/dist/esm/types/antdx.js.map +1 -1
  106. package/dist/umd/chat-sdk.min.js +1 -1
  107. package/package.json +1 -1
package/README.md CHANGED
@@ -8,6 +8,42 @@
8
8
  npm install @ai-group/chat-sdk
9
9
  ```
10
10
 
11
+ 本次源码版本(预发布):`3.8.0-alpha.1`。本次增加可选的会话级消息队列;原有 `useADKChat.startChat` 仍直接发送并返回 `Promise<void>`,现有接入无需迁移。
12
+
13
+ ### 生成期间排队发送(可选)
14
+
15
+ `useADKChat` 提供 `queueChat`、`queuedMessages`、`removeQueuedMessage` 和 `sendQueuedMessageNow`。生成期间调用 `queueChat` 会缓存完整消息;当前回复结束后按顺序发送。点击“立即发送”时,SDK 先调用可选的 `onInterrupt(sessionId)`,成功后停止当前前端流并发送选中的消息。后端接口的 URL、鉴权及请求体由业务方实现。
16
+
17
+ ```tsx
18
+ import { useADKChat } from '@ai-group/chat-sdk';
19
+
20
+ function MyChat() {
21
+ const chat = useADKChat({
22
+ url: 'https://your-api-server.com',
23
+ token: 'your-token',
24
+ config: { appNo: 'your-app-no' },
25
+ onInterrupt: async (sessionId) => {
26
+ await yourApi.interrupt({ sessionId });
27
+ },
28
+ });
29
+
30
+ return (
31
+ <>
32
+ <button onClick={() => chat.queueChat({ text: '下一条问题' })}>发送或排队</button>
33
+ {chat.queuedMessages
34
+ .filter((item) => item.sessionId === chat.currentSessionId)
35
+ .map((item) => (
36
+ <button key={item.id} onClick={() => void chat.sendQueuedMessageNow(item.id)}>
37
+ 立即发送:{item.preview}
38
+ </button>
39
+ ))}
40
+ </>
41
+ );
42
+ }
43
+ ```
44
+
45
+ 已有自定义聊天 Hook 的项目,可单独使用 `useADKMessageQueue`,传入 `sessionId`、`loading`、`send`、`stop` 和可选的 `interrupt`。详见 [Hook 文档](../../apps/docs/docs/hooks/use-adk-message-queue.md)。
46
+
11
47
  ## 使用方法
12
48
 
13
49
  ### 方式一:推荐写法(Coze 风格)
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/components/DislikeFeedback.tsx"],
4
- "sourcesContent": ["import { Button, Form, Input, message, Popover } from 'antd';\nimport React, { useState } from 'react';\nimport { DislikeFilled, DislikeOutlined } from '@ant-design/icons';\nimport FeedbackTags from './FeedbackTags';\n\nexport interface DislikeFeedbackProps {\n msg: any;\n activeColor: string;\n onSubmit: (v: any) => Promise<boolean>;\n}\n\nexport const DislikeFeedback: React.FC<DislikeFeedbackProps> = ({ msg, activeColor, onSubmit }) => {\n const [form] = Form.useForm();\n const [showFeedback, setShowFeedback] = useState(false);\n return (\n <Popover\n trigger={['click']}\n open={showFeedback}\n onOpenChange={(v) => {\n const isLike = msg.isLike ?? 0;\n if (v && isLike !== 0) {\n message.warning('已收到您的反馈,无需重复提交');\n return;\n }\n if (v) {\n form.resetFields();\n }\n setShowFeedback(v);\n }}\n title={(\n <div>\n <div>抱歉,未能满足您的需求</div>\n <div style={{ color: '#999', fontWeight: 'normal', marginBottom: 12 }}>\n 为提升您的使用体验,请告诉我们不足的方向\n </div>\n </div>\n )}\n content={(\n <div>\n <Form form={form}>\n <Form.Item name=\"feedbackTags\" style={{ marginBottom: 12 }}>\n <FeedbackTags />\n </Form.Item>\n <Form.Item name=\"feedbackDescription\" style={{ marginBottom: 12 }}>\n <Input.TextArea rows={4} placeholder=\"欢迎说说你的想法\" />\n </Form.Item>\n <Form.Item dependencies={['feedbackTags', 'feedbackDescription']} noStyle>\n {({ getFieldValue }) => {\n const feedbackTags = getFieldValue('feedbackTags') || [];\n const feedbackDescription = getFieldValue('feedbackDescription');\n const disabled = !feedbackTags.length && !feedbackDescription;\n return (\n <Button\n type=\"primary\"\n block\n disabled={disabled}\n onClick={async () => {\n const formData = await form.validateFields();\n const res = await onSubmit(formData);\n if (res) {\n setShowFeedback(false);\n }\n }}\n >\n 提交反馈\n </Button>\n );\n }}\n </Form.Item>\n </Form>\n </div>\n )}\n >\n {(msg.raw?.isLike || msg.isLike) === -1\n ? (\n <Button\n type=\"text\"\n size=\"small\"\n icon={<DislikeFilled style={{ color: activeColor }} />}\n />\n )\n : (\n <Button\n type=\"text\"\n size=\"small\"\n icon={<DislikeOutlined style={{ color: '#949494' }} />}\n />\n )}\n </Popover>\n );\n};\n\nexport default DislikeFeedback;\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAsD;AACtD,mBAAgC;AAChC,mBAA+C;AAC/C,0BAAyB;AA2BjB;AAnBD,IAAM,kBAAkD,CAAC,EAAE,KAAK,aAAa,SAAS,MAAM;AAXnG;AAYE,QAAM,CAAC,IAAI,IAAI,iBAAK,QAAQ;AAC5B,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAS,KAAK;AACtD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAS,CAAC,OAAO;AAAA,MACjB,MAAM;AAAA,MACN,cAAc,CAAC,MAAM;AACnB,cAAM,SAAS,IAAI,UAAU;AAC7B,YAAI,KAAK,WAAW,GAAG;AACrB,8BAAQ,QAAQ,gBAAgB;AAChC;AAAA,QACF;AACA,YAAI,GAAG;AACL,eAAK,YAAY;AAAA,QACnB;AACA,wBAAgB,CAAC;AAAA,MACnB;AAAA,MACA,OACE,6CAAC,SACC;AAAA,oDAAC,SAAI,yBAAW;AAAA,QAChB,4CAAC,SAAI,OAAO,EAAE,OAAO,QAAQ,YAAY,UAAU,cAAc,GAAG,GAAG,kCAEvE;AAAA,SACF;AAAA,MAEF,SACE,4CAAC,SACC,uDAAC,oBAAK,MACJ;AAAA,oDAAC,iBAAK,MAAL,EAAU,MAAK,gBAAe,OAAO,EAAE,cAAc,GAAG,GACvD,sDAAC,oBAAAA,SAAA,EAAa,GAChB;AAAA,QACA,4CAAC,iBAAK,MAAL,EAAU,MAAK,uBAAsB,OAAO,EAAE,cAAc,GAAG,GAC9D,sDAAC,kBAAM,UAAN,EAAe,MAAM,GAAG,aAAY,YAAW,GAClD;AAAA,QACA,4CAAC,iBAAK,MAAL,EAAU,cAAc,CAAC,gBAAgB,qBAAqB,GAAG,SAAO,MACtE,WAAC,EAAE,cAAc,MAAM;AACtB,gBAAM,eAAe,cAAc,cAAc,KAAK,CAAC;AACvD,gBAAM,sBAAsB,cAAc,qBAAqB;AAC/D,gBAAM,WAAW,CAAC,aAAa,UAAU,CAAC;AAC1C,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAK;AAAA,cACL;AAAA,cACA,SAAS,YAAY;AACnB,sBAAM,WAAW,MAAM,KAAK,eAAe;AAC3C,sBAAM,MAAM,MAAM,SAAS,QAAQ;AACnC,oBAAI,KAAK;AACP,kCAAgB,KAAK;AAAA,gBACvB;AAAA,cACF;AAAA,cACD;AAAA;AAAA,UAED;AAAA,QAEJ,GACF;AAAA,SACF,GACF;AAAA,MAGA,sBAAI,QAAJ,mBAAS,WAAU,IAAI,YAAY,KAE/B;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,MAAK;AAAA,UACL,MAAM,4CAAC,8BAAc,OAAO,EAAE,OAAO,YAAY,GAAG;AAAA;AAAA,MACtD,IAGA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,MAAK;AAAA,UACL,MAAM,4CAAC,gCAAgB,OAAO,EAAE,OAAO,UAAU,GAAG;AAAA;AAAA,MACtD;AAAA;AAAA,EAER;AAEJ;AAEA,IAAO,0BAAQ;",
4
+ "sourcesContent": ["import { Button, Form, Input, message, Popover } from 'antd';\nimport React, { useState } from 'react';\nimport { DislikeFilled, DislikeOutlined } from '@ant-design/icons';\nimport FeedbackTags from './FeedbackTags';\n\nexport interface DislikeFeedbackProps {\n msg: any;\n activeColor: string;\n onSubmit: (v: any) => Promise<boolean>;\n}\n\nexport const DislikeFeedback: React.FC<DislikeFeedbackProps> = ({ msg, activeColor, onSubmit }) => {\n const [form] = Form.useForm();\n const [showFeedback, setShowFeedback] = useState(false);\n return (\n <Popover\n trigger={['click']}\n open={showFeedback}\n onOpenChange={(v: boolean) => {\n const isLike = msg.isLike ?? 0;\n if (v && isLike !== 0) {\n message.warning('已收到您的反馈,无需重复提交');\n return;\n }\n if (v) {\n form.resetFields();\n }\n setShowFeedback(v);\n }}\n title={(\n <div>\n <div>抱歉,未能满足您的需求</div>\n <div style={{ color: '#999', fontWeight: 'normal', marginBottom: 12 }}>\n 为提升您的使用体验,请告诉我们不足的方向\n </div>\n </div>\n )}\n content={(\n <div>\n <Form form={form}>\n <Form.Item name=\"feedbackTags\" style={{ marginBottom: 12 }}>\n <FeedbackTags />\n </Form.Item>\n <Form.Item name=\"feedbackDescription\" style={{ marginBottom: 12 }}>\n <Input.TextArea rows={4} placeholder=\"欢迎说说你的想法\" />\n </Form.Item>\n <Form.Item dependencies={['feedbackTags', 'feedbackDescription']} noStyle>\n {({ getFieldValue }: { getFieldValue: (name: string) => any }) => {\n const feedbackTags = getFieldValue('feedbackTags') || [];\n const feedbackDescription = getFieldValue('feedbackDescription');\n const disabled = !feedbackTags.length && !feedbackDescription;\n return (\n <Button\n type=\"primary\"\n block\n disabled={disabled}\n onClick={async () => {\n const formData = await form.validateFields();\n const res = await onSubmit(formData);\n if (res) {\n setShowFeedback(false);\n }\n }}\n >\n 提交反馈\n </Button>\n );\n }}\n </Form.Item>\n </Form>\n </div>\n )}\n >\n {(msg.raw?.isLike || msg.isLike) === -1\n ? (\n <Button\n type=\"text\"\n size=\"small\"\n icon={<DislikeFilled style={{ color: activeColor }} />}\n />\n )\n : (\n <Button\n type=\"text\"\n size=\"small\"\n icon={<DislikeOutlined style={{ color: '#949494' }} />}\n />\n )}\n </Popover>\n );\n};\n\nexport default DislikeFeedback;\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAsD;AACtD,mBAAgC;AAChC,mBAA+C;AAC/C,0BAAyB;AA2BjB;AAnBD,IAAM,kBAAkD,CAAC,EAAE,KAAK,aAAa,SAAS,MAAM;AAXnG;AAYE,QAAM,CAAC,IAAI,IAAI,iBAAK,QAAQ;AAC5B,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAS,KAAK;AACtD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAS,CAAC,OAAO;AAAA,MACjB,MAAM;AAAA,MACN,cAAc,CAAC,MAAe;AAC5B,cAAM,SAAS,IAAI,UAAU;AAC7B,YAAI,KAAK,WAAW,GAAG;AACrB,8BAAQ,QAAQ,gBAAgB;AAChC;AAAA,QACF;AACA,YAAI,GAAG;AACL,eAAK,YAAY;AAAA,QACnB;AACA,wBAAgB,CAAC;AAAA,MACnB;AAAA,MACA,OACE,6CAAC,SACC;AAAA,oDAAC,SAAI,yBAAW;AAAA,QAChB,4CAAC,SAAI,OAAO,EAAE,OAAO,QAAQ,YAAY,UAAU,cAAc,GAAG,GAAG,kCAEvE;AAAA,SACF;AAAA,MAEF,SACE,4CAAC,SACC,uDAAC,oBAAK,MACJ;AAAA,oDAAC,iBAAK,MAAL,EAAU,MAAK,gBAAe,OAAO,EAAE,cAAc,GAAG,GACvD,sDAAC,oBAAAA,SAAA,EAAa,GAChB;AAAA,QACA,4CAAC,iBAAK,MAAL,EAAU,MAAK,uBAAsB,OAAO,EAAE,cAAc,GAAG,GAC9D,sDAAC,kBAAM,UAAN,EAAe,MAAM,GAAG,aAAY,YAAW,GAClD;AAAA,QACA,4CAAC,iBAAK,MAAL,EAAU,cAAc,CAAC,gBAAgB,qBAAqB,GAAG,SAAO,MACtE,WAAC,EAAE,cAAc,MAAgD;AAChE,gBAAM,eAAe,cAAc,cAAc,KAAK,CAAC;AACvD,gBAAM,sBAAsB,cAAc,qBAAqB;AAC/D,gBAAM,WAAW,CAAC,aAAa,UAAU,CAAC;AAC1C,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAK;AAAA,cACL;AAAA,cACA,SAAS,YAAY;AACnB,sBAAM,WAAW,MAAM,KAAK,eAAe;AAC3C,sBAAM,MAAM,MAAM,SAAS,QAAQ;AACnC,oBAAI,KAAK;AACP,kCAAgB,KAAK;AAAA,gBACvB;AAAA,cACF;AAAA,cACD;AAAA;AAAA,UAED;AAAA,QAEJ,GACF;AAAA,SACF,GACF;AAAA,MAGA,sBAAI,QAAJ,mBAAS,WAAU,IAAI,YAAY,KAE/B;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,MAAK;AAAA,UACL,MAAM,4CAAC,8BAAc,OAAO,EAAE,OAAO,YAAY,GAAG;AAAA;AAAA,MACtD,IAGA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,MAAK;AAAA,UACL,MAAM,4CAAC,gCAAgB,OAAO,EAAE,OAAO,UAAU,GAAG;AAAA;AAAA,MACtD;AAAA;AAAA,EAER;AAEJ;AAEA,IAAO,0BAAQ;",
6
6
  "names": ["FeedbackTags"]
7
7
  }
@@ -27,6 +27,7 @@ var import_antd = require("antd");
27
27
  var import_icons = require("@ant-design/icons");
28
28
  var import_styles = require("./styles");
29
29
  var import_jsx_runtime = require("react/jsx-runtime");
30
+ var Image = import_antd.Image;
30
31
  var getFileName = (file) => file.displayName || file.name || "";
31
32
  var getFileSize = (file) => file.size || 0;
32
33
  var getMimeType = (file) => file.mimeType || file.type || "";
@@ -146,7 +147,7 @@ var ImageWithLoading = ({
146
147
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "加载失败" })
147
148
  ] }),
148
149
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
149
- import_antd.Image,
150
+ Image,
150
151
  {
151
152
  src,
152
153
  alt,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/components/FileGallery/index.tsx"],
4
- "sourcesContent": ["import React, { useState, useEffect, useMemo } from \"react\";\nimport { Image, Tooltip, Progress } from \"antd\";\nimport {\n FileOutlined,\n FilePdfOutlined,\n FileWordOutlined,\n FileExcelOutlined,\n FilePptOutlined,\n FileImageOutlined,\n CloseOutlined,\n AudioOutlined,\n FileZipOutlined,\n VideoCameraOutlined,\n FileMarkdownOutlined,\n PictureOutlined,\n} from \"@ant-design/icons\";\nimport {\n FileItem,\n FileGalleryProps,\n FileIconType,\n ImageDisplayMode,\n FileCardType,\n} from \"@/types\";\nimport { useStyles } from \"./styles\";\n\n// ==================== 工具函数 ====================\n\nconst getFileName = (file: FileItem): string =>\n file.displayName || file.name || \"\";\n\nconst getFileSize = (file: FileItem): number => file.size || 0;\n\nconst getMimeType = (file: FileItem): string =>\n file.mimeType || file.type || \"\";\n\nconst getFileUrl = (file: FileItem): string =>\n file.fileUri ||\n file.response?.fileUrl ||\n file.response?.tempUrl ||\n file.tempUrl ||\n \"\";\n\nconst getFileId = (file: FileItem): string => file.id || file.uid || \"\";\n\nconst getFileExtension = (filename: string): string => {\n const ext = filename.split(\".\").pop();\n return ext ? ext.toLowerCase() : \"\";\n};\n\nconst IMAGE_EXTS = [\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"bmp\", \"svg\"];\nconst AUDIO_EXTS = [\"mp3\", \"wav\", \"m4a\", \"aac\", \"ogg\", \"flac\"];\nconst VIDEO_EXTS = [\"mp4\", \"mov\", \"webm\", \"mkv\", \"avi\"];\n\n/** 自动推断文件卡片类型 */\nconst inferFileType = (file: FileItem): FileCardType => {\n const mimeType = getMimeType(file);\n if (mimeType.startsWith(\"image/\")) return \"image\";\n if (mimeType.startsWith(\"audio/\")) return \"audio\";\n if (mimeType.startsWith(\"video/\")) return \"video\";\n\n const ext = getFileExtension(getFileName(file));\n if (IMAGE_EXTS.includes(ext)) return \"image\";\n if (AUDIO_EXTS.includes(ext)) return \"audio\";\n if (VIDEO_EXTS.includes(ext)) return \"video\";\n\n // URL 扩展名兜底\n const url = getFileUrl(file);\n const urlExtMatch = url.match(/\\.(\\w+)(\\?.*)?$/);\n if (urlExtMatch) {\n const urlExt = urlExtMatch[1].toLowerCase();\n if (IMAGE_EXTS.includes(urlExt)) return \"image\";\n if (AUDIO_EXTS.includes(urlExt)) return \"audio\";\n if (VIDEO_EXTS.includes(urlExt)) return \"video\";\n }\n\n return \"file\";\n};\n\n/** 根据扩展名获取文件图标类型 key */\nconst getFileIconType = (file: FileItem): FileIconType => {\n const ext = getFileExtension(getFileName(file));\n if (ext === \"pdf\") return \"pdf\";\n if ([\"doc\", \"docx\"].includes(ext)) return \"word\";\n if ([\"xls\", \"xlsx\", \"csv\"].includes(ext)) return \"excel\";\n if ([\"ppt\", \"pptx\"].includes(ext)) return \"ppt\";\n if (AUDIO_EXTS.includes(ext)) return \"audio\";\n if (VIDEO_EXTS.includes(ext)) return \"video\";\n if ([\"zip\", \"rar\", \"7z\", \"tar\", \"gz\", \"bz2\", \"xz\", \"tgz\"].includes(ext))\n return \"zip\";\n if ([\"md\", \"markdown\"].includes(ext)) return \"markdown\";\n if (IMAGE_EXTS.includes(ext)) return \"image\";\n return \"default\";\n};\n\n/** 默认图标映射 */\nconst DEFAULT_ICON_MAP: Record<\n FileIconType,\n { icon: React.ReactNode; color: string }\n> = {\n pdf: { icon: <FilePdfOutlined />, color: \"#ff4d4f\" },\n word: { icon: <FileWordOutlined />, color: \"#1677ff\" },\n excel: { icon: <FileExcelOutlined />, color: \"#22b35e\" },\n ppt: { icon: <FilePptOutlined />, color: \"#ff6e31\" },\n audio: { icon: <AudioOutlined />, color: \"#722ed1\" },\n video: { icon: <VideoCameraOutlined />, color: \"#fa8c16\" },\n zip: { icon: <FileZipOutlined />, color: \"#eb2f96\" },\n markdown: { icon: <FileMarkdownOutlined />, color: \"#13c2c2\" },\n image: { icon: <FileImageOutlined />, color: \"#8c8c8c\" },\n default: { icon: <FileOutlined />, color: \"#8c8c8c\" },\n};\n\nconst formatFileSize = (bytes: number): string => {\n if (bytes < 1024) return bytes + \" B\";\n if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + \" KB\";\n return (bytes / (1024 * 1024)).toFixed(1) + \" MB\";\n};\n\nconst FALLBACK_IMAGE =\n \"data:image/png;base64,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\";\n\n// ==================== 子组件:图片加载状态管理 ====================\n\nconst ImageWithLoading: React.FC<{\n src: string;\n alt: string;\n width?: number | string;\n height?: number | string;\n loading?: \"lazy\" | \"eager\";\n preview?: false | { src: string };\n fallback?: string;\n displayMode: ImageDisplayMode;\n className?: string;\n styles: ReturnType<typeof useStyles>;\n}> = ({\n src,\n alt,\n width,\n height,\n loading,\n preview,\n fallback,\n displayMode,\n className,\n styles,\n}) => {\n const [loadState, setLoadState] = useState<\n \"loading\" | \"loaded\" | \"error\"\n >(\"loading\");\n\n useEffect(() => {\n setLoadState(\"loading\");\n }, [src]);\n\n const modeClass =\n displayMode === \"full\"\n ? styles.imageFullMode\n : displayMode === \"preview\"\n ? styles.imagePreviewMode\n : styles.imageThumbnail;\n\n const containerStyle: React.CSSProperties = {};\n if (width)\n containerStyle.width = typeof width === \"number\" ? `${width}px` : width;\n if (height)\n containerStyle.height = typeof height === \"number\" ? `${height}px` : height;\n\n return (\n <div\n className={`${modeClass} ${className || \"\"}`}\n style={containerStyle}\n >\n {loadState === \"loading\" && (\n <div className={styles.imageLoadingSkeleton}>\n <PictureOutlined style={{ fontSize: 20, color: \"#d9d9d9\" }} />\n </div>\n )}\n\n {loadState === \"error\" && (\n <div className={styles.imageErrorFallback}>\n <PictureOutlined />\n <span>加载失败</span>\n </div>\n )}\n\n <Image\n src={src}\n alt={alt}\n fallback={fallback}\n preview={preview}\n loading={loading}\n style={{\n display: loadState === \"error\" ? \"none\" : \"block\",\n opacity: loadState === \"loading\" ? 0 : 1,\n transition: \"opacity 0.3s ease\",\n ...(displayMode === \"thumbnail\"\n ? { width: \"100%\", height: \"100%\", objectFit: \"cover\" }\n : { maxWidth: \"100%\", objectFit: \"contain\" }),\n }}\n onLoad={() => setLoadState(\"loaded\")}\n onError={() => setLoadState(\"error\")}\n />\n </div>\n );\n};\n\n// ==================== 主组件 ====================\n\nconst FileGallery: React.FC<FileGalleryProps> = ({\n file,\n align = \"left\",\n size = \"default\",\n removable = false,\n onRemove = () => {},\n className,\n style,\n onClick,\n type: customType,\n // 图片相关\n imageDisplayMode = \"thumbnail\",\n imageWidth,\n imageHeight,\n imageLoading = \"eager\",\n imageAlt,\n // 音视频相关\n mediaAutoPlay = false,\n mediaLoop = false,\n mediaMuted = false,\n mediaPoster,\n videoProps,\n audioProps,\n // 自定义图标\n iconMap,\n customIcon,\n}) => {\n const styles = useStyles();\n const [objectUrl, setObjectUrl] = useState(\"\");\n\n useEffect(() => {\n if (!file?.file) return;\n const url = URL.createObjectURL(file.file);\n setObjectUrl(url);\n return () => URL.revokeObjectURL(url);\n }, [file?.file]);\n\n if (!file) return null;\n\n const fileName = getFileName(file);\n const fileSize = getFileSize(file);\n const fileId = getFileId(file);\n const url = getFileUrl(file) || objectUrl;\n const isSmall = size === \"small\";\n const cardClassName = `${styles.fileCard} ${isSmall ? styles.fileCardSmall : \"\"} ${align === \"right\" ? \"align-right\" : \"\"} ${className || \"\"}`;\n\n // 核心逻辑:type prop 决定渲染方式(参考 file-card)\n // - 不传 type:自动推断,图片/音频/视频用原生渲染\n // - type=\"file\":强制附件卡片(音视频不可播放)\n // - type=\"image\"/\"audio\"/\"video\":强制指定类型\n const fileType = useMemo<FileCardType>(\n () => customType || inferFileType(file),\n [customType, file],\n );\n\n /** 获取图标(支持自定义) */\n const getIcon = (\n iconType: FileIconType,\n ): { icon: React.ReactNode; color: string } => {\n if (customIcon) return { icon: customIcon, color: \"inherit\" };\n if (iconMap?.[iconType])\n return { icon: iconMap[iconType]!, color: \"inherit\" };\n return DEFAULT_ICON_MAP[iconType];\n };\n\n // ==================== 渲染删除按钮 ====================\n const renderRemoveBtn = () =>\n removable ? (\n <button\n className={`${styles.fileRemoveBtn} ${isSmall ? styles.fileRemoveBtnSmall : \"\"}`}\n onClick={(e) => {\n e.stopPropagation();\n onRemove(fileId);\n }}\n disabled={file.status === \"uploading\"}\n aria-label=\"删除文件\"\n >\n <CloseOutlined />\n </button>\n ) : null;\n\n // ==================== 渲染上传进度遮罩 ====================\n const renderUploadOverlay = () =>\n file.status === \"uploading\" ? (\n <div className={styles.imageOverlay}>\n <Progress\n type=\"circle\"\n percent={file.progress}\n size={30}\n strokeColor=\"#1677ff\"\n />\n </div>\n ) : null;\n\n // ==================== 附件卡片渲染(type=\"file\" 或普通文档) ====================\n const renderFileCard = () => {\n const iconType = getFileIconType(file);\n const { icon, color } = getIcon(iconType);\n const isExternalUrl = url && url.startsWith(\"http\");\n\n return (\n <div\n className={cardClassName}\n style={{ ...style, cursor: onClick ? \"pointer\" : undefined }}\n onClick={() => onClick?.(file)}\n >\n <div\n className={`${styles.fileIcon} ${isSmall ? styles.fileIconSmall : \"\"}`}\n style={{ color }}\n >\n {icon}\n </div>\n\n <div className={styles.fileInfo}>\n {isExternalUrl && !onClick ? (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={styles.fileLink}\n >\n <Tooltip title={fileName}>\n <div className={`${styles.fileName} ${isSmall ? styles.fileNameSmall : \"\"}`}>\n {fileName.length > 15\n ? `${fileName.substring(0, 15)}...`\n : fileName}\n </div>\n </Tooltip>\n </a>\n ) : (\n <Tooltip title={fileName}>\n <div className={`${styles.fileName} ${isSmall ? styles.fileNameSmall : \"\"}`}>\n {fileName.length > 15\n ? `${fileName.substring(0, 15)}...`\n : fileName}\n </div>\n </Tooltip>\n )}\n\n {file.status === \"uploading\" ? (\n <Progress\n percent={file.progress}\n size=\"small\"\n showInfo={false}\n strokeColor=\"#1677ff\"\n className={`${styles.progress} ${isSmall ? styles.progressSmall : \"\"}`}\n />\n ) : (\n !isSmall && fileSize > 0 && (\n <div className={styles.fileSize}>\n {formatFileSize(fileSize)}\n </div>\n )\n )}\n\n {file.status === \"error\" && file.errorMessage && (\n <div\n style={{ fontSize: \"11px\", color: \"#ff4d4f\", marginTop: \"2px\" }}\n >\n {file.errorMessage}\n </div>\n )}\n </div>\n\n {renderRemoveBtn()}\n </div>\n );\n };\n\n // ==================== 图片渲染 ====================\n if (fileType === \"image\") {\n // full / preview 模式:独立展示\n if (imageDisplayMode === \"full\" || imageDisplayMode === \"preview\") {\n const containerStyle: React.CSSProperties = { ...style };\n if (imageWidth)\n containerStyle.width =\n typeof imageWidth === \"number\" ? `${imageWidth}px` : imageWidth;\n if (imageHeight)\n containerStyle.height =\n typeof imageHeight === \"number\" ? `${imageHeight}px` : imageHeight;\n\n return (\n <div\n className={`${imageDisplayMode === \"full\" ? styles.imageFullMode : styles.imagePreviewMode} ${className || \"\"}`}\n style={containerStyle}\n >\n <ImageWithLoading\n src={url}\n alt={imageAlt || fileName}\n displayMode={imageDisplayMode}\n loading={imageLoading}\n preview={onClick ? false : { src: url }}\n fallback={FALLBACK_IMAGE}\n styles={styles}\n />\n {renderUploadOverlay()}\n {renderRemoveBtn()}\n </div>\n );\n }\n\n // thumbnail 模式:卡片形式\n return (\n <div\n className={cardClassName}\n style={{ ...style, cursor: onClick ? \"pointer\" : undefined }}\n onClick={() => onClick?.(file)}\n >\n <ImageWithLoading\n src={url}\n alt={imageAlt || fileName}\n width={size === \"small\" ? 22 : imageWidth}\n height={size === \"small\" ? 22 : imageHeight}\n displayMode=\"thumbnail\"\n loading={imageLoading}\n preview={onClick ? false : { src: url }}\n fallback={FALLBACK_IMAGE}\n styles={styles}\n className={isSmall ? styles.imageThumbnailSmall : undefined}\n />\n {renderUploadOverlay()}\n\n <div className={styles.fileInfo}>\n <Tooltip title={fileName}>\n <div className={`${styles.fileName} ${isSmall ? styles.fileNameSmall : \"\"}`}>\n {fileName.length > 15\n ? `${fileName.substring(0, 15)}...`\n : fileName}\n </div>\n </Tooltip>\n {!isSmall && Number(file?.size) > 0 && (\n <div className={styles.fileSize}>{formatFileSize(fileSize)}</div>\n )}\n </div>\n\n {renderRemoveBtn()}\n </div>\n );\n }\n\n // ==================== 音频渲染(原生播放器,无卡片包裹) ====================\n if (fileType === \"audio\" && size !== \"small\") {\n return (\n <div\n className={`file-gallery-audio ${className || \"\"}`}\n style={{ position: \"relative\", width: 268, ...style }}\n >\n <audio\n src={url}\n controls\n autoPlay={mediaAutoPlay}\n loop={mediaLoop}\n muted={mediaMuted}\n style={{ width: \"100%\" }}\n {...audioProps}\n />\n {renderRemoveBtn()}\n </div>\n );\n }\n\n // ==================== 视频渲染(原生播放器,无卡片包裹) ====================\n if (fileType === \"video\" && size !== \"small\") {\n return (\n <div\n className={`file-gallery-video ${className || \"\"}`}\n style={{ position: \"relative\", width: 268, ...style }}\n >\n <video\n src={url}\n controls\n autoPlay={mediaAutoPlay}\n loop={mediaLoop}\n muted={mediaMuted}\n poster={mediaPoster}\n style={{\n width: \"100%\",\n aspectRatio: \"16 / 9\",\n borderRadius: 8,\n background: \"#000\",\n }}\n {...videoProps}\n />\n {renderRemoveBtn()}\n </div>\n );\n }\n\n // ==================== 其他文件(附件卡片) ====================\n return renderFileCard();\n};\n\nexport default FileGallery;\n"],
5
- "mappings": "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",
6
- "names": ["url"]
4
+ "sourcesContent": ["import React, { useState, useEffect, useMemo } from \"react\";\nimport { Image as RawImage, Tooltip, Progress } from \"antd\";\nimport type { ImageProps } from \"antd\";\nconst Image = RawImage as unknown as React.ComponentType<ImageProps>;\nimport {\n FileOutlined,\n FilePdfOutlined,\n FileWordOutlined,\n FileExcelOutlined,\n FilePptOutlined,\n FileImageOutlined,\n CloseOutlined,\n AudioOutlined,\n FileZipOutlined,\n VideoCameraOutlined,\n FileMarkdownOutlined,\n PictureOutlined,\n} from \"@ant-design/icons\";\nimport {\n FileItem,\n FileGalleryProps,\n FileIconType,\n ImageDisplayMode,\n FileCardType,\n} from \"@/types\";\nimport { useStyles } from \"./styles\";\n\n// ==================== 工具函数 ====================\n\nconst getFileName = (file: FileItem): string =>\n file.displayName || file.name || \"\";\n\nconst getFileSize = (file: FileItem): number => file.size || 0;\n\nconst getMimeType = (file: FileItem): string =>\n file.mimeType || file.type || \"\";\n\nconst getFileUrl = (file: FileItem): string =>\n file.fileUri ||\n file.response?.fileUrl ||\n file.response?.tempUrl ||\n file.tempUrl ||\n \"\";\n\nconst getFileId = (file: FileItem): string => file.id || file.uid || \"\";\n\nconst getFileExtension = (filename: string): string => {\n const ext = filename.split(\".\").pop();\n return ext ? ext.toLowerCase() : \"\";\n};\n\nconst IMAGE_EXTS = [\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"bmp\", \"svg\"];\nconst AUDIO_EXTS = [\"mp3\", \"wav\", \"m4a\", \"aac\", \"ogg\", \"flac\"];\nconst VIDEO_EXTS = [\"mp4\", \"mov\", \"webm\", \"mkv\", \"avi\"];\n\n/** 自动推断文件卡片类型 */\nconst inferFileType = (file: FileItem): FileCardType => {\n const mimeType = getMimeType(file);\n if (mimeType.startsWith(\"image/\")) return \"image\";\n if (mimeType.startsWith(\"audio/\")) return \"audio\";\n if (mimeType.startsWith(\"video/\")) return \"video\";\n\n const ext = getFileExtension(getFileName(file));\n if (IMAGE_EXTS.includes(ext)) return \"image\";\n if (AUDIO_EXTS.includes(ext)) return \"audio\";\n if (VIDEO_EXTS.includes(ext)) return \"video\";\n\n // URL 扩展名兜底\n const url = getFileUrl(file);\n const urlExtMatch = url.match(/\\.(\\w+)(\\?.*)?$/);\n if (urlExtMatch) {\n const urlExt = urlExtMatch[1].toLowerCase();\n if (IMAGE_EXTS.includes(urlExt)) return \"image\";\n if (AUDIO_EXTS.includes(urlExt)) return \"audio\";\n if (VIDEO_EXTS.includes(urlExt)) return \"video\";\n }\n\n return \"file\";\n};\n\n/** 根据扩展名获取文件图标类型 key */\nconst getFileIconType = (file: FileItem): FileIconType => {\n const ext = getFileExtension(getFileName(file));\n if (ext === \"pdf\") return \"pdf\";\n if ([\"doc\", \"docx\"].includes(ext)) return \"word\";\n if ([\"xls\", \"xlsx\", \"csv\"].includes(ext)) return \"excel\";\n if ([\"ppt\", \"pptx\"].includes(ext)) return \"ppt\";\n if (AUDIO_EXTS.includes(ext)) return \"audio\";\n if (VIDEO_EXTS.includes(ext)) return \"video\";\n if ([\"zip\", \"rar\", \"7z\", \"tar\", \"gz\", \"bz2\", \"xz\", \"tgz\"].includes(ext))\n return \"zip\";\n if ([\"md\", \"markdown\"].includes(ext)) return \"markdown\";\n if (IMAGE_EXTS.includes(ext)) return \"image\";\n return \"default\";\n};\n\n/** 默认图标映射 */\nconst DEFAULT_ICON_MAP: Record<\n FileIconType,\n { icon: React.ReactNode; color: string }\n> = {\n pdf: { icon: <FilePdfOutlined />, color: \"#ff4d4f\" },\n word: { icon: <FileWordOutlined />, color: \"#1677ff\" },\n excel: { icon: <FileExcelOutlined />, color: \"#22b35e\" },\n ppt: { icon: <FilePptOutlined />, color: \"#ff6e31\" },\n audio: { icon: <AudioOutlined />, color: \"#722ed1\" },\n video: { icon: <VideoCameraOutlined />, color: \"#fa8c16\" },\n zip: { icon: <FileZipOutlined />, color: \"#eb2f96\" },\n markdown: { icon: <FileMarkdownOutlined />, color: \"#13c2c2\" },\n image: { icon: <FileImageOutlined />, color: \"#8c8c8c\" },\n default: { icon: <FileOutlined />, color: \"#8c8c8c\" },\n};\n\nconst formatFileSize = (bytes: number): string => {\n if (bytes < 1024) return bytes + \" B\";\n if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + \" KB\";\n return (bytes / (1024 * 1024)).toFixed(1) + \" MB\";\n};\n\nconst FALLBACK_IMAGE =\n \"data:image/png;base64,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\";\n\n// ==================== 子组件:图片加载状态管理 ====================\n\nconst ImageWithLoading: React.FC<{\n src: string;\n alt: string;\n width?: number | string;\n height?: number | string;\n loading?: \"lazy\" | \"eager\";\n preview?: false | { src: string };\n fallback?: string;\n displayMode: ImageDisplayMode;\n className?: string;\n styles: ReturnType<typeof useStyles>;\n}> = ({\n src,\n alt,\n width,\n height,\n loading,\n preview,\n fallback,\n displayMode,\n className,\n styles,\n}) => {\n const [loadState, setLoadState] = useState<\n \"loading\" | \"loaded\" | \"error\"\n >(\"loading\");\n\n useEffect(() => {\n setLoadState(\"loading\");\n }, [src]);\n\n const modeClass =\n displayMode === \"full\"\n ? styles.imageFullMode\n : displayMode === \"preview\"\n ? styles.imagePreviewMode\n : styles.imageThumbnail;\n\n const containerStyle: React.CSSProperties = {};\n if (width)\n containerStyle.width = typeof width === \"number\" ? `${width}px` : width;\n if (height)\n containerStyle.height = typeof height === \"number\" ? `${height}px` : height;\n\n return (\n <div\n className={`${modeClass} ${className || \"\"}`}\n style={containerStyle}\n >\n {loadState === \"loading\" && (\n <div className={styles.imageLoadingSkeleton}>\n <PictureOutlined style={{ fontSize: 20, color: \"#d9d9d9\" }} />\n </div>\n )}\n\n {loadState === \"error\" && (\n <div className={styles.imageErrorFallback}>\n <PictureOutlined />\n <span>加载失败</span>\n </div>\n )}\n\n <Image\n src={src}\n alt={alt}\n fallback={fallback}\n preview={preview}\n loading={loading}\n style={{\n display: loadState === \"error\" ? \"none\" : \"block\",\n opacity: loadState === \"loading\" ? 0 : 1,\n transition: \"opacity 0.3s ease\",\n ...(displayMode === \"thumbnail\"\n ? { width: \"100%\", height: \"100%\", objectFit: \"cover\" }\n : { maxWidth: \"100%\", objectFit: \"contain\" }),\n }}\n onLoad={() => setLoadState(\"loaded\")}\n onError={() => setLoadState(\"error\")}\n />\n </div>\n );\n};\n\n// ==================== 主组件 ====================\n\nconst FileGallery: React.FC<FileGalleryProps> = ({\n file,\n align = \"left\",\n size = \"default\",\n removable = false,\n onRemove = () => {},\n className,\n style,\n onClick,\n type: customType,\n // 图片相关\n imageDisplayMode = \"thumbnail\",\n imageWidth,\n imageHeight,\n imageLoading = \"eager\",\n imageAlt,\n // 音视频相关\n mediaAutoPlay = false,\n mediaLoop = false,\n mediaMuted = false,\n mediaPoster,\n videoProps,\n audioProps,\n // 自定义图标\n iconMap,\n customIcon,\n}) => {\n const styles = useStyles();\n const [objectUrl, setObjectUrl] = useState(\"\");\n\n useEffect(() => {\n if (!file?.file) return;\n const url = URL.createObjectURL(file.file);\n setObjectUrl(url);\n return () => URL.revokeObjectURL(url);\n }, [file?.file]);\n\n if (!file) return null;\n\n const fileName = getFileName(file);\n const fileSize = getFileSize(file);\n const fileId = getFileId(file);\n const url = getFileUrl(file) || objectUrl;\n const isSmall = size === \"small\";\n const cardClassName = `${styles.fileCard} ${isSmall ? styles.fileCardSmall : \"\"} ${align === \"right\" ? \"align-right\" : \"\"} ${className || \"\"}`;\n\n // 核心逻辑:type prop 决定渲染方式(参考 file-card)\n // - 不传 type:自动推断,图片/音频/视频用原生渲染\n // - type=\"file\":强制附件卡片(音视频不可播放)\n // - type=\"image\"/\"audio\"/\"video\":强制指定类型\n const fileType = useMemo<FileCardType>(\n () => customType || inferFileType(file),\n [customType, file],\n );\n\n /** 获取图标(支持自定义) */\n const getIcon = (\n iconType: FileIconType,\n ): { icon: React.ReactNode; color: string } => {\n if (customIcon) return { icon: customIcon, color: \"inherit\" };\n if (iconMap?.[iconType])\n return { icon: iconMap[iconType]!, color: \"inherit\" };\n return DEFAULT_ICON_MAP[iconType];\n };\n\n // ==================== 渲染删除按钮 ====================\n const renderRemoveBtn = () =>\n removable ? (\n <button\n className={`${styles.fileRemoveBtn} ${isSmall ? styles.fileRemoveBtnSmall : \"\"}`}\n onClick={(e) => {\n e.stopPropagation();\n onRemove(fileId);\n }}\n disabled={file.status === \"uploading\"}\n aria-label=\"删除文件\"\n >\n <CloseOutlined />\n </button>\n ) : null;\n\n // ==================== 渲染上传进度遮罩 ====================\n const renderUploadOverlay = () =>\n file.status === \"uploading\" ? (\n <div className={styles.imageOverlay}>\n <Progress\n type=\"circle\"\n percent={file.progress}\n size={30}\n strokeColor=\"#1677ff\"\n />\n </div>\n ) : null;\n\n // ==================== 附件卡片渲染(type=\"file\" 或普通文档) ====================\n const renderFileCard = () => {\n const iconType = getFileIconType(file);\n const { icon, color } = getIcon(iconType);\n const isExternalUrl = url && url.startsWith(\"http\");\n\n return (\n <div\n className={cardClassName}\n style={{ ...style, cursor: onClick ? \"pointer\" : undefined }}\n onClick={() => onClick?.(file)}\n >\n <div\n className={`${styles.fileIcon} ${isSmall ? styles.fileIconSmall : \"\"}`}\n style={{ color }}\n >\n {icon}\n </div>\n\n <div className={styles.fileInfo}>\n {isExternalUrl && !onClick ? (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={styles.fileLink}\n >\n <Tooltip title={fileName}>\n <div className={`${styles.fileName} ${isSmall ? styles.fileNameSmall : \"\"}`}>\n {fileName.length > 15\n ? `${fileName.substring(0, 15)}...`\n : fileName}\n </div>\n </Tooltip>\n </a>\n ) : (\n <Tooltip title={fileName}>\n <div className={`${styles.fileName} ${isSmall ? styles.fileNameSmall : \"\"}`}>\n {fileName.length > 15\n ? `${fileName.substring(0, 15)}...`\n : fileName}\n </div>\n </Tooltip>\n )}\n\n {file.status === \"uploading\" ? (\n <Progress\n percent={file.progress}\n size=\"small\"\n showInfo={false}\n strokeColor=\"#1677ff\"\n className={`${styles.progress} ${isSmall ? styles.progressSmall : \"\"}`}\n />\n ) : (\n !isSmall && fileSize > 0 && (\n <div className={styles.fileSize}>\n {formatFileSize(fileSize)}\n </div>\n )\n )}\n\n {file.status === \"error\" && file.errorMessage && (\n <div\n style={{ fontSize: \"11px\", color: \"#ff4d4f\", marginTop: \"2px\" }}\n >\n {file.errorMessage}\n </div>\n )}\n </div>\n\n {renderRemoveBtn()}\n </div>\n );\n };\n\n // ==================== 图片渲染 ====================\n if (fileType === \"image\") {\n // full / preview 模式:独立展示\n if (imageDisplayMode === \"full\" || imageDisplayMode === \"preview\") {\n const containerStyle: React.CSSProperties = { ...style };\n if (imageWidth)\n containerStyle.width =\n typeof imageWidth === \"number\" ? `${imageWidth}px` : imageWidth;\n if (imageHeight)\n containerStyle.height =\n typeof imageHeight === \"number\" ? `${imageHeight}px` : imageHeight;\n\n return (\n <div\n className={`${imageDisplayMode === \"full\" ? styles.imageFullMode : styles.imagePreviewMode} ${className || \"\"}`}\n style={containerStyle}\n >\n <ImageWithLoading\n src={url}\n alt={imageAlt || fileName}\n displayMode={imageDisplayMode}\n loading={imageLoading}\n preview={onClick ? false : { src: url }}\n fallback={FALLBACK_IMAGE}\n styles={styles}\n />\n {renderUploadOverlay()}\n {renderRemoveBtn()}\n </div>\n );\n }\n\n // thumbnail 模式:卡片形式\n return (\n <div\n className={cardClassName}\n style={{ ...style, cursor: onClick ? \"pointer\" : undefined }}\n onClick={() => onClick?.(file)}\n >\n <ImageWithLoading\n src={url}\n alt={imageAlt || fileName}\n width={size === \"small\" ? 22 : imageWidth}\n height={size === \"small\" ? 22 : imageHeight}\n displayMode=\"thumbnail\"\n loading={imageLoading}\n preview={onClick ? false : { src: url }}\n fallback={FALLBACK_IMAGE}\n styles={styles}\n className={isSmall ? styles.imageThumbnailSmall : undefined}\n />\n {renderUploadOverlay()}\n\n <div className={styles.fileInfo}>\n <Tooltip title={fileName}>\n <div className={`${styles.fileName} ${isSmall ? styles.fileNameSmall : \"\"}`}>\n {fileName.length > 15\n ? `${fileName.substring(0, 15)}...`\n : fileName}\n </div>\n </Tooltip>\n {!isSmall && Number(file?.size) > 0 && (\n <div className={styles.fileSize}>{formatFileSize(fileSize)}</div>\n )}\n </div>\n\n {renderRemoveBtn()}\n </div>\n );\n }\n\n // ==================== 音频渲染(原生播放器,无卡片包裹) ====================\n if (fileType === \"audio\" && size !== \"small\") {\n return (\n <div\n className={`file-gallery-audio ${className || \"\"}`}\n style={{ position: \"relative\", width: 268, ...style }}\n >\n <audio\n src={url}\n controls\n autoPlay={mediaAutoPlay}\n loop={mediaLoop}\n muted={mediaMuted}\n style={{ width: \"100%\" }}\n {...audioProps}\n />\n {renderRemoveBtn()}\n </div>\n );\n }\n\n // ==================== 视频渲染(原生播放器,无卡片包裹) ====================\n if (fileType === \"video\" && size !== \"small\") {\n return (\n <div\n className={`file-gallery-video ${className || \"\"}`}\n style={{ position: \"relative\", width: 268, ...style }}\n >\n <video\n src={url}\n controls\n autoPlay={mediaAutoPlay}\n loop={mediaLoop}\n muted={mediaMuted}\n poster={mediaPoster}\n style={{\n width: \"100%\",\n aspectRatio: \"16 / 9\",\n borderRadius: 8,\n background: \"#000\",\n }}\n {...videoProps}\n />\n {renderRemoveBtn()}\n </div>\n );\n }\n\n // ==================== 其他文件(附件卡片) ====================\n return renderFileCard();\n};\n\nexport default FileGallery;\n"],
5
+ "mappings": "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",
6
+ "names": ["RawImage", "url"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/components/FolderTree/DirectoryTree.tsx"],
4
- "sourcesContent": ["import React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { Tree, Dropdown, Input } from \"antd\";\nimport { FileOutlined, FolderOutlined, MoreOutlined } from \"@ant-design/icons\";\nimport type { TreeProps } from \"antd\";\nimport type { DataNode } from \"antd/es/tree\";\nimport clsx from \"clsx\";\nimport type {\n FolderTreeData,\n FolderTreeProps,\n EditNodeInfo,\n CreateNodeContext,\n} from \"./types\";\nimport { useStyles } from \"./styles\";\nimport { useFolderDrag, type DropPosition } from \"./useFolderDrag\";\n\nconst { DirectoryTree: AntDirectoryTree } = Tree;\n\nexport interface DirectoryTreeComponentProps {\n treeData: FolderTreeData[];\n directoryIcons?: FolderTreeProps[\"directoryIcons\"];\n selectedKeys?: string[];\n expandedKeys?: string[];\n onSelect?: TreeProps[\"onSelect\"];\n onExpand?: TreeProps[\"onExpand\"];\n showLine?: FolderTreeProps[\"showLine\"];\n switcherIcon?: FolderTreeProps[\"switcherIcon\"];\n defaultExpandAll?: boolean;\n className?: string;\n directoryTitle?: FolderTreeProps[\"directoryTitle\"];\n moreActions?: FolderTreeProps[\"moreActions\"];\n width?: number | string;\n draggable?: FolderTreeProps[\"draggable\"];\n onDrop?: FolderTreeProps[\"onDrop\"];\n /** 当前编辑节点信息 */\n editNode?: EditNodeInfo | null;\n /** 编辑值变化回调 */\n onEditValueChange?: (value: string) => void;\n /** 编辑确认回调 */\n onEditConfirm?: (value?: string) => void;\n /** 编辑取消回调 */\n onEditCancel?: () => void;\n /** 新建上下文 */\n createInfo?: CreateNodeContext | null;\n /** 开始重命名回调(由父组件注入) */\n onStartRename?: (fullPath: string, node: FolderTreeData) => void;\n /** 创建节点回调(由父组件注入) */\n onCreateNode?: (\n parentPath: string,\n options: { type: \"file\" | \"folder\"; defaultName?: string; extra?: any },\n ) => void;\n}\n\ninterface InlineEditInputProps {\n editKey: string;\n value: string;\n className?: string;\n onValueChange?: (value: string) => void;\n onConfirm?: (value?: string) => void;\n onCancel?: () => void;\n}\n\nconst InlineEditInput: React.FC<InlineEditInputProps> = ({\n editKey,\n value,\n className,\n onValueChange,\n onConfirm,\n onCancel,\n}) => {\n const [draft, setDraft] = useState(value);\n const committedRef = useRef(false);\n\n useEffect(() => {\n setDraft(value);\n committedRef.current = false;\n }, [editKey, value]);\n\n const commit = useCallback(() => {\n if (committedRef.current) return;\n committedRef.current = true;\n onValueChange?.(draft);\n onConfirm?.(draft);\n }, [draft, onConfirm, onValueChange]);\n\n return (\n <Input\n size=\"small\"\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onBlur={commit}\n onPressEnter={commit}\n onKeyDown={(e) => {\n e.stopPropagation();\n if (e.key === \"Escape\") {\n committedRef.current = true;\n onCancel?.();\n }\n }}\n autoFocus\n className={className}\n onClick={(e) => e.stopPropagation()}\n onMouseDown={(e) => e.stopPropagation()}\n onSelect={(e) => e.stopPropagation()}\n onFocus={(e) => e.stopPropagation()}\n />\n );\n};\n\nconst DirectoryTreeComponent: React.FC<DirectoryTreeComponentProps> = ({\n treeData,\n selectedKeys,\n expandedKeys,\n onSelect,\n onExpand,\n showLine = false,\n switcherIcon,\n defaultExpandAll = true,\n className,\n directoryIcons,\n directoryTitle,\n moreActions,\n width,\n draggable,\n onDrop,\n editNode,\n onEditValueChange,\n onEditConfirm,\n onEditCancel,\n createInfo,\n onStartRename,\n onCreateNode,\n}) => {\n const styles = useStyles();\n\n const getIcon = useCallback(\n (node: FolderTreeData) => {\n if (directoryIcons === false || directoryIcons === null) {\n return null;\n }\n if (node.type == \"folder\") {\n const icon = directoryIcons?.directory;\n if (typeof icon === \"function\") return icon();\n return icon || <FolderOutlined />;\n }\n const filePath = node.path.toLowerCase();\n const extension = filePath.split(\".\").pop();\n if (extension) {\n const icon = directoryIcons?.[extension];\n if (icon) return typeof icon === \"function\" ? icon() : icon;\n }\n return <FileOutlined />;\n },\n [directoryIcons],\n );\n\n const buildPathSegments = useCallback(\n (node: FolderTreeData, parentSegments: string[] = []): string[] => {\n if (node.path === \"/\" && parentSegments.length === 0) {\n return [\"/\"];\n }\n return [...parentSegments, node.path].filter((s) => s !== \"\");\n },\n [],\n );\n\n const renderInlineInput = useCallback(\n (key: string, value: string) => (\n <span className={styles.treeNodeTitle}>\n <InlineEditInput\n editKey={key}\n value={value}\n className={styles.inlineInput}\n onValueChange={onEditValueChange}\n onConfirm={onEditConfirm}\n onCancel={onEditCancel}\n />\n </span>\n ),\n [onEditValueChange, onEditConfirm, onEditCancel, styles],\n );\n\n // ====== 拖拽模式 ======\n const dragMode = draggable === true ? \"file\" : draggable || false;\n\n // ====== 自定义拖拽 hook ======\n const handleDragDrop = useCallback(\n (dragNode: DataNode, dropNode: DataNode, dropPosition: DropPosition) => {\n const dragNd = dragNode as DataNode & { _originalNode?: FolderTreeData };\n const dropNd = dropNode as DataNode & { _originalNode?: FolderTreeData };\n const originalDragNode = dragNd._originalNode;\n const originalDropNode = dropNd._originalNode;\n if (!originalDragNode) return;\n\n const dragKey = String(dragNode.key);\n const dropKey = String(dropNode.key);\n const dragFileName = originalDragNode.path;\n let newPath: string;\n\n // 拖到底部 drop zone:放在根级别末尾\n if (!originalDropNode && dropKey === \"__bottom_placeholder__\") {\n newPath = dragFileName;\n onDrop?.({\n fileName: originalDragNode.title,\n extra: originalDragNode.extra,\n oldPath: dragKey,\n newPath,\n dragNode: originalDragNode,\n dropNode: { title: \"\", path: \"\" },\n });\n return;\n }\n\n if (!originalDropNode) return;\n\n if (!dropNode.isLeaf && dropPosition === 0) {\n // 拖入文件夹内部\n newPath = `${dropKey}/${dragFileName}`;\n } else {\n // 拖到节点旁边(同级)\n const dropParentPath = dropKey.includes(\"/\")\n ? dropKey.substring(0, dropKey.lastIndexOf(\"/\"))\n : \"\";\n newPath = dropParentPath\n ? `${dropParentPath}/${dragFileName}`\n : dragFileName;\n }\n\n onDrop?.({\n fileName: originalDragNode.title,\n extra: originalDragNode.extra,\n oldPath: dragKey,\n newPath,\n dragNode: originalDragNode,\n dropNode: originalDropNode,\n });\n },\n [onDrop],\n );\n\n const handleDragEnterExpand = useCallback(\n (nodeKey: string) => {\n const isExpanded = expandedKeys?.includes(nodeKey);\n if (!isExpanded) {\n onExpand?.([...(expandedKeys || []), nodeKey], {\n node: null as any,\n expanded: true,\n nativeEvent: null as any,\n });\n }\n },\n [expandedKeys, onExpand],\n );\n\n const {\n onDragStart,\n onDragEnd,\n onItemDragEnter,\n onItemDragOver,\n onItemDragLeave,\n onItemDrop,\n onContainerDragOver,\n onContainerDrop,\n canDragNode,\n } = useFolderDrag({\n dragMode,\n onDrop: handleDragDrop,\n onDragEnterExpand: handleDragEnterExpand,\n });\n\n // ====== 转换树数据 ======\n // 注意:convertToTreeData 不依赖拖拽状态,拖拽效果通过 DOM 操作实现\n const convertToTreeData = useCallback(\n (\n nodes: FolderTreeData[],\n parentSegments: string[] = [],\n parentKey: string = \"\",\n ): DataNode[] => {\n const result = nodes.map((node) => {\n const pathSegments = buildPathSegments(node, parentSegments);\n const fullPath = pathSegments.join(\"/\").replace(/^\\/+/, \"\");\n\n const isEditing = editNode && editNode.key === fullPath;\n\n const nodeChildren = node.children\n ? convertToTreeData(node.children, pathSegments, fullPath)\n : undefined;\n\n const baseNode: any = {\n key: fullPath,\n path: fullPath,\n pathSegments,\n icon: getIcon(node),\n isLeaf: !(node.type === \"folder\"),\n _originalNode: node,\n children: nodeChildren,\n };\n\n // 编辑态:直接渲染 inline input\n if (isEditing) {\n baseNode.title = renderInlineInput(\n editNode!.key,\n editNode!.currentValue,\n );\n return baseNode;\n }\n\n // title 内容(按钮、菜单等)\n const titleContent = (\n <span\n className={clsx(\n styles.treeNodeTitle,\n moreActions && styles.treeNodeTitleHover,\n )}\n >\n <span>{node.title}</span>\n {moreActions && (\n <Dropdown\n menu={moreActions(node, {\n startRename: () => onStartRename?.(fullPath, node),\n createNode: (options) => onCreateNode?.(fullPath, options),\n })}\n trigger={[\"click\"]}\n placement=\"bottomRight\"\n >\n <span\n className={clsx(styles.moreIcon, \"folder-tree-more-icon\")}\n onClick={(e) => e.stopPropagation()}\n >\n <MoreOutlined />\n </span>\n </Dropdown>\n )}\n </span>\n );\n\n // 启用拖拽时:在 title 外层包裹具有 data-node-key 属性的容器,\n // 用于 DOM 查询和事件绑定。拖拽状态通过 CSS class 控制。\n if (dragMode) {\n const nodeData = baseNode as DataNode & {\n _isTempNode?: boolean;\n };\n const draggableDiv = canDragNode(nodeData);\n\n baseNode.title = (\n <div\n className={clsx(\n styles.treeNodeWrapper,\n draggableDiv && styles.dragNodeHandle,\n )}\n data-node-key={fullPath}\n draggable={draggableDiv}\n onDragStart={\n draggableDiv ? (e) => onDragStart(e, baseNode) : undefined\n }\n onDragEnd={draggableDiv ? onDragEnd : undefined}\n onDragEnter={(e) => onItemDragEnter(e, baseNode)}\n onDragOver={(e) => onItemDragOver(e, baseNode)}\n onDragLeave={onItemDragLeave}\n onDrop={(e) => onItemDrop(e, baseNode)}\n >\n {titleContent}\n </div>\n );\n } else {\n baseNode.title = titleContent;\n }\n\n return baseNode;\n });\n\n // 新建模式:在目标父节点下插入临时节点\n if (createInfo && editNode && editNode.mode === \"create\") {\n const targetParentKey = createInfo.parentKey;\n if (\n targetParentKey === parentKey ||\n (targetParentKey === \"\" && parentKey === \"\") ||\n (targetParentKey === \"/\" && parentKey === \"\")\n ) {\n const tempNode: any = {\n key: editNode.key,\n title: renderInlineInput(editNode.key, editNode.currentValue),\n icon:\n createInfo.type === \"folder\" ? (\n <FolderOutlined />\n ) : (\n <FileOutlined />\n ),\n isLeaf: createInfo.type === \"file\",\n path: editNode.key,\n pathSegments: [editNode.key],\n _originalNode: null,\n _isTempNode: true,\n };\n result.push(tempNode);\n }\n }\n\n return result;\n },\n [\n buildPathSegments,\n getIcon,\n moreActions,\n styles,\n editNode,\n renderInlineInput,\n createInfo,\n onStartRename,\n onCreateNode,\n dragMode,\n canDragNode,\n onDragStart,\n onDragEnd,\n onItemDragEnter,\n onItemDragOver,\n onItemDragLeave,\n onItemDrop,\n ],\n );\n\n const treeDataConverted = useMemo(\n () => convertToTreeData(treeData, [], \"\"),\n [treeData, convertToTreeData],\n );\n\n const handleSelect: TreeProps[\"onSelect\"] = useCallback(\n (keys: any, info: any) => {\n // 编辑态不响应选中\n if (editNode) return;\n // 临时节点不可选中\n const node = info.node as DataNode & { _isTempNode?: boolean };\n if (node._isTempNode) return;\n // 点击 moreActions 区域不触发选中\n const target = info.nativeEvent?.target as HTMLElement | null;\n if (\n target &&\n (target.closest(\".folder-tree-more-icon\") ||\n target.closest(\".ant-dropdown\"))\n ) {\n return;\n }\n onSelect?.(keys, info);\n },\n [onSelect, editNode],\n );\n\n const titleNode =\n directoryTitle === false || directoryTitle === null\n ? null\n : typeof directoryTitle === \"function\"\n ? directoryTitle()\n : directoryTitle;\n\n return (\n <div className={clsx(styles.directoryPanel, className)} style={{ width }}>\n {titleNode && <div className={styles.directoryTitle}>{titleNode}</div>}\n <div\n className={styles.directoryTreeContent}\n onDragOver={dragMode ? onContainerDragOver : undefined}\n onDrop={dragMode ? onContainerDrop : undefined}\n >\n <AntDirectoryTree\n style={\n {\n \"--ant-tree-directory-node-selected-bg\": \"rgba(0,0,0,0.03)\",\n \"--ant-tree-directory-node-selected-color\": \"#1a1c1e\",\n } as React.CSSProperties\n }\n treeData={treeDataConverted}\n selectedKeys={selectedKeys}\n expandedKeys={expandedKeys}\n onSelect={handleSelect}\n onExpand={onExpand}\n multiple={false}\n blockNode\n showLine={showLine as any}\n switcherIcon={switcherIcon}\n defaultExpandAll={defaultExpandAll}\n draggable={false}\n />\n </div>\n </div>\n );\n};\n\nexport default DirectoryTreeComponent;\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { Tree, Dropdown, Input } from \"antd\";\nimport { FileOutlined, FolderOutlined, MoreOutlined } from \"@ant-design/icons\";\nimport type { TreeProps } from \"antd\";\nimport type { DataNode } from \"antd/es/tree\";\nimport clsx from \"clsx\";\nimport type {\n FolderTreeData,\n FolderTreeProps,\n EditNodeInfo,\n CreateNodeContext,\n} from \"./types\";\nimport { useStyles } from \"./styles\";\nimport { useFolderDrag, type DropPosition } from \"./useFolderDrag\";\n\nconst { DirectoryTree: AntDirectoryTree } = Tree;\n\nexport interface DirectoryTreeComponentProps {\n treeData: FolderTreeData[];\n directoryIcons?: FolderTreeProps[\"directoryIcons\"];\n selectedKeys?: string[];\n expandedKeys?: string[];\n onSelect?: TreeProps[\"onSelect\"];\n onExpand?: TreeProps[\"onExpand\"];\n showLine?: FolderTreeProps[\"showLine\"];\n switcherIcon?: FolderTreeProps[\"switcherIcon\"];\n defaultExpandAll?: boolean;\n className?: string;\n directoryTitle?: FolderTreeProps[\"directoryTitle\"];\n moreActions?: FolderTreeProps[\"moreActions\"];\n width?: number | string;\n draggable?: FolderTreeProps[\"draggable\"];\n onDrop?: FolderTreeProps[\"onDrop\"];\n /** 当前编辑节点信息 */\n editNode?: EditNodeInfo | null;\n /** 编辑值变化回调 */\n onEditValueChange?: (value: string) => void;\n /** 编辑确认回调 */\n onEditConfirm?: (value?: string) => void;\n /** 编辑取消回调 */\n onEditCancel?: () => void;\n /** 新建上下文 */\n createInfo?: CreateNodeContext | null;\n /** 开始重命名回调(由父组件注入) */\n onStartRename?: (fullPath: string, node: FolderTreeData) => void;\n /** 创建节点回调(由父组件注入) */\n onCreateNode?: (\n parentPath: string,\n options: { type: \"file\" | \"folder\"; defaultName?: string; extra?: any },\n ) => void;\n}\n\ninterface InlineEditInputProps {\n editKey: string;\n value: string;\n className?: string;\n onValueChange?: (value: string) => void;\n onConfirm?: (value?: string) => void;\n onCancel?: () => void;\n}\n\nconst InlineEditInput: React.FC<InlineEditInputProps> = ({\n editKey,\n value,\n className,\n onValueChange,\n onConfirm,\n onCancel,\n}) => {\n const [draft, setDraft] = useState(value);\n const committedRef = useRef(false);\n\n useEffect(() => {\n setDraft(value);\n committedRef.current = false;\n }, [editKey, value]);\n\n const commit = useCallback(() => {\n if (committedRef.current) return;\n committedRef.current = true;\n onValueChange?.(draft);\n onConfirm?.(draft);\n }, [draft, onConfirm, onValueChange]);\n\n return (\n <Input\n size=\"small\"\n value={draft}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setDraft(e.target.value)}\n onBlur={commit}\n onPressEnter={commit}\n onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {\n e.stopPropagation();\n if (e.key === \"Escape\") {\n committedRef.current = true;\n onCancel?.();\n }\n }}\n autoFocus\n className={className}\n onClick={(e: React.MouseEvent<HTMLInputElement>) => e.stopPropagation()}\n onMouseDown={(e: React.MouseEvent<HTMLInputElement>) => e.stopPropagation()}\n onSelect={(e: React.SyntheticEvent<HTMLInputElement>) => e.stopPropagation()}\n onFocus={(e: React.FocusEvent<HTMLInputElement>) => e.stopPropagation()}\n />\n );\n};\n\nconst DirectoryTreeComponent: React.FC<DirectoryTreeComponentProps> = ({\n treeData,\n selectedKeys,\n expandedKeys,\n onSelect,\n onExpand,\n showLine = false,\n switcherIcon,\n defaultExpandAll = true,\n className,\n directoryIcons,\n directoryTitle,\n moreActions,\n width,\n draggable,\n onDrop,\n editNode,\n onEditValueChange,\n onEditConfirm,\n onEditCancel,\n createInfo,\n onStartRename,\n onCreateNode,\n}) => {\n const styles = useStyles();\n\n const getIcon = useCallback(\n (node: FolderTreeData) => {\n if (directoryIcons === false || directoryIcons === null) {\n return null;\n }\n if (node.type == \"folder\") {\n const icon = directoryIcons?.directory;\n if (typeof icon === \"function\") return icon();\n return icon || <FolderOutlined />;\n }\n const filePath = node.path.toLowerCase();\n const extension = filePath.split(\".\").pop();\n if (extension) {\n const icon = directoryIcons?.[extension];\n if (icon) return typeof icon === \"function\" ? icon() : icon;\n }\n return <FileOutlined />;\n },\n [directoryIcons],\n );\n\n const buildPathSegments = useCallback(\n (node: FolderTreeData, parentSegments: string[] = []): string[] => {\n if (node.path === \"/\" && parentSegments.length === 0) {\n return [\"/\"];\n }\n return [...parentSegments, node.path].filter((s) => s !== \"\");\n },\n [],\n );\n\n const renderInlineInput = useCallback(\n (key: string, value: string) => (\n <span className={styles.treeNodeTitle}>\n <InlineEditInput\n editKey={key}\n value={value}\n className={styles.inlineInput}\n onValueChange={onEditValueChange}\n onConfirm={onEditConfirm}\n onCancel={onEditCancel}\n />\n </span>\n ),\n [onEditValueChange, onEditConfirm, onEditCancel, styles],\n );\n\n // ====== 拖拽模式 ======\n const dragMode = draggable === true ? \"file\" : draggable || false;\n\n // ====== 自定义拖拽 hook ======\n const handleDragDrop = useCallback(\n (dragNode: DataNode, dropNode: DataNode, dropPosition: DropPosition) => {\n const dragNd = dragNode as DataNode & { _originalNode?: FolderTreeData };\n const dropNd = dropNode as DataNode & { _originalNode?: FolderTreeData };\n const originalDragNode = dragNd._originalNode;\n const originalDropNode = dropNd._originalNode;\n if (!originalDragNode) return;\n\n const dragKey = String(dragNode.key);\n const dropKey = String(dropNode.key);\n const dragFileName = originalDragNode.path;\n let newPath: string;\n\n // 拖到底部 drop zone:放在根级别末尾\n if (!originalDropNode && dropKey === \"__bottom_placeholder__\") {\n newPath = dragFileName;\n onDrop?.({\n fileName: originalDragNode.title,\n extra: originalDragNode.extra,\n oldPath: dragKey,\n newPath,\n dragNode: originalDragNode,\n dropNode: { title: \"\", path: \"\" },\n });\n return;\n }\n\n if (!originalDropNode) return;\n\n if (!dropNode.isLeaf && dropPosition === 0) {\n // 拖入文件夹内部\n newPath = `${dropKey}/${dragFileName}`;\n } else {\n // 拖到节点旁边(同级)\n const dropParentPath = dropKey.includes(\"/\")\n ? dropKey.substring(0, dropKey.lastIndexOf(\"/\"))\n : \"\";\n newPath = dropParentPath\n ? `${dropParentPath}/${dragFileName}`\n : dragFileName;\n }\n\n onDrop?.({\n fileName: originalDragNode.title,\n extra: originalDragNode.extra,\n oldPath: dragKey,\n newPath,\n dragNode: originalDragNode,\n dropNode: originalDropNode,\n });\n },\n [onDrop],\n );\n\n const handleDragEnterExpand = useCallback(\n (nodeKey: string) => {\n const isExpanded = expandedKeys?.includes(nodeKey);\n if (!isExpanded) {\n onExpand?.([...(expandedKeys || []), nodeKey], {\n node: null as any,\n expanded: true,\n nativeEvent: null as any,\n });\n }\n },\n [expandedKeys, onExpand],\n );\n\n const {\n onDragStart,\n onDragEnd,\n onItemDragEnter,\n onItemDragOver,\n onItemDragLeave,\n onItemDrop,\n onContainerDragOver,\n onContainerDrop,\n canDragNode,\n } = useFolderDrag({\n dragMode,\n onDrop: handleDragDrop,\n onDragEnterExpand: handleDragEnterExpand,\n });\n\n // ====== 转换树数据 ======\n // 注意:convertToTreeData 不依赖拖拽状态,拖拽效果通过 DOM 操作实现\n const convertToTreeData = useCallback(\n (\n nodes: FolderTreeData[],\n parentSegments: string[] = [],\n parentKey: string = \"\",\n ): DataNode[] => {\n const result = nodes.map((node) => {\n const pathSegments = buildPathSegments(node, parentSegments);\n const fullPath = pathSegments.join(\"/\").replace(/^\\/+/, \"\");\n\n const isEditing = editNode && editNode.key === fullPath;\n\n const nodeChildren = node.children\n ? convertToTreeData(node.children, pathSegments, fullPath)\n : undefined;\n\n const baseNode: any = {\n key: fullPath,\n path: fullPath,\n pathSegments,\n icon: getIcon(node),\n isLeaf: !(node.type === \"folder\"),\n _originalNode: node,\n children: nodeChildren,\n };\n\n // 编辑态:直接渲染 inline input\n if (isEditing) {\n baseNode.title = renderInlineInput(\n editNode!.key,\n editNode!.currentValue,\n );\n return baseNode;\n }\n\n // title 内容(按钮、菜单等)\n const titleContent = (\n <span\n className={clsx(\n styles.treeNodeTitle,\n moreActions && styles.treeNodeTitleHover,\n )}\n >\n <span>{node.title}</span>\n {moreActions && (\n <Dropdown\n menu={moreActions(node, {\n startRename: () => onStartRename?.(fullPath, node),\n createNode: (options) => onCreateNode?.(fullPath, options),\n })}\n trigger={[\"click\"]}\n placement=\"bottomRight\"\n >\n <span\n className={clsx(styles.moreIcon, \"folder-tree-more-icon\")}\n onClick={(e) => e.stopPropagation()}\n >\n <MoreOutlined />\n </span>\n </Dropdown>\n )}\n </span>\n );\n\n // 启用拖拽时:在 title 外层包裹具有 data-node-key 属性的容器,\n // 用于 DOM 查询和事件绑定。拖拽状态通过 CSS class 控制。\n if (dragMode) {\n const nodeData = baseNode as DataNode & {\n _isTempNode?: boolean;\n };\n const draggableDiv = canDragNode(nodeData);\n\n baseNode.title = (\n <div\n className={clsx(\n styles.treeNodeWrapper,\n draggableDiv && styles.dragNodeHandle,\n )}\n data-node-key={fullPath}\n draggable={draggableDiv}\n onDragStart={\n draggableDiv ? (e) => onDragStart(e, baseNode) : undefined\n }\n onDragEnd={draggableDiv ? onDragEnd : undefined}\n onDragEnter={(e) => onItemDragEnter(e, baseNode)}\n onDragOver={(e) => onItemDragOver(e, baseNode)}\n onDragLeave={onItemDragLeave}\n onDrop={(e) => onItemDrop(e, baseNode)}\n >\n {titleContent}\n </div>\n );\n } else {\n baseNode.title = titleContent;\n }\n\n return baseNode;\n });\n\n // 新建模式:在目标父节点下插入临时节点\n if (createInfo && editNode && editNode.mode === \"create\") {\n const targetParentKey = createInfo.parentKey;\n if (\n targetParentKey === parentKey ||\n (targetParentKey === \"\" && parentKey === \"\") ||\n (targetParentKey === \"/\" && parentKey === \"\")\n ) {\n const tempNode: any = {\n key: editNode.key,\n title: renderInlineInput(editNode.key, editNode.currentValue),\n icon:\n createInfo.type === \"folder\" ? (\n <FolderOutlined />\n ) : (\n <FileOutlined />\n ),\n isLeaf: createInfo.type === \"file\",\n path: editNode.key,\n pathSegments: [editNode.key],\n _originalNode: null,\n _isTempNode: true,\n };\n result.push(tempNode);\n }\n }\n\n return result;\n },\n [\n buildPathSegments,\n getIcon,\n moreActions,\n styles,\n editNode,\n renderInlineInput,\n createInfo,\n onStartRename,\n onCreateNode,\n dragMode,\n canDragNode,\n onDragStart,\n onDragEnd,\n onItemDragEnter,\n onItemDragOver,\n onItemDragLeave,\n onItemDrop,\n ],\n );\n\n const treeDataConverted = useMemo(\n () => convertToTreeData(treeData, [], \"\"),\n [treeData, convertToTreeData],\n );\n\n const handleSelect: TreeProps[\"onSelect\"] = useCallback(\n (keys: any, info: any) => {\n // 编辑态不响应选中\n if (editNode) return;\n // 临时节点不可选中\n const node = info.node as DataNode & { _isTempNode?: boolean };\n if (node._isTempNode) return;\n // 点击 moreActions 区域不触发选中\n const target = info.nativeEvent?.target as HTMLElement | null;\n if (\n target &&\n (target.closest(\".folder-tree-more-icon\") ||\n target.closest(\".ant-dropdown\"))\n ) {\n return;\n }\n onSelect?.(keys, info);\n },\n [onSelect, editNode],\n );\n\n const titleNode =\n directoryTitle === false || directoryTitle === null\n ? null\n : typeof directoryTitle === \"function\"\n ? directoryTitle()\n : directoryTitle;\n\n return (\n <div className={clsx(styles.directoryPanel, className)} style={{ width }}>\n {titleNode && <div className={styles.directoryTitle}>{titleNode}</div>}\n <div\n className={styles.directoryTreeContent}\n onDragOver={dragMode ? onContainerDragOver : undefined}\n onDrop={dragMode ? onContainerDrop : undefined}\n >\n <AntDirectoryTree\n style={\n {\n \"--ant-tree-directory-node-selected-bg\": \"rgba(0,0,0,0.03)\",\n \"--ant-tree-directory-node-selected-color\": \"#1a1c1e\",\n } as React.CSSProperties\n }\n treeData={treeDataConverted}\n selectedKeys={selectedKeys}\n expandedKeys={expandedKeys}\n onSelect={handleSelect}\n onExpand={onExpand}\n multiple={false}\n blockNode\n showLine={showLine as any}\n switcherIcon={switcherIcon}\n defaultExpandAll={defaultExpandAll}\n draggable={false}\n />\n </div>\n </div>\n );\n};\n\nexport default DirectoryTreeComponent;\n"],
5
+ "mappings": "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",
6
6
  "names": ["clsx"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/components/Sources/components/CarouselCard.tsx"],
4
- "sourcesContent": ["import { LeftOutlined, RightOutlined } from '@ant-design/icons';\nimport { Carousel } from 'antd';\nimport { clsx } from 'clsx';\nimport React, { useEffect, useRef, useState } from 'react';\nimport type { SourcesItem, SourcesProps } from '@/types/Sources';\nimport useStyle from '../styles';\n\nexport interface CarouselCardProps {\n activeKey?: SourcesProps['activeKey'];\n prefixCls: string;\n items?: SourcesProps['items'];\n className?: string;\n style?: React.CSSProperties;\n onClick?: (item: SourcesItem) => void;\n}\n\nconst CarouselCard: React.FC<CarouselCardProps> = (props) => {\n const { prefixCls, items, activeKey, className, style } = props;\n const styles = useStyle();\n\n // Derive the target slide index from activeKey\n const activeSlideIndex = React.useMemo(\n () => Math.max(0, items?.findIndex(({ key }) => key === activeKey) ?? 0),\n [items, activeKey],\n );\n\n const [slide, setSlide] = useState<number>(activeSlideIndex);\n\n const carouselRef = useRef<React.ComponentRef<typeof Carousel>>(null);\n\n useEffect(() => {\n setSlide(activeSlideIndex);\n if (carouselRef.current) {\n carouselRef.current.goTo(activeSlideIndex, false);\n }\n }, [activeSlideIndex]);\n\n const handleClick = (item: SourcesItem) => {\n item.url && window.open(item.url, '_blank', 'noopener,noreferrer');\n props.onClick?.(item);\n };\n\n return (\n <div style={style} className={clsx(styles.carouselWrapper, className)}>\n <div className={styles.carouselTitle}>\n <div className={styles.carouselBtnWrapper}>\n <span\n className={clsx(styles.carouselBtn, {\n [styles.carouselBtnDisabled]: slide === 0,\n })}\n onClick={() => {\n if (slide > 0) {\n const next = slide - 1;\n setSlide(next);\n carouselRef.current?.goTo(next, false);\n }\n }}\n >\n <LeftOutlined />\n </span>\n <span\n className={clsx(styles.carouselBtn, {\n [styles.carouselBtnDisabled]: slide === (items?.length || 1) - 1,\n })}\n onClick={() => {\n const max = (items?.length || 1) - 1;\n if (slide < max) {\n const next = slide + 1;\n setSlide(next);\n carouselRef.current?.goTo(next, false);\n }\n }}\n >\n <RightOutlined />\n </span>\n </div>\n <div className={styles.carouselPage}>{`${slide + 1}/${items?.length || 1}`}</div>\n </div>\n <Carousel\n ref={carouselRef}\n arrows={false}\n infinite={false}\n dots={false}\n afterChange={setSlide}\n >\n {items?.map((item, index) => (\n <div\n key={item.key || index}\n className={styles.carouselItem}\n onClick={() => handleClick(item)}\n >\n <div className={styles.carouselItemTitleWrapper}>\n {item.icon && <span>{item.icon}</span>}\n <span className={styles.carouselItemTitle}>{item.title}</span>\n </div>\n {item.description && (\n <div className={styles.carouselItemDescription}>{item.description}</div>\n )}\n </div>\n ))}\n </Carousel>\n </div>\n );\n};\n\nexport default CarouselCard;"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { LeftOutlined, RightOutlined } from '@ant-design/icons';\nimport { Carousel } from 'antd';\nimport { clsx } from 'clsx';\nimport React, { useEffect, useRef, useState } from 'react';\nimport type { SourcesItem, SourcesProps } from '@/types/Sources';\nimport useStyle from '../styles';\n\nexport interface CarouselCardProps {\n activeKey?: SourcesProps['activeKey'];\n prefixCls: string;\n items?: SourcesProps['items'];\n className?: string;\n style?: React.CSSProperties;\n onClick?: (item: SourcesItem) => void;\n}\n\nconst CarouselCard: React.FC<CarouselCardProps> = (props) => {\n const { prefixCls, items, activeKey, className, style } = props;\n const styles = useStyle();\n\n // Derive the target slide index from activeKey\n const activeSlideIndex = React.useMemo(\n () => Math.max(0, items?.findIndex(({ key }) => key === activeKey) ?? 0),\n [items, activeKey],\n );\n\n const [slide, setSlide] = useState<number>(activeSlideIndex);\n\n const carouselRef = useRef<any>(null);\n\n useEffect(() => {\n setSlide(activeSlideIndex);\n if (carouselRef.current) {\n carouselRef.current.goTo(activeSlideIndex, false);\n }\n }, [activeSlideIndex]);\n\n const handleClick = (item: SourcesItem) => {\n item.url && window.open(item.url, '_blank', 'noopener,noreferrer');\n props.onClick?.(item);\n };\n\n return (\n <div style={style} className={clsx(styles.carouselWrapper, className)}>\n <div className={styles.carouselTitle}>\n <div className={styles.carouselBtnWrapper}>\n <span\n className={clsx(styles.carouselBtn, {\n [styles.carouselBtnDisabled]: slide === 0,\n })}\n onClick={() => {\n if (slide > 0) {\n const next = slide - 1;\n setSlide(next);\n carouselRef.current?.goTo(next, false);\n }\n }}\n >\n <LeftOutlined />\n </span>\n <span\n className={clsx(styles.carouselBtn, {\n [styles.carouselBtnDisabled]: slide === (items?.length || 1) - 1,\n })}\n onClick={() => {\n const max = (items?.length || 1) - 1;\n if (slide < max) {\n const next = slide + 1;\n setSlide(next);\n carouselRef.current?.goTo(next, false);\n }\n }}\n >\n <RightOutlined />\n </span>\n </div>\n <div className={styles.carouselPage}>{`${slide + 1}/${items?.length || 1}`}</div>\n </div>\n <Carousel\n ref={carouselRef}\n arrows={false}\n infinite={false}\n dots={false}\n afterChange={setSlide}\n >\n {items?.map((item, index) => (\n <div\n key={item.key || index}\n className={styles.carouselItem}\n onClick={() => handleClick(item)}\n >\n <div className={styles.carouselItemTitleWrapper}>\n {item.icon && <span>{item.icon}</span>}\n <span className={styles.carouselItemTitle}>{item.title}</span>\n </div>\n {item.description && (\n <div className={styles.carouselItemDescription}>{item.description}</div>\n )}\n </div>\n ))}\n </Carousel>\n </div>\n );\n};\n\nexport default CarouselCard;"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA4C;AAC5C,kBAAyB;AACzB,kBAAqB;AACrB,mBAAmD;AAEnD,oBAAqB;AAwCb;AA7BR,IAAM,eAA4C,CAAC,UAAU;AAC3D,QAAM,EAAE,WAAW,OAAO,WAAW,WAAW,MAAM,IAAI;AAC1D,QAAM,aAAS,cAAAA,SAAS;AAGxB,QAAM,mBAAmB,aAAAC,QAAM;AAAA,IAC7B,MAAM,KAAK,IAAI,IAAG,+BAAO,UAAU,CAAC,EAAE,IAAI,MAAM,QAAQ,eAAc,CAAC;AAAA,IACvE,CAAC,OAAO,SAAS;AAAA,EACnB;AAEA,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAiB,gBAAgB;AAE3D,QAAM,kBAAc,qBAAY,IAAI;AAEpC,8BAAU,MAAM;AACd,aAAS,gBAAgB;AACzB,QAAI,YAAY,SAAS;AACvB,kBAAY,QAAQ,KAAK,kBAAkB,KAAK;AAAA,IAClD;AAAA,EACF,GAAG,CAAC,gBAAgB,CAAC;AAErB,QAAM,cAAc,CAAC,SAAsB;AArC7C;AAsCI,SAAK,OAAO,OAAO,KAAK,KAAK,KAAK,UAAU,qBAAqB;AACjE,gBAAM,YAAN,+BAAgB;AAAA,EAClB;AAEA,SACE,6CAAC,SAAI,OAAc,eAAW,kBAAK,OAAO,iBAAiB,SAAS,GAClE;AAAA,iDAAC,SAAI,WAAW,OAAO,eACrB;AAAA,mDAAC,SAAI,WAAW,OAAO,oBACrB;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,eAAW,kBAAK,OAAO,aAAa;AAAA,cAClC,CAAC,OAAO,mBAAmB,GAAG,UAAU;AAAA,YAC1C,CAAC;AAAA,YACD,SAAS,MAAM;AAlD3B;AAmDc,kBAAI,QAAQ,GAAG;AACb,sBAAM,OAAO,QAAQ;AACrB,yBAAS,IAAI;AACb,kCAAY,YAAZ,mBAAqB,KAAK,MAAM;AAAA,cAClC;AAAA,YACF;AAAA,YAEA,sDAAC,6BAAa;AAAA;AAAA,QAChB;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,eAAW,kBAAK,OAAO,aAAa;AAAA,cAClC,CAAC,OAAO,mBAAmB,GAAG,YAAW,+BAAO,WAAU,KAAK;AAAA,YACjE,CAAC;AAAA,YACD,SAAS,MAAM;AAhE3B;AAiEc,oBAAM,QAAO,+BAAO,WAAU,KAAK;AACnC,kBAAI,QAAQ,KAAK;AACf,sBAAM,OAAO,QAAQ;AACrB,yBAAS,IAAI;AACb,kCAAY,YAAZ,mBAAqB,KAAK,MAAM;AAAA,cAClC;AAAA,YACF;AAAA,YAEA,sDAAC,8BAAc;AAAA;AAAA,QACjB;AAAA,SACF;AAAA,MACA,4CAAC,SAAI,WAAW,OAAO,cAAe,aAAG,QAAQ,MAAK,+BAAO,WAAU,KAAI;AAAA,OAC7E;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,MAAM;AAAA,QACN,aAAa;AAAA,QAEZ,yCAAO,IAAI,CAAC,MAAM,UACjB;AAAA,UAAC;AAAA;AAAA,YAEC,WAAW,OAAO;AAAA,YAClB,SAAS,MAAM,YAAY,IAAI;AAAA,YAE/B;AAAA,2DAAC,SAAI,WAAW,OAAO,0BACpB;AAAA,qBAAK,QAAQ,4CAAC,UAAM,eAAK,MAAK;AAAA,gBAC/B,4CAAC,UAAK,WAAW,OAAO,mBAAoB,eAAK,OAAM;AAAA,iBACzD;AAAA,cACC,KAAK,eACJ,4CAAC,SAAI,WAAW,OAAO,yBAA0B,eAAK,aAAY;AAAA;AAAA;AAAA,UAT/D,KAAK,OAAO;AAAA,QAWnB;AAAA;AAAA,IAEJ;AAAA,KACF;AAEJ;AAEA,IAAO,uBAAQ;",
6
6
  "names": ["useStyle", "React"]
7
7
  }
@@ -38,11 +38,12 @@ var import_clsx = __toESM(require("clsx"));
38
38
  var import_icons = require("@ant-design/icons");
39
39
  var import_copy_to_clipboard = __toESM(require("copy-to-clipboard"));
40
40
  var import_styles = require("./styles");
41
+ var import_utils = require("./utils");
41
42
  var import_MarkdownRender = __toESM(require("./components/MarkdownRender"));
42
43
  var import_FunctionCallRender = __toESM(require("./components/FunctionCallRender"));
43
44
  var import_FileGallery = __toESM(require("../FileGallery"));
44
45
  var import_XAdkThoughtChain = __toESM(require("../XAdkThoughtChain"));
45
- var import_utils = require("../../utils");
46
+ var import_utils2 = require("../../utils");
46
47
  var import_runtime = require("../../presets/runtime");
47
48
  var import_FunctionCallRender2 = require("../../types/FunctionCallRender");
48
49
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -80,7 +81,9 @@ var XAdkChatbot = (0, import_react.forwardRef)(
80
81
  toolKindResolver,
81
82
  strategies,
82
83
  preset,
83
- empty
84
+ empty,
85
+ showUserActions,
86
+ userActions
84
87
  }, ref) => {
85
88
  const styles = (0, import_styles.useStyles)();
86
89
  const listRef = (0, import_react.useRef)(null);
@@ -94,7 +97,32 @@ var XAdkChatbot = (0, import_react.forwardRef)(
94
97
  var _a;
95
98
  (_a = messagesEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior });
96
99
  }, []);
97
- (0, import_react.useImperativeHandle)(ref, () => ({ scrollToBottom }), [scrollToBottom]);
100
+ const getScrollContainer = (0, import_react.useCallback)(() => listRef.current, []);
101
+ const getMessagePosition = (0, import_react.useCallback)((messageId) => {
102
+ const list = listRef.current;
103
+ if (!list)
104
+ return null;
105
+ const group = Array.from(list.children).find(
106
+ (element) => element instanceof HTMLElement && element.dataset.messageId === messageId
107
+ );
108
+ if (!group)
109
+ return null;
110
+ return group.getBoundingClientRect().top - list.getBoundingClientRect().top + list.scrollTop;
111
+ }, []);
112
+ const scrollToMessage = (0, import_react.useCallback)(
113
+ (messageId, behavior = "smooth") => {
114
+ const list = listRef.current;
115
+ const top = getMessagePosition(messageId);
116
+ if (list && top !== null)
117
+ list.scrollTo({ top, behavior });
118
+ },
119
+ [getMessagePosition]
120
+ );
121
+ (0, import_react.useImperativeHandle)(
122
+ ref,
123
+ () => ({ scrollToBottom, getScrollContainer, getMessagePosition, scrollToMessage }),
124
+ [scrollToBottom, getScrollContainer, getMessagePosition, scrollToMessage]
125
+ );
98
126
  const mergedStrategies = (0, import_react.useMemo)(
99
127
  () => (0, import_runtime.mergeChatStrategies)(preset, strategies),
100
128
  [preset, strategies]
@@ -110,7 +138,7 @@ var XAdkChatbot = (0, import_react.forwardRef)(
110
138
  },
111
139
  [toolKindResolver, mergedStrategies]
112
140
  );
113
- const parseProcessMessage = mergedStrategies.parseProcessMessage ?? import_utils.parseAgentMessage;
141
+ const parseProcessMessage = mergedStrategies.parseProcessMessage ?? import_utils2.parseAgentMessage;
114
142
  const resourceLinkSchemes = mergedStrategies.markdownResourceLinkSchemes;
115
143
  (0, import_react.useEffect)(() => {
116
144
  if (!initialized)
@@ -260,10 +288,11 @@ var XAdkChatbot = (0, import_react.forwardRef)(
260
288
  }, [messages, enableGrouping]);
261
289
  const renderBotGroup = (0, import_react.useCallback)(
262
290
  (group, isLastGroup, renderFunctionCall2) => {
291
+ var _a, _b, _c, _d, _e, _f, _g, _h;
263
292
  const { msgs, allFiles } = group;
264
293
  if (!enableProcessParsing) {
265
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.botMsg, children: msgs.map((msg, i) => {
266
- var _a;
294
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.botMsg, "data-message-id": group.id, children: msgs.map((msg, i) => {
295
+ var _a2;
267
296
  if (msg.text) {
268
297
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
269
298
  import_MarkdownRender.default,
@@ -285,7 +314,7 @@ var XAdkChatbot = (0, import_react.forwardRef)(
285
314
  msg,
286
315
  showDetail: showFnCallDetail,
287
316
  onConfirm,
288
- kind: resolveToolKind((_a = msg.functionCall) == null ? void 0 : _a.name, msg),
317
+ kind: resolveToolKind((_a2 = msg.functionCall) == null ? void 0 : _a2.name, msg),
289
318
  renderApproval: mergedStrategies.renderApproval,
290
319
  renderHandoff: mergedStrategies.renderHandoff
291
320
  },
@@ -397,24 +426,45 @@ var XAdkChatbot = (0, import_react.forwardRef)(
397
426
  flushProcessItems();
398
427
  const fullTextToCopy = msgs.filter((m) => !m.functionCall && !m.functionResponse).map((m) => m.text || "").join("");
399
428
  const lastBotMsg = msgs[msgs.length - 1];
400
- const hasProcess = nodes.some((n) => n.type === "process");
401
429
  const isGroupLoading = loading && isLastGroup;
402
430
  const actionProps = { message: group, isLastBotMsg: isLastGroup };
403
431
  const actionExtraNode = actionsExtra == null ? void 0 : actionsExtra(actionProps);
432
+ const firstProcessIdx = nodes.findIndex((n) => n.type === "process");
404
433
  const lastProcessIdx = nodes.findLastIndex((n) => n.type === "process");
405
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: styles.botMsg, children: [
434
+ const processItems = nodes.slice(firstProcessIdx, lastProcessIdx + 1).flatMap(
435
+ (node) => node.type === "process" ? node.items : [
436
+ {
437
+ type: "text",
438
+ key: node.key,
439
+ title: "过程分析",
440
+ content: node.content
441
+ }
442
+ ]
443
+ );
444
+ const activeItem = processItems[processItems.length - 1];
445
+ const activeStep = (activeItem == null ? void 0 : activeItem.type) === "tool" ? String(
446
+ ((_c = (_b = (_a = activeItem.msg) == null ? void 0 : _a.functionCall) == null ? void 0 : _b.args) == null ? void 0 : _c.agentName) || ((_f = (_e = (_d = activeItem.msg) == null ? void 0 : _d.functionCall) == null ? void 0 : _e.args) == null ? void 0 : _f.componentName) || ((_h = (_g = activeItem.msg) == null ? void 0 : _g.functionCall) == null ? void 0 : _h.name) || ""
447
+ ) : typeof (activeItem == null ? void 0 : activeItem.title) === "string" ? activeItem.title : "";
448
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: styles.botMsg, "data-message-id": group.id, children: [
406
449
  nodes.map((node, idx) => {
450
+ if (node.type === "text" && idx > firstProcessIdx && idx < lastProcessIdx) {
451
+ return null;
452
+ }
407
453
  if (node.type === "process") {
408
- const isLastProcess = idx === lastProcessIdx;
454
+ if (idx !== firstProcessIdx) {
455
+ return null;
456
+ }
409
457
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
410
458
  import_XAdkThoughtChain.default,
411
459
  {
412
- loading: isLastProcess && isGroupLoading,
413
- title: "思维链已完成",
414
- items: node.items,
460
+ collapsible: true,
461
+ loading: isGroupLoading,
462
+ title: "已完成",
463
+ activeStep,
464
+ items: processItems,
415
465
  showFnCallDetail,
416
466
  onConfirm,
417
- defaultOpen: isLastProcess && isGroupLoading,
467
+ defaultOpen: isGroupLoading,
418
468
  renderFunctionCall: renderFunctionCall2,
419
469
  toolKindResolver: (name) => resolveToolKind(name),
420
470
  strategies: mergedStrategies
@@ -503,7 +553,7 @@ var XAdkChatbot = (0, import_react.forwardRef)(
503
553
  );
504
554
  const renderUserGroup = (0, import_react.useCallback)(
505
555
  (group) => {
506
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.userMsg, children: group.msgs.map((m, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: styles.userContainer, children: [
556
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.userMsg, "data-message-id": group.id, children: group.msgs.map((m, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: styles.userContainer, children: [
507
557
  m.fileData && m.fileData.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: m.fileData.map((file, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
508
558
  import_FileGallery.default,
509
559
  {
@@ -518,9 +568,14 @@ var XAdkChatbot = (0, import_react.forwardRef)(
518
568
  "div",
519
569
  {
520
570
  className: styles.card,
571
+ "data-xadk-user-card": true,
521
572
  onCopy: (event) => {
522
573
  event.preventDefault();
523
- event.clipboardData.setData("text/plain", m.text || "");
574
+ const selected = (0, import_utils.getSelectionText)(event.currentTarget);
575
+ event.clipboardData.setData(
576
+ "text/plain",
577
+ selected || m.text || ""
578
+ );
524
579
  },
525
580
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
526
581
  import_MarkdownRender.default,
@@ -537,7 +592,26 @@ var XAdkChatbot = (0, import_react.forwardRef)(
537
592
  }
538
593
  )
539
594
  }
540
- )
595
+ ),
596
+ showUserActions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `x-user-actions ${styles.userActions}`, children: (userActions == null ? void 0 : userActions({ message: group })) ?? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
597
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: styles.userTime, children: (0, import_utils.formatTimestamp)(m.timestamp) }),
598
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Tooltip, { title: "复制内容", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
599
+ import_icons.CopyOutlined,
600
+ {
601
+ className: styles.userActionIcon,
602
+ onClick: () => {
603
+ const card = document.querySelector(
604
+ `[data-message-id="${group.id}"] [data-xadk-user-card]`
605
+ );
606
+ const selected = (0, import_utils.getSelectionText)(
607
+ card
608
+ );
609
+ (0, import_copy_to_clipboard.default)(selected || m.text || "");
610
+ import_antd.message.success("复制成功");
611
+ }
612
+ }
613
+ ) })
614
+ ] }) })
541
615
  ] }, m.id || i)) }, group.id);
542
616
  },
543
617
  [
@@ -546,7 +620,9 @@ var XAdkChatbot = (0, import_react.forwardRef)(
546
620
  onResourceLinkClick,
547
621
  renderResourceLink,
548
622
  mergedMarkdownExtensions,
549
- resourceLinkSchemes
623
+ resourceLinkSchemes,
624
+ showUserActions,
625
+ userActions
550
626
  ]
551
627
  );
552
628
  const renderSuggestions = () => {