@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.
@@ -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 acRef = useRef(null);
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 ac = new AbortController();
74
- acRef.current = ac;
75
- ac.signal.addEventListener('abort', () => {
76
- if (acRef.current === ac) {
77
- acRef.current = null;
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, ac.signal);
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 ac = new AbortController();
101
- acRef.current = ac;
102
- ac.signal.addEventListener('abort', () => {
103
- if (acRef.current === ac) {
104
- acRef.current = null;
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, ac.signal);
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
- const sleep = useSleep();
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 (acRef.current) {
207
- acRef.current.abort('User abort');
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
- if (responding && acRef.current) {
230
- acRef.current.abort('User abort');
231
- // 中断后等待一会再开始
232
- await sleep(50);
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 && acRef.current) {
298
- acRef.current.abort('User abort');
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('image', 1);
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
- try {
115
- const sourceType = await new Promise(resolve => {
116
- ty.showActionSheet({
117
- itemList: [t('t-agent.input.upload.source-type.camera'), t('t-agent.input.upload.source-type.album')],
118
- success: _ref => {
119
- let {
120
- tapIndex
121
- } = _ref;
122
- if (tapIndex === 0) {
123
- ty.authorize({
124
- scope: 'scope.camera',
125
- success() {
126
- resolve('camera');
127
- },
128
- fail() {
129
- ty.showToast({
130
- title: t('t-agent.input.upload.source-type.camera.require-permission'),
131
- icon: 'none'
132
- });
133
- resolve('');
134
- }
135
- });
136
- } else if (tapIndex === 1) {
137
- resolve('album');
138
- } else {
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
- if (!sourceType) {
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 {
@@ -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;
@@ -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',
@@ -49,6 +49,7 @@
49
49
  display: -webkit-box;
50
50
  -webkit-line-clamp: 2; /* 控制显示行数 */
51
51
  -webkit-box-orient: vertical;
52
+ line-height: 33rpx;
52
53
  height: 66rpx;
53
54
 
54
55
 
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-9",
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": "cfe00de066a09da4a63e7c965d42a622f95cda4c"
43
+ "gitHead": "dd56b31b0095fb82d0fb5d4f58581d00ae2970ee"
44
44
  }