@ray-js/t-agent-ui-ray 0.2.0-beta-3 → 0.2.0-beta-5

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.
@@ -32,6 +32,13 @@ export default function ChatContainer(props) {
32
32
  if (!agent.plugins.ui) {
33
33
  throw new Error('UI plugin not found');
34
34
  }
35
+ agent.session.initValue('UIRay.multiSelect.show', false);
36
+ agent.session.initValue('UIRay.multiSelect.selected', []);
37
+ agent.session.onChange((key, value) => {
38
+ if (key === 'UIRay.multiSelect.show' && !value) {
39
+ agent.session.set('UIRay.multiSelect.selected', []);
40
+ }
41
+ });
35
42
  return agent;
36
43
  });
37
44
  if (props.agentRef) {
@@ -1,12 +1,14 @@
1
1
  import React from 'react';
2
2
  interface Props {
3
3
  amplitudeCount: number;
4
+ responding: boolean;
4
5
  recording: boolean;
5
6
  disabled?: boolean;
6
7
  onConfirm: () => void;
7
8
  onRecord: () => void;
8
9
  onCancel: () => void;
9
10
  onBack: () => void;
11
+ onAbort: () => void;
10
12
  }
11
13
  declare const AsrInput: (props: Props) => React.JSX.Element;
12
14
  export default AsrInput;
@@ -4,13 +4,11 @@ import { Button, View } from '@ray-js/ray';
4
4
  import cx from 'clsx';
5
5
  import { useTranslate } from '../../hooks';
6
6
  import { WaveformVisualizer } from './WaveformVisualizer';
7
- import { useSleep } from '../../hooks/useSleep';
8
7
  const AsrInput = props => {
9
8
  const touchRef = useRef({
10
9
  startAt: 0,
11
10
  y: 0
12
11
  });
13
- useSleep();
14
12
  const t = useTranslate();
15
13
  const [active, setActive] = useState(false);
16
14
  const [cancel, setCancel] = useState(false);
@@ -27,7 +25,7 @@ const AsrInput = props => {
27
25
  return;
28
26
  }
29
27
  if (Date.now() - touchRef.current.startAt < 500) {
30
- // 这种情况是误触
28
+ // 这种情况是误触,直接取消
31
29
  touchRef.current = {
32
30
  startAt: 0,
33
31
  y: 0
@@ -74,9 +72,9 @@ const AsrInput = props => {
74
72
  }, t('t-agent.input.asr.oninput.text.center')), /*#__PURE__*/React.createElement(WaveformVisualizer, {
75
73
  amplitudeCount: props.amplitudeCount
76
74
  }))), !props.recording && /*#__PURE__*/React.createElement(Button, {
77
- className: "t-agent-message-input-button t-agent-message-input-button-keyboard",
75
+ className: cx('t-agent-message-input-button', 't-agent-message-input-ptt-action-button', props.responding ? 't-agent-message-input-button-stop' : 't-agent-message-input-button-keyboard'),
78
76
  "data-testid": "t-agent-message-input-button-asr",
79
- onClick: props.onBack
77
+ onClick: props.responding ? props.onAbort : props.onBack
80
78
  }));
81
79
  };
82
80
  export default AsrInput;
@@ -207,6 +207,7 @@ export default function MessageInputAIStream(props) {
207
207
  }));
208
208
  } else {
209
209
  container = /*#__PURE__*/React.createElement(AsrInput, {
210
+ responding: responding,
210
211
  disabled: responding,
211
212
  amplitudeCount: AMPLITUDE_COUNT,
212
213
  recording: record.recording,
@@ -269,7 +270,12 @@ export default function MessageInputAIStream(props) {
269
270
  var _record$cancel;
270
271
  (_record$cancel = record.cancel) === null || _record$cancel === void 0 || _record$cancel.call(record);
271
272
  },
272
- onBack: () => setMode('text')
273
+ onBack: () => setMode('text'),
274
+ onAbort: () => {
275
+ if (responding && acRef.current) {
276
+ acRef.current.abort('User abort');
277
+ }
278
+ }
273
279
  });
274
280
  }
