@ray-js/t-agent-ui-ray 0.2.0-beta-8 → 0.2.0-beta-10
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.d.ts +2 -0
- package/dist/MessageInput/MessageInputAIStream/index.js +61 -27
- package/dist/MessageInput/icons/video.svg +10 -1
- package/dist/i18n/strings.d.ts +2 -0
- package/dist/i18n/strings.js +2 -5
- package/dist/tiles/BubbleTile/index.less +2 -0
- package/dist/tiles/FileTile/index.less +2 -1
- package/dist/tiles/TextTile/index.less +2 -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,8 @@ interface Props {
|
|
|
12
12
|
placeholder?: string;
|
|
13
13
|
style?: React.CSSProperties;
|
|
14
14
|
attachment?: boolean | AttachmentOptions;
|
|
15
|
+
maxTextLength?: number;
|
|
16
|
+
maxAudioMs?: number;
|
|
15
17
|
}
|
|
16
18
|
export default function MessageInputAIStream(props: Props): React.JSX.Element;
|
|
17
19
|
export {};
|
|
@@ -12,7 +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 {
|
|
15
|
+
import { useAttachmentInput, useChatAgent, useEmitEvent, useIsUnmounted, useOnEvent, useTranslate } from '../../hooks';
|
|
16
16
|
import AsrInput from './AsrInput';
|
|
17
17
|
import { useSleep } from '../../hooks/useSleep';
|
|
18
18
|
const AMPLITUDE_COUNT = 60;
|
|
@@ -47,7 +47,7 @@ export default function MessageInputAIStream(props) {
|
|
|
47
47
|
setUploaded,
|
|
48
48
|
upload
|
|
49
49
|
} = attachmentInput;
|
|
50
|
-
const
|
|
50
|
+
const abortRef = useRef(null);
|
|
51
51
|
const [responding, setResponding] = useState(false);
|
|
52
52
|
const [mode, setMode] = useState('text');
|
|
53
53
|
const agent = useChatAgent();
|
|
@@ -61,7 +61,7 @@ export default function MessageInputAIStream(props) {
|
|
|
61
61
|
setResponding(false);
|
|
62
62
|
}
|
|
63
63
|
});
|
|
64
|
-
const
|
|
64
|
+
const hasMore = attachmentOptions.image || attachmentOptions.video;
|
|
65
65
|
const isMore = !text.trim().length && hasMore;
|
|
66
66
|
const send = async inputBlocks => {
|
|
67
67
|
if (!(inputBlocks !== null && inputBlocks !== void 0 && inputBlocks.length)) {
|
|
@@ -70,16 +70,26 @@ export default function MessageInputAIStream(props) {
|
|
|
70
70
|
setUploaded([]);
|
|
71
71
|
setText('');
|
|
72
72
|
setResponding(true);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
const controller = new AbortController();
|
|
74
|
+
let abortResolve;
|
|
75
|
+
abortRef.current = {
|
|
76
|
+
controller,
|
|
77
|
+
promise: new Promise(resolve => {
|
|
78
|
+
abortResolve = resolve;
|
|
79
|
+
})
|
|
80
|
+
};
|
|
81
|
+
controller.signal.addEventListener('abort', () => {
|
|
82
|
+
var _abortRef$current;
|
|
83
|
+
if (((_abortRef$current = abortRef.current) === null || _abortRef$current === void 0 ? void 0 : _abortRef$current.controller) === controller) {
|
|
84
|
+
abortRef.current = null;
|
|
78
85
|
setResponding(false);
|
|
79
86
|
}
|
|
80
87
|
});
|
|
81
88
|
try {
|
|
82
|
-
await agent.pushInputBlocks(inputBlocks,
|
|
89
|
+
await agent.pushInputBlocks(inputBlocks, controller.signal);
|
|
90
|
+
if (controller.signal.aborted) {
|
|
91
|
+
abortResolve();
|
|
92
|
+
}
|
|
83
93
|
} finally {
|
|
84
94
|
if (!isUnmounted()) {
|
|
85
95
|
setResponding(false);
|
|
@@ -97,16 +107,25 @@ export default function MessageInputAIStream(props) {
|
|
|
97
107
|
setMoreOpen(false);
|
|
98
108
|
setUploaded([]);
|
|
99
109
|
setResponding(true);
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
const controller = new AbortController();
|
|
111
|
+
let abortResolve;
|
|
112
|
+
abortRef.current = {
|
|
113
|
+
controller,
|
|
114
|
+
promise: new Promise(resolve => {
|
|
115
|
+
abortResolve = resolve;
|
|
116
|
+
})
|
|
117
|
+
};
|
|
118
|
+
controller.signal.addEventListener('abort', () => {
|
|
119
|
+
if (abortRef.current.controller === controller) {
|
|
120
|
+
abortRef.current = null;
|
|
105
121
|
}
|
|
106
122
|
setResponding(false);
|
|
107
123
|
});
|
|
108
124
|
try {
|
|
109
|
-
await agent.pushInputBlocks(blocks,
|
|
125
|
+
await agent.pushInputBlocks(blocks, controller.signal);
|
|
126
|
+
if (controller.signal.aborted) {
|
|
127
|
+
abortResolve();
|
|
128
|
+
}
|
|
110
129
|
} finally {
|
|
111
130
|
if (!isUnmounted()) {
|
|
112
131
|
setResponding(false);
|
|
@@ -143,7 +162,7 @@ export default function MessageInputAIStream(props) {
|
|
|
143
162
|
});
|
|
144
163
|
}
|
|
145
164
|
};
|
|
146
|
-
|
|
165
|
+
useSleep();
|
|
147
166
|
let container;
|
|
148
167
|
const canSend = text.trim().length && !responding && !attachmentInput.uploading;
|
|
149
168
|
if (mode === 'text') {
|
|
@@ -166,7 +185,7 @@ export default function MessageInputAIStream(props) {
|
|
|
166
185
|
}]);
|
|
167
186
|
}
|
|
168
187
|
},
|
|
169
|
-
maxLength: 200,
|
|
188
|
+
maxLength: props.maxTextLength || 200,
|
|
170
189
|
placeholderStyle: "color: var(--app-B1-N4)",
|
|
171
190
|
onFocus: () => {
|
|
172
191
|
setMoreOpen(false);
|
|
@@ -203,8 +222,8 @@ export default function MessageInputAIStream(props) {
|
|
|
203
222
|
"data-testid": "t-agent-message-input-button-main",
|
|
204
223
|
onClick: async () => {
|
|
205
224
|
if (responding) {
|
|
206
|
-
if (
|
|
207
|
-
|
|
225
|
+
if (abortRef.current) {
|
|
226
|
+
abortRef.current.controller.abort('User abort');
|
|
208
227
|
}
|
|
209
228
|
} else if (isMore) {
|
|
210
229
|
openMoreClick();
|
|
@@ -226,16 +245,28 @@ export default function MessageInputAIStream(props) {
|
|
|
226
245
|
if (attachmentInput.uploading) {
|
|
227
246
|
return;
|
|
228
247
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
248
|
+
const abort = abortRef.current;
|
|
249
|
+
if (responding && abort) {
|
|
250
|
+
abort.controller.abort('User abort');
|
|
251
|
+
// 中断后等待上次结束再开始
|
|
252
|
+
await abort.promise;
|
|
233
253
|
}
|
|
234
254
|
const emitter = new Emitter();
|
|
235
|
-
|
|
255
|
+
const id = setTimeout(() => {
|
|
256
|
+
if (r.recording) {
|
|
257
|
+
ty.showToast({
|
|
258
|
+
icon: 'none',
|
|
259
|
+
title: t('t-agent.input.asr.error.timeout')
|
|
260
|
+
});
|
|
261
|
+
r.confirm();
|
|
262
|
+
}
|
|
263
|
+
}, props.maxAudioMs || 30000);
|
|
264
|
+
const r = {
|
|
236
265
|
startAt: Date.now(),
|
|
237
266
|
recording: true,
|
|
238
267
|
confirm: () => {
|
|
268
|
+
r.recording = false;
|
|
269
|
+
clearTimeout(id);
|
|
239
270
|
emitter.dispatchEvent(new EmitterEvent('confirm'));
|
|
240
271
|
setRecord({
|
|
241
272
|
startAt: 0,
|
|
@@ -245,6 +276,8 @@ export default function MessageInputAIStream(props) {
|
|
|
245
276
|
});
|
|
246
277
|
},
|
|
247
278
|
cancel: () => {
|
|
279
|
+
r.recording = false;
|
|
280
|
+
clearTimeout(id);
|
|
248
281
|
emitter.dispatchEvent(new EmitterEvent('cancel'));
|
|
249
282
|
setRecord({
|
|
250
283
|
startAt: 0,
|
|
@@ -253,7 +286,8 @@ export default function MessageInputAIStream(props) {
|
|
|
253
286
|
cancel: null
|
|
254
287
|
});
|
|
255
288
|
}
|
|
256
|
-
}
|
|
289
|
+
};
|
|
290
|
+
setRecord(r);
|
|
257
291
|
emitter.addEventListener('error', () => {
|
|
258
292
|
ty.showToast({
|
|
259
293
|
icon: 'error',
|
|
@@ -286,8 +320,8 @@ export default function MessageInputAIStream(props) {
|
|
|
286
320
|
setMoreOpen(false);
|
|
287
321
|
},
|
|
288
322
|
onAbort: () => {
|
|
289
|
-
if (responding &&
|
|
290
|
-
|
|
323
|
+
if (responding && abortRef.current) {
|
|
324
|
+
abortRef.current.controller.abort('User abort');
|
|
291
325
|
}
|
|
292
326
|
}
|
|
293
327
|
});
|
|
@@ -1 +1,10 @@
|
|
|
1
|
-
<svg
|
|
1
|
+
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g clip-path="url(#clip0_1_4)">
|
|
3
|
+
<path d="M0 0H200V200H0V0.015V0ZM20.009 20.009V179.991H179.991V20.024H20.009V20.009ZM59.997 43.7078L157.492 100L59.997 156.292V43.7078ZM80.006 78.3561V121.659L117.504 100L80.006 78.3561Z" fill="#3D3D3D"/>
|
|
4
|
+
</g>
|
|
5
|
+
<defs>
|
|
6
|
+
<clipPath id="clip0_1_4">
|
|
7
|
+
<rect width="200" height="200" fill="white"/>
|
|
8
|
+
</clipPath>
|
|
9
|
+
</defs>
|
|
10
|
+
</svg>
|
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-10",
|
|
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": "a17ba630a9ec71afe6a0fc46c427f664d46acf82"
|
|
44
44
|
}
|