@baishuyun/ai-design 2.0.38 → 2.1.0-alpha.0

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 (269) hide show
  1. package/.storybook/main.ts +31 -31
  2. package/.storybook/preview.ts +16 -16
  3. package/CHANGELOG.md +512 -501
  4. package/components.json +22 -22
  5. package/package.json +9 -6
  6. package/scripts/strip-layers.cjs +41 -41
  7. package/src/components/abs-fullscreen-gradient-bg.tsx +35 -35
  8. package/src/components/agent-helper/agent-category-select-menu/index.tsx +57 -57
  9. package/src/components/agent-helper/agent-entry-card/index.tsx +94 -94
  10. package/src/components/agent-helper/agent-helper-card/index.tsx +179 -179
  11. package/src/components/agent-helper/agent-helper-card-list/index.tsx +59 -59
  12. package/src/components/agent-helper/agent-helper-main-layout/index.tsx +60 -60
  13. package/src/components/agent-helper/agent-sort-select/index.tsx +89 -89
  14. package/src/components/agent-helper/agent-tag/index.tsx +29 -29
  15. package/src/components/agent-helper/app-select/index.tsx +559 -559
  16. package/src/components/agent-helper/condition-group/index.tsx +75 -75
  17. package/src/components/agent-helper/condition-input/index.tsx +233 -233
  18. package/src/components/agent-helper/condition-item/index.tsx +237 -237
  19. package/src/components/agent-helper/condition-quantifier-select/index.tsx +142 -142
  20. package/src/components/agent-helper/condition-trigger-description/index.tsx +49 -49
  21. package/src/components/agent-helper/date-picker/calendar-panel.tsx +164 -164
  22. package/src/components/agent-helper/date-picker/date-input.tsx +91 -91
  23. package/src/components/agent-helper/date-picker/date-picker.tsx +327 -327
  24. package/src/components/agent-helper/date-picker/footer-panel.tsx +44 -44
  25. package/src/components/agent-helper/date-picker/index.ts +3 -3
  26. package/src/components/agent-helper/date-picker/month-panel.tsx +82 -82
  27. package/src/components/agent-helper/date-picker/time-panel.tsx +146 -146
  28. package/src/components/agent-helper/date-picker/types.ts +37 -37
  29. package/src/components/agent-helper/date-picker/utils.ts +121 -121
  30. package/src/components/agent-helper/date-picker/year-panel.tsx +86 -86
  31. package/src/components/agent-helper/exec-log-filter/index.tsx +232 -232
  32. package/src/components/agent-helper/field-edit-preference-select/index.tsx +291 -291
  33. package/src/components/agent-helper/field-select/index.tsx +466 -466
  34. package/src/components/agent-helper/field-value-input/china-address-data.ts +4117 -4117
  35. package/src/components/agent-helper/field-value-input/display.tsx +71 -71
  36. package/src/components/agent-helper/field-value-input/icons.tsx +38 -38
  37. package/src/components/agent-helper/field-value-input/index.tsx +144 -144
  38. package/src/components/agent-helper/field-value-input/inputs.tsx +247 -247
  39. package/src/components/agent-helper/field-value-input/picker-inputs.tsx +1141 -1141
  40. package/src/components/agent-helper/flow-node-action-trigger-select/index.tsx +57 -57
  41. package/src/components/agent-helper/form-action-trigger-select/index.tsx +60 -60
  42. package/src/components/agent-helper/form-divider/index.tsx +44 -44
  43. package/src/components/agent-helper/form-select/index.tsx +732 -732
  44. package/src/components/agent-helper/monthdays-select/index.tsx +378 -378
  45. package/src/components/agent-helper/offset-preference-select/index.tsx +268 -268
  46. package/src/components/agent-helper/offset-type-select/index.tsx +66 -66
  47. package/src/components/agent-helper/offset-unit-select/index.tsx +54 -54
  48. package/src/components/agent-helper/placeholder-card/index.tsx +75 -75
  49. package/src/components/agent-helper/prompt-input/index.tsx +646 -646
  50. package/src/components/agent-helper/relational-operator-select/index.tsx +179 -179
  51. package/src/components/agent-helper/repeat-by-type-select/index.tsx +53 -53
  52. package/src/components/agent-helper/repeat-preference-select/index.tsx +162 -162
  53. package/src/components/agent-helper/repeat-preference-type-select/index.tsx +58 -58
  54. package/src/components/agent-helper/search-input/index.tsx +35 -35
  55. package/src/components/agent-helper/table/index.tsx +114 -114
  56. package/src/components/agent-helper/trigger-select/index.tsx +121 -121
  57. package/src/components/agent-helper/trigger-time-preference-select/index.tsx +189 -189
  58. package/src/components/agent-helper/trigger-time-source-type-select/index.tsx +53 -53
  59. package/src/components/agent-helper/weekdays-select/index.tsx +230 -230
  60. package/src/components/agent-scroll-selector.tsx +193 -193
  61. package/src/components/agent-select.tsx +154 -154
  62. package/src/components/attachment-part-group.tsx +35 -35
  63. package/src/components/attachment-part.tsx +296 -296
  64. package/src/components/attachments-previewer.tsx +272 -272
  65. package/src/components/base-button.tsx +49 -49
  66. package/src/components/border-animation.tsx +107 -107
  67. package/src/components/border-color-animation.tsx +66 -66
  68. package/src/components/bot-avatar-name.tsx +36 -36
  69. package/src/components/bot-icon.tsx +23 -23
  70. package/src/components/bs-entry-select/app-group-folder.tsx +61 -61
  71. package/src/components/bs-entry-select/bs-entry-select.tsx +296 -296
  72. package/src/components/bs-entry-select/build-tree.ts +265 -265
  73. package/src/components/bs-entry-select/entry-item.tsx +45 -45
  74. package/src/components/bs-entry-select/entry-popover.tsx +99 -99
  75. package/src/components/bs-entry-select/index.ts +7 -7
  76. package/src/components/bs-entry-select/other-app-section.tsx +96 -96
  77. package/src/components/bs-entry-select/scroll-slider.tsx +872 -872
  78. package/src/components/bs-entry-select/search-input.tsx +4 -4
  79. package/src/components/bs-entry-select/tree-item-node.tsx +84 -84
  80. package/src/components/bs-entry-select/types.ts +46 -46
  81. package/src/components/bs-icons.tsx +1362 -1362
  82. package/src/components/card-field.tsx +23 -23
  83. package/src/components/card.tsx +36 -36
  84. package/src/components/chat-area-header.tsx +65 -65
  85. package/src/components/check-card.tsx +77 -77
  86. package/src/components/circle-checker.tsx +66 -66
  87. package/src/components/collapsible-txt-msg.tsx +75 -75
  88. package/src/components/collapsible.tsx +9 -9
  89. package/src/components/confirm-dialog.tsx +139 -139
  90. package/src/components/date-range-picker.tsx +91 -91
  91. package/src/components/fake-bot-msg.tsx +51 -51
  92. package/src/components/field-checker-list-msg.tsx +146 -146
  93. package/src/components/field-checker.tsx +185 -185
  94. package/src/components/field-icon.tsx +13 -13
  95. package/src/components/field-value-checker.tsx +116 -116
  96. package/src/components/fields-design-info-table.tsx +160 -160
  97. package/src/components/fields-generating-indicator.tsx +95 -95
  98. package/src/components/fields-portal.tsx +18 -18
  99. package/src/components/fields-previewer.tsx +216 -216
  100. package/src/components/font-icon.tsx +65 -65
  101. package/src/components/form-info-editor.tsx +263 -263
  102. package/src/components/formula-tag.tsx +32 -32
  103. package/src/components/generate-animation.tsx +56 -56
  104. package/src/components/icon-btn.tsx +43 -43
  105. package/src/components/icon-card.tsx +67 -67
  106. package/src/components/icons/calendar-icon.tsx +5 -5
  107. package/src/components/icons/clear-all-icon.tsx +5 -5
  108. package/src/components/icons/empty-agent-icon.tsx +93 -93
  109. package/src/components/icons/empty-table-icon.tsx +104 -104
  110. package/src/components/icons/grids-icon.tsx +5 -5
  111. package/src/components/img-part.tsx +27 -27
  112. package/src/components/legacy-slot.tsx +110 -110
  113. package/src/components/line-checker.tsx +168 -168
  114. package/src/components/linear-gradient-border-btn.tsx +48 -48
  115. package/src/components/linear-gradient-color-bg-animation.tsx +103 -103
  116. package/src/components/memoized-markdown.tsx +32 -32
  117. package/src/components/message-content.tsx +21 -21
  118. package/src/components/number-part.tsx +46 -46
  119. package/src/components/office-container.tsx +289 -289
  120. package/src/components/option-item.tsx +27 -27
  121. package/src/components/output-config-editor.tsx +478 -478
  122. package/src/components/previewer-header.tsx +270 -270
  123. package/src/components/primary-confirm-btn.tsx +58 -58
  124. package/src/components/primary-entry-btn.tsx +48 -48
  125. package/src/components/radio-card.tsx +63 -63
  126. package/src/components/scroll-to-bottom-btn.tsx +28 -28
  127. package/src/components/split-line.tsx +9 -9
  128. package/src/components/square-checker.tsx +87 -87
  129. package/src/components/subform-fields-value-checker.tsx +184 -184
  130. package/src/components/tab-radio-group.tsx +83 -83
  131. package/src/components/tooltip.tsx +53 -53
  132. package/src/components/ui/button.tsx +51 -51
  133. package/src/components/ui/calendar.tsx +57 -57
  134. package/src/components/ui/dialog.tsx +104 -104
  135. package/src/components/ui/drawer.tsx +279 -279
  136. package/src/components/ui/form.tsx +202 -202
  137. package/src/components/ui/icons.tsx +1267 -1267
  138. package/src/components/ui/input.tsx +28 -28
  139. package/src/components/ui/label.tsx +30 -30
  140. package/src/components/ui/popover.tsx +85 -85
  141. package/src/components/ui/select.tsx +236 -236
  142. package/src/components/ui/spinner.tsx +17 -17
  143. package/src/components/ui/switch.tsx +33 -33
  144. package/src/components/ui/tabs.tsx +62 -62
  145. package/src/components/ui/textarea.tsx +28 -28
  146. package/src/components/ui/tooltip.tsx +53 -53
  147. package/src/components/user-txt-msg-bubble.tsx +47 -47
  148. package/src/components/warning-msg.tsx +27 -27
  149. package/src/const/ui.ts +119 -119
  150. package/src/css.d.ts +4 -4
  151. package/src/hooks/use-column-appear.ts +78 -78
  152. package/src/hooks/use-portal-container.ts +27 -27
  153. package/src/hooks/use-resizable-width.ts +129 -129
  154. package/src/hooks/use-select-popover-position.ts +74 -74
  155. package/src/hooks/use-shadow.ts +127 -127
  156. package/src/index.ts +226 -226
  157. package/src/lib/utils.ts +138 -138
  158. package/src/stories/AbsFullscreenGradientBg.stories.tsx +36 -36
  159. package/src/stories/AgentCategorySelectMenu.stories.tsx +33 -33
  160. package/src/stories/AgentEntryCard.stories.tsx +90 -90
  161. package/src/stories/AgentHelperCard.stories.tsx +105 -105
  162. package/src/stories/AgentHelperCardList.stories.tsx +120 -120
  163. package/src/stories/AgentHelperMainLayout.stories.tsx +71 -71
  164. package/src/stories/AgentScrollSelector.stories.tsx +95 -95
  165. package/src/stories/AgentSelect.stories.tsx +134 -134
  166. package/src/stories/AgentTag.stories.tsx +44 -44
  167. package/src/stories/AppSelect.stories.tsx +250 -250
  168. package/src/stories/AttachmentPart.stories.tsx +122 -122
  169. package/src/stories/AttachmentPartGroup.stories.tsx +152 -152
  170. package/src/stories/AttachmentsPreviewer.stories.tsx +306 -306
  171. package/src/stories/BaseButton.stories.tsx +33 -33
  172. package/src/stories/BorderAnimation.stories.tsx +116 -116
  173. package/src/stories/BorderColorAnimation.stories.tsx +63 -63
  174. package/src/stories/BotAvatarAndName.stories.ts +24 -24
  175. package/src/stories/BotIcon.stories.ts +22 -22
  176. package/src/stories/BsConditionInput.stories.tsx +460 -460
  177. package/src/stories/BsEntrySelect.stories.tsx +343 -343
  178. package/src/stories/BsTooltip.stories.tsx +32 -32
  179. package/src/stories/Card.stories.tsx +28 -28
  180. package/src/stories/CardField.stories.tsx +87 -87
  181. package/src/stories/ChatAreaHeader.stories.ts +26 -26
  182. package/src/stories/CheckCard.stories.tsx +114 -114
  183. package/src/stories/CircleChecker.stories.tsx +89 -89
  184. package/src/stories/CollapsibleTextMsg.stories.ts +44 -44
  185. package/src/stories/ConditionGroup.stories.tsx +216 -216
  186. package/src/stories/ConditionInput.stories.tsx +502 -502
  187. package/src/stories/ConditionItem.stories.tsx +163 -163
  188. package/src/stories/ConditonQuantifierSelect.stories.tsx +54 -54
  189. package/src/stories/ConditonTriggerDescription.stories.tsx +61 -61
  190. package/src/stories/ConfirmDialog.stories.tsx +123 -123
  191. package/src/stories/DataTable.stories.tsx +191 -191
  192. package/src/stories/DatePicker.stories.tsx +174 -174
  193. package/src/stories/ExecLogFilter.stories.tsx +145 -145
  194. package/src/stories/FakeBotMsg.stories.tsx +36 -36
  195. package/src/stories/FieldEditPreferenceSelect.stories.tsx +161 -161
  196. package/src/stories/FieldSelect.stories.tsx +228 -228
  197. package/src/stories/FieldValueInput.stories.tsx +442 -442
  198. package/src/stories/FlowNodeActionTriggerSelect.stories.tsx +80 -80
  199. package/src/stories/Form.stories.tsx +302 -302
  200. package/src/stories/FormActionTriggerSelect.stories.tsx +80 -80
  201. package/src/stories/FormDivider.stories.tsx +31 -31
  202. package/src/stories/FormInfoEditor.stories.tsx +173 -173
  203. package/src/stories/FormSelect.stories.tsx +222 -222
  204. package/src/stories/FormulaTag.stories.tsx +68 -68
  205. package/src/stories/GenerateAnimation.stories.ts +20 -20
  206. package/src/stories/IconBtn.stories.tsx +32 -32
  207. package/src/stories/IconCard.stories.tsx +35 -35
  208. package/src/stories/IconLib.tsx +45 -45
  209. package/src/stories/Icons.stories.ts +22 -22
  210. package/src/stories/ImgMsgPart.stories.tsx +42 -42
  211. package/src/stories/Input.stories.tsx +102 -102
  212. package/src/stories/Label.stories.tsx +66 -66
  213. package/src/stories/LegacySlot.stories.tsx +56 -56
  214. package/src/stories/LineChecker.stories.tsx +133 -133
  215. package/src/stories/LinearGradientBorderBtn.stories.tsx +86 -86
  216. package/src/stories/LinearGradientColorBgAnimation.stories.tsx +60 -60
  217. package/src/stories/MemoizedMarkdown.stories.tsx +102 -102
  218. package/src/stories/MessageContent.stories.tsx +36 -36
  219. package/src/stories/MonthdaysSelect.stories.tsx +78 -78
  220. package/src/stories/NumbersMsgPart.stories.tsx +42 -42
  221. package/src/stories/OfficeContainer.stories.tsx +287 -287
  222. package/src/stories/OffsetPreferenceSelect.stories.tsx +118 -118
  223. package/src/stories/OffsetTypeSelect.stories.tsx +75 -75
  224. package/src/stories/OffsetUnitSelect.stories.tsx +61 -61
  225. package/src/stories/OptionItem.stories.tsx +77 -77
  226. package/src/stories/OutputConfigEditor.stories.tsx +96 -96
  227. package/src/stories/PlaceholderCard.stories.tsx +35 -35
  228. package/src/stories/PreviewerHeader.stories.tsx +91 -91
  229. package/src/stories/PrimaryConfirmBtn.stories.tsx +60 -60
  230. package/src/stories/PrimaryEntryBtn.stories.tsx +74 -74
  231. package/src/stories/PromptInput.stories.tsx +228 -228
  232. package/src/stories/RadioCard.stories.tsx +103 -103
  233. package/src/stories/RelationalOperatorSelect.stories.tsx +61 -61
  234. package/src/stories/RepeatByTypeSelect.stories.tsx +58 -58
  235. package/src/stories/RepeatPreferenceSelect.stories.tsx +107 -107
  236. package/src/stories/RepeatPreferenceTypeSelect.stories.tsx +58 -58
  237. package/src/stories/ScrollToBottom.stories.tsx +24 -24
  238. package/src/stories/SearchInput.stories.tsx +50 -50
  239. package/src/stories/Select.stories.tsx +128 -128
  240. package/src/stories/SplitLine.stories.tsx +40 -40
  241. package/src/stories/SquareChecker.stories.tsx +96 -96
  242. package/src/stories/TabRadioGroup.stories.tsx +84 -84
  243. package/src/stories/Textarea.stories.tsx +67 -67
  244. package/src/stories/TriggerSelect.stories.tsx +48 -48
  245. package/src/stories/TriggerTimePreferenceSelect.stories.tsx +106 -106
  246. package/src/stories/TriggerTimeSourceTypeSelect.stories.tsx +68 -68
  247. package/src/stories/UserTextMsgBubble.stories.ts +24 -24
  248. package/src/stories/WarningMessage.stories.tsx +23 -23
  249. package/src/stories/WeekdaysSelect.stories.tsx +66 -66
  250. package/src/stories/assets/demo.css +539 -539
  251. package/src/stories/assets/demo_index.html +7988 -7988
  252. package/src/stories/assets/iconfont.css +1377 -1377
  253. package/src/stories/assets/iconfont.json +2382 -2382
  254. package/src/stories/assets/iconfont.svg +697 -697
  255. package/src/stories/collapsible.stories.tsx +47 -47
  256. package/src/stories/drawer.stories.tsx +196 -196
  257. package/src/stories/field-checker-list-msg.stories.tsx +95 -95
  258. package/src/stories/field-checker.stories.tsx +117 -117
  259. package/src/stories/field-icon.stories.tsx +70 -70
  260. package/src/stories/field-value-checker.stories.tsx +113 -113
  261. package/src/stories/fields-design-info-table.stories.tsx +203 -203
  262. package/src/stories/fields-generating.stories.tsx +41 -41
  263. package/src/stories/fields-previewer.stories.tsx +77 -77
  264. package/src/stories/subform-fields-value-checker.stories.tsx +98 -98
  265. package/src/style.css +226 -226
  266. package/src/types.ts +8 -8
  267. package/style.css +3652 -3652
  268. package/tsconfig.json +13 -13
  269. package/vite.config.ts +44 -44
