@ray-js/t-agent-ui-ray 0.2.6-beta-6 → 0.2.6-beta-8

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.
@@ -11,7 +11,6 @@ import "core-js/modules/esnext.iterator.map.js";
11
11
  import "core-js/modules/web.dom-collections.iterator.js";
12
12
  import MarkdownIt from 'markdown-it';
13
13
  import { full as emoji } from 'markdown-it-emoji';
14
- import footnote from 'markdown-it-footnote';
15
14
  import { generateId } from '@ray-js/t-agent';
16
15
  const md = new MarkdownIt();
17
16
  function addClassToTag(MD) {
@@ -25,6 +24,10 @@ function addClassToTag(MD) {
25
24
  token.attrPush(['height', 'auto']);
26
25
  return;
27
26
  }
27
+ if (token.tag === 'hr') {
28
+ token.attrPush(['class', "h2w__hr"]);
29
+ return;
30
+ }
28
31
  if (token.children && token.children.length) {
29
32
  token.children.forEach(addClass);
30
33
  return;
@@ -258,4 +261,4 @@ export class BlockParser {
258
261
  return position;
259
262
  }
260
263
  }
261
- md.use(addClassToTag).use(tableWrapper).use(fenceWrapper).use(emoji).use(footnote).use(BlockParser.plugin);
264
+ md.use(addClassToTag).use(tableWrapper).use(fenceWrapper).use(emoji).use(BlockParser.plugin);
@@ -321,8 +321,10 @@
321
321
  }
322
322
 
323
323
  .h2w__hr {
324
- height: 8rpx;
325
- margin: 40rpx 0;
324
+ border-top: none;
325
+ border-left: none;
326
+ border-right: none;
327
+ border-bottom: 2rpx solid;
326
328
  }
327
329
 
328
330
  /**荧光标记**/
@@ -50,7 +50,7 @@
50
50
  }
51
51
 
52
52
  .h2w__dark .h2w__hr {
53
- background-color:#242424;
53
+ border-color: #3d3d3d;
54
54
  }
55
55
 
56
56
  .h2w__dark .h2w__mark {
@@ -50,7 +50,7 @@
50
50
  }
51
51
 
52
52
  .h2w__light .h2w__hr {
53
- background-color: #eee;
53
+ border-color: #eee;
54
54
  }
55
55
 
