@ray-js/t-agent-ui-ray 0.0.8-beta-1 → 0.0.8-beta-2
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/ChatContainer/index.js +37 -14
- package/dist/MessageInput/AsrInput.js +3 -2
- package/dist/MessageInput/index.js +10 -7
- package/dist/MessageInput/index.less +12 -1
- package/dist/MessageList/index.js +13 -3
- package/dist/contexts.d.ts +2 -2
- package/dist/contexts.js +1 -2
- package/dist/hooks/context.d.ts +1 -1
- package/dist/hooks/useAsrInput.js +36 -15
- package/package.json +2 -2
|
@@ -5,25 +5,20 @@ import "core-js/modules/web.dom-collections.iterator.js";
|
|
|
5
5
|
import './index.less';
|
|
6
6
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
7
7
|
import { View } from '@ray-js/components';
|
|
8
|
-
import {
|
|
8
|
+
import { SocketStatus } from '@ray-js/t-agent-plugin-assistant';
|
|
9
|
+
import cx from 'clsx';
|
|
9
10
|
import { ChatAgentContext, MessageContext, RenderContext } from '../contexts';
|
|
10
11
|
import { defaultRenderOptions } from '../renderOption';
|
|
11
12
|
import logger from '../logger';
|
|
12
13
|
export default function ChatContainer(props) {
|
|
14
|
+
const [keyboardHeight, setKeyboardHeight] = useState(0);
|
|
13
15
|
const {
|
|
14
16
|
createAgent,
|
|
15
17
|
renderOptions = defaultRenderOptions,
|
|
16
18
|
children,
|
|
17
|
-
className
|
|
19
|
+
className
|
|
18
20
|
} = props;
|
|
19
21
|
const [messages, setMessages] = useState([]);
|
|
20
|
-
const [socketStatus, setSocketStatus] = useState(() => {
|
|
21
|
-
try {
|
|
22
|
-
return getWebSocketStatusSync().status;
|
|
23
|
-
} catch (e) {
|
|
24
|
-
return SocketStatus.WAITING;
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
22
|
const [agent] = useState(() => {
|
|
28
23
|
const agent = createAgent();
|
|
29
24
|
if (!agent.plugins.ui) {
|
|
@@ -40,7 +35,9 @@ export default function ChatContainer(props) {
|
|
|
40
35
|
emitEvent
|
|
41
36
|
} = agent.plugins.ui;
|
|
42
37
|
const offSocketStatusChange = agent.plugins.assistant.onSocketStatusChange(status => {
|
|
43
|
-
|
|
38
|
+
emitEvent('networkChange', {
|
|
39
|
+
online: status === SocketStatus.SUCCESS
|
|
40
|
+
});
|
|
44
41
|
});
|
|
45
42
|
const offMessageListInit = onEvent('messageListInit', _ref => {
|
|
46
43
|
let {
|
|
@@ -71,7 +68,8 @@ export default function ChatContainer(props) {
|
|
|
71
68
|
if (((_prev = prev[prev.length - 1]) === null || _prev === void 0 ? void 0 : _prev.id) === message.id) {
|
|
72
69
|
// 是最后一条消息,滚动到最底部
|
|
73
70
|
emitEvent('scrollToBottom', {
|
|
74
|
-
animation: false
|
|
71
|
+
animation: false,
|
|
72
|
+
follow: true
|
|
75
73
|
});
|
|
76
74
|
}
|
|
77
75
|
}
|
|
@@ -98,9 +96,9 @@ export default function ChatContainer(props) {
|
|
|
98
96
|
const messageValue = useMemo(() => {
|
|
99
97
|
return {
|
|
100
98
|
messages,
|
|
101
|
-
|
|
99
|
+
keyboardHeight
|
|
102
100
|
};
|
|
103
|
-
}, [messages,
|
|
101
|
+
}, [messages, keyboardHeight]);
|
|
104
102
|
useEffect(() => {
|
|
105
103
|
logger.debug('ChatProvider agent.start');
|
|
106
104
|
agent.start().then(() => {
|
|
@@ -112,8 +110,33 @@ export default function ChatContainer(props) {
|
|
|
112
110
|
agent.dispose();
|
|
113
111
|
};
|
|
114
112
|
}, []);
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
const show = _ref3 => {
|
|
115
|
+
let {
|
|
116
|
+
height
|
|
117
|
+
} = _ref3;
|
|
118
|
+
setKeyboardHeight(height);
|
|
119
|
+
};
|
|
120
|
+
const hide = () => {
|
|
121
|
+
setKeyboardHeight(0);
|
|
122
|
+
};
|
|
123
|
+
ty.onKeyboardWillShow(show);
|
|
124
|
+
ty.onKeyboardWillHide(hide);
|
|
125
|
+
ty.onKeyboardHeightChange(show);
|
|
126
|
+
return () => {
|
|
127
|
+
ty.offKeyboardWillShow(show);
|
|
128
|
+
ty.offKeyboardWillHide(hide);
|
|
129
|
+
ty.offKeyboardHeightChange(show);
|
|
130
|
+
};
|
|
131
|
+
}, []);
|
|
115
132
|
return /*#__PURE__*/React.createElement(View, {
|
|
116
|
-
|
|
133
|
+
style: {
|
|
134
|
+
// @ts-ignore
|
|
135
|
+
'--t-agent-chat-container-keyboard-height': "".concat(keyboardHeight, "px")
|
|
136
|
+
},
|
|
137
|
+
className: cx('t-agent-chat-container', className, {
|
|
138
|
+
't-agent-chat-container-keyboard-show': keyboardHeight > 0
|
|
139
|
+
})
|
|
117
140
|
}, /*#__PURE__*/React.createElement(ChatAgentContext.Provider, {
|
|
118
141
|
value: agent
|
|
119
142
|
}, /*#__PURE__*/React.createElement(RenderContext.Provider, {
|
|
@@ -28,11 +28,12 @@ export default function AsrInput(props) {
|
|
|
28
28
|
}), /*#__PURE__*/React.createElement(View, {
|
|
29
29
|
className: "t-agent-message-input-voice-bubble",
|
|
30
30
|
"data-testid": "t-agent-message-input-asr-bubble"
|
|
31
|
-
}, state === 'recording' ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, currentText), /*#__PURE__*/React.createElement(Text, {
|
|
31
|
+
}, 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, {
|
|
32
32
|
className: "t-agent-message-input-voice-bubble-predicted"
|
|
33
33
|
}, incomingText)) : /*#__PURE__*/React.createElement(Textarea, {
|
|
34
34
|
"data-testid": "t-agent-message-input-asr-bubble-input",
|
|
35
35
|
autoHeight: true,
|
|
36
|
+
maxLength: 2048,
|
|
36
37
|
className: "t-agent-message-input-voice-bubble-input",
|
|
37
38
|
value: text,
|
|
38
39
|
onInput: event => setText(event.value)
|
|
@@ -56,8 +57,8 @@ export default function AsrInput(props) {
|
|
|
56
57
|
className: "t-agent-message-input-button t-agent-message-input-button-voice-active",
|
|
57
58
|
disabled: sendDisabled,
|
|
58
59
|
onTouchStart: () => {
|
|
59
|
-
press();
|
|
60
60
|
closeMore();
|
|
61
|
+
press();
|
|
61
62
|
},
|
|
62
63
|
onClick: release,
|
|
63
64
|
onTouchCancel: release,
|
|
@@ -6,7 +6,7 @@ import './index.less';
|
|
|
6
6
|
import { Button, View } from '@ray-js/components';
|
|
7
7
|
import React, { useState } from 'react';
|
|
8
8
|
import { Image, Input, ScrollView } from '@ray-js/ray';
|
|
9
|
-
import { Asr
|
|
9
|
+
import { Asr } from '@ray-js/t-agent-plugin-assistant';
|
|
10
10
|
import cx from 'clsx';
|
|
11
11
|
import PrivateImage from '../PrivateImage';
|
|
12
12
|
import imageSvg from './icons/image.svg';
|
|
@@ -31,8 +31,11 @@ export default function MessageInput(props) {
|
|
|
31
31
|
const agent = useChatAgent();
|
|
32
32
|
const emitEvent = useEmitEvent();
|
|
33
33
|
const isUnmounted = useIsUnmounted();
|
|
34
|
-
useOnEvent('
|
|
35
|
-
|
|
34
|
+
useOnEvent('networkChange', _ref => {
|
|
35
|
+
let {
|
|
36
|
+
online
|
|
37
|
+
} = _ref;
|
|
38
|
+
if (!online && responding) {
|
|
36
39
|
setResponding(false);
|
|
37
40
|
}
|
|
38
41
|
});
|
|
@@ -53,10 +56,10 @@ export default function MessageInput(props) {
|
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
58
|
};
|
|
56
|
-
useOnEvent('sendMessage', async
|
|
59
|
+
useOnEvent('sendMessage', async _ref2 => {
|
|
57
60
|
let {
|
|
58
61
|
blocks
|
|
59
|
-
} =
|
|
62
|
+
} = _ref2;
|
|
60
63
|
if (uploading || responding) {
|
|
61
64
|
return;
|
|
62
65
|
}
|
|
@@ -72,10 +75,10 @@ export default function MessageInput(props) {
|
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
77
|
});
|
|
75
|
-
useOnEvent('setInputBlocks', async
|
|
78
|
+
useOnEvent('setInputBlocks', async _ref3 => {
|
|
76
79
|
let {
|
|
77
80
|
blocks
|
|
78
|
-
} =
|
|
81
|
+
} = _ref3;
|
|
79
82
|
if (uploading || responding) {
|
|
80
83
|
return;
|
|
81
84
|
}
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
padding-bottom: var(--t-agent-safe-bottom);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
.t-agent-chat-container-keyboard-show .t-agent-message-input {
|
|
16
|
+
padding-bottom: 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
.t-agent-message-input-container {
|
|
16
20
|
border-top: 2rpx solid var(--t-agent-input-border-color);
|
|
17
21
|
background: var(--app-B1);
|
|
@@ -261,7 +265,10 @@
|
|
|
261
265
|
color: var(--app-M1-N1);
|
|
262
266
|
font-size: 32rpx;
|
|
263
267
|
transition: height 0.2s ease-in-out;
|
|
264
|
-
|
|
268
|
+
|
|
269
|
+
text {
|
|
270
|
+
white-space: pre-wrap;
|
|
271
|
+
}
|
|
265
272
|
|
|
266
273
|
&::after {
|
|
267
274
|
content: '';
|
|
@@ -277,6 +284,10 @@
|
|
|
277
284
|
}
|
|
278
285
|
}
|
|
279
286
|
|
|
287
|
+
.t-agent-chat-container-keyboard-show .t-agent-message-input-voice-bubble {
|
|
288
|
+
bottom: 260rpx;
|
|
289
|
+
}
|
|
290
|
+
|
|
280
291
|
.t-agent-message-input-voice-bubble-predicted {
|
|
281
292
|
color: var(--app-M1-N3);
|
|
282
293
|
}
|
|
@@ -4,7 +4,7 @@ import "core-js/modules/esnext.iterator.map.js";
|
|
|
4
4
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
5
5
|
import './index.less';
|
|
6
6
|
import { View } from '@ray-js/components';
|
|
7
|
-
import React, { useMemo, useState } from 'react';
|
|
7
|
+
import React, { useMemo, useRef, useState } from 'react';
|
|
8
8
|
import { ScrollView } from '@ray-js/ray';
|
|
9
9
|
import MessageRender from '../MessageRender';
|
|
10
10
|
import { useAgentMessage, useOnEvent, useSleep } from '../hooks';
|
|
@@ -19,13 +19,19 @@ export default function MessageList(props) {
|
|
|
19
19
|
const [scrollAnimation, setScrollAnimation] = useState(false);
|
|
20
20
|
// 最大整数位数,用于强制滚动到底部,收到连续 scrollToBottom 时,每 10 毫秒更新一次
|
|
21
21
|
const [scrollTop, setScrollTop] = useState(() => 1000000000000000 + Math.floor(Date.now() / 10));
|
|
22
|
+
const followNewMessageRef = useRef(true);
|
|
22
23
|
const sleep = useSleep();
|
|
23
24
|
|
|
24
25
|
// 强制滚动到底部
|
|
25
26
|
useOnEvent('scrollToBottom', _ref => {
|
|
26
27
|
let {
|
|
27
|
-
animation
|
|
28
|
+
animation,
|
|
29
|
+
follow
|
|
28
30
|
} = _ref;
|
|
31
|
+
// 如果发送的跟随新消息的滚动,判断当前是不是滚动到了底部,如果没有滚动到底部,不执行滚动
|
|
32
|
+
if (follow && !followNewMessageRef.current) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
29
35
|
sleep(100).then(() => {
|
|
30
36
|
setScrollAnimation(!!animation);
|
|
31
37
|
const top = 1000000000000000 + Math.floor(Date.now() / 10);
|
|
@@ -46,7 +52,11 @@ export default function MessageList(props) {
|
|
|
46
52
|
scrollTop: scrollTop,
|
|
47
53
|
refresherTriggered: false,
|
|
48
54
|
enableFlex: true,
|
|
49
|
-
scrollY: true
|
|
55
|
+
scrollY: true,
|
|
56
|
+
onScroll: event => {
|
|
57
|
+
// 使用了 flex-direction: column-reverse,所以滚动到顶部时,scrollTop = 0
|
|
58
|
+
followNewMessageRef.current = event.detail.scrollTop > -10;
|
|
59
|
+
}
|
|
50
60
|
}, /*#__PURE__*/React.createElement(View, {
|
|
51
61
|
className: "t-agent-message-list-padding"
|
|
52
62
|
}), reversed.map((msg, index) => {
|
package/dist/contexts.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { ChatAgent, ChatMessageObject, UIPlugin } from '@ray-js/t-agent';
|
|
3
|
-
import { AssistantPlugin
|
|
3
|
+
import { AssistantPlugin } from '@ray-js/t-agent-plugin-assistant';
|
|
4
4
|
import { RenderOptions, TileProps } from './types';
|
|
5
5
|
export declare const MessageContext: import("react").Context<{
|
|
6
6
|
messages: ChatMessageObject[];
|
|
7
|
-
|
|
7
|
+
keyboardHeight: number;
|
|
8
8
|
}>;
|
|
9
9
|
export declare const RenderContext: import("react").Context<RenderOptions>;
|
|
10
10
|
export declare const ChatAgentContext: import("react").Context<ChatAgent<UIPlugin & AssistantPlugin>>;
|
package/dist/contexts.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { createContext } from 'react';
|
|
2
|
-
import { SocketStatus } from '@ray-js/t-agent-plugin-assistant';
|
|
3
2
|
export const MessageContext = /*#__PURE__*/createContext({
|
|
4
3
|
messages: [],
|
|
5
|
-
|
|
4
|
+
keyboardHeight: 0
|
|
6
5
|
});
|
|
7
6
|
export const RenderContext = /*#__PURE__*/createContext({
|
|
8
7
|
renderTileAs: () => null,
|
package/dist/hooks/context.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { TTTAction } from '@ray-js/t-agent-plugin-assistant';
|
|
|
2
2
|
export declare const useChatAgent: () => import("@ray-js/t-agent").ChatAgent<import("@ray-js/t-agent").UIPlugin & import("@ray-js/t-agent-plugin-assistant").AssistantPlugin>;
|
|
3
3
|
export declare const useAgentMessage: () => {
|
|
4
4
|
messages: import("@ray-js/t-agent").ChatMessageObject[];
|
|
5
|
-
|
|
5
|
+
keyboardHeight: number;
|
|
6
6
|
};
|
|
7
7
|
export declare const useRenderOptions: () => import("..").RenderOptions;
|
|
8
8
|
export declare const useOnEvent: (eventName: string, callback: (details: any) => void) => void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
2
|
-
import { useRef, useState } from 'react';
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { Asr, AsrDetectResultState } from '@ray-js/t-agent-plugin-assistant';
|
|
4
|
+
import { useIsUnmounted } from './useIsUnmounted';
|
|
4
5
|
export let AsrErrorCode = /*#__PURE__*/function (AsrErrorCode) {
|
|
5
6
|
AsrErrorCode[AsrErrorCode["SHORT_TIME"] = 0] = "SHORT_TIME";
|
|
6
7
|
return AsrErrorCode;
|
|
@@ -47,8 +48,18 @@ export function useAsrInput(options) {
|
|
|
47
48
|
const asrRef = useRef(null);
|
|
48
49
|
const [currentText, setCurrentText] = useState('');
|
|
49
50
|
const [incomingText, setIncomingText] = useState('');
|
|
51
|
+
const isUnmounted = useIsUnmounted();
|
|
50
52
|
const [state, setState] = useState('init');
|
|
51
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
|
+
}, []);
|
|
52
63
|
return {
|
|
53
64
|
currentText,
|
|
54
65
|
incomingText,
|
|
@@ -61,21 +72,31 @@ export function useAsrInput(options) {
|
|
|
61
72
|
},
|
|
62
73
|
press: async () => {
|
|
63
74
|
setState('recording');
|
|
64
|
-
|
|
75
|
+
|
|
76
|
+
// 保存当前文本,用于恢复
|
|
65
77
|
const initial = text;
|
|
66
|
-
|
|
78
|
+
|
|
79
|
+
// 上次识别的结果
|
|
80
|
+
let last = '';
|
|
67
81
|
setCurrentText(initial);
|
|
68
82
|
setIncomingText('');
|
|
69
83
|
|
|
70
84
|
// 开始录音时
|
|
71
|
-
const asr = Asr.detect(
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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);
|
|
79
100
|
}
|
|
80
101
|
if (res.state === AsrDetectResultState.ERROR) {
|
|
81
102
|
onError(new AsrError(res.errorCode));
|
|
@@ -83,6 +104,7 @@ export function useAsrInput(options) {
|
|
|
83
104
|
});
|
|
84
105
|
try {
|
|
85
106
|
await asr.start();
|
|
107
|
+
startAt.current = Date.now();
|
|
86
108
|
asrRef.current = asr;
|
|
87
109
|
} catch (error) {
|
|
88
110
|
onError(error);
|
|
@@ -92,15 +114,14 @@ export function useAsrInput(options) {
|
|
|
92
114
|
if (state !== 'recording') {
|
|
93
115
|
return;
|
|
94
116
|
}
|
|
117
|
+
if (!text && startAt.current - Date.now() < 400) {
|
|
118
|
+
onError(new AsrError(AsrErrorCode.SHORT_TIME));
|
|
119
|
+
}
|
|
95
120
|
if (text) {
|
|
96
121
|
setState('pending');
|
|
97
122
|
} else {
|
|
98
|
-
onError(new AsrError(AsrErrorCode.SHORT_TIME));
|
|
99
123
|
setState('init');
|
|
100
124
|
}
|
|
101
|
-
if (Date.now() - startAt.current < 500 && text.length === 0) {
|
|
102
|
-
onError(new AsrError(AsrErrorCode.SHORT_TIME));
|
|
103
|
-
}
|
|
104
125
|
if (asrRef.current) {
|
|
105
126
|
await asrRef.current.stop();
|
|
106
127
|
asrRef.current = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ray-js/t-agent-ui-ray",
|
|
3
|
-
"version": "0.0.8-beta-
|
|
3
|
+
"version": "0.0.8-beta-2",
|
|
4
4
|
"author": "Tuya.inc",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"@types/echarts": "^4.9.22",
|
|
42
42
|
"@types/markdown-it": "^14.1.1"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "0fe6e7c1daafbeffd1394a7ee85ab4ae86d8f93e"
|
|
45
45
|
}
|