@@ -1,296 +1,296 @@
1
- import { ComponentType } from 'react';
2
- import { getIconUnicode } from '@baishuyun/ai-feat-utils';
3
- import { cn } from '../lib/utils';
4
- import { FontIcon } from './font-icon';
5
- import { ImgMsgPart } from './img-part';
6
- import { XlsxIcon, CsvFileIcon, TxtFileIcon, UnknownFileIcon } from './bs-icons';
7
-
8
- /** File part from AI message */
9
- export interface FilePart {
10
- type: 'file';
11
- url: string;
12
- name?: string;
13
- mediaType?: string;
14
- }
15
-
16
- export interface AttachmentPartProps {
17
- part: FilePart;
18
- className?: string;
19
- /** Size in pixels (width & height), defaults to 102 */
20
- size?: number;
21
- /** Show only the icon without file name */
22
- iconOnly?: boolean;
23
- /** Click handler for the attachment */
24
- onClick?: (part: FilePart) => void;
25
- }
26
-
27
- type FileIconComponent = ComponentType<{ color?: string }>;
28
-
29
- /**
30
- * 字体图标方案:文件类型图标使用 baishu 主字体的 icon-bsy-* 字形
31
- * (码位见 ai-feat-utils/iconfont.ts)。注意宿主的 .icon-bsy-form 类自带
32
- * color 规则(PC 为 #0265ff),继承色会被覆盖,因此颜色必须通过
33
- * FontIcon 的 color prop 以内联样式落到 <i> 上
34
- */
35
- const createFontFileIcon = (iconName: string): FileIconComponent => {
36
- const FontFileIcon = ({ color }: { color?: string }) => (
37
- <FontIcon
38
- fontCls="icon-bsy-form"
39
- code={getIconUnicode(iconName)}
40
- color={color}
41
- style={{ fontSize: 16, lineHeight: 1 }}
42
- />
43
- );
44
- FontFileIcon.displayName = `FontFileIcon(${iconName})`;
45
- return FontFileIcon;
46
- };
47
-
48
- const DocFileIcon = createFontFileIcon('bsy-doc');
49
- const DocxFileIcon = createFontFileIcon('bsy-docx');
50
- const PptFileIcon = createFontFileIcon('bsy-ppt');
51
- const PptxFileIcon = createFontFileIcon('bsy-pptx');
52
- const PdfFileIcon = createFontFileIcon('bsy-pdf');
53
- const ZipFileIcon = createFontFileIcon('bsy-zip');
54
- const RarFileIcon = createFontFileIcon('bsy-rar');
55
- const Mp4FileIcon = createFontFileIcon('bsy-mp4');
56
- const WebmFileIcon = createFontFileIcon('bsy-webm');
57
- const MovFileIcon = createFontFileIcon('bsy-mov');
58
- const AudioFileIcon = createFontFileIcon('bsy-ogg');
59
-
60
- export interface FileTypeConfig {
61
- icon: FileIconComponent;
62
- /** Icon original size for scaling */
63
- iconSize: number;
64
- /** Icon color (对齐 baishu BsyFileUpload 的 icon-bsy-* 配色),仅对字体图标生效 */
65
- color?: string;
66
- }
67
-
68
- /** Map media types to icon configurations */
69
- const FILE_TYPE_MAP: Record<string, FileTypeConfig> = {
70
- // Excel files
71
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {
72
- icon: XlsxIcon,
73
- iconSize: 52,
74
- },
75
- 'application/vnd.ms-excel': { icon: XlsxIcon, iconSize: 52 },
76
-
77
- // CSV files
78
- 'text/csv': { icon: CsvFileIcon, iconSize: 16 },
79
-
80
- // Text files
81
- 'text/plain': { icon: TxtFileIcon, iconSize: 16 },
82
- 'application/octet-stream': { icon: TxtFileIcon, iconSize: 16 }, // treat as text for ts/js files
83
-
84
- // Word files
85
- 'application/msword': { icon: DocFileIcon, iconSize: 16, color: '#096dd9' },
86
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': {
87
- icon: DocxFileIcon,
88
- iconSize: 16,
89
- color: '#096dd9',
90
- },
91
-
92
- // PowerPoint files
93
- 'application/vnd.ms-powerpoint': { icon: PptFileIcon, iconSize: 16, color: '#d46b08' },
94
- 'application/vnd.openxmlformats-officedocument.presentationml.presentation': {
95
- icon: PptxFileIcon,
96
- iconSize: 16,
97
- color: '#08979c',
98
- },
99
-
100
- // PDF files
101
- 'application/pdf': { icon: PdfFileIcon, iconSize: 16, color: '#d4380d' },
102
-
103
- // Archive files
104
- 'application/zip': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
105
- 'application/x-zip-compressed': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
106
- 'application/x-rar-compressed': { icon: RarFileIcon, iconSize: 16, color: '#389e0d' },
107
- 'application/vnd.rar': { icon: RarFileIcon, iconSize: 16, color: '#389e0d' },
108
- 'application/x-7z-compressed': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
109
- 'application/gzip': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
110
-
111
- // Video files
112
- 'video/mp4': { icon: Mp4FileIcon, iconSize: 16, color: '#1d39c4' },
113
- 'video/webm': { icon: WebmFileIcon, iconSize: 16, color: '#d46b08' },
114
- 'video/quicktime': { icon: MovFileIcon, iconSize: 16, color: '#1d39c4' }, // mov
115
-
116
- // Audio files
117
- 'audio/ogg': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' },
118
- 'audio/mpeg': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' }, // mp3
119
- 'audio/wav': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' },
120
- 'audio/x-wav': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' },
121
- };
122
-
123
- const DEFAULT_FILE_CONFIG: FileTypeConfig = {
124
- icon: UnknownFileIcon,
125
- iconSize: 16,
126
- };
127
-
128
- /** Check if media type is an image */
129
- export const isImageType = (mediaType?: string): boolean => {
130
- return Boolean(mediaType?.startsWith('image/'));
131
- };
132
-
133
- /** Get file config based on media type and file extension */
134
- export const getFileConfig = (mediaType?: string, fileName?: string): FileTypeConfig => {
135
- // Check media type first
136
- if (mediaType && FILE_TYPE_MAP[mediaType]) {
137
- return FILE_TYPE_MAP[mediaType];
138
- }
139
-
140
- // Prefix match for video/audio (covers mkv/avi/flac/m4a etc.)
141
- if (mediaType?.startsWith('video/')) {
142
- return FILE_TYPE_MAP['video/mp4'];
143
- }
144
- if (mediaType?.startsWith('audio/')) {
145
- return FILE_TYPE_MAP['audio/ogg'];
146
- }
147
-
148
- // Fallback to file extension
149
- if (fileName) {
150
- const ext = fileName.split('.').pop()?.toLowerCase();
151
- switch (ext) {
152
- case 'xlsx':
153
- case 'xls':
154
- return FILE_TYPE_MAP['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
155
- case 'csv':
156
- return FILE_TYPE_MAP['text/csv'];
157
- case 'txt':
158
- case 'ts':
159
- case 'tsx':
160
- case 'js':
161
- case 'jsx':
162
- case 'json':
163
- case 'md':
164
- return FILE_TYPE_MAP['text/plain'];
165
- case 'doc':
166
- return FILE_TYPE_MAP['application/msword'];
167
- case 'docx':
168
- return FILE_TYPE_MAP['application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
169
- case 'ppt':
170
- return FILE_TYPE_MAP['application/vnd.ms-powerpoint'];
171
- case 'pptx':
172
- return FILE_TYPE_MAP['application/vnd.openxmlformats-officedocument.presentationml.presentation'];
173
- case 'pdf':
174
- return FILE_TYPE_MAP['application/pdf'];
175
- case 'zip':
176
- case '7z':
177
- case 'gz':
178
- case 'tar':
179
- return FILE_TYPE_MAP['application/zip'];
180
- case 'rar':
181
- return FILE_TYPE_MAP['application/x-rar-compressed'];
182
- case 'mp4':
183
- case 'mkv':
184
- case 'avi':
185
- return FILE_TYPE_MAP['video/mp4'];
186
- case 'webm':
187
- return FILE_TYPE_MAP['video/webm'];
188
- case 'mov':
189
- return FILE_TYPE_MAP['video/quicktime'];
190
- case 'ogg':
191
- case 'mp3':
192
- case 'wav':
193
- case 'flac':
194
- case 'aac':
195
- case 'm4a':
196
- return FILE_TYPE_MAP['audio/ogg'];
197
- }
198
- }
199
-
200
- return DEFAULT_FILE_CONFIG;
201
- };
202
-
203
- /** Extract display file name from full path */
204
- const getDisplayFileName = (name?: string): string | undefined => {
205
- if (!name) return undefined;
206
- // Get last segment of path
207
- const segments = name.split('/');
208
- return segments[segments.length - 1];
209
- };
210
-
211
- /** Generic file message part component */
212
- const FileMsgPart = ({
213
- fileName,
214
- icon: Icon,
215
- iconSize,
216
- color,
217
- className,
218
- size = 102,
219
- }: {
220
- fileName?: string;
221
- icon: FileIconComponent;
222
- iconSize: number;
223
- color?: string;
224
- className?: string;
225
- size?: number;
226
- }) => {
227
- const scale = size / 102;
228
- const scaledIconSize = (52 / 102) * size; // Target icon takes ~51% of container
229
- const iconScale = scaledIconSize / iconSize;
230
- const hasFileName = Boolean(fileName);
231
-
232
- return (
233
- <div
234
- className={cn(
235
- 'relative flex flex-col items-center justify-center rounded-[10px] bg-[#EFF0F6] px-[10px]',
236
- hasFileName && 'gap-[6px]',
237
- className
238
- )}
239
- style={{ width: size, height: size }}
240
- >
241
- <div
242
- className="shrink-0 flex items-center justify-center"
243
- style={{
244
- width: scaledIconSize,
245
- height: scaledIconSize,
246
- color,
247
- }}
248
- >
249
- <div
250
- style={{
251
- transform: iconScale !== 1 ? `scale(${iconScale})` : undefined,
252
- transformOrigin: 'center',
253
- }}
254
- >
255
- <Icon color={color} />
256
- </div>
257
- </div>
258
- {hasFileName && (
259
- <p
260
- className="block max-w-full shrink-0 truncate text-center text-[14px] leading-normal text-[#030303]"
261
- style={{ fontSize: 14 * scale, maxWidth: '100%', display: 'block' }}
262
- title={fileName}
263
- >
264
- {fileName}
265
- </p>
266
- )}
267
- </div>
268
- );
269
- };
270
-
271
- export const AttachmentPart = ({ part, className, size = 102, iconOnly, onClick }: AttachmentPartProps) => {
272
- const { url, name, mediaType } = part;
273
- const handleClick = onClick ? () => onClick(part) : undefined;
274
-
275
- // Render image for image types
276
- if (isImageType(mediaType)) {
277
- return <ImgMsgPart url={url} alt={name} className={className} size={size} onClick={handleClick} />;
278
- }
279
-
280
- // Render file icon for other types
281
- const { icon, iconSize, color } = getFileConfig(mediaType, name);
282
- const displayName = iconOnly ? undefined : getDisplayFileName(name);
283
-
284
- return (
285
- <div onClick={handleClick} className={onClick ? 'cursor-pointer' : undefined}>
286
- <FileMsgPart
287
- fileName={displayName}
288
- icon={icon}
289
- iconSize={iconSize}
290
- color={color}
291
- className={className}
292
- size={size}
293
- />
294
- </div>
295
- );
296
- };
1
+ import { ComponentType } from 'react';
2
+ import { getIconUnicode } from '@baishuyun/ai-feat-utils';
3
+ import { cn } from '../lib/utils';
4
+ import { FontIcon } from './font-icon';
5
+ import { ImgMsgPart } from './img-part';
6
+ import { XlsxIcon, CsvFileIcon, TxtFileIcon, UnknownFileIcon } from './bs-icons';
7
+
8
+ /** File part from AI message */
9
+ export interface FilePart {
10
+ type: 'file';
11
+ url: string;
12
+ name?: string;
13
+ mediaType?: string;
14
+ }
15
+
16
+ export interface AttachmentPartProps {
17
+ part: FilePart;
18
+ className?: string;
19
+ /** Size in pixels (width & height), defaults to 102 */
20
+ size?: number;
21
+ /** Show only the icon without file name */
22
+ iconOnly?: boolean;
23
+ /** Click handler for the attachment */
24
+ onClick?: (part: FilePart) => void;
25
+ }
26
+
27
+ type FileIconComponent = ComponentType<{ color?: string }>;
28
+
29
+ /**
30
+ * 字体图标方案:文件类型图标使用 baishu 主字体的 icon-bsy-* 字形
31
+ * (码位见 ai-feat-utils/iconfont.ts)。注意宿主的 .icon-bsy-form 类自带
32
+ * color 规则(PC 为 #0265ff),继承色会被覆盖,因此颜色必须通过
33
+ * FontIcon 的 color prop 以内联样式落到 <i> 上
34
+ */
35
+ const createFontFileIcon = (iconName: string): FileIconComponent => {
36
+ const FontFileIcon = ({ color }: { color?: string }) => (
37
+ <FontIcon
38
+ fontCls="icon-bsy-form"
39
+ code={getIconUnicode(iconName)}
40
+ color={color}
41
+ style={{ fontSize: 16, lineHeight: 1 }}
42
+ />
43
+ );
44
+ FontFileIcon.displayName = `FontFileIcon(${iconName})`;
45
+ return FontFileIcon;
46
+ };
47
+
48
+ const DocFileIcon = createFontFileIcon('bsy-doc');
49
+ const DocxFileIcon = createFontFileIcon('bsy-docx');
50
+ const PptFileIcon = createFontFileIcon('bsy-ppt');
51
+ const PptxFileIcon = createFontFileIcon('bsy-pptx');
52
+ const PdfFileIcon = createFontFileIcon('bsy-pdf');
53
+ const ZipFileIcon = createFontFileIcon('bsy-zip');
54
+ const RarFileIcon = createFontFileIcon('bsy-rar');
55
+ const Mp4FileIcon = createFontFileIcon('bsy-mp4');
56
+ const WebmFileIcon = createFontFileIcon('bsy-webm');
57
+ const MovFileIcon = createFontFileIcon('bsy-mov');
58
+ const AudioFileIcon = createFontFileIcon('bsy-ogg');
59
+
60
+ export interface FileTypeConfig {
61
+ icon: FileIconComponent;
62
+ /** Icon original size for scaling */
63
+ iconSize: number;
64
+ /** Icon color (对齐 baishu BsyFileUpload 的 icon-bsy-* 配色),仅对字体图标生效 */
65
+ color?: string;
66
+ }
67
+
68
+ /** Map media types to icon configurations */
69
+ const FILE_TYPE_MAP: Record<string, FileTypeConfig> = {
70
+ // Excel files
71
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {
72
+ icon: XlsxIcon,
73
+ iconSize: 52,
74
+ },
75
+ 'application/vnd.ms-excel': { icon: XlsxIcon, iconSize: 52 },
76
+
77
+ // CSV files
78
+ 'text/csv': { icon: CsvFileIcon, iconSize: 16 },
79
+
80
+ // Text files
81
+ 'text/plain': { icon: TxtFileIcon, iconSize: 16 },
82
+ 'application/octet-stream': { icon: TxtFileIcon, iconSize: 16 }, // treat as text for ts/js files
83
+
84
+ // Word files
85
+ 'application/msword': { icon: DocFileIcon, iconSize: 16, color: '#096dd9' },
86
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': {
87
+ icon: DocxFileIcon,
88
+ iconSize: 16,
89
+ color: '#096dd9',
90
+ },
91
+
92
+ // PowerPoint files
93
+ 'application/vnd.ms-powerpoint': { icon: PptFileIcon, iconSize: 16, color: '#d46b08' },
94
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation': {
95
+ icon: PptxFileIcon,
96
+ iconSize: 16,
97
+ color: '#08979c',
98
+ },
99
+
100
+ // PDF files
101
+ 'application/pdf': { icon: PdfFileIcon, iconSize: 16, color: '#d4380d' },
102
+
103
+ // Archive files
104
+ 'application/zip': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
105
+ 'application/x-zip-compressed': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
106
+ 'application/x-rar-compressed': { icon: RarFileIcon, iconSize: 16, color: '#389e0d' },
107
+ 'application/vnd.rar': { icon: RarFileIcon, iconSize: 16, color: '#389e0d' },
108
+ 'application/x-7z-compressed': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
109
+ 'application/gzip': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
110
+
111
+ // Video files
112
+ 'video/mp4': { icon: Mp4FileIcon, iconSize: 16, color: '#1d39c4' },
113
+ 'video/webm': { icon: WebmFileIcon, iconSize: 16, color: '#d46b08' },
114
+ 'video/quicktime': { icon: MovFileIcon, iconSize: 16, color: '#1d39c4' }, // mov
115
+
116
+ // Audio files
117
+ 'audio/ogg': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' },
118
+ 'audio/mpeg': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' }, // mp3
119
+ 'audio/wav': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' },
120
+ 'audio/x-wav': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' },
121
+ };
122
+
123
+ const DEFAULT_FILE_CONFIG: FileTypeConfig = {
124
+ icon: UnknownFileIcon,
125
+ iconSize: 16,
126
+ };
127
+
128
+ /** Check if media type is an image */
129
+ export const isImageType = (mediaType?: string): boolean => {
130
+ return Boolean(mediaType?.startsWith('image/'));
131
+ };
132
+
133
+ /** Get file config based on media type and file extension */
134
+ export const getFileConfig = (mediaType?: string, fileName?: string): FileTypeConfig => {
135
+ // Check media type first
136
+ if (mediaType && FILE_TYPE_MAP[mediaType]) {
137
+ return FILE_TYPE_MAP[mediaType];
138
+ }
139
+
140
+ // Prefix match for video/audio (covers mkv/avi/flac/m4a etc.)
141
+ if (mediaType?.startsWith('video/')) {
142
+ return FILE_TYPE_MAP['video/mp4'];
143
+ }
144
+ if (mediaType?.startsWith('audio/')) {
145
+ return FILE_TYPE_MAP['audio/ogg'];
146
+ }
147
+
148
+ // Fallback to file extension
149
+ if (fileName) {
150
+ const ext = fileName.split('.').pop()?.toLowerCase();
151
+ switch (ext) {
152
+ case 'xlsx':
153
+ case 'xls':
154
+ return FILE_TYPE_MAP['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
155
+ case 'csv':
156
+ return FILE_TYPE_MAP['text/csv'];
157
+ case 'txt':
158
+ case 'ts':
159
+ case 'tsx':
160
+ case 'js':
161
+ case 'jsx':
162
+ case 'json':
163
+ case 'md':
164
+ return FILE_TYPE_MAP['text/plain'];
165
+ case 'doc':
166
+ return FILE_TYPE_MAP['application/msword'];
167
+ case 'docx':
168
+ return FILE_TYPE_MAP['application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
169
+ case 'ppt':
170
+ return FILE_TYPE_MAP['application/vnd.ms-powerpoint'];
171
+ case 'pptx':
172
+ return FILE_TYPE_MAP['application/vnd.openxmlformats-officedocument.presentationml.presentation'];
173
+ case 'pdf':
174
+ return FILE_TYPE_MAP['application/pdf'];
175
+ case 'zip':
176
+ case '7z':
177
+ case 'gz':
178
+ case 'tar':
179
+ return FILE_TYPE_MAP['application/zip'];
180
+ case 'rar':
181
+ return FILE_TYPE_MAP['application/x-rar-compressed'];
182
+ case 'mp4':
183
+ case 'mkv':
184
+ case 'avi':
185
+ return FILE_TYPE_MAP['video/mp4'];
186
+ case 'webm':
187
+ return FILE_TYPE_MAP['video/webm'];
188
+ case 'mov':
189
+ return FILE_TYPE_MAP['video/quicktime'];
190
+ case 'ogg':
191
+ case 'mp3':
192
+ case 'wav':
193
+ case 'flac':
194
+ case 'aac':
195
+ case 'm4a':
196
+ return FILE_TYPE_MAP['audio/ogg'];
197
+ }
198
+ }
199
+
200
+ return DEFAULT_FILE_CONFIG;
201
+ };
202
+
203
+ /** Extract display file name from full path */
204
+ const getDisplayFileName = (name?: string): string | undefined => {
205
+ if (!name) return undefined;
206
+ // Get last segment of path
207
+ const segments = name.split('/');
208
+ return segments[segments.length - 1];
209
+ };
210
+
211
+ /** Generic file message part component */
212
+ const FileMsgPart = ({
213
+ fileName,
214
+ icon: Icon,
215
+ iconSize,
216
+ color,
217
+ className,
218
+ size = 102,
219
+ }: {
220
+ fileName?: string;
221
+ icon: FileIconComponent;
222
+ iconSize: number;
223
+ color?: string;
224
+ className?: string;
225
+ size?: number;
226
+ }) => {
227
+ const scale = size / 102;
228
+ const scaledIconSize = (52 / 102) * size; // Target icon takes ~51% of container
229
+ const iconScale = scaledIconSize / iconSize;
230
+ const hasFileName = Boolean(fileName);
231
+
232
+ return (
233
+ <div
234
+ className={cn(
235
+ 'relative flex flex-col items-center justify-center rounded-[10px] bg-[#EFF0F6] px-[10px]',
236
+ hasFileName && 'gap-[6px]',
237
+ className
238
+ )}
239
+ style={{ width: size, height: size }}
240
+ >
241
+ <div
242
+ className="shrink-0 flex items-center justify-center"
243
+ style={{
244
+ width: scaledIconSize,
245
+ height: scaledIconSize,
246
+ color,
247
+ }}
248
+ >
249
+ <div
250
+ style={{
251
+ transform: iconScale !== 1 ? `scale(${iconScale})` : undefined,
252
+ transformOrigin: 'center',
253
+ }}
254
+ >
255
+ <Icon color={color} />
256
+ </div>
257
+ </div>
258
+ {hasFileName && (
259
+ <p
260
+ className="block max-w-full shrink-0 truncate text-center text-[14px] leading-normal text-[#030303]"
261
+ style={{ fontSize: 14 * scale, maxWidth: '100%', display: 'block' }}
262
+ title={fileName}
263
+ >
264
+ {fileName}
265
+ </p>
266
+ )}
267
+ </div>
268
+ );
269
+ };
270
+
271
+ export const AttachmentPart = ({ part, className, size = 102, iconOnly, onClick }: AttachmentPartProps) => {
272
+ const { url, name, mediaType } = part;
273
+ const handleClick = onClick ? () => onClick(part) : undefined;
274
+
275
+ // Render image for image types
276
+ if (isImageType(mediaType)) {
277
+ return <ImgMsgPart url={url} alt={name} className={className} size={size} onClick={handleClick} />;
278
+ }
279
+
280
+ // Render file icon for other types
281
+ const { icon, iconSize, color } = getFileConfig(mediaType, name);
282
+ const displayName = iconOnly ? undefined : getDisplayFileName(name);
283
+
284
+ return (
285
+ <div onClick={handleClick} className={onClick ? 'cursor-pointer' : undefined}>
286
+ <FileMsgPart
287
+ fileName={displayName}
288
+ icon={icon}
289
+ iconSize={iconSize}
290
+ color={color}
291
+ className={className}
292
+ size={size}
293
+ />
294
+ </div>
295
+ );
296
+ };