@ray-js/t-agent-ui-ray 0.2.0-beta-9 → 0.2.0-beta-11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/MessageInput/MessageInputAIStream/AsrInput.js +7 -1
- package/dist/MessageInput/MessageInputAIStream/index.js +74 -24
- package/dist/MessageInput/icons/camera.svg +1 -0
- package/dist/hooks/useAttachmentInput.d.ts +1 -1
- package/dist/hooks/useAttachmentInput.js +68 -35
- package/dist/i18n/strings.d.ts +2 -0
- package/dist/i18n/strings.js +2 -5
- package/dist/tiles/FileTile/index.less +1 -0
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
2
|
-
import React, { useRef, useState } from 'react';
|
|
2
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { Button, View } from '@ray-js/ray';
|
|
4
4
|
import cx from 'clsx';
|
|
5
5
|
import { useTranslate } from '../../hooks';
|
|
@@ -39,6 +39,12 @@ const AsrInput = props => {
|
|
|
39
39
|
}
|
|
40
40
|
props.onConfirm();
|
|
41
41
|
};
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (active && !props.recording) {
|
|
44
|
+
setActive(false);
|
|
45
|
+
setCancel(false);
|
|
46
|
+
}
|
|
47
|
+
}, [props.recording]);
|
|
42
48
|
return /*#__PURE__*/React.createElement(View, {
|
|
43
49
|
className: "t-agent-message-input-ptt-bar"
|
|
44
50
|
}, /*#__PURE__*/React.createElement(View, {
|
|
@@ -12,6 +12,7 @@ import { Emitter, EmitterEvent } from '@ray-js/t-agent';
|
|
|
12
12
|
import cx from 'clsx';
|
|
13
13
|
import imageSvg from '../icons/image.svg';
|
|
14
14
|
import videoSvg from '../icons/video.svg';
|
|
15
|
+
import cameraSvg from '../icons/camera.svg';
|
|
15
16
|
import { useAttachmentInput, useChatAgent, useEmitEvent, useIsUnmounted, useOnEvent, useTranslate } from '../../hooks';
|
|
16
17
|
import AsrInput from './AsrInput';
|
|
17
18
|
import { useSleep } from '../../hooks/useSleep';
|
|
@@ -47,7 +48,7 @@ export default function MessageInputAIStream(props) {
|
|
|
47
48
|
setUploaded,
|
|
48
49
|
upload
|
|
49
50
|
} = attachmentInput;
|
|
50
|
-
const
|
|
51
|
+
const abortRef = useRef(null);
|
|
51
52
|
const [responding, setResponding] = useState(false);
|
|
52
53
|
const [mode, setMode] = useState('text');
|
|
53
54
|
const agent = useChatAgent();
|
|
@@ -70,16 +71,26 @@ export default function MessageInputAIStream(props) {
|
|
|
70
71
|
setUploaded([]);
|
|
71
72
|
setText('');
|
|
72
73
|
setResponding(true);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
const controller = new AbortController();
|
|
75
|
+
let abortResolve;
|
|
76
|
+
abortRef.current = {
|
|
77
|
+
controller,
|
|
78
|
+
promise: new Promise(resolve => {
|
|
79
|
+
abortResolve = resolve;
|
|
80
|
+
})
|
|
81
|
+
};
|
|
82
|
+
controller.signal.addEventListener('abort', () => {
|
|
83
|
+
var _abortRef$current;
|
|
84
|
+
if (((_abortRef$current = abortRef.current) === null || _abortRef$current === void 0 ? void 0 : _abortRef$current.controller) === controller) {
|
|
85
|
+
abortRef.current = null;
|
|
78
86
|
setResponding(false);
|
|
79
87
|
}
|
|
80
88
|
});
|
|
81
89
|
try {
|
|
82
|
-
await agent.pushInputBlocks(inputBlocks,
|
|
90
|
+
await agent.pushInputBlocks(inputBlocks, controller.signal);
|
|
91
|
+
if (controller.signal.aborted) {
|
|
92
|
+
abortResolve();
|
|
93
|
+
}
|
|
83
94
|
} finally {
|
|
84
95
|
if (!isUnmounted()) {
|
|
85
96
|
setResponding(false);
|
|
@@ -97,16 +108,25 @@ export default function MessageInputAIStream(props) {
|
|
|
97
108
|
setMoreOpen(false);
|
|
98
109
|
setUploaded([]);
|
|
99
110
|
setResponding(true);
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
111
|
+
const controller = new AbortController();
|
|
112
|
+
let abortResolve;
|
|
113
|
+
abortRef.current = {
|
|
114
|
+
controller,
|
|
115
|
+
promise: new Promise(resolve => {
|
|
116
|
+
abortResolve = resolve;
|
|
117
|
+
})
|
|
118
|
+
};
|
|
119
|
+
controller.signal.addEventListener('abort', () => {
|
|
120
|
+
if (abortRef.current.controller === controller) {
|
|
121
|
+
abortRef.current = null;
|
|
105
122
|
}
|
|
106
123
|
setResponding(false);
|
|
107
124
|
});
|
|
108
125
|
try {
|
|
109
|
-
await agent.pushInputBlocks(blocks,
|
|
126
|
+
await agent.pushInputBlocks(blocks, controller.signal);
|
|
127
|
+
if (controller.signal.aborted) {
|
|
128
|
+
abortResolve();
|
|
129
|
+
}
|
|
110
130
|
} finally {
|
|
111
131
|
if (!isUnmounted()) {
|
|
112
132
|
setResponding(false);
|
|
@@ -143,7 +163,7 @@ export default function MessageInputAIStream(props) {
|
|
|
143
163
|
});
|
|
144
164
|
}
|
|
145
165
|
};
|
|
146
|
-
|
|
166
|
+
useSleep();
|
|
147
167
|
let container;
|
|
148
168
|
const canSend = text.trim().length && !responding && !attachmentInput.uploading;
|
|
149
169
|
if (mode === 'text') {
|
|
@@ -203,8 +223,8 @@ export default function MessageInputAIStream(props) {
|
|
|
203
223
|
"data-testid": "t-agent-message-input-button-main",
|
|
204
224
|
onClick: async () => {
|
|
205
225
|
if (responding) {
|
|
206
|
-
if (
|
|
207
|
-
|
|
226
|
+
if (abortRef.current) {
|
|
227
|
+
abortRef.current.controller.abort('User abort');
|
|
208
228
|
}
|
|
209
229
|
} else if (isMore) {
|
|
210
230
|
openMoreClick();
|
|
@@ -226,14 +246,19 @@ export default function MessageInputAIStream(props) {
|
|
|
226
246
|
if (attachmentInput.uploading) {
|
|
227
247
|
return;
|
|
228
248
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
249
|
+
const abort = abortRef.current;
|
|
250
|
+
if (responding && abort) {
|
|
251
|
+
abort.controller.abort('User abort');
|
|
252
|
+
// 中断后等待上次结束再开始
|
|
253
|
+
await abort.promise;
|
|
233
254
|
}
|
|
234
255
|
const emitter = new Emitter();
|
|
235
|
-
setTimeout(() => {
|
|
256
|
+
const id = setTimeout(() => {
|
|
236
257
|
if (r.recording) {
|
|
258
|
+
ty.showToast({
|
|
259
|
+
icon: 'none',
|
|
260
|
+
title: t('t-agent.input.asr.error.timeout')
|
|
261
|
+
});
|
|
237
262
|
r.confirm();
|
|
238
263
|
}
|
|
239
264
|
}, props.maxAudioMs || 30000);
|
|
@@ -242,6 +267,7 @@ export default function MessageInputAIStream(props) {
|
|
|
242
267
|
recording: true,
|
|
243
268
|
confirm: () => {
|
|
244
269
|
r.recording = false;
|
|
270
|
+
clearTimeout(id);
|
|
245
271
|
emitter.dispatchEvent(new EmitterEvent('confirm'));
|
|
246
272
|
setRecord({
|
|
247
273
|
startAt: 0,
|
|
@@ -252,6 +278,7 @@ export default function MessageInputAIStream(props) {
|
|
|
252
278
|
},
|
|
253
279
|
cancel: () => {
|
|
254
280
|
r.recording = false;
|
|
281
|
+
clearTimeout(id);
|
|
255
282
|
emitter.dispatchEvent(new EmitterEvent('cancel'));
|
|
256
283
|
setRecord({
|
|
257
284
|
startAt: 0,
|
|
@@ -294,8 +321,8 @@ export default function MessageInputAIStream(props) {
|
|
|
294
321
|
setMoreOpen(false);
|
|
295
322
|
},
|
|
296
323
|
onAbort: () => {
|
|
297
|
-
if (responding &&
|
|
298
|
-
|
|
324
|
+
if (responding && abortRef.current) {
|
|
325
|
+
abortRef.current.controller.abort('User abort');
|
|
299
326
|
}
|
|
300
327
|
}
|
|
301
328
|
});
|
|
@@ -363,7 +390,7 @@ export default function MessageInputAIStream(props) {
|
|
|
363
390
|
});
|
|
364
391
|
return;
|
|
365
392
|
}
|
|
366
|
-
await upload('
|
|
393
|
+
await upload('album', 1);
|
|
367
394
|
} catch (e) {
|
|
368
395
|
ty.showToast({
|
|
369
396
|
icon: 'error',
|
|
@@ -375,6 +402,29 @@ export default function MessageInputAIStream(props) {
|
|
|
375
402
|
className: "t-agent-message-input-panel-button-icon"
|
|
376
403
|
}, /*#__PURE__*/React.createElement(Image, {
|
|
377
404
|
src: imageSvg
|
|
405
|
+
}))), attachmentOptions.image && /*#__PURE__*/React.createElement(Button, {
|
|
406
|
+
className: "t-agent-message-input-panel-button",
|
|
407
|
+
onClick: async () => {
|
|
408
|
+
try {
|
|
409
|
+
if (uploaded.filter(f => f.type === 'image').length >= (attachmentOptions.imageCount || 1)) {
|
|
410
|
+
ty.showToast({
|
|
411
|
+
icon: 'none',
|
|
412
|
+
title: t('t-agent.input.upload.image.max-reached')
|
|
413
|
+
});
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
await upload('camera', 1);
|
|
417
|
+
} catch (e) {
|
|
418
|
+
ty.showToast({
|
|
419
|
+
icon: 'error',
|
|
420
|
+
title: t('t-agent.input.upload.failed')
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}, /*#__PURE__*/React.createElement(View, {
|
|
425
|
+
className: "t-agent-message-input-panel-button-icon"
|
|
426
|
+
}, /*#__PURE__*/React.createElement(Image, {
|
|
427
|
+
src: cameraSvg
|
|
378
428
|
}))), attachmentOptions.video && /*#__PURE__*/React.createElement(Button, {
|
|
379
429
|
className: "t-agent-message-input-panel-button",
|
|
380
430
|
onClick: async () => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="20" height="18" viewBox="0 0 20 18"><g><path d="M7.82843,2L5.82843,4L2,4L2,16L18,16L18,4L14.1716,4L12.1716,2L7.82843,2ZM7,0L13,0L15,2L19,2C19.5523,2,20,2.44772,20,3L20,17C20,17.5523,19.5523,18,19,18L1,18C0.44772,18,0,17.5523,0,17L0,3C0,2.44772,0.44772,2,1,2L5,2L7,0ZM10,15C6.96243,15,4.5,12.5376,4.5,9.5C4.5,6.46243,6.96243,4,10,4C13.0376,4,15.5,6.46243,15.5,9.5C15.5,12.5376,13.0376,15,10,15ZM10,13C11.933,13,13.5,11.433,13.5,9.5C13.5,7.567,11.933,6,10,6C8.067,6,6.5,7.567,6.5,9.5C6.5,11.433,8.067,13,10,13Z" fill="#3D3D3D" fill-opacity="1"/></g></svg>
|
|
@@ -18,5 +18,5 @@ export declare function useAttachmentInput({ local }?: {
|
|
|
18
18
|
setUploaded: import("react").Dispatch<import("react").SetStateAction<UploadFile[]>>;
|
|
19
19
|
uploading: boolean;
|
|
20
20
|
loadBlocks: (blocks: InputBlock[], append?: boolean) => void;
|
|
21
|
-
upload: (type: 'image' | 'video', count?: any) => Promise<void>;
|
|
21
|
+
upload: (type: 'image' | 'video' | 'camera' | 'album', count?: any) => Promise<void>;
|
|
22
22
|
};
|
|
@@ -109,47 +109,80 @@ export function useAttachmentInput() {
|
|
|
109
109
|
} = useRenderOptions();
|
|
110
110
|
const upload = useCallback(async function (type) {
|
|
111
111
|
let count = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
|
|
112
|
-
if (type === 'image') {
|
|
112
|
+
if (type === 'image' || type === 'camera' || type === 'album') {
|
|
113
113
|
let file = [];
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
114
|
+
if (type === 'image') {
|
|
115
|
+
try {
|
|
116
|
+
const sourceType = await new Promise(resolve => {
|
|
117
|
+
ty.showActionSheet({
|
|
118
|
+
itemList: [t('t-agent.input.upload.source-type.camera'), t('t-agent.input.upload.source-type.album')],
|
|
119
|
+
success: _ref => {
|
|
120
|
+
let {
|
|
121
|
+
tapIndex
|
|
122
|
+
} = _ref;
|
|
123
|
+
if (tapIndex === 0) {
|
|
124
|
+
ty.authorize({
|
|
125
|
+
scope: 'scope.camera',
|
|
126
|
+
success() {
|
|
127
|
+
resolve('camera');
|
|
128
|
+
},
|
|
129
|
+
fail() {
|
|
130
|
+
ty.showToast({
|
|
131
|
+
title: t('t-agent.input.upload.source-type.camera.require-permission'),
|
|
132
|
+
icon: 'none'
|
|
133
|
+
});
|
|
134
|
+
resolve('');
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
} else if (tapIndex === 1) {
|
|
138
|
+
resolve('album');
|
|
139
|
+
} else {
|
|
140
|
+
resolve('');
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
fail() {
|
|
139
144
|
resolve('');
|
|
140
145
|
}
|
|
141
|
-
}
|
|
142
|
-
fail() {
|
|
143
|
-
resolve('');
|
|
144
|
-
}
|
|
146
|
+
});
|
|
145
147
|
});
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
if (!sourceType) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
file = await chooseImage(count, sourceType);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (type === 'camera') {
|
|
157
|
+
try {
|
|
158
|
+
const allowUseCamera = await new Promise(resolve => {
|
|
159
|
+
ty.authorize({
|
|
160
|
+
scope: 'scope.camera',
|
|
161
|
+
success() {
|
|
162
|
+
resolve(true);
|
|
163
|
+
},
|
|
164
|
+
fail() {
|
|
165
|
+
ty.showToast({
|
|
166
|
+
title: t('t-agent.input.upload.source-type.camera.require-permission'),
|
|
167
|
+
icon: 'none'
|
|
168
|
+
});
|
|
169
|
+
resolve(false);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
if (allowUseCamera) {
|
|
174
|
+
file = await chooseImage(count, 'camera');
|
|
175
|
+
}
|
|
176
|
+
} catch (err) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (type === 'album') {
|
|
181
|
+
try {
|
|
182
|
+
file = await chooseImage(count, 'album');
|
|
183
|
+
} catch (err) {
|
|
148
184
|
return;
|
|
149
185
|
}
|
|
150
|
-
file = await chooseImage(count, sourceType);
|
|
151
|
-
} catch (err) {
|
|
152
|
-
return;
|
|
153
186
|
}
|
|
154
187
|
await Promise.all(file.map(async _ref2 => {
|
|
155
188
|
let {
|
package/dist/i18n/strings.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ declare const _default: {
|
|
|
22
22
|
't-agent.input.asr.ptt': string;
|
|
23
23
|
't-agent.input.asr.error.too-short': string;
|
|
24
24
|
't-agent.input.asr.error.empty': string;
|
|
25
|
+
't-agent.input.asr.error.timeout': string;
|
|
25
26
|
't-agent.message.feedback.success': string;
|
|
26
27
|
't-agent.message.bubble.aborted': string;
|
|
27
28
|
't-agent.message.action.copy': string;
|
|
@@ -164,6 +165,7 @@ declare const _default: {
|
|
|
164
165
|
't-agent.input.asr.ptt': string;
|
|
165
166
|
't-agent.input.asr.error.too-short': string;
|
|
166
167
|
't-agent.input.asr.error.empty': string;
|
|
168
|
+
't-agent.input.asr.error.timeout': string;
|
|
167
169
|
't-agent.message.feedback.success': string;
|
|
168
170
|
't-agent.message.bubble.aborted': string;
|
|
169
171
|
't-agent.message.action.copy': string;
|
package/dist/i18n/strings.js
CHANGED
|
@@ -22,6 +22,7 @@ export default {
|
|
|
22
22
|
't-agent.input.asr.ptt': '按住说话',
|
|
23
23
|
't-agent.input.asr.error.too-short': '说话时间太短',
|
|
24
24
|
't-agent.input.asr.error.empty': '未能从语音中识别到文字',
|
|
25
|
+
't-agent.input.asr.error.timeout': '语音识别已达时长限制,将直接发送',
|
|
25
26
|
't-agent.message.feedback.success': '反馈成功',
|
|
26
27
|
't-agent.message.bubble.aborted': '用户中断',
|
|
27
28
|
't-agent.message.action.copy': '复制消息',
|
|
@@ -160,15 +161,11 @@ export default {
|
|
|
160
161
|
't-agent.input.voice.require-permission': 'Recording Permission Required',
|
|
161
162
|
't-agent.input.upload.failed': 'File Upload Failed',
|
|
162
163
|
't-agent.input.asr.oninput.text.top': 'Listening, speak now',
|
|
163
|
-
// 语音激活态顶部提示
|
|
164
164
|
't-agent.input.asr.oninput.text.center': 'Release to send · Swipe up to cancel',
|
|
165
|
-
// 交互操作指引
|
|
166
165
|
't-agent.input.asr.ptt': 'Press & hold to talk',
|
|
167
|
-
// Push-to-Talk 按钮提示
|
|
168
166
|
't-agent.input.asr.error.too-short': 'Speaking time too short',
|
|
169
|
-
// 语音输入时间过短
|
|
170
167
|
't-agent.input.asr.error.empty': 'No text recognized from voice',
|
|
171
|
-
|
|
168
|
+
't-agent.input.asr.error.timeout': 'Voice recognition has reached time limit, sending directly',
|
|
172
169
|
't-agent.message.feedback.success': 'Feedback Successful',
|
|
173
170
|
't-agent.message.bubble.aborted': 'User Aborted',
|
|
174
171
|
't-agent.message.action.copy': 'Copy Message',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ray-js/t-agent-ui-ray",
|
|
3
|
-
"version": "0.2.0-beta-
|
|
3
|
+
"version": "0.2.0-beta-11",
|
|
4
4
|
"author": "Tuya.inc",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -40,5 +40,5 @@
|
|
|
40
40
|
"@types/echarts": "^4.9.22",
|
|
41
41
|
"@types/markdown-it": "^14.1.1"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "dd56b31b0095fb82d0fb5d4f58581d00ae2970ee"
|
|
44
44
|
}
|