@opensumi/ide-ai-native 3.7.2-next-1740323956.0 → 3.7.2-next-1740366031.0

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.
Files changed (73) hide show
  1. package/lib/browser/ai-core.contribution.d.ts +3 -1
  2. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  3. package/lib/browser/ai-core.contribution.js +13 -1
  4. package/lib/browser/ai-core.contribution.js.map +1 -1
  5. package/lib/browser/chat/chat-manager.service.d.ts +30 -2
  6. package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
  7. package/lib/browser/chat/chat-manager.service.js +45 -1
  8. package/lib/browser/chat/chat-manager.service.js.map +1 -1
  9. package/lib/browser/chat/chat-model.d.ts +33 -6
  10. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  11. package/lib/browser/chat/chat-model.js +50 -29
  12. package/lib/browser/chat/chat-model.js.map +1 -1
  13. package/lib/browser/chat/chat-proxy.service.d.ts +1 -1
  14. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  15. package/lib/browser/chat/chat-proxy.service.js +1 -1
  16. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  17. package/lib/browser/chat/chat.internal.service.d.ts +4 -2
  18. package/lib/browser/chat/chat.internal.service.d.ts.map +1 -1
  19. package/lib/browser/chat/chat.internal.service.js +31 -12
  20. package/lib/browser/chat/chat.internal.service.js.map +1 -1
  21. package/lib/browser/chat/chat.module.less +8 -42
  22. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  23. package/lib/browser/chat/chat.view.js +85 -31
  24. package/lib/browser/chat/chat.view.js.map +1 -1
  25. package/lib/browser/components/ChatHistory.d.ts +21 -0
  26. package/lib/browser/components/ChatHistory.d.ts.map +1 -0
  27. package/lib/browser/components/ChatHistory.js +148 -0
  28. package/lib/browser/components/ChatHistory.js.map +1 -0
  29. package/lib/browser/components/ChatReply.d.ts.map +1 -1
  30. package/lib/browser/components/ChatReply.js +3 -1
  31. package/lib/browser/components/ChatReply.js.map +1 -1
  32. package/lib/browser/components/ChatThinking.js +1 -1
  33. package/lib/browser/components/ChatThinking.js.map +1 -1
  34. package/lib/browser/components/chat-history.css +139 -0
  35. package/lib/browser/components/components.module.less +2 -2
  36. package/lib/browser/components/utils.d.ts +2 -2
  37. package/lib/browser/layout/layout.module.less +1 -1
  38. package/lib/browser/mcp/tools/components/Terminal.d.ts +4 -0
  39. package/lib/browser/mcp/tools/components/Terminal.d.ts.map +1 -0
  40. package/lib/browser/mcp/tools/components/Terminal.js +64 -0
  41. package/lib/browser/mcp/tools/components/Terminal.js.map +1 -0
  42. package/lib/browser/mcp/tools/components/index.module.less +40 -0
  43. package/lib/browser/mcp/tools/handlers/RunCommand.d.ts +43 -0
  44. package/lib/browser/mcp/tools/handlers/RunCommand.d.ts.map +1 -0
  45. package/lib/browser/mcp/tools/handlers/RunCommand.js +104 -0
  46. package/lib/browser/mcp/tools/handlers/RunCommand.js.map +1 -0
  47. package/lib/browser/mcp/tools/runTerminalCmd.d.ts +1 -6
  48. package/lib/browser/mcp/tools/runTerminalCmd.d.ts.map +1 -1
  49. package/lib/browser/mcp/tools/runTerminalCmd.js +9 -55
  50. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  51. package/lib/browser/model/msg-history-manager.d.ts +10 -0
  52. package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
  53. package/lib/browser/model/msg-history-manager.js +14 -9
  54. package/lib/browser/model/msg-history-manager.js.map +1 -1
  55. package/package.json +23 -23
  56. package/src/browser/ai-core.contribution.ts +13 -1
  57. package/src/browser/chat/chat-manager.service.ts +83 -7
  58. package/src/browser/chat/chat-model.ts +62 -12
  59. package/src/browser/chat/chat-proxy.service.ts +1 -1
  60. package/src/browser/chat/chat.internal.service.ts +23 -5
  61. package/src/browser/chat/chat.module.less +8 -42
  62. package/src/browser/chat/chat.view.tsx +143 -60
  63. package/src/browser/components/ChatHistory.tsx +292 -0
  64. package/src/browser/components/ChatReply.tsx +5 -1
  65. package/src/browser/components/ChatThinking.tsx +1 -1
  66. package/src/browser/components/chat-history.css +139 -0
  67. package/src/browser/components/components.module.less +2 -2
  68. package/src/browser/layout/layout.module.less +1 -1
  69. package/src/browser/mcp/tools/components/Terminal.tsx +97 -0
  70. package/src/browser/mcp/tools/components/index.module.less +40 -0
  71. package/src/browser/mcp/tools/handlers/RunCommand.ts +115 -0
  72. package/src/browser/mcp/tools/runTerminalCmd.ts +7 -68
  73. package/src/browser/model/msg-history-manager.ts +15 -1
