@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.
@@ -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 { useAgentSessionValue, useAttachmentInput, useChatAgent, useEmitEvent, useIsUnmounted, useOnEvent, useTranslate } from '../../hooks';
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 acRef = useRef(null);
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 [hasMore] = useAgentSessionValue('multiModal');
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 ac = new AbortController();
74
- acRef.current = ac;
75
- ac.signal.addEventListener('abort', () => {
76
- if (acRef.current === ac) {
77
- acRef.current = null;
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, ac.signal);
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 ac = new AbortController();
101
- acRef.current = ac;
102
- ac.signal.addEventListener('abort', () => {
103
- if (acRef.current === ac) {
104
- acRef.current = null;
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, ac.signal);
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
- const sleep = useSleep();
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 (acRef.current) {
207
- acRef.current.abort('User abort');
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
- if (responding && acRef.current) {
230
- acRef.current.abort('User abort');
231
- // 中断后等待一会再开始
232
- await sleep(50);
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
- setRecord({
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 && acRef.current) {
290
- acRef.current.abort('User abort');
323
+ if (responding && abortRef.current) {
324
+ abortRef.current.controller.abort('User abort');
291
325
  }
292
326
  }
293
327
  });
@@ -1 +1,10 @@
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>
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>
@@ -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',
@@ -31,6 +31,8 @@
31
31
  .t-agent-bubble-tile-error-alert-text {
32
32
  flex: 0 1 auto;
33
33
  min-width: 0;
34
+ word-break: normal;
35
+ overflow-wrap: break-word;
34
36
  }
35
37
  }
36
38
 
@@ -42,7 +42,8 @@
42
42
  }
43
43
 
44
44
  .t-agent-file-tile-filename {
45
- word-break: break-all; /* 允许单词内断行 */
45
+ word-break: normal;
46
+ overflow-wrap: break-word;
46
47
 
47
48
  /* 限制两行 */
48
49
  display: -webkit-box;
@@ -1,3 +1,5 @@
1
1
  .t-agent-text-tile {
2
2
  display: inline;
3
+ word-break: normal;
4
+ overflow-wrap: break-word;
3
5
  }
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-8",
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": "586a5010616634911334e31f9c1e1993231dfb95"
43
+ "gitHead": "a17ba630a9ec71afe6a0fc46c427f664d46acf82"
44
44
  }