@fe-free/ai 6.0.11 → 6.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/package.json +4 -4
- package/src/sender/actions.tsx +9 -1
- package/src/sender/files.tsx +11 -6
- package/src/sender/index.tsx +11 -9
- package/src/sender/sender.stories.tsx +34 -3
- package/src/sender/types.ts +11 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @fe-free/ai
|
|
2
2
|
|
|
3
|
+
## 6.0.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @fe-free/core@6.0.13
|
|
9
|
+
- @fe-free/icons@6.0.13
|
|
10
|
+
- @fe-free/tool@6.0.13
|
|
11
|
+
|
|
12
|
+
## 6.0.12
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- ai
|
|
17
|
+
- @fe-free/core@6.0.12
|
|
18
|
+
- @fe-free/icons@6.0.12
|
|
19
|
+
- @fe-free/tool@6.0.12
|
|
20
|
+
|
|
3
21
|
## 6.0.11
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fe-free/ai",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.13",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"lodash-es": "^4.17.21",
|
|
18
18
|
"uuid": "^13.0.0",
|
|
19
19
|
"zustand": "^4.5.7",
|
|
20
|
-
"@fe-free/core": "6.0.
|
|
20
|
+
"@fe-free/core": "6.0.13"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"antd": "^6.2.1",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"i18next-icu": "^2.4.1",
|
|
28
28
|
"react": "^19.2.0",
|
|
29
29
|
"react-i18next": "^16.4.0",
|
|
30
|
-
"@fe-free/icons": "6.0.
|
|
31
|
-
"@fe-free/tool": "6.0.
|
|
30
|
+
"@fe-free/icons": "6.0.13",
|
|
31
|
+
"@fe-free/tool": "6.0.13"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"test": "echo \"Error: no test specified\" && exit 1",
|
package/src/sender/actions.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import type { UploadFile } from 'antd';
|
|
|
3
3
|
import { Button } from 'antd';
|
|
4
4
|
import type { TextAreaRef } from 'antd/es/input/TextArea';
|
|
5
5
|
import type { RefObject } from 'react';
|
|
6
|
+
|
|
6
7
|
import SendIcon from '../svgs/send.svg?react';
|
|
7
8
|
import { FileAction } from './files';
|
|
8
9
|
import { RecordAction } from './record';
|
|
@@ -21,6 +22,7 @@ function Actions(
|
|
|
21
22
|
},
|
|
22
23
|
) {
|
|
23
24
|
const {
|
|
25
|
+
value,
|
|
24
26
|
refUpload,
|
|
25
27
|
isUploading,
|
|
26
28
|
fileUrls,
|
|
@@ -36,7 +38,7 @@ function Actions(
|
|
|
36
38
|
return (
|
|
37
39
|
<div className="flex items-center gap-2">
|
|
38
40
|
<div className="flex flex-1 gap-1">
|
|
39
|
-
{allowUpload && (
|
|
41
|
+
{allowUpload && !allowUpload.renderUpload && (
|
|
40
42
|
<FileAction
|
|
41
43
|
{...props}
|
|
42
44
|
refUpload={refUpload}
|
|
@@ -44,6 +46,12 @@ function Actions(
|
|
|
44
46
|
setFileUrls={setFileUrls}
|
|
45
47
|
/>
|
|
46
48
|
)}
|
|
49
|
+
{allowUpload &&
|
|
50
|
+
allowUpload.renderUpload &&
|
|
51
|
+
allowUpload.renderUpload({
|
|
52
|
+
value,
|
|
53
|
+
refUpload,
|
|
54
|
+
})}
|
|
47
55
|
</div>
|
|
48
56
|
{/* <Divider type="vertical" /> */}
|
|
49
57
|
<div className="flex items-center gap-2">
|
package/src/sender/files.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import { App, Button, Dropdown, Input, Modal, Upload } from 'antd';
|
|
|
5
5
|
import type { RefObject } from 'react';
|
|
6
6
|
import { useState } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
|
+
|
|
8
9
|
import { FileView } from '../files';
|
|
9
10
|
import FilesIcon from '../svgs/files.svg?react';
|
|
10
11
|
import type { SenderProps } from './types';
|
|
@@ -53,7 +54,10 @@ function FileAction(
|
|
|
53
54
|
],
|
|
54
55
|
}}
|
|
55
56
|
>
|
|
56
|
-
<Button
|
|
57
|
+
<Button
|
|
58
|
+
shape="circle"
|
|
59
|
+
icon={<Icons component={PlusOutlined} className="h-[28px]! text-lg!" />}
|
|
60
|
+
/>
|
|
57
61
|
</Dropdown>
|
|
58
62
|
{open && (
|
|
59
63
|
<Modal
|
|
@@ -96,7 +100,7 @@ function FileUpload(
|
|
|
96
100
|
},
|
|
97
101
|
) {
|
|
98
102
|
const { allowUpload, refUpload, fileList, setFileList, uploadMaxCount } = props;
|
|
99
|
-
const { uploadAction, filesMaxCount } = allowUpload || {};
|
|
103
|
+
const { uploadAction, filesMaxCount, accept } = allowUpload || {};
|
|
100
104
|
|
|
101
105
|
const { message } = App.useApp();
|
|
102
106
|
const { t } = useTranslation();
|
|
@@ -104,6 +108,7 @@ function FileUpload(
|
|
|
104
108
|
return (
|
|
105
109
|
<Upload.Dragger
|
|
106
110
|
action={uploadAction}
|
|
111
|
+
accept={accept}
|
|
107
112
|
fileList={fileList}
|
|
108
113
|
multiple
|
|
109
114
|
pastable
|
|
@@ -142,12 +147,12 @@ function UploadFileItem({ file, onDelete }: { file: UploadFile; onDelete: () =>
|
|
|
142
147
|
<FileView url={file.name} />
|
|
143
148
|
)}
|
|
144
149
|
{!isDone && (
|
|
145
|
-
<div className="absolute inset-0 flex items-center justify-center
|
|
150
|
+
<div className="bg-01/80 absolute inset-0 flex items-center justify-center">
|
|
146
151
|
{(file.percent ?? 0).toFixed(0)}%
|
|
147
152
|
</div>
|
|
148
153
|
)}
|
|
149
154
|
<CloseOutlined
|
|
150
|
-
className="absolute
|
|
155
|
+
className="bg-04 absolute top-1 right-1 hidden cursor-pointer rounded-full text-white group-hover:block"
|
|
151
156
|
onClick={onDelete}
|
|
152
157
|
/>
|
|
153
158
|
</div>
|
|
@@ -157,11 +162,11 @@ function UploadFileItem({ file, onDelete }: { file: UploadFile; onDelete: () =>
|
|
|
157
162
|
function UrlFileItem({ url, onDelete }: { url: string; onDelete: () => void }) {
|
|
158
163
|
return (
|
|
159
164
|
<div className="group relative">
|
|
160
|
-
<div className="flex h-[60px] w-[250px] items-center rounded
|
|
165
|
+
<div className="bg-01 flex h-[60px] w-[250px] items-center rounded px-2">
|
|
161
166
|
<div className="line-clamp-2">{url}</div>
|
|
162
167
|
</div>
|
|
163
168
|
<CloseOutlined
|
|
164
|
-
className="absolute
|
|
169
|
+
className="bg-04 absolute top-1 right-1 hidden cursor-pointer rounded-full text-white group-hover:block"
|
|
165
170
|
onClick={onDelete}
|
|
166
171
|
/>
|
|
167
172
|
</div>
|
package/src/sender/index.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import classNames from 'classnames';
|
|
|
6
6
|
import type { RefObject } from 'react';
|
|
7
7
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
|
+
|
|
9
10
|
import { Actions } from './actions';
|
|
10
11
|
import { FileUpload, Files } from './files';
|
|
11
12
|
import type { SenderProps, SenderRef } from './types';
|
|
@@ -28,7 +29,8 @@ function Text(
|
|
|
28
29
|
// Enter: 提交
|
|
29
30
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
30
31
|
e.preventDefault();
|
|
31
|
-
onSubmit
|
|
32
|
+
// 兼容 onSubmit 返回 Promise 的情况:在 keydown 事件里不需要等待结果
|
|
33
|
+
void onSubmit?.();
|
|
32
34
|
}
|
|
33
35
|
},
|
|
34
36
|
[onSubmit],
|
|
@@ -88,27 +90,27 @@ function Sender(originProps: SenderProps) {
|
|
|
88
90
|
const [fileList, originSetFileList] = useState<UploadFile[]>([]);
|
|
89
91
|
|
|
90
92
|
const handleFilesChange = useCallback(
|
|
91
|
-
({ fileUrls, fileList }) => {
|
|
93
|
+
({ fileUrls: urls, fileList: list }) => {
|
|
92
94
|
onChange?.({
|
|
93
95
|
...value,
|
|
94
|
-
files: [...(
|
|
96
|
+
files: [...(list.map((file) => file.response?.data?.url) || []), ...urls],
|
|
95
97
|
});
|
|
96
98
|
},
|
|
97
99
|
[value, onChange],
|
|
98
100
|
);
|
|
99
101
|
|
|
100
102
|
const setFileUrls = useCallback(
|
|
101
|
-
(
|
|
102
|
-
originSetFileUrls(
|
|
103
|
-
handleFilesChange({ fileUrls, fileList });
|
|
103
|
+
(urls: string[]) => {
|
|
104
|
+
originSetFileUrls(urls);
|
|
105
|
+
handleFilesChange({ fileUrls: urls, fileList });
|
|
104
106
|
},
|
|
105
107
|
[fileList, handleFilesChange],
|
|
106
108
|
);
|
|
107
109
|
|
|
108
110
|
const setFileList = useCallback(
|
|
109
|
-
(
|
|
110
|
-
originSetFileList(
|
|
111
|
-
handleFilesChange({ fileUrls, fileList });
|
|
111
|
+
(list: UploadFile[]) => {
|
|
112
|
+
originSetFileList(list);
|
|
113
|
+
handleFilesChange({ fileUrls, fileList: list });
|
|
112
114
|
},
|
|
113
115
|
[fileUrls, handleFilesChange],
|
|
114
116
|
);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Sender } from '@fe-free/ai';
|
|
2
|
+
import Icons, { PlusOutlined } from '@fe-free/icons';
|
|
2
3
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
|
+
import { Button } from 'antd';
|
|
3
5
|
import { useState } from 'react';
|
|
6
|
+
|
|
4
7
|
import type { SenderProps, SenderValue } from './types';
|
|
5
8
|
|
|
6
9
|
const meta: Meta<typeof Sender> = {
|
|
@@ -17,9 +20,9 @@ function Component(props: Omit<SenderProps, 'value' | 'onChange' | 'onSubmit'>)
|
|
|
17
20
|
return (
|
|
18
21
|
<Sender
|
|
19
22
|
value={v}
|
|
20
|
-
onChange={(
|
|
21
|
-
console.log('newValue',
|
|
22
|
-
setV(
|
|
23
|
+
onChange={(nextValue) => {
|
|
24
|
+
console.log('newValue', nextValue);
|
|
25
|
+
setV(nextValue);
|
|
23
26
|
}}
|
|
24
27
|
onSubmit={(value) => {
|
|
25
28
|
console.log('onSubmit', value);
|
|
@@ -58,6 +61,34 @@ export const AllowUpload: Story = {
|
|
|
58
61
|
render: (props) => <Component {...props} />,
|
|
59
62
|
};
|
|
60
63
|
|
|
64
|
+
export const AllowUploadWithAccept: Story = {
|
|
65
|
+
args: {
|
|
66
|
+
allowUpload: {
|
|
67
|
+
uploadAction: '/api/ai-service/v1/file_upload/upload',
|
|
68
|
+
filesMaxCount: 3,
|
|
69
|
+
accept: 'image/*,.pdf',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
render: (props) => <Component {...props} />,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const RenderUpload: Story = {
|
|
76
|
+
args: {
|
|
77
|
+
allowUpload: {
|
|
78
|
+
uploadAction: '/api/ai-service/v1/file_upload/upload',
|
|
79
|
+
accept: 'image/*',
|
|
80
|
+
renderUpload: (props) => (
|
|
81
|
+
<Button
|
|
82
|
+
type="text"
|
|
83
|
+
shape="circle"
|
|
84
|
+
icon={<Icons component={PlusOutlined} onClick={() => props.refUpload.current?.click()} />}
|
|
85
|
+
/>
|
|
86
|
+
),
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
render: (props) => <Component {...props} />,
|
|
90
|
+
};
|
|
91
|
+
|
|
61
92
|
export const AllowSpeech: Story = {
|
|
62
93
|
render: (props) => {
|
|
63
94
|
const [recording, setRecording] = useState(true);
|
package/src/sender/types.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { RefObject } from 'react';
|
|
3
|
+
|
|
1
4
|
interface SenderRef {
|
|
2
5
|
focus: () => void;
|
|
3
6
|
}
|
|
@@ -19,8 +22,16 @@ interface SenderProps {
|
|
|
19
22
|
allowUpload?: {
|
|
20
23
|
/** 上传文件的接口地址,约定返回的 {data: {url: string}} */
|
|
21
24
|
uploadAction?: string;
|
|
25
|
+
/** 限制可上传文件类型,语法同 input.accept,如 image/*,.pdf */
|
|
26
|
+
accept?: string;
|
|
22
27
|
/** files 最大数量 */
|
|
23
28
|
filesMaxCount?: number;
|
|
29
|
+
/** 自定义上传按钮 */
|
|
30
|
+
renderUpload?: (props: {
|
|
31
|
+
value?: SenderValue;
|
|
32
|
+
/** refUpload.current?.click() 触发上传 */
|
|
33
|
+
refUpload: RefObject<HTMLDivElement | null>;
|
|
34
|
+
}) => ReactNode;
|
|
24
35
|
};
|
|
25
36
|
|
|
26
37
|
/** 是否允许语音输入 */
|