@@ -0,0 +1,292 @@
1
+ import cls from 'classnames';
2
+ import React, { FC, memo, useCallback, useEffect, useRef, useState } from 'react';
3
+
4
+ import { Icon, Input, Loading, Popover, PopoverPosition, PopoverTriggerType, getIcon } from '@opensumi/ide-components';
5
+ import './chat-history.css';
6
+ import { localize } from '@opensumi/ide-core-browser';
7
+ import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native';
8
+
9
+ export interface IChatHistoryItem {
10
+ id: string;
11
+ title: string;
12
+ updatedAt: number;
13
+ loading: boolean;
14
+ }
15
+
16
+ export interface IChatHistoryProps {
17
+ title: string;
18
+ historyList: IChatHistoryItem[];
19
+ currentId?: string;
20
+ className?: string;
21
+ onNewChat: () => void;
22
+ onHistoryItemSelect: (item: IChatHistoryItem) => void;
23
+ onHistoryItemDelete: (item: IChatHistoryItem) => void;
24
+ onHistoryItemChange: (item: IChatHistoryItem, title: string) => void;
25
+ }
26
+
27
+ // 最大历史记录数
28
+ const MAX_HISTORY_LIST = 100;
29
+
30
+ const ChatHistory: FC<IChatHistoryProps> = memo(
31
+ ({
32
+ title,
33
+ historyList,
34
+ currentId,
35
+ onNewChat,
36
+ onHistoryItemSelect,
37
+ onHistoryItemChange,
38
+ onHistoryItemDelete,
39
+ className,
40
+ }) => {
41
+ const [historyTitleEditable, setHistoryTitleEditable] = useState<{
42
+ [key: string]: boolean;
43
+ } | null>(null);
44
+ const [searchValue, setSearchValue] = useState('');
45
+ const inputRef = useRef<any>(null);
46
+
47
+ // 处理搜索输入变化
48
+ const handleSearchChange = useCallback(
49
+ (event: React.ChangeEvent<HTMLInputElement>) => {
50
+ setSearchValue(event.target.value);
51
+ },
52
+ [searchValue],
53
+ );
54
+
55
+ // 处理历史记录项选择
56
+ const handleHistoryItemSelect = useCallback(
57
+ (item: IChatHistoryItem) => {
58
+ onHistoryItemSelect(item);
59
+ setSearchValue('');
60
+ },
61
+ [onHistoryItemSelect, searchValue],
62
+ );
63
+
64
+ // 处理标题编辑
65
+ const handleTitleEdit = useCallback(
66
+ (item: IChatHistoryItem) => {
67
+ setHistoryTitleEditable({
68
+ [item.id]: true,
69
+ });
70
+ },
71
+ [historyTitleEditable],
72
+ );
73
+
74
+ // 处理标题编辑完成
75
+ const handleTitleEditComplete = useCallback(
76
+ (item: IChatHistoryItem, newTitle: string) => {
77
+ setHistoryTitleEditable({
78
+ [item.id]: false,
79
+ });
80
+ onHistoryItemChange(item, newTitle);
81
+ },
82
+ [onHistoryItemChange, historyTitleEditable],
83
+ );
84
+
85
+ // 处理标题编辑取消
86
+ const handleTitleEditCancel = useCallback(
87
+ (item: IChatHistoryItem) => {
88
+ setHistoryTitleEditable({
89
+ [item.id]: false,
90
+ });
91
+ },
92
+ [historyTitleEditable],
93
+ );
94
+
95
+ // 处理新建聊天
96
+ const handleNewChat = useCallback(() => {
97
+ onNewChat();
98
+ }, [onNewChat]);
99
+
100
+ useEffect(() => {
101
+ if (historyTitleEditable) {
102
+ inputRef.current?.focus({ cursor: 'end' });
103
+ }
104
+ }, [historyTitleEditable]);
105
+
106
+ // 处理删除历史记录
107
+ const handleHistoryItemDelete = useCallback(
108
+ (item: IChatHistoryItem) => {
109
+ onHistoryItemDelete(item);
110
+ },
111
+ [onHistoryItemDelete],
112
+ );
113
+
114
+ // 获取时间标签
115
+ const getTimeKey = useCallback((diff: number): string => {
116
+ if (diff < 60 * 60 * 1000) {
117
+ const minutes = Math.floor(diff / (60 * 1000));
118
+ return minutes === 0 ? 'Just now' : `${minutes}m ago`;
119
+ } else if (diff < 24 * 60 * 60 * 1000) {
120
+ const hours = Math.floor(diff / (60 * 60 * 1000));
121
+ return `${hours}h ago`;
122
+ } else if (diff < 7 * 24 * 60 * 60 * 1000) {
123
+ const days = Math.floor(diff / (24 * 60 * 60 * 1000));
124
+ return `${days}d ago`;
125
+ } else if (diff < 30 * 24 * 60 * 60 * 1000) {
126
+ const weeks = Math.floor(diff / (7 * 24 * 60 * 60 * 1000));
127
+ return `${weeks}w ago`;
128
+ } else if (diff < 365 * 24 * 60 * 60 * 1000) {
129
+ const months = Math.floor(diff / (30 * 24 * 60 * 60 * 1000));
130
+ return `${months}mo ago`;
131
+ }
132
+ const years = Math.floor(diff / (365 * 24 * 60 * 60 * 1000));
133
+ return `${years}y ago`;
134
+ }, []);
135
+
136
+ // 格式化历史记录
137
+ const formatHistory = useCallback(
138
+ (list: IChatHistoryItem[]) => {
139
+ const now = new Date();
140
+ const result = [] as { key: string; items: typeof list }[];
141
+
142
+ list.forEach((item: IChatHistoryItem) => {
143
+ const updatedAt = new Date(item.updatedAt);
144
+ const diff = now.getTime() - updatedAt.getTime();
145
+ const key = getTimeKey(diff);
146
+
147
+ const existingGroup = result.find((group) => group.key === key);
148
+ if (existingGroup) {
149
+ existingGroup.items.push(item);
150
+ } else {
151
+ result.push({ key, items: [item] });
152
+ }
153
+ });
154
+
155
+ return result;
156
+ },
157
+ [getTimeKey],
158
+ );
159
+
160
+ // 渲染历史记录项
161
+ const renderHistoryItem = useCallback(
162
+ (item: IChatHistoryItem) => (
163
+ <div
164
+ key={item.id}
165
+ className={cls('dm-chat-history-item', item.id === currentId ? 'dm-chat-history-item-selected' : '')}
166
+ onClick={() => handleHistoryItemSelect(item)}
167
+ >
168
+ <div className='dm-chat-history-item-content'>
169
+ {item.loading ? (
170
+ <Loading />
171
+ ) : (
172
+ <Icon icon='message' style={{ width: '16px', height: '16px', marginRight: 4 }} />
173
+ )}
174
+ {!historyTitleEditable?.[item.id] ? (
175
+ <span id={`dm-chat-history-item-title-${item.id}`} className='dm-chat-history-item-title'>
176
+ {item.title}
177
+ </span>
178
+ ) : (
179
+ <Input
180
+ className='dm-chat-history-item-title'
181
+ defaultValue={item.title}
182
+ ref={inputRef}
183
+ onPressEnter={(e: any) => {
184
+ handleTitleEditComplete(item, e.target.value);
185
+ }}
186
+ onBlur={() => handleTitleEditCancel(item)}
187
+ />
188
+ )}
189
+ </div>
190
+ <div className='dm-chat-history-item-actions'>
191
+ {/* <EditOutlined
192
+ title={localize('aiNative.operate.chatHistory.edit')}
193
+ style={{ marginRight: 8 }}
194
+ onClick={(e) => {
195
+ e.preventDefault();
196
+ e.stopPropagation();
197
+ handleTitleEdit(item);
198
+ }}
199
+ /> */}
200
+ <EnhanceIcon
201
+ className={cls('dm-chat-history-item-actions-delete', getIcon('delete'))}
202
+ onClick={(e) => {
203
+ e.preventDefault();
204
+ e.stopPropagation();
205
+ handleHistoryItemDelete(item);
206
+ }}
207
+ ariaLabel={localize('aiNative.operate.chatHistory.delete')}
208
+ />
209
+ </div>
210
+ </div>
211
+ ),
212
+ [
213
+ historyTitleEditable,
214
+ handleHistoryItemSelect,
215
+ handleTitleEditComplete,
216
+ handleTitleEditCancel,
217
+ handleTitleEdit,
218
+ handleHistoryItemDelete,
219
+ currentId,
220
+ inputRef,
221
+ ],
222
+ );
223
+
224
+ // 渲染历史记录列表
225
+ const renderHistory = useCallback(() => {
226
+ const filteredList = historyList
227
+ .slice(0, MAX_HISTORY_LIST)
228
+ .filter((item) => item.title && item.title.includes(searchValue));
229
+
230
+ const groupedHistoryList = formatHistory(filteredList);
231
+
232
+ return (
233
+ <div>
234
+ <Input
235
+ placeholder={localize('aiNative.operate.chatHistory.searchPlaceholder')}
236
+ style={{ width: '100%', maxWidth: '100%' }}
237
+ value={searchValue}
238
+ onChange={handleSearchChange}
239
+ />
240
+ <div className='dm-chat-history-list'>
241
+ {groupedHistoryList.map((group) => (
242
+ <div key={group.key} style={{ padding: '4px' }}>
243
+ <div className='dm-chat-history-time'>{group.key}</div>
244
+ {group.items.map(renderHistoryItem)}
245
+ </div>
246
+ ))}
247
+ </div>
248
+ </div>
249
+ );
250
+ }, [historyList, searchValue, formatHistory, handleSearchChange, renderHistoryItem]);
251
+
252
+ // getPopupContainer 处理函数
253
+ const getPopupContainer = useCallback((triggerNode: HTMLElement) => triggerNode.parentElement!, []);
254
+
255
+ return (
256
+ <div className={cls('dm-chat-history-header', className)}>
257
+ <div className='dm-chat-history-header-title'>
258
+ <span>{title}</span>
259
+ </div>
260
+ <div className='dm-chat-history-header-actions'>
261
+ <Popover
262
+ id='dm-chat-history-header-actions-history'
263
+ content={renderHistory()}
264
+ trigger={PopoverTriggerType.click}
265
+ position={PopoverPosition.bottomRight}
266
+ title={localize('aiNative.operate.chatHistory.title')}
267
+ getPopupContainer={getPopupContainer}
268
+ >
269
+ <div
270
+ className='dm-chat-history-header-actions-history'
271
+ title={localize('aiNative.operate.chatHistory.title')}
272
+ >
273
+ <EnhanceIcon className={cls('dm-chat-history-header-actions-history', 'codicon codicon-history')} />
274
+ </div>
275
+ </Popover>
276
+ <Popover
277
+ id={'ai-chat-header-close'}
278
+ position={PopoverPosition.top}
279
+ title={localize('aiNative.operate.newChat.title')}
280
+ >
281
+ <EnhanceIcon
282
+ className={cls('dm-chat-history-header-actions-new', getIcon('plus'))}
283
+ onClick={handleNewChat}
284
+ />
285
+ </Popover>
286
+ </div>
287
+ </div>
288
+ );
289
+ },
290
+ );
291
+
292
+ export default ChatHistory;
@@ -360,7 +360,11 @@ export const ChatReply = (props: IChatReplyProps) => {
360
360
 
361
361
  return (
362
362
  <ChatThinkingResult
363
- hasMessage={request.response.responseParts.length > 0 || !!request.response.errorDetails?.message}
363
+ hasMessage={
364
+ request.response.responseParts.length > 0 ||
365
+ request.response.responseContents.length > 0 ||
366
+ !!request.response.errorDetails?.message
367
+ }
364
368
  onRegenerate={handleRegenerate}
365
369
  requestId={request.requestId}
366
370
  >
@@ -118,7 +118,7 @@ export const ChatThinkingResult = ({
118
118
 
119
119
  const isRenderRegenerate = useMemo(() => {
120
120
  if (isUndefined(showRegenerate)) {
121
- return latestRequestId === requestId;
121
+ return latestRequestId === requestId && !!requestId;
122
122
  }
123
123
 
124
124
  return !!showRegenerate;
@@ -0,0 +1,139 @@
1
+ .dm-chat-history-header {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: space-between;
5
+ font-size: 13px;
6
+ padding: 0 4px 0 12px;
7
+ color: var(--editor-foreground);
8
+ text-overflow: ellipsis;
9
+ white-space: nowrap;
10
+
11
+ .dm-chat-history-header-title {
12
+ opacity: 0.6;
13
+ display: flex;
14
+ align-items: center;
15
+ overflow: hidden;
16
+ span {
17
+ overflow: hidden;
18
+ text-overflow: ellipsis;
19
+ max-width: calc(100vw - 80px);
20
+ display: inline-block;
21
+ white-space: nowrap;
22
+ }
23
+ }
24
+
25
+ .dm-chat-history-header-actions {
26
+ display: flex;
27
+ align-items: center;
28
+ font-size: 12px;
29
+
30
+ .dm-chat-history-header-actions-history {
31
+ cursor: pointer;
32
+ }
33
+
34
+ .dm-chat-history-header-actions-new {
35
+ margin-left: 2px;
36
+ cursor: pointer;
37
+ }
38
+
39
+ .kt-popover-title {
40
+ margin-bottom: 8px;
41
+ }
42
+ }
43
+
44
+ .kt-popover {
45
+ .kt-popover-content {
46
+ width: 300px;
47
+ }
48
+ .kt-popover-content {
49
+ color: var(--editor-foreground);
50
+ background-color: var(--editor-background);
51
+ padding: 12px 12px;
52
+ border-radius: 3px;
53
+ box-shadow: 0 0 8px 2px var(--widget-shadow);
54
+ }
55
+ .kt-popover-title {
56
+ color: var(--editor-foreground);
57
+ font-size: 13px;
58
+ }
59
+ .kt-input {
60
+ color: var(--input-foreground);
61
+ background: var(--input-background);
62
+ border: 1px solid var(--input-border);
63
+ text-align: left;
64
+ height: 24px;
65
+ font-size: 13px;
66
+ border-radius: 4px;
67
+ &:focus {
68
+ border-color: var(--inputValidation-infoBorder);
69
+ }
70
+
71
+ &::placeholder {
72
+ color: var(--input-placeholderForeground);
73
+ opacity: 0.6;
74
+ }
75
+ }
76
+ }
77
+
78
+ .dm-chat-history-list {
79
+ overflow: auto;
80
+ max-height: 400px;
81
+ margin-top: 4px;
82
+ font-size: 13px;
83
+ }
84
+
85
+ .dm-chat-history-time {
86
+ opacity: 0.6;
87
+ padding-left: 4px;
88
+ }
89
+
90
+ .dm-chat-history-item {
91
+ display: flex;
92
+ align-items: center;
93
+ justify-content: space-between;
94
+ cursor: pointer;
95
+ padding: 4px;
96
+ margin-top: 2px;
97
+ border-radius: 3px;
98
+
99
+ .dm-chat-history-item-content {
100
+ display: flex;
101
+ align-items: center;
102
+ width: 100%;
103
+ max-width: 100%;
104
+ height: 24px;
105
+ }
106
+
107
+ .dm-chat-history-item-title {
108
+ overflow: hidden;
109
+ text-overflow: ellipsis;
110
+ display: inline-block;
111
+ white-space: nowrap;
112
+ }
113
+
114
+ .dm-chat-history-item-actions {
115
+ display: none;
116
+ }
117
+
118
+ .dm-chat-history-item-selected {
119
+ background: var(--textPreformat-background);
120
+ }
121
+
122
+ &:hover {
123
+ background: var(--textPreformat-background);
124
+
125
+ .dm-chat-history-item-actions {
126
+ display: block;
127
+ }
128
+ .dm-chat-history-item-content {
129
+ max-width: calc(100% - 50px);
130
+ }
131
+ }
132
+ }
133
+
134
+ svg {
135
+ path {
136
+ fill: var(--foreground);
137
+ }
138
+ }
139
+ }
@@ -35,7 +35,7 @@
35
35
  }
36
36
 
37
37
  .thinking_text {
38
- font-size: 14px;
38
+ font-size: 12px;
39
39
  }
40
40
 
41
41
  .bottom_container {
@@ -480,7 +480,7 @@
480
480
  * welcome
481
481
  */
482
482
  .chat_welcome_head {
483
- font-size: 14px;
483
+ font-size: 12px;
484
484
  line-height: 22px;
485
485
  a {
486
486
  color: #3c8dff;
@@ -23,7 +23,7 @@
23
23
  .rce-mbox-text {
24
24
  line-height: 18px;
25
25
  width: inherit;
26
- font-size: 14px;
26
+ font-size: 12px;
27
27
  }
28
28
 
29
29
  .rce-smsg {
@@ -0,0 +1,97 @@
1
+ import React, { memo, useCallback, useMemo, useState } from 'react';
2
+
3
+ import { useInjectable } from '@opensumi/ide-core-browser';
4
+ import { Button, Icon } from '@opensumi/ide-core-browser/lib/components';
5
+ import { localize } from '@opensumi/ide-core-common';
6
+
7
+ import { IMCPServerToolComponentProps } from '../../../types';
8
+ import { RunCommandHandler } from '../handlers/RunCommand';
9
+
10
+ import styles from './index.module.less';
11
+
12
+ function getResult(raw: string) {
13
+ const result: {
14
+ isError?: boolean;
15
+ text?: string;
16
+ } = {};
17
+
18
+ try {
19
+ const data: {
20
+ content: { type: string; text: string }[];
21
+ isError?: boolean;
22
+ } = JSON.parse(raw);
23
+ if (data.isError) {
24
+ result.isError = data.isError;
25
+ }
26
+
27
+ if (data.content) {
28
+ result.text = data.content.map((item) => item.text).join('\n');
29
+ }
30
+
31
+ return result;
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ export const TerminalToolComponent = memo((props: IMCPServerToolComponentProps) => {
38
+ const { args, toolCallId } = props;
39
+ const handler = useInjectable<RunCommandHandler>(RunCommandHandler);
40
+ const [disabled, toggleDisabled] = useState(false);
41
+
42
+ const handleClick = useCallback((approval: boolean) => {
43
+ if (!toolCallId) {
44
+ return;
45
+ }
46
+ handler.handleApproval(toolCallId, approval);
47
+ toggleDisabled(true);
48
+ }, []);
49
+
50
+ const output = useMemo(() => {
51
+ if (props.result) {
52
+ return getResult(props.result);
53
+ }
54
+ return null;
55
+ }, [props]);
56
+
57
+ return (
58
+ <div className={styles.run_cmd_tool}>
59
+ {props.state === 'result' && (
60
+ <div>
61
+ <div className={styles.command_title}>
62
+ <Icon icon='terminal' />
63
+ <span>{localize('ai.native.mcp.terminal.output')}</span>
64
+ </div>
65
+ {output ? (
66
+ <div className={styles.command_content}>
67
+ <code>{output.text}</code>
68
+ </div>
69
+ ) : (
70
+ ''
71
+ )}
72
+ </div>
73
+ )}
74
+
75
+ {props.state === 'complete' && args?.require_user_approval && (
76
+ <div>
77
+ <div className={styles.command_title}>
78
+ <Icon icon='terminal' />
79
+ <span>{localize('ai.native.mcp.terminal.allow-question')}</span>
80
+ </div>
81
+ <p className={styles.command_content}>
82
+ <code>$ {args.command}</code>
83
+ </p>
84
+ <p className={styles.comand_description}>{args.explanation}</p>
85
+ <div className={styles.cmmand_footer}>
86
+ <Button type='link' size='small' disabled={disabled} onClick={() => handleClick(true)}>
87
+ {localize('ai.native.mcp.terminal.allow')}
88
+ </Button>
89
+ <Button type='link' size='small' disabled={disabled} onClick={() => handleClick(false)}>
90
+ {localize('ai.native.mcp.terminal.deny')}
91
+ </Button>
92
+ </div>
93
+ </div>
94
+ )}
95
+ </div>
96
+ );
97
+ });
@@ -130,3 +130,43 @@
130
130
  flex-basis: 0px;
131
131
  flex-grow: 1;
132
132
  }
133
+
134
+ .run_cmd_tool {
135
+ background-color: var(--design-chatInput-background);
136
+ padding: 10px;
137
+ border-radius: 4px;
138
+
139
+ .command_title {
140
+ display: flex;
141
+ align-items: center;
142
+ span {
143
+ margin-left: 5px;
144
+ }
145
+ }
146
+
147
+ .command_content {
148
+ padding: 4px;
149
+ font-size: 12px;
150
+ color: var(--design-text-foreground);
151
+ margin: 0px;
152
+ background-color: var(--terminal-background);
153
+ margin: 10px 0px;
154
+ border-radius: 4px;
155
+ overflow: auto;
156
+
157
+ code {
158
+ font-size: 12px;
159
+ white-space: pre;
160
+ }
161
+ }
162
+
163
+ .comand_description {
164
+ font-size: 11px;
165
+ color: var(--descriptionForeground);
166
+ }
167
+
168
+ .cmmand_footer {
169
+ display: flex;
170
+ justify-content: flex-end;
171
+ }
172
+ }