@opensumi/ide-ai-native 3.8.3-next-1741949132.0 → 3.8.3-next-1742183446.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.
- package/lib/browser/chat/chat-manager.service.d.ts +1 -1
- package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-manager.service.js +2 -3
- package/lib/browser/chat/chat-manager.service.js.map +1 -1
- package/lib/browser/chat/chat-model.d.ts.map +1 -1
- package/lib/browser/chat/chat-model.js +2 -11
- package/lib/browser/chat/chat-model.js.map +1 -1
- package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-proxy.service.js +0 -1
- package/lib/browser/chat/chat-proxy.service.js.map +1 -1
- package/lib/browser/chat/chat.feature.registry.d.ts +1 -4
- package/lib/browser/chat/chat.feature.registry.d.ts.map +1 -1
- package/lib/browser/chat/chat.feature.registry.js +0 -6
- package/lib/browser/chat/chat.feature.registry.js.map +1 -1
- package/lib/browser/chat/chat.internal.service.d.ts +1 -1
- package/lib/browser/chat/chat.internal.service.d.ts.map +1 -1
- package/lib/browser/chat/chat.internal.service.js +2 -2
- package/lib/browser/chat/chat.internal.service.js.map +1 -1
- package/lib/browser/chat/chat.view.d.ts.map +1 -1
- package/lib/browser/chat/chat.view.js +7 -10
- package/lib/browser/chat/chat.view.js.map +1 -1
- package/lib/browser/components/ChatEditor.d.ts +1 -2
- package/lib/browser/components/ChatEditor.d.ts.map +1 -1
- package/lib/browser/components/ChatEditor.js +2 -5
- package/lib/browser/components/ChatEditor.js.map +1 -1
- package/lib/browser/components/ChatInput.d.ts +1 -1
- package/lib/browser/components/ChatInput.d.ts.map +1 -1
- package/lib/browser/components/ChatInput.js +1 -1
- package/lib/browser/components/ChatInput.js.map +1 -1
- package/lib/browser/components/ChatMentionInput.d.ts +1 -3
- package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
- package/lib/browser/components/ChatMentionInput.js +3 -51
- package/lib/browser/components/ChatMentionInput.js.map +1 -1
- package/lib/browser/components/components.module.less +0 -49
- package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
- package/lib/browser/components/mention-input/mention-input.js +112 -74
- package/lib/browser/components/mention-input/mention-input.js.map +1 -1
- package/lib/browser/components/mention-input/mention-input.module.less +0 -1
- package/lib/browser/components/mention-input/types.d.ts +1 -3
- package/lib/browser/components/mention-input/types.d.ts.map +1 -1
- package/lib/browser/components/mention-input/types.js.map +1 -1
- package/lib/browser/context/llm-context.service.d.ts.map +1 -1
- package/lib/browser/context/llm-context.service.js.map +1 -1
- package/lib/browser/mcp/config/components/mcp-config.view.d.ts.map +1 -1
- package/lib/browser/mcp/config/components/mcp-config.view.js +17 -3
- package/lib/browser/mcp/config/components/mcp-config.view.js.map +1 -1
- package/lib/browser/mcp/mcp-server-proxy.service.d.ts +1 -0
- package/lib/browser/mcp/mcp-server-proxy.service.d.ts.map +1 -1
- package/lib/browser/model/msg-history-manager.d.ts +1 -1
- package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
- package/lib/browser/model/msg-history-manager.js.map +1 -1
- package/lib/browser/types.d.ts +1 -7
- package/lib/browser/types.d.ts.map +1 -1
- package/lib/browser/types.js.map +1 -1
- package/lib/common/index.d.ts +1 -6
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js.map +1 -1
- package/lib/common/llm-context.d.ts.map +1 -1
- package/lib/common/llm-context.js.map +1 -1
- package/lib/node/base-language-model.d.ts +1 -1
- package/lib/node/base-language-model.d.ts.map +1 -1
- package/lib/node/base-language-model.js +4 -15
- package/lib/node/base-language-model.js.map +1 -1
- package/package.json +23 -23
- package/src/browser/chat/chat-manager.service.ts +2 -3
- package/src/browser/chat/chat-model.ts +2 -10
- package/src/browser/chat/chat-proxy.service.ts +0 -1
- package/src/browser/chat/chat.feature.registry.ts +1 -10
- package/src/browser/chat/chat.internal.service.ts +2 -2
- package/src/browser/chat/chat.view.tsx +8 -18
- package/src/browser/components/ChatEditor.tsx +0 -8
- package/src/browser/components/ChatInput.tsx +2 -2
- package/src/browser/components/ChatMentionInput.tsx +6 -93
- package/src/browser/components/components.module.less +0 -49
- package/src/browser/components/mention-input/mention-input.module.less +0 -1
- package/src/browser/components/mention-input/mention-input.tsx +129 -81
- package/src/browser/components/mention-input/types.ts +1 -3
- package/src/browser/context/llm-context.service.ts +0 -2
- package/src/browser/mcp/config/components/mcp-config.view.tsx +17 -3
- package/src/browser/model/msg-history-manager.ts +1 -1
- package/src/browser/types.ts +1 -8
- package/src/common/index.ts +8 -7
- package/src/common/llm-context.ts +0 -2
- package/src/node/base-language-model.ts +3 -25
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import { DataContent } from 'ai';
|
|
2
1
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
2
|
|
|
4
|
-
import { Image } from '@opensumi/ide-components/lib/image';
|
|
5
3
|
import { LabelService, RecentFilesManager, useInjectable } from '@opensumi/ide-core-browser';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { getIcon } from '@opensumi/ide-core-browser/lib/components';
|
|
5
|
+
import { URI, localize } from '@opensumi/ide-core-common';
|
|
8
6
|
import { CommandService } from '@opensumi/ide-core-common/lib/command';
|
|
9
7
|
import { WorkbenchEditorService } from '@opensumi/ide-editor';
|
|
10
8
|
import { FileSearchServicePath, IFileSearchService } from '@opensumi/ide-file-search';
|
|
11
|
-
import { IMessageService } from '@opensumi/ide-overlay';
|
|
12
9
|
import { IWorkspaceService } from '@opensumi/ide-workspace';
|
|
13
10
|
|
|
14
11
|
import { IChatInternalService } from '../../common';
|
|
15
|
-
import { ChatFeatureRegistry } from '../chat/chat.feature.registry';
|
|
16
12
|
import { ChatInternalService } from '../chat/chat.internal.service';
|
|
17
13
|
import { OPEN_MCP_CONFIG_COMMAND } from '../mcp/config/mcp-config.commands';
|
|
18
14
|
|
|
@@ -21,13 +17,7 @@ import { MentionInput } from './mention-input/mention-input';
|
|
|
21
17
|
import { FooterButtonPosition, FooterConfig, MentionItem, MentionType } from './mention-input/types';
|
|
22
18
|
|
|
23
19
|
export interface IChatMentionInputProps {
|
|
24
|
-
onSend: (
|
|
25
|
-
value: string,
|
|
26
|
-
images?: string[],
|
|
27
|
-
agentId?: string,
|
|
28
|
-
command?: string,
|
|
29
|
-
option?: { model: string; [key: string]: any },
|
|
30
|
-
) => void;
|
|
20
|
+
onSend: (value: string, agentId?: string, command?: string, option?: { model: string; [key: string]: any }) => void;
|
|
31
21
|
onValueChange?: (value: string) => void;
|
|
32
22
|
onExpand?: (value: boolean) => void;
|
|
33
23
|
placeholder?: string;
|
|
@@ -36,7 +26,6 @@ export interface IChatMentionInputProps {
|
|
|
36
26
|
sendBtnClassName?: string;
|
|
37
27
|
defaultHeight?: number;
|
|
38
28
|
value?: string;
|
|
39
|
-
images?: Array<DataContent | URL>;
|
|
40
29
|
autoFocus?: boolean;
|
|
41
30
|
theme?: string | null;
|
|
42
31
|
setTheme: (theme: string | null) => void;
|
|
@@ -52,7 +41,6 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
|
|
|
52
41
|
const { onSend, disabled = false } = props;
|
|
53
42
|
|
|
54
43
|
const [value, setValue] = useState(props.value || '');
|
|
55
|
-
const [images, setImages] = useState(props.images || []);
|
|
56
44
|
const aiChatService = useInjectable<ChatInternalService>(IChatInternalService);
|
|
57
45
|
const commandService = useInjectable<CommandService>(CommandService);
|
|
58
46
|
const searchService = useInjectable<IFileSearchService>(FileSearchServicePath);
|
|
@@ -60,8 +48,7 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
|
|
|
60
48
|
const workspaceService = useInjectable<IWorkspaceService>(IWorkspaceService);
|
|
61
49
|
const editorService = useInjectable<WorkbenchEditorService>(WorkbenchEditorService);
|
|
62
50
|
const labelService = useInjectable<LabelService>(LabelService);
|
|
63
|
-
|
|
64
|
-
const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
|
|
51
|
+
|
|
65
52
|
const handleShowMCPConfig = React.useCallback(() => {
|
|
66
53
|
commandService.executeCommand(OPEN_MCP_CONFIG_COMMAND.id);
|
|
67
54
|
}, [commandService]);
|
|
@@ -252,24 +239,6 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
|
|
|
252
239
|
onClick: handleShowMCPConfig,
|
|
253
240
|
position: FooterButtonPosition.LEFT,
|
|
254
241
|
},
|
|
255
|
-
{
|
|
256
|
-
id: 'upload-image',
|
|
257
|
-
iconClass: 'codicon codicon-file-media',
|
|
258
|
-
title: 'Upload Image',
|
|
259
|
-
onClick: () => {
|
|
260
|
-
const input = document.createElement('input');
|
|
261
|
-
input.type = 'file';
|
|
262
|
-
input.accept = 'image/*';
|
|
263
|
-
input.onchange = (e) => {
|
|
264
|
-
const file = (e.target as HTMLInputElement).files?.[0];
|
|
265
|
-
if (file) {
|
|
266
|
-
handleImageUpload(file);
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
input.click();
|
|
270
|
-
},
|
|
271
|
-
position: FooterButtonPosition.LEFT,
|
|
272
|
-
},
|
|
273
242
|
],
|
|
274
243
|
showModelSelector: true,
|
|
275
244
|
}),
|
|
@@ -285,47 +254,13 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
|
|
|
285
254
|
if (disabled) {
|
|
286
255
|
return;
|
|
287
256
|
}
|
|
288
|
-
onSend(
|
|
289
|
-
content,
|
|
290
|
-
images.map((image) => image.toString()),
|
|
291
|
-
undefined,
|
|
292
|
-
undefined,
|
|
293
|
-
option,
|
|
294
|
-
);
|
|
295
|
-
setImages(props.images || []);
|
|
296
|
-
},
|
|
297
|
-
[onSend, images, disabled],
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
const handleImageUpload = useCallback(
|
|
301
|
-
async (file: File) => {
|
|
302
|
-
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif'];
|
|
303
|
-
if (!allowedTypes.includes(file.type)) {
|
|
304
|
-
messageService.error('Only JPG, PNG, WebP and GIF images are supported');
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const imageUploadProvider = chatFeatureRegistry.getImageUploadProvider();
|
|
309
|
-
if (!imageUploadProvider) {
|
|
310
|
-
messageService.error('No image upload provider found');
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
const data = await imageUploadProvider.imageUpload(file);
|
|
314
|
-
setImages([...images, data]);
|
|
257
|
+
onSend(content, undefined, undefined, option);
|
|
315
258
|
},
|
|
316
|
-
[
|
|
317
|
-
);
|
|
318
|
-
|
|
319
|
-
const handleDeleteImage = useCallback(
|
|
320
|
-
(index: number) => {
|
|
321
|
-
setImages(images.filter((_, i) => i !== index));
|
|
322
|
-
},
|
|
323
|
-
[images],
|
|
259
|
+
[onSend, editorService, disabled],
|
|
324
260
|
);
|
|
325
261
|
|
|
326
262
|
return (
|
|
327
263
|
<div className={styles.chat_input_container}>
|
|
328
|
-
{images.length > 0 && <ImagePreviewer images={images} onDelete={handleDeleteImage} />}
|
|
329
264
|
<MentionInput
|
|
330
265
|
mentionItems={defaultMenuItems}
|
|
331
266
|
onSend={handleSend}
|
|
@@ -335,29 +270,7 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
|
|
|
335
270
|
workspaceService={workspaceService}
|
|
336
271
|
placeholder={localize('aiNative.chat.input.placeholder.default')}
|
|
337
272
|
footerConfig={defaultMentionInputFooterOptions}
|
|
338
|
-
onImageUpload={handleImageUpload}
|
|
339
273
|
/>
|
|
340
274
|
</div>
|
|
341
275
|
);
|
|
342
276
|
};
|
|
343
|
-
|
|
344
|
-
const ImagePreviewer = ({
|
|
345
|
-
images,
|
|
346
|
-
onDelete,
|
|
347
|
-
}: {
|
|
348
|
-
images: Array<DataContent | URL>;
|
|
349
|
-
onDelete: (index: number) => void;
|
|
350
|
-
}) => (
|
|
351
|
-
<div>
|
|
352
|
-
<div className={styles.thumbnail_container}>
|
|
353
|
-
{images.map((image, index) => (
|
|
354
|
-
<div key={index} className={styles.thumbnail}>
|
|
355
|
-
<Image src={image.toString()} />
|
|
356
|
-
<button onClick={() => onDelete(index)} className={styles.delete_button}>
|
|
357
|
-
<Icon iconClass='codicon codicon-close' />
|
|
358
|
-
</button>
|
|
359
|
-
</div>
|
|
360
|
-
))}
|
|
361
|
-
</div>
|
|
362
|
-
</div>
|
|
363
|
-
);
|
|
@@ -601,52 +601,3 @@
|
|
|
601
601
|
vertical-align: middle;
|
|
602
602
|
font-size: 12px;
|
|
603
603
|
}
|
|
604
|
-
|
|
605
|
-
.thumbnail_container {
|
|
606
|
-
display: flex;
|
|
607
|
-
gap: 4px;
|
|
608
|
-
padding: 4px 12px;
|
|
609
|
-
.thumbnail {
|
|
610
|
-
width: 36px;
|
|
611
|
-
height: 36px;
|
|
612
|
-
border-radius: 4px;
|
|
613
|
-
position: relative;
|
|
614
|
-
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.3);
|
|
615
|
-
padding: 2px;
|
|
616
|
-
display: inline-flex;
|
|
617
|
-
align-items: center;
|
|
618
|
-
img {
|
|
619
|
-
width: 100%;
|
|
620
|
-
height: 100%;
|
|
621
|
-
object-fit: cover;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
.delete_button {
|
|
625
|
-
position: absolute;
|
|
626
|
-
top: -5px;
|
|
627
|
-
right: -5px;
|
|
628
|
-
font-size: 12px;
|
|
629
|
-
padding: 2px;
|
|
630
|
-
border: 0;
|
|
631
|
-
border-radius: 50%;
|
|
632
|
-
transition: transform 0.2s ease-in-out;
|
|
633
|
-
width: 16px;
|
|
634
|
-
height: 16px;
|
|
635
|
-
display: flex;
|
|
636
|
-
justify-content: center;
|
|
637
|
-
align-items: center;
|
|
638
|
-
background-color: var(--badge-background);
|
|
639
|
-
&:hover {
|
|
640
|
-
cursor: pointer;
|
|
641
|
-
transform: scale(1.2);
|
|
642
|
-
}
|
|
643
|
-
:global(.codicon) {
|
|
644
|
-
font-size: 12px;
|
|
645
|
-
color: var(--badge-foreground);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
.image_wrapper {
|
|
651
|
-
display: inline-block;
|
|
652
|
-
}
|
|
@@ -18,7 +18,6 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
18
18
|
loading = false,
|
|
19
19
|
mentionKeyword = MENTION_KEYWORD,
|
|
20
20
|
onSelectionChange,
|
|
21
|
-
onImageUpload,
|
|
22
21
|
labelService,
|
|
23
22
|
workspaceService,
|
|
24
23
|
placeholder = 'Ask anything, @ to mention',
|
|
@@ -276,21 +275,6 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
276
275
|
}
|
|
277
276
|
};
|
|
278
277
|
|
|
279
|
-
// 处理图片粘贴事件
|
|
280
|
-
const handlePaste = (e: React.ClipboardEvent<HTMLDivElement>) => {
|
|
281
|
-
const items = e.clipboardData.items;
|
|
282
|
-
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
283
|
-
for (let i = 0; i < items.length; i++) {
|
|
284
|
-
if (items[i].kind === 'file' && items[i].type.startsWith('image/')) {
|
|
285
|
-
const file = items[i].getAsFile();
|
|
286
|
-
if (file && onImageUpload) {
|
|
287
|
-
e.preventDefault();
|
|
288
|
-
onImageUpload(file);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
};
|
|
293
|
-
|
|
294
278
|
// 处理键盘事件
|
|
295
279
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
296
280
|
// 如果按下ESC键且提及面板处于活动状态或内联搜索处于活动状态
|
|
@@ -334,14 +318,74 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
334
318
|
inlineSearchStartPos: null,
|
|
335
319
|
loading: false,
|
|
336
320
|
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 处理上下方向键导航历史记录
|
|
324
|
+
if ((e.key === 'ArrowUp' || e.key === 'ArrowDown') && !e.shiftKey && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
325
|
+
// 只有在非提及面板激活状态下才处理历史导航
|
|
326
|
+
if (!mentionState.active && !mentionState.inlineSearchActive && editorRef.current && history.length > 0) {
|
|
327
|
+
const currentContent = editorRef.current.innerHTML;
|
|
328
|
+
|
|
329
|
+
// 检查是否应该触发历史导航
|
|
330
|
+
const shouldTriggerHistory =
|
|
331
|
+
// 当前内容为空
|
|
332
|
+
!currentContent ||
|
|
333
|
+
currentContent === '<br>' ||
|
|
334
|
+
// 或者当前内容与历史记录中的某一项匹配(正在浏览历史)
|
|
335
|
+
(isNavigatingHistory && historyIndex >= 0 && history[history.length - 1 - historyIndex] === currentContent);
|
|
336
|
+
|
|
337
|
+
if (shouldTriggerHistory) {
|
|
338
|
+
e.preventDefault();
|
|
339
|
+
|
|
340
|
+
// 如果是第一次按上下键,保存当前输入
|
|
341
|
+
if (!isNavigatingHistory) {
|
|
342
|
+
setCurrentInput(currentContent);
|
|
343
|
+
setIsNavigatingHistory(true);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 计算新的历史索引
|
|
347
|
+
let newIndex = historyIndex;
|
|
348
|
+
if (e.key === 'ArrowUp') {
|
|
349
|
+
// 向上导航到较早的历史记录
|
|
350
|
+
newIndex = Math.min(history.length - 1, historyIndex + 1);
|
|
351
|
+
} else {
|
|
352
|
+
// 向下导航到较新的历史记录
|
|
353
|
+
newIndex = Math.max(-1, historyIndex - 1);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
setHistoryIndex(newIndex);
|
|
337
357
|
|
|
338
|
-
|
|
358
|
+
// 更新编辑器内容
|
|
359
|
+
if (newIndex === -1) {
|
|
360
|
+
// 恢复到当前输入
|
|
361
|
+
editorRef.current.innerHTML = currentInput;
|
|
362
|
+
} else {
|
|
363
|
+
// 显示历史记录
|
|
364
|
+
editorRef.current.innerHTML = history[history.length - 1 - newIndex];
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// 将光标移到末尾
|
|
368
|
+
const range = document.createRange();
|
|
369
|
+
range.selectNodeContents(editorRef.current);
|
|
370
|
+
range.collapse(false);
|
|
371
|
+
const selection = window.getSelection();
|
|
372
|
+
if (selection) {
|
|
373
|
+
selection.removeAllRanges();
|
|
374
|
+
selection.addRange(range);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
} else if (isNavigatingHistory && e.key !== 'ArrowUp' && e.key !== 'ArrowDown') {
|
|
381
|
+
// 如果用户在浏览历史记录后开始输入其他内容,退出历史导航模式
|
|
382
|
+
setIsNavigatingHistory(false);
|
|
383
|
+
setHistoryIndex(-1);
|
|
339
384
|
}
|
|
340
385
|
|
|
341
386
|
// 添加对 Enter 键的处理,只有在按下 Shift+Enter 时才允许换行
|
|
342
387
|
if (e.key === 'Enter') {
|
|
343
388
|
// 检查是否是输入法的回车键
|
|
344
|
-
// isComposing 属性表示是否正在进行输入法组合输入
|
|
345
389
|
if (e.nativeEvent.isComposing) {
|
|
346
390
|
return; // 如果是输入法组合输入过程中的回车,不做任何处理
|
|
347
391
|
}
|
|
@@ -355,57 +399,6 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
355
399
|
}
|
|
356
400
|
}
|
|
357
401
|
|
|
358
|
-
// 处理上下方向键导航历史记录
|
|
359
|
-
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
|
360
|
-
// 只有在非提及面板激活状态下才处理历史导航
|
|
361
|
-
if (!mentionState.active && !mentionState.inlineSearchActive && editorRef.current) {
|
|
362
|
-
e.preventDefault();
|
|
363
|
-
|
|
364
|
-
// 如果是第一次按上下键,保存当前输入
|
|
365
|
-
if (!isNavigatingHistory) {
|
|
366
|
-
setCurrentInput(editorRef.current.innerHTML);
|
|
367
|
-
setIsNavigatingHistory(true);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// 计算新的历史索引
|
|
371
|
-
let newIndex = historyIndex;
|
|
372
|
-
if (e.key === 'ArrowUp') {
|
|
373
|
-
// 向上导航到较早的历史记录
|
|
374
|
-
newIndex = Math.min(history.length - 1, historyIndex + 1);
|
|
375
|
-
} else {
|
|
376
|
-
// 向下导航到较新的历史记录
|
|
377
|
-
newIndex = Math.max(-1, historyIndex - 1);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
setHistoryIndex(newIndex);
|
|
381
|
-
|
|
382
|
-
// 更新编辑器内容
|
|
383
|
-
if (newIndex === -1) {
|
|
384
|
-
// 恢复到当前输入
|
|
385
|
-
editorRef.current.innerHTML = currentInput;
|
|
386
|
-
} else {
|
|
387
|
-
// 显示历史记录
|
|
388
|
-
editorRef.current.innerHTML = history[history.length - 1 - newIndex];
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// 将光标移到末尾
|
|
392
|
-
const range = document.createRange();
|
|
393
|
-
range.selectNodeContents(editorRef.current);
|
|
394
|
-
range.collapse(false);
|
|
395
|
-
const selection = window.getSelection();
|
|
396
|
-
if (selection) {
|
|
397
|
-
selection.removeAllRanges();
|
|
398
|
-
selection.addRange(range);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
} else if (isNavigatingHistory && e.key !== 'ArrowUp' && e.key !== 'ArrowDown') {
|
|
404
|
-
// 如果用户在浏览历史记录后开始输入,退出历史导航模式
|
|
405
|
-
setIsNavigatingHistory(false);
|
|
406
|
-
setHistoryIndex(-1);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
402
|
// 如果提及面板未激活,不处理其他键盘事件
|
|
410
403
|
if (!mentionState.active) {
|
|
411
404
|
return;
|
|
@@ -420,8 +413,6 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
420
413
|
filteredItems = filteredItems.filter((item) => item.text.toLowerCase().includes(searchText));
|
|
421
414
|
}
|
|
422
415
|
|
|
423
|
-
// 二级菜单过滤已经在 getCurrentItems 中处理
|
|
424
|
-
|
|
425
416
|
if (filteredItems.length === 0) {
|
|
426
417
|
return;
|
|
427
418
|
}
|
|
@@ -447,14 +438,6 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
447
438
|
e.preventDefault();
|
|
448
439
|
}
|
|
449
440
|
}
|
|
450
|
-
|
|
451
|
-
// 处理 Backspace 键,检查是否需要清空编辑器
|
|
452
|
-
if (e.key === 'Backspace' && editorRef.current) {
|
|
453
|
-
const content = editorRef.current.innerHTML;
|
|
454
|
-
if (content === '<br>' || content === '<br/>') {
|
|
455
|
-
editorRef.current.innerHTML = '';
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
441
|
};
|
|
459
442
|
|
|
460
443
|
// 添加对输入法事件的处理
|
|
@@ -463,6 +446,71 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
463
446
|
// 这里可以添加额外的逻辑,如果需要的话
|
|
464
447
|
};
|
|
465
448
|
|
|
449
|
+
// 添加粘贴事件处理
|
|
450
|
+
const handlePaste = (e: React.ClipboardEvent<HTMLDivElement>) => {
|
|
451
|
+
// 阻止默认粘贴行为
|
|
452
|
+
e.preventDefault();
|
|
453
|
+
|
|
454
|
+
// 获取剪贴板中的纯文本内容
|
|
455
|
+
const text = e.clipboardData.getData('text/plain');
|
|
456
|
+
|
|
457
|
+
// 处理文本,保留换行和缩进
|
|
458
|
+
const processedText = text
|
|
459
|
+
// 将制表符转换为4个空格
|
|
460
|
+
.replace(/\t/g, ' ')
|
|
461
|
+
// 将连续的换行符转换为单个换行
|
|
462
|
+
.replace(/\n\s*\n/g, '\n\n')
|
|
463
|
+
// 移除行尾空格
|
|
464
|
+
.replace(/[ \t]+$/gm, '');
|
|
465
|
+
|
|
466
|
+
const selection = window.getSelection();
|
|
467
|
+
if (!selection || !selection.rangeCount) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const range = selection.getRangeAt(0);
|
|
472
|
+
range.deleteContents();
|
|
473
|
+
|
|
474
|
+
// 将处理后的文本按行分割
|
|
475
|
+
const lines = processedText.split('\n');
|
|
476
|
+
const fragment = document.createDocumentFragment();
|
|
477
|
+
|
|
478
|
+
lines.forEach((line, index) => {
|
|
479
|
+
// 处理行首空格,将每个空格转换为
|
|
480
|
+
const processedLine = line.replace(/^[ ]+/g, (match) => {
|
|
481
|
+
const span = document.createElement('span');
|
|
482
|
+
span.innerHTML = '\u00A0'.repeat(match.length);
|
|
483
|
+
return span.innerHTML;
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// 创建一个临时容器来保持 HTML 内容
|
|
487
|
+
const container = document.createElement('span');
|
|
488
|
+
container.innerHTML = processedLine;
|
|
489
|
+
|
|
490
|
+
// 将容器的内容添加到文档片段
|
|
491
|
+
while (container.firstChild) {
|
|
492
|
+
fragment.appendChild(container.firstChild);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// 如果不是最后一行,添加换行符
|
|
496
|
+
if (index < lines.length - 1) {
|
|
497
|
+
fragment.appendChild(document.createElement('br'));
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// 插入处理后的内容
|
|
502
|
+
range.insertNode(fragment);
|
|
503
|
+
|
|
504
|
+
// 将光标移动到插入内容的末尾
|
|
505
|
+
range.setStartAfter(fragment);
|
|
506
|
+
range.setEndAfter(fragment);
|
|
507
|
+
selection.removeAllRanges();
|
|
508
|
+
selection.addRange(range);
|
|
509
|
+
|
|
510
|
+
// 触发 input 事件以更新状态
|
|
511
|
+
handleInput();
|
|
512
|
+
};
|
|
513
|
+
|
|
466
514
|
// 初始化编辑器
|
|
467
515
|
React.useEffect(() => {
|
|
468
516
|
if (editorRef.current) {
|
|
@@ -883,7 +931,7 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
883
931
|
title={button.title}
|
|
884
932
|
>
|
|
885
933
|
<EnhanceIcon
|
|
886
|
-
className={cls(
|
|
934
|
+
className={cls(getIcon(button.icon), styles[`${button.id}_logo`])}
|
|
887
935
|
tabIndex={0}
|
|
888
936
|
role='button'
|
|
889
937
|
ariaLabel={button.title}
|
|
@@ -917,8 +965,8 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
917
965
|
contentEditable={true}
|
|
918
966
|
onInput={handleInput}
|
|
919
967
|
onKeyDown={handleKeyDown}
|
|
920
|
-
onPaste={handlePaste}
|
|
921
968
|
onCompositionEnd={handleCompositionEnd}
|
|
969
|
+
onPaste={handlePaste}
|
|
922
970
|
/>
|
|
923
971
|
</div>
|
|
924
972
|
<div className={styles.footer}>
|
|
@@ -55,8 +55,7 @@ export enum MentionType {
|
|
|
55
55
|
|
|
56
56
|
interface FooterButton {
|
|
57
57
|
id: string;
|
|
58
|
-
icon
|
|
59
|
-
iconClass?: string;
|
|
58
|
+
icon: string;
|
|
60
59
|
title: string;
|
|
61
60
|
onClick?: () => void;
|
|
62
61
|
position: FooterButtonPosition;
|
|
@@ -76,7 +75,6 @@ export interface MentionInputProps {
|
|
|
76
75
|
placeholder?: string;
|
|
77
76
|
loading?: boolean;
|
|
78
77
|
onSelectionChange?: (value: string) => void;
|
|
79
|
-
onImageUpload?: (file: File) => void;
|
|
80
78
|
footerConfig?: FooterConfig; // 新增配置项
|
|
81
79
|
mentionKeyword?: string;
|
|
82
80
|
labelService?: LabelService;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { DataContent } from 'ai';
|
|
2
|
-
|
|
3
1
|
import { Autowired, Injectable } from '@opensumi/di';
|
|
4
2
|
import { AppConfig } from '@opensumi/ide-core-browser/lib/react-providers/config-provider';
|
|
5
3
|
import { WithEventBus } from '@opensumi/ide-core-common/lib/event-bus/event-decorator';
|
|
@@ -26,7 +26,21 @@ export const MCPConfigView: React.FC = () => {
|
|
|
26
26
|
const [editingServer, setEditingServer] = React.useState<MCPServerFormData | undefined>();
|
|
27
27
|
const [loadingServer, setLoadingServer] = React.useState<string | undefined>();
|
|
28
28
|
const loadServers = useCallback(async () => {
|
|
29
|
-
const
|
|
29
|
+
const userServers = preferenceService.get<MCPServerDescription[]>(AINativeSettingSectionsId.MCPServers, []);
|
|
30
|
+
const runningServers = await mcpServerProxyService.$getServers();
|
|
31
|
+
const builtinServer = runningServers.find((server) => server.name === BUILTIN_MCP_SERVER_NAME);
|
|
32
|
+
const allServers = userServers.map((server) => {
|
|
33
|
+
const runningServer = runningServers.find((s) => s.name === server.name);
|
|
34
|
+
return {
|
|
35
|
+
...server,
|
|
36
|
+
name: server.name,
|
|
37
|
+
isStarted: runningServer?.isStarted,
|
|
38
|
+
tools: runningServer?.tools,
|
|
39
|
+
};
|
|
40
|
+
}) as MCPServer[];
|
|
41
|
+
if (builtinServer) {
|
|
42
|
+
allServers.unshift(builtinServer);
|
|
43
|
+
}
|
|
30
44
|
setServers(allServers);
|
|
31
45
|
}, [mcpServerProxyService]);
|
|
32
46
|
|
|
@@ -93,8 +107,8 @@ export const MCPConfigView: React.FC = () => {
|
|
|
93
107
|
setLoadingServer(undefined);
|
|
94
108
|
} catch (error) {
|
|
95
109
|
const msg = error.message || error;
|
|
96
|
-
logger.error(`Failed to ${start ? 'start' : 'stop'} server ${serverName}:`,
|
|
97
|
-
messageService.error(
|
|
110
|
+
logger.error(`Failed to ${start ? 'start' : 'stop'} server ${serverName}:`, msg);
|
|
111
|
+
messageService.error(msg);
|
|
98
112
|
setLoadingServer(undefined);
|
|
99
113
|
}
|
|
100
114
|
},
|
|
@@ -62,7 +62,7 @@ export class MsgHistoryManager extends Disposable {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
public addUserMessage(
|
|
65
|
-
message: Required<Pick<IExcludeMessage, 'agentId' | 'agentCommand' | 'content' | 'relationId'
|
|
65
|
+
message: Required<Pick<IExcludeMessage, 'agentId' | 'agentCommand' | 'content' | 'relationId'>>,
|
|
66
66
|
): string {
|
|
67
67
|
return this.doAddMessage({
|
|
68
68
|
...message,
|
package/src/browser/types.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DataContent } from 'ai';
|
|
2
1
|
import React from 'react';
|
|
3
2
|
import { ZodSchema } from 'zod';
|
|
4
3
|
|
|
@@ -130,7 +129,6 @@ export interface IChatSlashCommandHandler {
|
|
|
130
129
|
}
|
|
131
130
|
|
|
132
131
|
export interface IChatFeatureRegistry {
|
|
133
|
-
registerImageUploadProvider(provider: IImageUploadProvider): void;
|
|
134
132
|
registerWelcome(content: IChatWelcomeMessageContent | React.ReactNode, sampleQuestions?: ISampleQuestions[]): void;
|
|
135
133
|
registerSlashCommand(command: IChatSlashCommandItem, handler: IChatSlashCommandHandler): void;
|
|
136
134
|
}
|
|
@@ -142,14 +140,13 @@ export type ChatWelcomeRender = (props: {
|
|
|
142
140
|
export type ChatAIRoleRender = (props: { content: string }) => React.ReactElement | React.JSX.Element;
|
|
143
141
|
export type ChatUserRoleRender = (props: {
|
|
144
142
|
content: string;
|
|
145
|
-
images?: string[];
|
|
146
143
|
agentId?: string;
|
|
147
144
|
command?: string;
|
|
148
145
|
}) => React.ReactElement | React.JSX.Element;
|
|
149
146
|
export type ChatThinkingRender = (props: { thinkingText?: string }) => React.ReactElement | React.JSX.Element;
|
|
150
147
|
export type ChatThinkingResultRender = (props: { thinkingResult?: string }) => React.ReactElement | React.JSX.Element;
|
|
151
148
|
export type ChatInputRender = (props: {
|
|
152
|
-
onSend: (value: string,
|
|
149
|
+
onSend: (value: string, agentId?: string, command?: string) => void;
|
|
153
150
|
onValueChange?: (value: string) => void;
|
|
154
151
|
onExpand?: (value: boolean) => void;
|
|
155
152
|
placeholder?: string;
|
|
@@ -292,10 +289,6 @@ export interface IProblemFixProviderRegistry {
|
|
|
292
289
|
registerHoverFixProvider(handler: IHoverFixHandler): void;
|
|
293
290
|
}
|
|
294
291
|
|
|
295
|
-
export interface IImageUploadProvider {
|
|
296
|
-
imageUpload(file: File): Promise<DataContent | URL>;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
292
|
export const AINativeCoreContribution = Symbol('AINativeCoreContribution');
|
|
300
293
|
|
|
301
294
|
export interface AINativeCoreContribution {
|
package/src/common/index.ts
CHANGED
|
@@ -52,10 +52,6 @@ export interface IChatMessageStructure {
|
|
|
52
52
|
* 用于 chat 面板展示
|
|
53
53
|
*/
|
|
54
54
|
message: string;
|
|
55
|
-
/**
|
|
56
|
-
* 图片
|
|
57
|
-
*/
|
|
58
|
-
images?: string[];
|
|
59
55
|
/**
|
|
60
56
|
* 实际调用的 prompt
|
|
61
57
|
*/
|
|
@@ -138,7 +134,14 @@ export interface ISumiMCPServerBackend {
|
|
|
138
134
|
initBuiltinMCPServer(enabled: boolean): void;
|
|
139
135
|
initExternalMCPServers(servers: MCPServerDescription[]): void;
|
|
140
136
|
getAllMCPTools(): Promise<MCPTool[]>;
|
|
141
|
-
getServers(): Promise<
|
|
137
|
+
getServers(): Promise<
|
|
138
|
+
Array<{
|
|
139
|
+
name: string;
|
|
140
|
+
isStarted: boolean;
|
|
141
|
+
type: string;
|
|
142
|
+
tools: MCPTool[];
|
|
143
|
+
}>
|
|
144
|
+
>;
|
|
142
145
|
startServer(serverName: string): Promise<void>;
|
|
143
146
|
stopServer(serverName: string): Promise<void>;
|
|
144
147
|
addOrUpdateServer(description: MCPServerDescription): void;
|
|
@@ -204,7 +207,6 @@ export interface IChatAgentRequest {
|
|
|
204
207
|
requestId: string;
|
|
205
208
|
command?: string;
|
|
206
209
|
message: string;
|
|
207
|
-
images?: string[];
|
|
208
210
|
regenerate?: boolean;
|
|
209
211
|
}
|
|
210
212
|
|
|
@@ -242,7 +244,6 @@ export type IChatFollowup = IChatReplyFollowup | IChatResponseCommandFollowup;
|
|
|
242
244
|
|
|
243
245
|
export interface IChatRequestMessage {
|
|
244
246
|
prompt: string;
|
|
245
|
-
images?: string[];
|
|
246
247
|
agentId: string;
|
|
247
248
|
command?: string;
|
|
248
249
|
}
|