275
281
  return /*#__PURE__*/React.createElement(View, {
@@ -146,15 +146,18 @@
146
146
  right: 10rpx;
147
147
  }
148
148
 
149
- .t-agent-message-input-button-keyboard {
150
- border: 2rpx solid var(--t-agent-input-border-color);
151
- background: transparent url('icons/text.svg') no-repeat center;
149
+ .t-agent-message-input-ptt-action-button {
152
150
  position: absolute;
153
151
  top: 50%;
154
152
  transform: translateY(-50%);
155
153
  right: 32rpx;
156
154
  }
157
155
 
156
+ .t-agent-message-input-button-keyboard {
157
+ border: 2rpx solid var(--t-agent-input-border-color);
158
+ background: transparent url('icons/text.svg') no-repeat center;
159
+ }
160
+
158
161
  .t-agent-message-input-button-stop {
159
162
  background: rgba(0, 0, 0, 0) url('icons/stop.svg') no-repeat center;
160
163
  border: 2rpx solid var(--t-agent-input-border-color);
@@ -5,16 +5,12 @@ interface Props {
5
5
  style?: React.CSSProperties;
6
6
  wrapperClassName?: string;
7
7
  wrapperStyle?: React.CSSProperties;
8
+ renderTop?: React.ReactNode;
8
9
  roleSide?: {
9
10
  user?: 'start' | 'end' | string;
10
11
  assistant?: 'start' | 'end' | string;
11
12
  [key: string]: 'start' | 'end' | string;
12
13
  };
13
- multiSelect?: {
14
- show: boolean;
15
- select: string[];
16
- onSelect: (msgs: string[]) => void;
17
- };
18
14
  historyLimit?: {
19
15
  /** 历史消息数量限制 */
20
16
  count: number;
@@ -10,7 +10,7 @@ import { ScrollView } from '@ray-js/ray';
10
10
  import { generateId } from '@ray-js/t-agent';
11
11
  import { isVersionMatch } from '@ray-js/t-agent-plugin-aistream';
12
12
  import MessageRender from '../MessageRender';
13
- import { useAgentMessage, useOnEvent } from '../hooks';
13
+ import { useAgentMessage, useAgentSessionValue, useOnEvent } from '../hooks';
14
14
  import { useDebouncedFn } from '../hooks/useDebouncedFn';
15
15
  import { useSleep } from '../hooks/useSleep';
16
16
  export default function MessageList(props) {
@@ -20,8 +20,7 @@ export default function MessageList(props) {
20
20
  style,
21
21
  wrapperClassName,
22
22
  wrapperStyle,
23
- historyLimit,
24
- multiSelect
23
+ historyLimit
25
24
  } = props;
26
25
  const {
27
26
  messages
@@ -32,6 +31,7 @@ export default function MessageList(props) {
32
31
  const [scrollTop, setScrollTop] = useState(() => 1000000000000000 + Math.floor(Date.now() / 10));
33
32
  const followNewMessageRef = useRef(true);
34
33
  const sleep = useSleep();
34
+ const [showSelect] = useAgentSessionValue('UIRay.multiSelect.show');
35
35
  const canIUse = useMemo(() => {
36
36
  // @ts-ignore
37
37
  const {
@@ -104,7 +104,7 @@ export default function MessageList(props) {
104
104
  side = msg.role === 'user' ? 'end' : 'start';
105
105
  }
106
106
  return /*#__PURE__*/React.createElement(MessageRender, {
107
- multiSelect: multiSelect,
107
+ showSelect: showSelect,
108
108
  key: msg.id,
109
109
  message: msg,
110
110
  isLatestMessage: index === 0,
@@ -112,7 +112,7 @@ export default function MessageList(props) {
112
112
  });
113
113
  }), historyLimit && historyLimit.count < reversed.length && /*#__PURE__*/React.createElement(View, {
114
114
  className: "t-agent-message-list-history-tip"
115
- }, historyLimit.tipText), /*#__PURE__*/React.createElement(View, {
115
+ }, historyLimit.tipText), props.renderTop, /*#__PURE__*/React.createElement(View, {
116
116
  className: "t-agent-message-list-padding-start"
117
117
  })));
118
118
  }
@@ -5,11 +5,7 @@ interface Props {
5
5
  message: ChatMessageObject;
6
6
  isLatestMessage: boolean;
7
7
  side: 'start' | 'end' | string;
8
- multiSelect?: {
9
- show: boolean;
10
- select: string[];
11
- onSelect: (msgs: string[]) => void;
12
- };
8
+ showSelect?: boolean;
13
9
  }
14
- export default function MessageRender({ message, isLatestMessage, side, multiSelect }: Props): React.JSX.Element;
10
+ export default function MessageRender({ message, isLatestMessage, side, showSelect }: Props): React.JSX.Element;
15
11
  export {};
@@ -4,24 +4,34 @@ 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 from 'react';
7
+ import React, { useState } from 'react';
8
8
  import TileRender from '../TileRender';
9
+ import { useChatAgent, useOnEvent } from '../hooks';
9
10
  export default function MessageRender(_ref) {
10
11
  let {
11
12
  message,
12
13
  isLatestMessage,
13
14
  side,
14
- multiSelect
15
+ showSelect
15
16
  } = _ref;
16
17
  const {
17
18
  id,
18
19
  tiles
19
20
  } = message;
20
- const {
21
- select = [],
22
- onSelect = () => {},
23
- show: showMultiSelect = false
24
- } = multiSelect || {};
21
+ const agent = useChatAgent();
22
+ const [isSelected, setIsSelected] = useState(() => {
23
+ var _agent$session$get;
24
+ return !!((_agent$session$get = agent.session.get("UIRay.multiSelect.selected")) !== null && _agent$session$get !== void 0 && _agent$session$get.includes(id));
25
+ });
26
+ useOnEvent('sessionChange', _ref2 => {
27
+ let {
28
+ key,
29
+ value
30
+ } = _ref2;
31
+ if (key === 'UIRay.multiSelect.selected') {
32
+ setIsSelected(!!(value !== null && value !== void 0 && value.includes(id)));
33
+ }
34
+ });
25
35
  if (tiles.length === 0) {
26
36
  return /*#__PURE__*/React.createElement(View, {
27
37
  key: id,
@@ -30,14 +40,19 @@ export default function MessageRender(_ref) {
30
40
  }
31
41
  return /*#__PURE__*/React.createElement(View, {
32
42
  key: id,
33
- className: "t-agent-message-list-row-container "
34
- }, showMultiSelect && /*#__PURE__*/React.createElement(View, {
43
+ className: "t-agent-message-list-row-container",
44
+ onClick: () => {
45
+ if (showSelect) {
46
+ const prev = agent.session.get("UIRay.multiSelect.selected") || [];
47
+ isSelected ? agent.session.set("UIRay.multiSelect.selected", prev.filter(i => i !== id)) : agent.session.set("UIRay.multiSelect.selected", [...prev, id]);
48
+ }
49
+ }
50
+ }, showSelect && /*#__PURE__*/React.createElement(View, {
35
51
  className: "t-agent-message-list-row-check"
36
52
  }, /*#__PURE__*/React.createElement(View, {
37
- className: "checkbox-container ".concat(select.includes(id) ? 'selected' : ''),
38
- onClick: () => select.includes(id) ? onSelect([...select.filter(v => v !== id)]) : onSelect([...select, id])
53
+ className: "t-agent-message-list-checkbox-container ".concat(isSelected ? 't-agent-message-list-checkbox-selected' : '')
39
54
  }, /*#__PURE__*/React.createElement(View, {
40
- className: "checkbox-check"
55
+ className: "t-agent-message-list-checkbox-check"
41
56
  }))), /*#__PURE__*/React.createElement(View, {
42
57
  className: "t-agent-message-list-row t-agent-message-list-row-".concat(side)
43
58
  }, tiles.map(tile => {
@@ -28,7 +28,7 @@
28
28
  @border-color: rgba(0, 0, 0, 0.3);
29
29
  @active-color: #3678E3;
30
30
 
31
- .checkbox-container {
31
+ .t-agent-message-list-checkbox-container {
32
32
  position: relative;
33
33
  width: @checkbox-size;
34
34
  height: @checkbox-size;
@@ -38,17 +38,17 @@
38
38
  cursor: pointer;
39
39
  transition: all 0.2s ease-in-out;
40
40
 
41
- &.selected {
41
+ &.t-agent-message-list-checkbox-selected {
42
42
  background: @active-color;
43
43
  border-color: transparent;
44
44
 
45
- .checkbox-check {
45
+ .t-agent-message-list-checkbox-check {
46
46
  opacity: 1;
47
47
  }
48
48
  }
49
49
  }
50
50
 
51
- .checkbox-check {
51
+ .t-agent-message-list-checkbox-check {
52
52
  position: absolute;
53
53
  top: 50%;
54
54
  left: 50%;
@@ -58,4 +58,4 @@
58
58
  background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgMTIgOSI+PHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjRkZGIiBzdHJva2Utd2lkdGg9IjIiIGQ9Ik0xIDRsMy43NSA0TDEwLjMgMSIvPjwvc3ZnPg==");
59
59
  opacity: 0;
60
60
  transition: opacity 0.2s ease-in-out;
61
- }
61
+ }
@@ -3,4 +3,3 @@ export * from './useAttachmentInput';
3
3
  export * from './useIsUnmounted';
4
4
  export * from './useLongPress';
5
5
  export * from './useTranslate';
6
- export * from './useMultiSelect';
@@ -2,5 +2,4 @@ export * from './context';
2
2
  export * from './useAttachmentInput';
3
3
  export * from './useIsUnmounted';
4
4
  export * from './useLongPress';
5
- export * from './useTranslate';
6
- export * from './useMultiSelect';
5
+ export * from './useTranslate';
@@ -84,7 +84,7 @@ export function useLongPress() {
84
84
  key: 'multiSelect',
85
85
  label: t('t-agent.message.action.multiSelect'),
86
86
  action: () => {
87
- emitEvent('multiSelect', {});
87
+ agent.session.set('UIRay.multiSelect.show', !agent.session.get('UIRay.multiSelect.show'));
88
88
  }
89
89
  });
90
90
  break;
@@ -3,15 +3,16 @@ import "core-js/modules/esnext.iterator.constructor.js";
3
3
  import "core-js/modules/esnext.iterator.find.js";
4
4
  import "core-js/modules/esnext.iterator.map.js";
5
5
  import "core-js/modules/esnext.iterator.some.js";
6
+ import "core-js/modules/web.dom-collections.iterator.js";
6
7
  import './index.less';
7
8
  import { View } from '@ray-js/components';
8
- import React, { useCallback, memo } from 'react';
9
+ import React, { memo, useCallback, useMemo } from 'react';
9
10
  import { Image } from '@ray-js/ray';
10
11
  import { BubbleTileStatus, ChatMessageStatus } from '@ray-js/t-agent';
11
12
  import TileRender from '../../TileRender';
12
13
  import noticeSvg from './notice.svg';
13
14
  import noticeWarnSvg from './notice-warn.svg';
14
- import { useChatAgent, useTranslate, useLongPress, useRenderOptions } from '../../hooks';
15
+ import { useChatAgent, useLongPress, useRenderOptions, useTranslate } from '../../hooks';
15
16
  import RollBack from '../WorkflowTile/RollBack';
16
17
  const BubbleTile = props => {
17
18
  var _workflowTile$data;
@@ -73,7 +74,36 @@ const BubbleTile = props => {
73
74
  const showRollBack = side === 'start' && children.some(child => child.type === 'workflow');
74
75
  const workflowTile = children.find(child => child.type === 'workflow');
75
76
  const workflowNode = workflowTile === null || workflowTile === void 0 || (_workflowTile$data = workflowTile.data) === null || _workflowTile$data === void 0 ? void 0 : _workflowTile$data.nodeId;
76
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(View, _extends({
77
+ const isErrorBubble = useMemo(() => {
78
+ let empty = true;
79
+ for (const child of children) {
80
+ // 如果子元素是文本类型,并且文本不为空,则不是空消息
81
+ if (child.type === 'text') {
82
+ if (child.data.text !== '') {
83
+ empty = false;
84
+ break;
85
+ }
86
+ } else {
87
+ // 如果子元素是其他类型,则不是空消息
88
+ empty = false;
89
+ break;
90
+ }
91
+ }
92
+ return empty && role === 'assistant' && bubbleStatus === BubbleTileStatus.ERROR && status === ChatMessageStatus.FINISH;
93
+ }, [status, role, bubbleStatus, children]);
94
+ if (isErrorBubble) {
95
+ return /*#__PURE__*/React.createElement(View, _extends({
96
+ className: "t-agent-bubble-tile t-agent-bubble-tile-error-alert",
97
+ "data-testid": "t-agent-bubble-tile"
98
+ }, longPressRes.longPressProps), /*#__PURE__*/React.createElement(Image, {
99
+ src: noticeSvg,
100
+ className: "t-agent-bubble-tile-error",
101
+ "data-testid": "t-agent-bubble-tile-error"
102
+ }), /*#__PURE__*/React.createElement(View, {
103
+ className: "t-agent-bubble-tile-error-alert-text"
104
+ }, data.info || t('t-agent.unknown-error')), longPressBlock);
105
+ }
106
+ return /*#__PURE__*/React.createElement(View, _extends({
77
107
  className: "t-agent-bubble-tile t-agent-bubble-tile-".concat(side),
78
108
  "data-testid": "t-agent-bubble-tile"
79
109
  }, longPressRes.longPressProps), side === 'end' && /*#__PURE__*/React.createElement(ErrorNotice, {
@@ -96,7 +126,7 @@ const BubbleTile = props => {
96
126
  }, t('t-agent.message.bubble.aborted'))), side === 'start' && /*#__PURE__*/React.createElement(ErrorNotice, {
97
127
  bubbleStatus: bubbleStatus,
98
128
  showInfo: showInfo
99
- })), longPressBlock, showRollBack && !isLatestMessage && workflowNode && /*#__PURE__*/React.createElement(RollBack, {
129
+ }), longPressBlock, showRollBack && !isLatestMessage && workflowNode && /*#__PURE__*/React.createElement(RollBack, {
100
130
  nodeId: workflowNode
101
131
  }));
102
132
  };
@@ -18,6 +18,24 @@
18
18
  position: relative;
19
19
  }
20
20
 
21
+ .t-agent-bubble-tile-error-alert {
22
+ background: var(--app-M2_1);
23
+ border-radius: 32rpx;
24
+ padding: 24rpx;
25
+
26
+ .t-agent-bubble-tile-error {
27
+ margin: 0 12rpx 0 0;
28
+ }
29
+
30
+ .t-agent-bubble-tile-error-alert-text {
31
+ flex: 0 1 auto;
32
+ min-width: 0;
33
+ white-space: nowrap;
34
+ overflow: hidden;
35
+ text-overflow: ellipsis;
36
+ }
37
+ }
38
+
21
39
  .t-agent-bubble-tile-bubble-loader {
22
40
  display: inline-flex;
23
41
 
@@ -64,7 +82,7 @@
64
82
  display: flex;
65
83
  justify-content: center;
66
84
  align-items: center;
67
-
85
+
68
86
  .t-agent-bubble-tile-bubble-loader-ball {
69
87
  width: 6px;
70
88
  height: 6px;
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",
3
+ "version": "0.2.0-beta-5",
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": "03c4af7fe4cfe27069db83d99a61042c8053399e"
43
+ "gitHead": "a0beda17c8e1c3ef81c718300c02a23d88a3ee4a"
44
44
  }
@@ -1,7 +0,0 @@
1
- /// <reference types="react" />
2
- export declare const useMultiSelectMessage: () => {
3
- show: boolean;
4
- select: string[];
5
- onSelect: import("react").Dispatch<import("react").SetStateAction<string[]>>;
6
- setShow: import("react").Dispatch<import("react").SetStateAction<boolean>>;
7
- };
@@ -1,12 +0,0 @@
1
- import "core-js/modules/web.dom-collections.iterator.js";
2
- import { useState } from 'react';
3
- export const useMultiSelectMessage = () => {
4
- const [select, setSelect] = useState([]);
5
- const [show, setShow] = useState(false);
6
- return {
7
- show,
8
- select,
9
- onSelect: setSelect,
10
- setShow
11
- };
12
- };