56
56
  .h2w__light .h2w__mark {
@@ -95,6 +95,7 @@ declare const _default: {
95
95
  't-agent.error.stream-exists': string;
96
96
  't-agent.error.timeout': string;
97
97
  't-agent.error.asr-empty': string;
98
+ 't-agent.message.delete.button': string;
98
99
  };
99
100
  'zh-Hant': {
100
101
  't-agent.build-in.button.create_scene_manually': string;
@@ -192,6 +193,7 @@ declare const _default: {
192
193
  't-agent.error.stream-exists': string;
193
194
  't-agent.error.timeout': string;
194
195
  't-agent.error.asr-empty': string;
196
+ 't-agent.message.delete.button': string;
195
197
  };
196
198
  en: {
197
199
  't-agent.build-in.button.create_scene_manually': string;
@@ -275,6 +277,7 @@ declare const _default: {
275
277
  't-agent.input.upload.image.max-reached': string;
276
278
  't-agent.input.upload.video.max-reached': string;
277
279
  't-agent.file-tile.unknown-filename': string;
280
+ 't-agent.error.unknown-error': string;
278
281
  't-agent.error.network-offline': string;
279
282
  't-agent.error.invalid-params': string;
280
283
  't-agent.error.session-create-failed': string;
@@ -288,6 +291,7 @@ declare const _default: {
288
291
  't-agent.error.stream-exists': string;
289
292
  't-agent.error.timeout': string;
290
293
  't-agent.error.asr-empty': string;
294
+ 't-agent.message.delete.button': string;
291
295
  };
292
296
  ja: {
293
297
  't-agent.build-in.button.create_scene_manually': string;
@@ -385,6 +389,7 @@ declare const _default: {
385
389
  't-agent.error.stream-exists': string;
386
390
  't-agent.error.timeout': string;
387
391
  't-agent.error.asr-empty': string;
392
+ 't-agent.message.delete.button': string;
388
393
  };
389
394
  de: {
390
395
  't-agent.build-in.button.create_scene_manually': string;
@@ -482,6 +487,7 @@ declare const _default: {
482
487
  't-agent.error.stream-exists': string;
483
488
  't-agent.error.timeout': string;
484
489
  't-agent.error.asr-empty': string;
490
+ 't-agent.message.clear-history.button': string;
485
491
  };
486
492
  fr: {
487
493
  't-agent.build-in.button.create_scene_manually': string;
@@ -579,6 +585,7 @@ declare const _default: {
579
585
  't-agent.error.stream-exists': string;
580
586
  't-agent.error.timeout': string;
581
587
  't-agent.error.asr-empty': string;
588
+ 't-agent.message.clear-history.button': string;
582
589
  };
583
590
  es: {
584
591
  't-agent.build-in.button.create_scene_manually': string;
@@ -676,6 +683,7 @@ declare const _default: {
676
683
  't-agent.error.stream-exists': string;
677
684
  't-agent.error.timeout': string;
678
685
  't-agent.error.asr-empty': string;
686
+ 't-agent.message.clear-history.button': string;
679
687
  };
680
688
  it: {
681
689
  't-agent.build-in.button.create_scene_manually': string;
@@ -773,6 +781,7 @@ declare const _default: {
773
781
  't-agent.error.stream-exists': string;
774
782
  't-agent.error.timeout': string;
775
783
  't-agent.error.asr-empty': string;
784
+ 't-agent.message.clear-history.button': string;
776
785
  };
777
786
  };
778
787
  export default _default;
@@ -94,7 +94,8 @@ export default {
94
94
  't-agent.error.event-no-data-code': '消息发送异常,请稍后再试',
95
95
  't-agent.error.stream-exists': '消息发送异常,请稍后再试',
96
96
  't-agent.error.timeout': '发送超时',
97
- 't-agent.error.asr-empty': '语音识别结果为空'
97
+ 't-agent.error.asr-empty': '语音识别结果为空',
98
+ 't-agent.message.delete.button': '删除消息'
98
99
  },
99
100
  'zh-Hant': {
100
101
  't-agent.build-in.button.create_scene_manually': '手動創建場景',
@@ -191,7 +192,8 @@ export default {
191
192
  't-agent.error.event-no-data-code': '消息發送異常,請稍後再試',
192
193
  't-agent.error.stream-exists': '消息發送異常,請稍後再試',
193
194
  't-agent.error.timeout': '發送超時',
194
- 't-agent.error.asr-empty': '語音識別結果為空'
195
+ 't-agent.error.asr-empty': '語音識別結果為空',
196
+ 't-agent.message.delete.button': '刪除消息'
195
197
  },
196
198
  en: {
197
199
  't-agent.build-in.button.create_scene_manually': 'Create Scene Manually',
@@ -216,7 +218,7 @@ export default {
216
218
  't-agent.input.asr.ptt': 'Press & hold to talk',
217
219
  't-agent.input.asr.error.too-short': 'Speaking time too short',
218
220
  't-agent.input.asr.error.empty': 'No text recognized from voice',
219
- 't-agent.input.asr.error.unknown': 'Text recognized failed',
221
+ 't-agent.input.asr.error.unknown': 'Voice recognition failed',
220
222
  't-agent.input.asr.error.timeout': 'Voice recognition has reached time limit, sending directly',
221
223
  't-agent.message.feedback.success': 'Feedback Successful',
222
224
  't-agent.message.bubble.aborted': 'User Aborted',
@@ -275,6 +277,7 @@ export default {
275
277
  't-agent.input.upload.image.max-reached': 'Image upload limit reached',
276
278
  't-agent.input.upload.video.max-reached': 'Video upload limit reached',
277
279
  't-agent.file-tile.unknown-filename': 'File',
280
+ 't-agent.error.unknown-error': 'Unknown error',
278
281
  't-agent.error.network-offline': 'Network disconnected, please check your connection',
279
282
  't-agent.error.invalid-params': 'Invalid parameters, please try again',
280
283
  't-agent.error.session-create-failed': 'Connection failed, please try again',
@@ -287,7 +290,8 @@ export default {
287
290
  't-agent.error.event-no-data-code': 'Message sending exception, please try again later',
288
291
  't-agent.error.stream-exists': 'Message sending exception, please try again later',
289
292
  't-agent.error.timeout': 'Sending timeout',
290
- 't-agent.error.asr-empty': 'Voice recognition result is empty'
293
+ 't-agent.error.asr-empty': 'Voice recognition result is empty',
294
+ 't-agent.message.delete.button': 'Delete Message'
291
295
  },
292
296
  ja: {
293
297
  't-agent.build-in.button.create_scene_manually': 'シーンを手動で作成',
@@ -384,7 +388,8 @@ export default {
384
388
  't-agent.error.event-no-data-code': 'メッセージ送信例外、後でもう一度お試しください',
385
389
  't-agent.error.stream-exists': 'メッセージ送信例外、後でもう一度お試しください',
386
390
  't-agent.error.timeout': '送信タイムアウト',
387
- 't-agent.error.asr-empty': '音声認識結果が空です'
391
+ 't-agent.error.asr-empty': '音声認識結果が空です',
392
+ 't-agent.message.delete.button': 'メッセージを削除'
388
393
  },
389
394
  de: {
390
395
  't-agent.build-in.button.create_scene_manually': 'Szene manuell erstellen',
@@ -481,7 +486,8 @@ export default {
481
486
  't-agent.error.event-no-data-code': 'Nachrichtensendeausnahme, bitte später erneut versuchen',
482
487
  't-agent.error.stream-exists': 'Nachrichtensendeausnahme, bitte später erneut versuchen',
483
488
  't-agent.error.timeout': 'Sendetimeout',
484
- 't-agent.error.asr-empty': 'Spracherkennungsergebnis ist leer'
489
+ 't-agent.error.asr-empty': 'Spracherkennungsergebnis ist leer',
490
+ 't-agent.message.clear-history.button': 'Verlauf löschen'
485
491
  },
486
492
  fr: {
487
493
  't-agent.build-in.button.create_scene_manually': 'Créer une scène manuellement',
@@ -578,7 +584,8 @@ export default {
578
584
  't-agent.error.event-no-data-code': "Exception d'envoi de message, veuillez réessayer plus tard",
579
585
  't-agent.error.stream-exists': "Exception d'envoi de message, veuillez réessayer plus tard",
580
586
  't-agent.error.timeout': "Délai d'envoi dépassé",
581
- 't-agent.error.asr-empty': 'Le résultat de la reconnaissance vocale est vide'
587
+ 't-agent.error.asr-empty': 'Le résultat de la reconnaissance vocale est vide',
588
+ 't-agent.message.clear-history.button': "Effacer l'historique"
582
589
  },
583
590
  es: {
584
591
  't-agent.build-in.button.create_scene_manually': 'Crear escena manualmente',
@@ -675,7 +682,8 @@ export default {
675
682
  't-agent.error.event-no-data-code': 'Excepción al enviar mensaje, por favor intenta más tarde',
676
683
  't-agent.error.stream-exists': 'Excepción al enviar mensaje, por favor intenta más tarde',
677
684
  't-agent.error.timeout': 'Tiempo de envío agotado',
678
- 't-agent.error.asr-empty': 'El resultado del reconocimiento de voz está vacío'
685
+ 't-agent.error.asr-empty': 'El resultado del reconocimiento de voz está vacío',
686
+ 't-agent.message.clear-history.button': 'Borrar historial'
679
687
  },
680
688
  it: {
681
689
  't-agent.build-in.button.create_scene_manually': 'Crea scena manualmente',
@@ -772,6 +780,7 @@ export default {
772
780
  't-agent.error.event-no-data-code': "Eccezione nell'invio del messaggio, riprova più tardi",
773
781
  't-agent.error.stream-exists': "Eccezione nell'invio del messaggio, riprova più tardi",
774
782
  't-agent.error.timeout': 'Timeout di invio',
775
- 't-agent.error.asr-empty': 'Il risultato del riconoscimento vocale è vuoto'
783
+ 't-agent.error.asr-empty': 'Il risultato del riconoscimento vocale è vuoto',
784
+ 't-agent.message.clear-history.button': 'Cancella cronologia'
776
785
  }
777
786
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/t-agent-ui-ray",
3
- "version": "0.2.6-beta-6",
3
+ "version": "0.2.6-beta-8",
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": "2f18554f908818b62ab24ead773d4e74fd5ef7fc"
43
+ "gitHead": "6fc9a8195706d72c2bab10479e8d65a26854bdde"
44
44
  }
@@ -1,18 +0,0 @@
1
- import '../index.less';
2
- import React from 'react';
3
- interface Props {
4
- text: string;
5
- setText: (text: string) => void;
6
- onBack: () => void;
7
- sendDisabled: boolean;
8
- responding: boolean;
9
- onSend: () => Promise<any>;
10
- onError: (error: Error) => void;
11
- onMoreClick: () => void;
12
- closeMore: () => void;
13
- moreOpen: boolean;
14
- hasMore: boolean;
15
- onAbort: () => void;
16
- }
17
- export default function AsrInput(props: Props): React.JSX.Element;
18
- export {};
@@ -1,89 +0,0 @@
1
- import '../index.less';
2
- import { Button, Text, View, Textarea } from '@ray-js/components';
3
- import React from 'react';
4
- import cx from 'clsx';
5
- import { useAsrInput } from './useAsrInput';
6
- export default function AsrInput(props) {
7
- const {
8
- responding,
9
- sendDisabled,
10
- onMoreClick,
11
- moreOpen,
12
- closeMore,
13
- hasMore,
14
- text,
15
- setText,
16
- onAbort
17
- } = props;
18
- const {
19
- state,
20
- press,
21
- release,
22
- clear,
23
- currentText,
24
- incomingText
25
- } = useAsrInput(props);
26
- return /*#__PURE__*/React.createElement(View, {
27
- className: "t-agent-message-input-voice-bar"
28
- }, (state === 'recording' || state === 'pending') && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(View, {
29
- className: "t-agent-message-input-voice-mask"
30
- }), /*#__PURE__*/React.createElement(View, {
31
- className: "t-agent-message-input-voice-bubble",
32
- "data-testid": "t-agent-message-input-asr-bubble"
33
- }, state === 'recording' ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, currentText.endsWith(' ') ? /*#__PURE__*/React.createElement(React.Fragment, null, currentText, "\xA0") : currentText), /*#__PURE__*/React.createElement(Text, {
34
- className: "t-agent-message-input-voice-bubble-predicted"
35
- }, incomingText)) : /*#__PURE__*/React.createElement(Textarea, {
36
- "data-testid": "t-agent-message-input-asr-bubble-input",
37
- autoHeight: true,
38
- maxLength: 2048,
39
- className: "t-agent-message-input-voice-bubble-input",
40
- value: text,
41
- onInput: event => setText(event.value)
42
- }))), /*#__PURE__*/React.createElement(Button, {
43
- "data-testid": "t-agent-message-input-asr-button-left",
44
- className: cx('t-agent-message-input-button', {
45
- 't-agent-message-input-button-text': state === 'init',
46
- 't-agent-message-input-button-hide': state === 'recording',
47
- 't-agent-message-input-button-clear': state === 'pending'
48
- }),
49
- onClick: () => {
50
- if (state === 'init') {
51
- clear();
52
- props.onBack();
53
- } else if (state === 'pending') {
54
- clear();
55
- }
56
- }
57
- }), /*#__PURE__*/React.createElement(Button, {
58
- "data-testid": "t-agent-message-input-asr-button-middle",
59
- className: "t-agent-message-input-button t-agent-message-input-button-voice-active",
60
- disabled: sendDisabled,
61
- onTouchStart: () => {
62
- closeMore();
63
- press();
64
- },
65
- onClick: release,
66
- onTouchCancel: release,
67
- onTouchEnd: release
68
- }), /*#__PURE__*/React.createElement(Button, {
69
- disabled: sendDisabled,
70
- "data-testid": "t-agent-message-input-asr-button-right",
71
- className: cx('t-agent-message-input-button', {
72
- 't-agent-message-input-button-voice-send': state === 'pending',
73
- 't-agent-message-input-button-hide': (state === 'recording' || !hasMore && state !== 'pending') && !responding,
74
- 't-agent-message-input-button-more': state === 'init' && hasMore,
75
- 't-agent-message-input-button-more-open': state === 'init' && moreOpen,
76
- 't-agent-message-input-button-stop': state === 'init' && responding
77
- }),
78
- onClick: () => {
79
- if (responding) {
80
- onAbort();
81
- } else if (state === 'init' && hasMore) {
82
- onMoreClick();
83
- } else {
84
- props.onSend();
85
- clear();
86
- }
87
- }
88
- }));
89
- }
@@ -1,30 +0,0 @@
1
- import { Emitter } from '@ray-js/t-agent';
2
- import { AsrListenerManager } from '../../utils/ttt';
3
- export interface DetectResult {
4
- /** managerId */
5
- managerId: number;
6
- /** 拾音状态 0. 未开启 1.进行中 2.结束 3.发送错误 */
7
- state: number;
8
- /** 语言转换内容 */
9
- text: string;
10
- /** 错误码 0. 录音时间太短 */
11
- errorCode: number;
12
- }
13
- export declare enum AsrDetectResultState {
14
- WAIT = 0,
15
- MID = 1,
16
- END = 2,
17
- ERROR = 3
18
- }
19
- export declare class Asr {
20
- static manager: AsrListenerManager | null;
21
- static emitter: Emitter;
22
- static authorize(): Promise<boolean>;
23
- static createManager(): Promise<AsrListenerManager>;
24
- static listener: (detail: DetectResult) => void;
25
- static dispose: () => void;
26
- static detect(callback: (params: DetectResult) => void): {
27
- start(): Promise<void>;
28
- stop(): Promise<void>;
29
- };
30
- }
@@ -1,126 +0,0 @@
1
- import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
2
- var _Asr;
3
- import "core-js/modules/es.json.stringify.js";
4
- import { Emitter, EmitterEvent } from '@ray-js/t-agent';
5
- import logger from '../../logger';
6
- import { authorize, getAppInfo, getAsrListenerManager, getCurrentHomeInfo } from '../../utils/ttt';
7
- export let AsrDetectResultState = /*#__PURE__*/function (AsrDetectResultState) {
8
- AsrDetectResultState[AsrDetectResultState["WAIT"] = 0] = "WAIT";
9
- AsrDetectResultState[AsrDetectResultState["MID"] = 1] = "MID";
10
- AsrDetectResultState[AsrDetectResultState["END"] = 2] = "END";
11
- AsrDetectResultState[AsrDetectResultState["ERROR"] = 3] = "ERROR";
12
- return AsrDetectResultState;
13
- }({});
14
- export class Asr {
15
- // 录音权限
16
- static authorize() {
17
- return authorize({
18
- scope: 'scope.record'
19
- });
20
- }
21
- static async createManager() {
22
- if (Asr.manager) {
23
- return Asr.manager;
24
- }
25
- const {
26
- homeId
27
- } = await getCurrentHomeInfo();
28
- const systemInfo = ty.getSystemInfoSync();
29
- const appInfo = await getAppInfo();
30
- const isCnApp = appInfo.regionCode === 'AY';
31
- const lang = systemInfo.language;
32
- const params = {
33
- homeId,
34
- sampleRate: isCnApp ? 16000 : 8000,
35
- channels: 1,
36
- codec: isCnApp ? 0 : 1,
37
- options: JSON.stringify({
38
- format: isCnApp ? 'wav' : 'mulaw',
39
- lang,
40
- channel: '1',
41
- sampleRate: isCnApp ? '16000' : '8000',
42
- realTimeAsr: 'true',
43
- 'asr.only': 'true'
44
- })
45
- };
46
- logger.debug('Asr createManager', params);
47
- Asr.manager = await getAsrListenerManager(params);
48
- Asr.manager.onDetect(Asr.listener);
49
- return Asr.manager;
50
- }
51
- static detect(callback) {
52
- const listener = event => {
53
- var _event$detail;
54
- callback(event.detail);
55
- if (((_event$detail = event.detail) === null || _event$detail === void 0 ? void 0 : _event$detail.state) === AsrDetectResultState.END) {
56
- Asr.emitter.removeEventListener('result', listener);
57
- }
58
- };
59
- const promise = Asr.createManager();
60
- Asr.emitter.addEventListener('result', listener);
61
- return {
62
- start() {
63
- logger.debug('Asr detect start');
64
- return new Promise((resolve, reject) => {
65
- promise.then(manager => {
66
- manager.getAsrActive({
67
- success: _ref => {
68
- let {
69
- isActive
70
- } = _ref;
71
- logger.debug('Asr manager.getAsrActive', isActive);
72
- if (!isActive) {
73
- manager.startDetect({
74
- success: function (res) {
75
- logger.debug('Asr startDetect success', res);
76
- resolve();
77
- },
78
- fail: function (err) {
79
- logger.error('Asr startDetect fail', err);
80
- reject(err);
81
- }
82
- });
83
- } else {
84
- resolve();
85
- }
86
- },
87
- failure: params => {
88
- logger.error('Asr manager.getAsrActive failure', params);
89
- reject(params);
90
- }
91
- });
92
- });
93
- });
94
- },
95
- async stop() {
96
- const manager = await promise;
97
- logger.debug('Asr detect stop');
98
- await new Promise((resolve, reject) => {
99
- manager.stopDetect({
100
- success: resolve,
101
- fail: reject
102
- });
103
- });
104
- }
105
- };
106
- }
107
- }
108
- _Asr = Asr;
109
- _defineProperty(Asr, "manager", null);
110
- _defineProperty(Asr, "emitter", new Emitter());
111
- _defineProperty(Asr, "listener", detail => {
112
- const {
113
- text,
114
- state
115
- } = detail;
116
- logger.debug('Asr listener', detail);
117
- _Asr.emitter.dispatchEvent(new EmitterEvent('result', {
118
- detail
119
- }));
120
- });
121
- _defineProperty(Asr, "dispose", () => {
122
- if (_Asr.manager) {
123
- _Asr.manager.offDetect(_Asr.listener);
124
- _Asr.manager = null;
125
- }
126
- });
@@ -1,11 +0,0 @@
1
- import '../index.less';
2
- import React from 'react';
3
- interface Props {
4
- className?: string;
5
- renderTop?: React.ReactNode;
6
- placeholder?: string;
7
- style?: React.CSSProperties;
8
- multiModal?: boolean;
9
- }
10
- export default function MessageInputAssistant(props: Props): React.JSX.Element;
11
- export {};
@@ -1,325 +0,0 @@
1
- import "core-js/modules/es.string.trim.js";
2
- import "core-js/modules/esnext.iterator.constructor.js";
3
- import "core-js/modules/esnext.iterator.filter.js";
4
- import "core-js/modules/esnext.iterator.map.js";
5
- import "core-js/modules/web.dom-collections.iterator.js";
6
- import '../index.less';
7
- import { Button, View } from '@ray-js/components';
8
- import React, { useRef, useState } from 'react';
9
- import { Image, Input, ScrollView } from '@ray-js/ray';
10
- import cx from 'clsx';
11
- import PrivateImage from '../../PrivateImage';
12
- import imageSvg from '../icons/image.svg';
13
- import videoSvg from '../icons/video.svg';
14
- import loadingSvg from '../icons/loading.svg';
15
- import closeCircleSvg from '../icons/close-circle.svg';
16
- import { AbortController } from '../../utils/abort';
17
- import { useAttachmentInput, useChatAgent, useEmitEvent, useIsUnmounted, useOnEvent, useRenderOptions, useTranslate } from '../../hooks';
18
- import AsrInput from './AsrInput';
19
- import { authorize } from '../../utils/ttt';
20
- export default function MessageInputAssistant(props) {
21
- const [moreOpen, setMoreOpen] = useState(false);
22
- const t = useTranslate();
23
- const [text, setText] = useState('');
24
- const [asrText, setAsrText] = useState('');
25
- const attachmentInput = useAttachmentInput();
26
- const {
27
- uploading,
28
- uploaded,
29
- setUploaded,
30
- upload
31
- } = attachmentInput;
32
- const acRef = useRef(null);
33
- const [responding, setResponding] = useState(false);
34
- const [mode, setMode] = useState('text');
35
- const agent = useChatAgent();
36
- const emitEvent = useEmitEvent();
37
- const isUnmounted = useIsUnmounted();
38
- useOnEvent('networkChange', _ref => {
39
- let {
40
- online
41
- } = _ref;
42
- if (!online && responding) {
43
- setResponding(false);
44
- }
45
- });
46
- const hasMore = !!props.multiModal;
47
- const isMore = !text.trim().length && hasMore;
48
- const send = async inputBlocks => {
49
- if (!(inputBlocks !== null && inputBlocks !== void 0 && inputBlocks.length) || attachmentInput.uploading || responding) {
50
- return;
51
- }
52
- setUploaded([]);
53
- setText('');
54
- setResponding(true);
55
- const ac = new AbortController();
56
- acRef.current = ac;
57
- ac.signal.addEventListener('abort', () => {
58
- if (acRef.current === ac) {
59
- acRef.current = null;
60
- }
61
- setResponding(false);
62
- });
63
- try {
64
- await agent.pushInputBlocks(inputBlocks, ac.signal);
65
- } finally {
66
- if (!isUnmounted()) {
67
- setResponding(false);
68
- }
69
- }
70
- };
71
- useOnEvent('sendMessage', async _ref2 => {
72
- let {
73
- blocks
74
- } = _ref2;
75
- if (uploading || responding) {
76
- return;
77
- }
78
- setText('');
79
- setMoreOpen(false);
80
- setUploaded([]);
81
- setResponding(true);
82
- const ac = new AbortController();
83
- acRef.current = ac;
84
- ac.signal.addEventListener('abort', () => {
85
- if (acRef.current === ac) {
86
- acRef.current = null;
87
- }
88
- setResponding(false);
89
- });
90
- try {
91
- await agent.pushInputBlocks(blocks, ac.signal);
92
- } finally {
93
- if (!isUnmounted()) {
94
- setResponding(false);
95
- }
96
- }
97
- });
98
- useOnEvent('setInputBlocks', async _ref3 => {
99
- let {
100
- blocks
101
- } = _ref3;
102
- if (uploading || responding) {
103
- return;
104
- }
105
- if (mode !== 'text') {
106
- setMode('text');
107
- }
108
- attachmentInput.loadBlocks(blocks);
109
- let t = '';
110
- for (const block of blocks) {
111
- if (block.type === 'text') {
112
- t = block.text;
113
- }
114
- }
115
- if (t) {
116
- setText(t);
117
- }
118
- setMoreOpen(false);
119
- });
120
- const openMoreClick = () => {
121
- setMoreOpen(!moreOpen);
122
- if (!moreOpen) {
123
- emitEvent('scrollToBottom', {
124
- animation: true
125
- });
126
- }
127
- };
128
- const {
129
- getStaticResourceBizType
130
- } = useRenderOptions();
131
- let container;
132
- if (mode === 'text') {
133
- container = /*#__PURE__*/React.createElement(View, {
134
- className: "t-agent-message-input-text-bar"
135
- }, /*#__PURE__*/React.createElement(View, {
136
- className: "t-agent-message-input-text-group"
137
- }, /*#__PURE__*/React.createElement(Input, {
138
- confirmType: "send",
139
- value: text,
140
- onInput: event => setText(event.detail.value),
141
- placeholder: props.placeholder,
142
- "data-testid": "t-agent-message-input-text-inner",
143
- className: "t-agent-message-input-text-inner",
144
- onConfirm: () => {
145
- if (text.trim().length) {
146
- send([...attachmentInput.blocks, {
147
- type: 'text',
148
- text
149
- }]);
150
- }
151
- },
152
- maxLength: 200,
153
- placeholderStyle: "color: var(--app-B1-N4)",
154
- onFocus: () => {
155
- setMoreOpen(false);
156
- emitEvent('scrollToBottom', {
157
- animation: true
158
- });
159
- }
160
- }), /*#__PURE__*/React.createElement(Button, {
161
- "data-testid": "t-agent-message-input-button-asr",
162
- onClick: async () => {
163
- const auth = await authorize({
164
- scope: 'scope.record'
165
- });
166
- if (!auth) {
167
- ty.showToast({
168
- icon: 'none',
169
- title: t('t-agent.input.voice.require-permission')
170
- });
171
- return;
172
- }
173
- setText('');
174
- setMode('voice');
175
- },
176
- className: "t-agent-message-input-button t-agent-message-input-button-voice"
177
- })), /*#__PURE__*/React.createElement(Button, {
178
- disabled: !isMore && uploading,
179
- className: cx('t-agent-message-input-button', {
180
- 't-agent-message-input-button-more': isMore,
181
- 't-agent-message-input-button-more-open': moreOpen && isMore,
182
- 't-agent-message-input-button-send': !isMore && !responding,
183
- 't-agent-message-input-button-stop': responding
184
- }),
185
- "data-testid": "t-agent-message-input-button-main",
186
- onClick: async () => {
187
- if (responding) {
188
- if (acRef.current) {
189
- acRef.current.abort('User abort');
190
- }
191
- } else if (isMore) {
192
- openMoreClick();
193
- } else if (text.trim().length) {
194
- await send([...attachmentInput.blocks, {
195
- type: 'text',
196
- text
197
- }]);
198
- }
199
- }
200
- }));
201
- } else {
202
- container = /*#__PURE__*/React.createElement(AsrInput, {
203
- text: asrText,
204
- setText: setAsrText,
205
- onBack: () => {
206
- setText('');
207
- setMode('text');
208
- },
209
- onAbort: () => {
210
- if (acRef.current) {
211
- acRef.current.abort('User abort');
212
- }
213
- },
214
- moreOpen: moreOpen,
215
- closeMore: () => setMoreOpen(false),
216
- onMoreClick: openMoreClick,
217
- responding: responding,
218
- sendDisabled: uploading,
219
- onSend: async () => {
220
- if (asrText) {
221
- await send([...attachmentInput.blocks, {
222
- type: 'text',
223
- text: asrText
224
- }]);
225
- }
226
- },
227
- hasMore: hasMore,
228
- onError: error => {
229
- ty.showToast({
230
- icon: 'error',
231
- title: error.message
232
- });
233
- }
234
- });
235
- }
236
- return /*#__PURE__*/React.createElement(View, {
237
- className: "".concat(props.className || '', " t-agent-message-input"),
238
- style: props.style
239
- }, /*#__PURE__*/React.createElement(View, {
240
- className: "t-agent-message-input-container"
241
- }, props.renderTop, !!uploaded.length && /*#__PURE__*/React.createElement(ScrollView, {
242
- scrollX: true,
243
- scrollY: false,
244
- enableFlex: true,
245
- className: "t-agent-message-input-uploaded-files",
246
- refresherTriggered: false
247
- }, uploaded.map(file => {
248
- let content;
249
- switch (file.type) {
250
- case 'image':
251
- content = /*#__PURE__*/React.createElement(PrivateImage, {
252
- className: "t-agent-message-input-uploaded-file-image",
253
- mode: "aspectFill",
254
- bizType: getStaticResourceBizType(file.url, 'image:view'),
255
- src: file.url
256
- });
257
- break;
258
- case 'video':
259
- content = /*#__PURE__*/React.createElement(PrivateImage, {
260
- bizType: getStaticResourceBizType(file.thumbUrl, 'videoThumb:view'),
261
- className: "t-agent-message-input-uploaded-file-image",
262
- mode: "aspectFill",
263
- src: file.thumbUrl
264
- });
265
- break;
266
- case 'loading':
267
- content = /*#__PURE__*/React.createElement(View, {
268
- className: "t-agent-message-input-uploaded-file-loading",
269
- key: file.id
270
- }, /*#__PURE__*/React.createElement(Image, {
271
- src: loadingSvg,
272
- className: "t-agent-message-input-uploaded-file-loading-icon"
273
- }));
274
- break;
275
- default:
276
- content = null;
277
- }
278
- return /*#__PURE__*/React.createElement(View, {
279
- key: file.id,
280
- className: "t-agent-message-input-uploaded-file"
281
- }, /*#__PURE__*/React.createElement(Image, {
282
- onClick: () => {
283
- setUploaded(prev => prev.filter(f => f.id !== file.id));
284
- },
285
- className: "t-agent-message-input-uploaded-file-delete",
286
- src: closeCircleSvg
287
- }), content);
288
- })), container), /*#__PURE__*/React.createElement(View, {
289
- className: "t-agent-message-input-panel ".concat(moreOpen ? '' : 't-agent-message-input-panel-close')
290
- }, /*#__PURE__*/React.createElement(View, {
291
- className: "t-agent-message-input-panel-content"
292
- }, /*#__PURE__*/React.createElement(Button, {
293
- className: "t-agent-message-input-panel-button",
294
- onClick: async () => {
295
- try {
296
- await upload('image', 1);
297
- } catch (e) {
298
- ty.showToast({
299
- icon: 'error',
300
- title: t('t-agent.input.upload.failed')
301
- });
302
- }
303
- }
304
- }, /*#__PURE__*/React.createElement(View, {
305
- className: "t-agent-message-input-panel-button-icon"
306
- }, /*#__PURE__*/React.createElement(Image, {
307
- src: imageSvg
308
- }))), /*#__PURE__*/React.createElement(Button, {
309
- className: "t-agent-message-input-panel-button",
310
- onClick: async () => {
311
- try {
312
- await upload('video', 1);
313
- } catch (e) {
314
- ty.showToast({
315
- icon: 'error',
316
- title: t('t-agent.input.upload.failed')
317
- });
318
- }
319
- }
320
- }, /*#__PURE__*/React.createElement(View, {
321
- className: "t-agent-message-input-panel-button-icon"
322
- }, /*#__PURE__*/React.createElement(Image, {
323
- src: videoSvg
324
- }))))));
325
- }
@@ -1,37 +0,0 @@
1
- export declare enum AsrErrorCode {
2
- SHORT_TIME = 0
3
- }
4
- export declare class AsrError extends Error {
5
- readonly errorCode: AsrErrorCode;
6
- constructor(errorCode: AsrErrorCode, message?: string);
7
- }
8
- export interface UseAsrInputOptions {
9
- text: string;
10
- setText: (text: string) => void;
11
- onError: (error: Error) => void;
12
- }
13
- /**
14
- init 初始状态,有返回按钮
15
- recording 按住说话按钮收音,无按钮
16
- pending 已有文本,待发送,有清除、发送按钮
17
-
18
- +-------------------------------------------------------+
19
- | clear/send |
20
- ↓ |
21
- +--------+ press +------------+ release +---------+
22
- | init | ----------> | recording | ----------> | pending |
23
- +--------+ +------------+ +---------+
24
- ^ | ^ |
25
- | | | |
26
- | | | |
27
- | empty release | | press |
28
- +-------------------------+ +--------------------------+
29
- */
30
- export declare function useAsrInput(options: UseAsrInputOptions): {
31
- currentText: string;
32
- incomingText: string;
33
- state: "pending" | "recording" | "init";
34
- clear: () => void;
35
- press: () => Promise<void>;
36
- release: () => Promise<void>;
37
- };
@@ -1,131 +0,0 @@
1
- import "core-js/modules/web.dom-collections.iterator.js";
2
- import { useEffect, useRef, useState } from 'react';
3
- import { Asr, AsrDetectResultState } from './asr';
4
- import { useIsUnmounted } from '../../hooks';
5
- export let AsrErrorCode = /*#__PURE__*/function (AsrErrorCode) {
6
- AsrErrorCode[AsrErrorCode["SHORT_TIME"] = 0] = "SHORT_TIME";
7
- return AsrErrorCode;
8
- }({});
9
- export class AsrError extends Error {
10
- constructor(errorCode, message) {
11
- if (!message) {
12
- switch (errorCode) {
13
- case AsrErrorCode.SHORT_TIME:
14
- message = 'Recording time is too short';
15
- break;
16
- default:
17
- message = 'Unknown error';
18
- break;
19
- }
20
- }
21
- super(message);
22
- this.errorCode = errorCode;
23
- }
24
- }
25
- /**
26
- init 初始状态,有返回按钮
27
- recording 按住说话按钮收音,无按钮
28
- pending 已有文本,待发送,有清除、发送按钮
29
-
30
- +-------------------------------------------------------+
31
- | clear/send |
32
- ↓ |
33
- +--------+ press +------------+ release +---------+
34
- | init | ----------> | recording | ----------> | pending |
35
- +--------+ +------------+ +---------+
36
- ^ | ^ |
37
- | | | |
38
- | | | |
39
- | empty release | | press |
40
- +-------------------------+ +--------------------------+
41
- */
42
- export function useAsrInput(options) {
43
- const {
44
- text,
45
- setText,
46
- onError
47
- } = options;
48
- const asrRef = useRef(null);
49
- const [currentText, setCurrentText] = useState('');
50
- const [incomingText, setIncomingText] = useState('');
51
- const isUnmounted = useIsUnmounted();
52
- const [state, setState] = useState('init');
53
- const startAt = useRef(0);
54
- useEffect(() => {
55
- return () => {
56
- if (asrRef.current) {
57
- asrRef.current.stop();
58
- asrRef.current = null;
59
- Asr.dispose();
60
- }
61
- };
62
- }, []);
63
- return {
64
- currentText,
65
- incomingText,
66
- state,
67
- clear: () => {
68
- setState('init');
69
- setText('');
70
- setCurrentText('');
71
- setIncomingText('');
72
- },
73
- press: async () => {
74
- setState('recording');
75
-
76
- // 保存当前文本,用于恢复
77
- const initial = text;
78
-
79
- // 上次识别的结果
80
- let last = '';
81
- setCurrentText(initial);
82
- setIncomingText('');
83
-
84
- // 开始录音时
85
- const asr = Asr.detect(res => {
86
- if (isUnmounted()) {
87
- return;
88
- }
89
- if (res.state === AsrDetectResultState.MID || res.state === AsrDetectResultState.END) {
90
- if (res.text.startsWith(last)) {
91
- const incoming = res.text.slice(last.length);
92
- setIncomingText(incoming);
93
- setCurrentText(initial + last);
94
- last = res.text;
95
- } else {
96
- setIncomingText(res.text);
97
- setCurrentText(initial);
98
- }
99
- setText(initial + res.text);
100
- }
101
- if (res.state === AsrDetectResultState.ERROR) {
102
- onError(new AsrError(res.errorCode));
103
- }
104
- });
105
- try {
106
- await asr.start();
107
- startAt.current = Date.now();
108
- asrRef.current = asr;
109
- } catch (error) {
110
- onError(error);
111
- }
112
- },
113
- release: async () => {
114
- if (state !== 'recording') {
115
- return;
116
- }
117
- if (!text && startAt.current - Date.now() < 400) {
118
- onError(new AsrError(AsrErrorCode.SHORT_TIME));
119
- }
120
- if (text) {
121
- setState('pending');
122
- } else {
123
- setState('init');
124
- }
125
- if (asrRef.current) {
126
- await asrRef.current.stop();
127
- asrRef.current = null;
128
- }
129
- }
130
- };
131
- }