@opensumi/ide-ai-native 3.8.1-next-1741080291.0 → 3.8.1-next-1741091353.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/ai-core.contribution.d.ts +0 -3
- package/lib/browser/ai-core.contribution.d.ts.map +1 -1
- package/lib/browser/ai-core.contribution.js +1 -45
- package/lib/browser/ai-core.contribution.js.map +1 -1
- package/lib/browser/chat/chat-agent.service.d.ts +8 -0
- package/lib/browser/chat/chat-agent.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-agent.service.js +32 -5
- package/lib/browser/chat/chat-agent.service.js.map +1 -1
- package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-manager.service.js +4 -0
- 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 +3 -2
- 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 +5 -9
- package/lib/browser/chat/chat-proxy.service.js.map +1 -1
- package/lib/browser/chat/chat.module.less +1 -2
- package/lib/browser/chat/chat.view.d.ts.map +1 -1
- package/lib/browser/chat/chat.view.js +13 -39
- package/lib/browser/chat/chat.view.js.map +1 -1
- package/lib/browser/components/ChatContext/index.js +2 -2
- package/lib/browser/components/ChatContext/index.js.map +1 -1
- package/lib/browser/components/ChatInput.d.ts.map +1 -1
- package/lib/browser/components/ChatInput.js +25 -1
- package/lib/browser/components/ChatInput.js.map +1 -1
- package/lib/browser/components/ChatToolRender.d.ts.map +1 -1
- package/lib/browser/components/ChatToolRender.js +2 -3
- package/lib/browser/components/ChatToolRender.js.map +1 -1
- package/lib/browser/components/chat-history.module.less +2 -1
- package/lib/browser/components/components.module.less +20 -0
- package/lib/browser/context/llm-context.service.d.ts +18 -5
- package/lib/browser/context/llm-context.service.d.ts.map +1 -1
- package/lib/browser/context/llm-context.service.js +80 -47
- package/lib/browser/context/llm-context.service.js.map +1 -1
- package/lib/browser/layout/layout.module.less +4 -4
- package/lib/browser/mcp/base-apply.service.d.ts +18 -7
- package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
- package/lib/browser/mcp/base-apply.service.js +185 -65
- package/lib/browser/mcp/base-apply.service.js.map +1 -1
- package/lib/browser/mcp/tools/components/EditFile.d.ts.map +1 -1
- package/lib/browser/mcp/tools/components/EditFile.js +15 -9
- package/lib/browser/mcp/tools/components/EditFile.js.map +1 -1
- package/lib/browser/mcp/tools/components/ExpandableFileList.d.ts +13 -0
- package/lib/browser/mcp/tools/components/ExpandableFileList.d.ts.map +1 -0
- package/lib/browser/mcp/tools/components/{SearchResult.js → ExpandableFileList.js} +29 -19
- package/lib/browser/mcp/tools/components/ExpandableFileList.js.map +1 -0
- package/lib/browser/mcp/tools/components/index.module.less +4 -0
- package/lib/browser/mcp/tools/createNewFileWithText.d.ts +1 -0
- package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -1
- package/lib/browser/mcp/tools/createNewFileWithText.js +19 -11
- package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -1
- package/lib/browser/mcp/tools/fileSearch.d.ts +1 -0
- package/lib/browser/mcp/tools/fileSearch.d.ts.map +1 -1
- package/lib/browser/mcp/tools/fileSearch.js +14 -5
- package/lib/browser/mcp/tools/fileSearch.js.map +1 -1
- package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts.map +1 -1
- package/lib/browser/mcp/tools/getDiagnosticsByPath.js +1 -0
- package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
- package/lib/browser/mcp/tools/grepSearch.d.ts.map +1 -1
- package/lib/browser/mcp/tools/grepSearch.js +6 -3
- package/lib/browser/mcp/tools/grepSearch.js.map +1 -1
- package/lib/browser/mcp/tools/handlers/EditFile.js +1 -1
- package/lib/browser/mcp/tools/handlers/EditFile.js.map +1 -1
- package/lib/browser/mcp/tools/handlers/ListDir.d.ts +1 -0
- package/lib/browser/mcp/tools/handlers/ListDir.d.ts.map +1 -1
- package/lib/browser/mcp/tools/handlers/ListDir.js +3 -0
- package/lib/browser/mcp/tools/handlers/ListDir.js.map +1 -1
- package/lib/browser/mcp/tools/handlers/RunCommand.d.ts.map +1 -1
- package/lib/browser/mcp/tools/handlers/RunCommand.js +2 -0
- package/lib/browser/mcp/tools/handlers/RunCommand.js.map +1 -1
- package/lib/browser/mcp/tools/listDir.d.ts +1 -0
- package/lib/browser/mcp/tools/listDir.d.ts.map +1 -1
- package/lib/browser/mcp/tools/listDir.js +35 -4
- package/lib/browser/mcp/tools/listDir.js.map +1 -1
- package/lib/browser/mcp/tools/runTerminalCmd.d.ts.map +1 -1
- package/lib/browser/mcp/tools/runTerminalCmd.js +1 -0
- package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
- package/lib/browser/model/msg-history-manager.d.ts +1 -0
- package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
- package/lib/browser/model/msg-history-manager.js +3 -0
- package/lib/browser/model/msg-history-manager.js.map +1 -1
- package/lib/browser/preferences/schema.d.ts.map +1 -1
- package/lib/browser/preferences/schema.js +1 -7
- package/lib/browser/preferences/schema.js.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff-manager.d.ts.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff-manager.js +68 -8
- package/lib/browser/widget/inline-diff/inline-diff-manager.js.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff-previewer.d.ts +10 -4
- package/lib/browser/widget/inline-diff/inline-diff-previewer.d.ts.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff-previewer.js +14 -3
- package/lib/browser/widget/inline-diff/inline-diff-previewer.js.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff-widget.module.less +25 -4
- package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts +3 -3
- package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff.controller.js +10 -5
- package/lib/browser/widget/inline-diff/inline-diff.controller.js.map +1 -1
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts +46 -17
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts.map +1 -1
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js +110 -53
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts +4 -0
- package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts.map +1 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js +26 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js.map +1 -1
- package/lib/common/index.d.ts +0 -1
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js +0 -2
- package/lib/common/index.js.map +1 -1
- package/lib/common/llm-context.d.ts +13 -9
- package/lib/common/llm-context.d.ts.map +1 -1
- package/lib/common/llm-context.js.map +1 -1
- package/lib/common/prompts/context-prompt-provider.d.ts +4 -3
- package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
- package/lib/common/prompts/context-prompt-provider.js +33 -22
- package/lib/common/prompts/context-prompt-provider.js.map +1 -1
- package/lib/common/types.d.ts +1 -0
- package/lib/common/types.d.ts.map +1 -1
- package/lib/node/anthropic/anthropic-language-model.d.ts +1 -3
- package/lib/node/anthropic/anthropic-language-model.d.ts.map +1 -1
- package/lib/node/anthropic/anthropic-language-model.js +2 -6
- package/lib/node/anthropic/anthropic-language-model.js.map +1 -1
- package/lib/node/base-language-model.d.ts +1 -4
- package/lib/node/base-language-model.d.ts.map +1 -1
- package/lib/node/base-language-model.js +6 -7
- package/lib/node/base-language-model.js.map +1 -1
- package/lib/node/deepseek/deepseek-language-model.d.ts +1 -3
- package/lib/node/deepseek/deepseek-language-model.d.ts.map +1 -1
- package/lib/node/deepseek/deepseek-language-model.js +2 -6
- package/lib/node/deepseek/deepseek-language-model.js.map +1 -1
- package/lib/node/openai/openai-language-model.d.ts +4 -5
- package/lib/node/openai/openai-language-model.d.ts.map +1 -1
- package/lib/node/openai/openai-language-model.js +7 -8
- package/lib/node/openai/openai-language-model.js.map +1 -1
- package/package.json +23 -24
- package/src/browser/ai-core.contribution.ts +1 -56
- package/src/browser/chat/chat-agent.service.ts +38 -7
- package/src/browser/chat/chat-manager.service.ts +6 -0
- package/src/browser/chat/chat-model.ts +11 -6
- package/src/browser/chat/chat-proxy.service.ts +6 -9
- package/src/browser/chat/chat.module.less +1 -2
- package/src/browser/chat/chat.view.tsx +14 -72
- package/src/browser/components/ChatContext/index.tsx +2 -2
- package/src/browser/components/ChatInput.tsx +67 -3
- package/src/browser/components/ChatToolRender.tsx +1 -2
- package/src/browser/components/chat-history.module.less +2 -1
- package/src/browser/components/components.module.less +20 -0
- package/src/browser/context/llm-context.service.ts +93 -54
- package/src/browser/layout/layout.module.less +4 -4
- package/src/browser/mcp/base-apply.service.ts +222 -67
- package/src/browser/mcp/tools/components/EditFile.tsx +16 -9
- package/src/browser/mcp/tools/components/ExpandableFileList.tsx +133 -0
- package/src/browser/mcp/tools/components/index.module.less +4 -0
- package/src/browser/mcp/tools/createNewFileWithText.ts +21 -12
- package/src/browser/mcp/tools/fileSearch.ts +14 -4
- package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -0
- package/src/browser/mcp/tools/grepSearch.ts +6 -3
- package/src/browser/mcp/tools/handlers/EditFile.ts +1 -1
- package/src/browser/mcp/tools/handlers/ListDir.ts +4 -0
- package/src/browser/mcp/tools/handlers/RunCommand.ts +2 -0
- package/src/browser/mcp/tools/listDir.ts +36 -5
- package/src/browser/mcp/tools/runTerminalCmd.ts +1 -0
- package/src/browser/model/msg-history-manager.ts +4 -0
- package/src/browser/preferences/schema.ts +1 -7
- package/src/browser/widget/inline-diff/inline-diff-manager.tsx +143 -21
- package/src/browser/widget/inline-diff/inline-diff-previewer.ts +25 -7
- package/src/browser/widget/inline-diff/inline-diff-widget.module.less +25 -4
- package/src/browser/widget/inline-diff/inline-diff.controller.ts +16 -8
- package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +139 -68
- package/src/browser/widget/inline-stream-diff/live-preview.decoration.tsx +30 -1
- package/src/common/index.ts +0 -2
- package/src/common/llm-context.ts +10 -4
- package/src/common/prompts/context-prompt-provider.ts +38 -29
- package/src/common/types.ts +1 -0
- package/src/node/anthropic/anthropic-language-model.ts +2 -7
- package/src/node/base-language-model.ts +12 -10
- package/src/node/deepseek/deepseek-language-model.ts +2 -7
- package/src/node/openai/openai-language-model.ts +9 -10
- package/lib/browser/mcp/tools/components/SearchResult.d.ts +0 -11
- package/lib/browser/mcp/tools/components/SearchResult.d.ts.map +0 -1
- package/lib/browser/mcp/tools/components/SearchResult.js.map +0 -1
- package/lib/common/model.d.ts +0 -12
- package/lib/common/model.d.ts.map +0 -1
- package/lib/common/model.js +0 -83
- package/lib/common/model.js.map +0 -1
- package/lib/node/openai-compatible/openai-compatible-language-model.d.ts +0 -10
- package/lib/node/openai-compatible/openai-compatible-language-model.d.ts.map +0 -1
- package/lib/node/openai-compatible/openai-compatible-language-model.js +0 -32
- package/lib/node/openai-compatible/openai-compatible-language-model.js.map +0 -1
- package/src/browser/mcp/tools/components/SearchResult.tsx +0 -92
- package/src/common/model.ts +0 -90
- package/src/node/openai-compatible/openai-compatible-language-model.ts +0 -30
|
@@ -6,12 +6,14 @@ import { WorkbenchEditorService } from '@opensumi/ide-editor';
|
|
|
6
6
|
import {
|
|
7
7
|
EditorGroupCloseEvent,
|
|
8
8
|
EditorGroupOpenEvent,
|
|
9
|
+
IEditorDocumentModelService,
|
|
9
10
|
RegisterEditorSideComponentEvent,
|
|
10
11
|
} from '@opensumi/ide-editor/lib/browser';
|
|
11
12
|
import { IMarkerService } from '@opensumi/ide-markers';
|
|
12
|
-
import { ICodeEditor, Position, Range, Selection, SelectionDirection } from '@opensumi/ide-monaco';
|
|
13
|
-
import { Deferred, Emitter, URI, path } from '@opensumi/ide-utils';
|
|
13
|
+
import { ICodeEditor, ITextModel, Position, Range, Selection, SelectionDirection } from '@opensumi/ide-monaco';
|
|
14
|
+
import { Deferred, DisposableMap, Emitter, IDisposable, URI, path } from '@opensumi/ide-utils';
|
|
14
15
|
import { SumiReadableStream } from '@opensumi/ide-utils/lib/stream';
|
|
16
|
+
import { EditOperation } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/editOperation';
|
|
15
17
|
|
|
16
18
|
import { IChatInternalService } from '../../common';
|
|
17
19
|
import { CodeBlockData, CodeBlockStatus } from '../../common/types';
|
|
@@ -23,15 +25,9 @@ import {
|
|
|
23
25
|
InlineDiffService,
|
|
24
26
|
LiveInlineDiffPreviewer,
|
|
25
27
|
} from '../widget/inline-diff';
|
|
26
|
-
import {
|
|
28
|
+
import { BaseInlineStreamDiffHandler } from '../widget/inline-stream-diff/inline-stream-diff.handler';
|
|
27
29
|
|
|
28
|
-
import { FileHandler } from './tools/handlers/ReadFile';
|
|
29
|
-
|
|
30
|
-
// 提供代码块的唯一索引,迭代轮次,生成状态管理(包括取消),关联文件位置这些信息的记录,后续并行 apply 的支持
|
|
31
30
|
export abstract class BaseApplyService extends WithEventBus {
|
|
32
|
-
@Autowired(FileHandler)
|
|
33
|
-
protected fileHandler: FileHandler;
|
|
34
|
-
|
|
35
31
|
@Autowired(IChatInternalService)
|
|
36
32
|
protected chatInternalService: ChatInternalService;
|
|
37
33
|
|
|
@@ -47,9 +43,14 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
47
43
|
@Autowired(IMarkerService)
|
|
48
44
|
private readonly markerService: IMarkerService;
|
|
49
45
|
|
|
46
|
+
@Autowired(IEditorDocumentModelService)
|
|
47
|
+
private readonly editorDocumentModelService: IEditorDocumentModelService;
|
|
48
|
+
|
|
50
49
|
private onCodeBlockUpdateEmitter = new Emitter<CodeBlockData>();
|
|
51
50
|
public onCodeBlockUpdate = this.onCodeBlockUpdateEmitter.event;
|
|
52
51
|
|
|
52
|
+
private currentSessionId?: string;
|
|
53
|
+
|
|
53
54
|
constructor() {
|
|
54
55
|
super();
|
|
55
56
|
this.addDispose(
|
|
@@ -57,6 +58,15 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
57
58
|
this.cancelAllApply();
|
|
58
59
|
}),
|
|
59
60
|
);
|
|
61
|
+
this.currentSessionId = this.chatInternalService.sessionModel.sessionId;
|
|
62
|
+
this.addDispose(
|
|
63
|
+
this.chatInternalService.onChangeSession((sessionId) => {
|
|
64
|
+
if (sessionId !== this.currentSessionId) {
|
|
65
|
+
this.cancelAllApply();
|
|
66
|
+
this.currentSessionId = sessionId;
|
|
67
|
+
}
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
60
70
|
this.addDispose(
|
|
61
71
|
this.chatInternalService.onRegenerateRequest(() => {
|
|
62
72
|
const messages = this.chatInternalService.sessionModel.history.getMessages();
|
|
@@ -85,30 +95,69 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
85
95
|
return message?.codeBlockMap;
|
|
86
96
|
}
|
|
87
97
|
|
|
88
|
-
private
|
|
98
|
+
private getSessionCodeBlocksForPath(relativePath: string, sessionId?: string) {
|
|
99
|
+
sessionId = sessionId || this.chatInternalService.sessionModel.sessionId;
|
|
100
|
+
const sessionModel = this.chatInternalService.getSession(sessionId);
|
|
101
|
+
if (!sessionModel) {
|
|
102
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
103
|
+
}
|
|
104
|
+
const sessionAdditionals = sessionModel.history.sessionAdditionals;
|
|
105
|
+
const codeBlocks: CodeBlockData[] = Array.from(sessionAdditionals.values())
|
|
106
|
+
.map((additional) => additional.codeBlockMap as { [toolCallId: string]: CodeBlockData })
|
|
107
|
+
.reduce((acc, cur) => {
|
|
108
|
+
Object.values(cur).forEach((block) => {
|
|
109
|
+
if (block.relativePath === relativePath) {
|
|
110
|
+
acc.push(block);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return acc;
|
|
114
|
+
}, [] as CodeBlockData[])
|
|
115
|
+
.sort((a, b) => b.version - a.version);
|
|
116
|
+
return codeBlocks;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private activePreviewerMap = this.registerDispose(
|
|
120
|
+
new DisposableMap<string, BaseInlineDiffPreviewer<BaseInlineStreamDiffHandler>>(),
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
private editorListenerMap = this.registerDispose(new DisposableMap<string, IDisposable>());
|
|
89
124
|
|
|
90
125
|
@OnEvent(EditorGroupCloseEvent)
|
|
91
126
|
onEditorGroupClose(event: EditorGroupCloseEvent) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
127
|
+
const relativePath = path.relative(this.appConfig.workspaceDir, event.payload.resource.uri.path.toString());
|
|
128
|
+
const activePreviewer = this.activePreviewerMap.get(relativePath);
|
|
129
|
+
if (activePreviewer) {
|
|
130
|
+
this.activePreviewerMap.disposeKey(relativePath);
|
|
95
131
|
}
|
|
132
|
+
this.editorListenerMap.disposeKey(event.payload.resource.uri.toString());
|
|
96
133
|
}
|
|
97
134
|
|
|
98
135
|
@OnEvent(EditorGroupOpenEvent)
|
|
99
136
|
async onEditorGroupOpen(event: EditorGroupOpenEvent) {
|
|
100
|
-
|
|
137
|
+
const relativePath = path.relative(this.appConfig.workspaceDir, event.payload.resource.uri.path.toString());
|
|
138
|
+
if (
|
|
139
|
+
this.duringApply ||
|
|
140
|
+
this.activePreviewerMap.has(relativePath) ||
|
|
141
|
+
!this.chatInternalService.sessionModel.history.getMessages().length
|
|
142
|
+
) {
|
|
101
143
|
return;
|
|
102
144
|
}
|
|
103
|
-
const relativePath = path.relative(this.appConfig.workspaceDir, event.payload.resource.uri.path.toString());
|
|
104
145
|
const filePendingApplies = Object.values(
|
|
105
146
|
this.getMessageCodeBlocks(this.chatInternalService.sessionModel.history.lastMessageId!) || {},
|
|
106
147
|
).filter((block) => block.relativePath === relativePath && block.status === 'pending');
|
|
107
148
|
// TODO: 刷新后重新应用,事件无法恢复 & 恢复继续请求,需要改造成批量apply形式
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
this.renderApplyResult(filePendingApplies[0], filePendingApplies[0].updatedCode
|
|
149
|
+
if (filePendingApplies.length > 0 && filePendingApplies[0].updatedCode) {
|
|
150
|
+
const editor = event.payload.group.codeEditor.monacoEditor;
|
|
151
|
+
this.renderApplyResult(editor, filePendingApplies[0], filePendingApplies[0].updatedCode);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
get currentPreviewer() {
|
|
156
|
+
const currentUri = this.editorService.currentEditor?.currentUri;
|
|
157
|
+
if (!currentUri) {
|
|
158
|
+
return undefined;
|
|
111
159
|
}
|
|
160
|
+
return this.activePreviewerMap.get(path.relative(this.appConfig.workspaceDir, currentUri.path.toString()));
|
|
112
161
|
}
|
|
113
162
|
|
|
114
163
|
getUriPendingCodeBlock(uri: URI): CodeBlockData | undefined {
|
|
@@ -127,6 +176,25 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
127
176
|
);
|
|
128
177
|
}
|
|
129
178
|
|
|
179
|
+
getPendingPaths(sessionId?: string): string[] {
|
|
180
|
+
sessionId = sessionId || this.chatInternalService.sessionModel.sessionId;
|
|
181
|
+
const sessionModel = this.chatInternalService.getSession(sessionId);
|
|
182
|
+
if (!sessionModel) {
|
|
183
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
184
|
+
}
|
|
185
|
+
const sessionAdditionals = sessionModel.history.sessionAdditionals;
|
|
186
|
+
return Array.from(sessionAdditionals.values())
|
|
187
|
+
.map((additional) => additional.codeBlockMap as { [toolCallId: string]: CodeBlockData })
|
|
188
|
+
.reduce((acc, cur) => {
|
|
189
|
+
Object.values(cur).forEach((block) => {
|
|
190
|
+
if (block.status === 'pending' && !acc.includes(block.relativePath)) {
|
|
191
|
+
acc.push(block.relativePath);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
return acc;
|
|
195
|
+
}, [] as string[]);
|
|
196
|
+
}
|
|
197
|
+
|
|
130
198
|
getCodeBlock(toolCallId: string, messageId?: string): CodeBlockData | undefined {
|
|
131
199
|
messageId = messageId || this.chatInternalService.sessionModel.history.lastMessageId;
|
|
132
200
|
if (!messageId) {
|
|
@@ -155,9 +223,12 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
155
223
|
this.onCodeBlockUpdateEmitter.fire(codeBlock);
|
|
156
224
|
}
|
|
157
225
|
|
|
158
|
-
registerCodeBlock(relativePath: string, content: string, toolCallId: string): CodeBlockData {
|
|
226
|
+
async registerCodeBlock(relativePath: string, content: string, toolCallId: string): Promise<CodeBlockData> {
|
|
159
227
|
const lastMessageId = this.chatInternalService.sessionModel.history.lastMessageId!;
|
|
160
228
|
const savedCodeBlockMap = this.getMessageCodeBlocks(lastMessageId) || {};
|
|
229
|
+
const originalModelRef = await this.editorDocumentModelService.createModelReference(
|
|
230
|
+
URI.file(path.join(this.appConfig.workspaceDir, relativePath)),
|
|
231
|
+
);
|
|
161
232
|
const newBlock: CodeBlockData = {
|
|
162
233
|
codeEdit: content,
|
|
163
234
|
relativePath,
|
|
@@ -166,11 +237,13 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
166
237
|
version: 1,
|
|
167
238
|
createdAt: Date.now(),
|
|
168
239
|
toolCallId,
|
|
240
|
+
// TODO: 支持range
|
|
241
|
+
originalCode: originalModelRef.instance.getText(),
|
|
169
242
|
};
|
|
170
243
|
const samePathCodeBlocks = Object.values(savedCodeBlockMap).filter((block) => block.relativePath === relativePath);
|
|
171
244
|
if (samePathCodeBlocks.length > 0) {
|
|
172
245
|
newBlock.version = samePathCodeBlocks.length;
|
|
173
|
-
for (const block of samePathCodeBlocks.sort((a, b) =>
|
|
246
|
+
for (const block of samePathCodeBlocks.sort((a, b) => a.version - b.version)) {
|
|
174
247
|
// 如果连续的上一个同文件apply结果存在LintError,则iterationCount++
|
|
175
248
|
if (block.relativePath === relativePath && block.applyResult?.diagnosticInfos?.length) {
|
|
176
249
|
newBlock.iterationCount++;
|
|
@@ -187,21 +260,40 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
187
260
|
return newBlock;
|
|
188
261
|
}
|
|
189
262
|
|
|
263
|
+
private duringApply?: boolean;
|
|
264
|
+
|
|
190
265
|
/**
|
|
191
266
|
* Apply changes of a code block
|
|
192
267
|
*/
|
|
193
268
|
async apply(codeBlock: CodeBlockData): Promise<CodeBlockData> {
|
|
194
269
|
try {
|
|
270
|
+
this.duringApply = true;
|
|
195
271
|
if (codeBlock.iterationCount > 3) {
|
|
196
272
|
throw new Error('Lint error max iteration count exceeded');
|
|
197
273
|
}
|
|
198
|
-
|
|
274
|
+
// 新建文件场景,直接返回codeEdit
|
|
275
|
+
const fastApplyFileResult = !codeBlock.originalCode
|
|
276
|
+
? {
|
|
277
|
+
result: codeBlock.codeEdit,
|
|
278
|
+
}
|
|
279
|
+
: await this.doApply(codeBlock);
|
|
199
280
|
if (!fastApplyFileResult.stream && !fastApplyFileResult.result) {
|
|
200
281
|
throw new Error('No apply content provided');
|
|
201
282
|
}
|
|
202
283
|
|
|
284
|
+
if (this.activePreviewerMap.has(codeBlock.relativePath)) {
|
|
285
|
+
this.activePreviewerMap.disposeKey(codeBlock.relativePath);
|
|
286
|
+
}
|
|
287
|
+
// FIXME: 同一个bubble单个文件多次写入(如迭代)兼容
|
|
203
288
|
// trigger diffPreivewer & return expected diff result directly
|
|
289
|
+
const result = await this.editorService.open(
|
|
290
|
+
URI.file(path.join(this.appConfig.workspaceDir, codeBlock.relativePath)),
|
|
291
|
+
);
|
|
292
|
+
if (!result) {
|
|
293
|
+
throw new Error('Failed to open file');
|
|
294
|
+
}
|
|
204
295
|
const applyResult = await this.renderApplyResult(
|
|
296
|
+
result.group.codeEditor.monacoEditor,
|
|
205
297
|
codeBlock,
|
|
206
298
|
(fastApplyFileResult.result || fastApplyFileResult.stream)!,
|
|
207
299
|
fastApplyFileResult.range,
|
|
@@ -217,31 +309,39 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
217
309
|
codeBlock.status = 'failed';
|
|
218
310
|
this.updateCodeBlock(codeBlock);
|
|
219
311
|
throw err;
|
|
312
|
+
} finally {
|
|
313
|
+
this.duringApply = false;
|
|
220
314
|
}
|
|
221
315
|
}
|
|
222
316
|
|
|
223
317
|
async renderApplyResult(
|
|
318
|
+
editor: ICodeEditor,
|
|
224
319
|
codeBlock: CodeBlockData,
|
|
225
320
|
updatedContentOrStream: string | SumiReadableStream<IChatProgress>,
|
|
226
321
|
range?: Range,
|
|
227
322
|
): Promise<{ diff: string; diagnosticInfos: IMarker[] } | undefined> {
|
|
228
|
-
const {
|
|
229
|
-
const openResult = await this.editorService.open(URI.file(path.join(this.appConfig.workspaceDir, relativePath)));
|
|
230
|
-
if (!openResult) {
|
|
231
|
-
throw new Error('Failed to open editor');
|
|
232
|
-
}
|
|
233
|
-
const editor = openResult.group.codeEditor.monacoEditor;
|
|
323
|
+
const deferred = new Deferred<{ diff: string; diagnosticInfos: IMarker[] }>();
|
|
234
324
|
const inlineDiffController = InlineDiffController.get(editor)!;
|
|
235
|
-
|
|
236
|
-
// 强刷展示 manager 视图
|
|
237
|
-
this.eventBus.fire(new RegisterEditorSideComponentEvent());
|
|
238
|
-
this.updateCodeBlock(codeBlock);
|
|
239
|
-
|
|
240
|
-
const fullOriginalContent = editor.getModel()!.getValue();
|
|
241
|
-
range = range || editor.getModel()?.getFullModelRange()!;
|
|
242
|
-
// const savedRangeContent = editor.getModel()!.getValueInRange(range);
|
|
325
|
+
range = range || editor.getModel()!.getFullModelRange();
|
|
243
326
|
|
|
244
327
|
if (typeof updatedContentOrStream === 'string') {
|
|
328
|
+
const editorCurrentContent = editor.getModel()!.getValue();
|
|
329
|
+
const document = this.editorDocumentModelService.getModelReference(
|
|
330
|
+
URI.file(path.join(this.appConfig.workspaceDir, codeBlock.relativePath)),
|
|
331
|
+
);
|
|
332
|
+
if (editorCurrentContent !== updatedContentOrStream || document?.instance.dirty) {
|
|
333
|
+
editor.getModel()?.pushEditOperations([], [EditOperation.replace(range, updatedContentOrStream)], () => null);
|
|
334
|
+
await this.editorService.save(URI.file(path.join(this.appConfig.workspaceDir, codeBlock.relativePath)));
|
|
335
|
+
}
|
|
336
|
+
const earlistPendingCodeBlock = this.getSessionCodeBlocksForPath(codeBlock.relativePath).find(
|
|
337
|
+
(block) => block.status === 'pending',
|
|
338
|
+
);
|
|
339
|
+
if ((earlistPendingCodeBlock?.originalCode || codeBlock.originalCode) === updatedContentOrStream) {
|
|
340
|
+
codeBlock.status = 'cancelled';
|
|
341
|
+
this.updateCodeBlock(codeBlock);
|
|
342
|
+
deferred.resolve();
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
245
345
|
// Create diff previewer
|
|
246
346
|
const previewer = inlineDiffController.createDiffPreviewer(
|
|
247
347
|
editor,
|
|
@@ -249,18 +349,41 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
249
349
|
{
|
|
250
350
|
disposeWhenEditorClosed: true,
|
|
251
351
|
renderRemovedWidgetImmediately: true,
|
|
352
|
+
reverse: true,
|
|
252
353
|
},
|
|
253
354
|
) as LiveInlineDiffPreviewer;
|
|
254
|
-
|
|
255
|
-
this.activePreviewer = previewer;
|
|
355
|
+
this.activePreviewerMap.set(codeBlock.relativePath, previewer);
|
|
256
356
|
codeBlock.updatedCode = updatedContentOrStream;
|
|
257
|
-
|
|
357
|
+
codeBlock.status = 'pending';
|
|
358
|
+
this.updateCodeBlock(codeBlock);
|
|
359
|
+
// 新建文件场景,为避免model为空,加一个空行
|
|
360
|
+
previewer.setValue(earlistPendingCodeBlock?.originalCode || codeBlock.originalCode || '\n');
|
|
361
|
+
// 强刷展示 manager 视图
|
|
362
|
+
this.eventBus.fire(new RegisterEditorSideComponentEvent());
|
|
363
|
+
|
|
364
|
+
this.listenPartialEdit(editor.getModel()!, codeBlock).then((result) => {
|
|
365
|
+
if (result) {
|
|
366
|
+
codeBlock.applyResult = result;
|
|
367
|
+
}
|
|
368
|
+
this.updateCodeBlock(codeBlock);
|
|
369
|
+
this.editorService.save(URI.file(path.join(this.appConfig.workspaceDir, codeBlock.relativePath)));
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const { diff, rangesFromDiffHunk } = this.getDiffResult(
|
|
373
|
+
codeBlock.originalCode,
|
|
374
|
+
codeBlock.updatedCode,
|
|
375
|
+
codeBlock.relativePath,
|
|
376
|
+
);
|
|
377
|
+
const diagnosticInfos = this.getDiagnosticInfos(editor.getModel()!.uri.toString(), rangesFromDiffHunk);
|
|
378
|
+
deferred.resolve({
|
|
379
|
+
diff,
|
|
380
|
+
diagnosticInfos,
|
|
381
|
+
});
|
|
258
382
|
} else {
|
|
259
383
|
const controller = new InlineChatController();
|
|
260
384
|
controller.mountReadable(updatedContentOrStream);
|
|
261
|
-
const inlineDiffHandler = InlineDiffController.get(editor)!;
|
|
262
385
|
|
|
263
|
-
|
|
386
|
+
const previewer = inlineDiffController.showPreviewerByStream(editor, {
|
|
264
387
|
crossSelection: Selection.fromRange(range, SelectionDirection.LTR),
|
|
265
388
|
chatResponse: controller,
|
|
266
389
|
previewerOptions: {
|
|
@@ -269,14 +392,27 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
269
392
|
},
|
|
270
393
|
}) as LiveInlineDiffPreviewer;
|
|
271
394
|
this.addDispose(
|
|
272
|
-
|
|
395
|
+
// 流式输出结束后,转为直接输出逻辑
|
|
396
|
+
previewer.getNode()!.onDiffFinished(async (diffModel) => {
|
|
273
397
|
codeBlock.updatedCode = diffModel.newFullRangeTextLines.join('\n');
|
|
398
|
+
// TODO: 添加 reapply
|
|
399
|
+
// 实际应用结果为空,则取消
|
|
400
|
+
if (codeBlock.updatedCode === codeBlock.originalCode) {
|
|
401
|
+
codeBlock.status = 'cancelled';
|
|
402
|
+
this.updateCodeBlock(codeBlock);
|
|
403
|
+
previewer.dispose();
|
|
404
|
+
deferred.resolve();
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
274
407
|
this.updateCodeBlock(codeBlock);
|
|
408
|
+
previewer.dispose();
|
|
409
|
+
const result = await this.renderApplyResult(editor, codeBlock, codeBlock.updatedCode);
|
|
410
|
+
deferred.resolve(result);
|
|
275
411
|
}),
|
|
276
412
|
);
|
|
413
|
+
this.activePreviewerMap.set(codeBlock.relativePath, previewer);
|
|
277
414
|
}
|
|
278
|
-
|
|
279
|
-
return this.listenPartialEdit(editor, codeBlock, fullOriginalContent);
|
|
415
|
+
return deferred.promise;
|
|
280
416
|
}
|
|
281
417
|
|
|
282
418
|
/**
|
|
@@ -284,9 +420,12 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
284
420
|
*/
|
|
285
421
|
cancelApply(blockData: CodeBlockData): void {
|
|
286
422
|
if (blockData.status === 'generating' || blockData.status === 'pending') {
|
|
287
|
-
if (this.
|
|
288
|
-
this.
|
|
289
|
-
|
|
423
|
+
if (this.activePreviewerMap.has(blockData.relativePath)) {
|
|
424
|
+
this.activePreviewerMap
|
|
425
|
+
.get(blockData.relativePath)
|
|
426
|
+
?.getNode()
|
|
427
|
+
?.livePreviewDiffDecorationModel.discardUnProcessed();
|
|
428
|
+
this.activePreviewerMap.disposeKey(blockData.relativePath);
|
|
290
429
|
}
|
|
291
430
|
blockData.status = 'cancelled';
|
|
292
431
|
this.updateCodeBlock(blockData);
|
|
@@ -327,7 +466,9 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
327
466
|
if (!codeBlock) {
|
|
328
467
|
throw new Error('No pending code block found');
|
|
329
468
|
}
|
|
330
|
-
const decorationModel = this.
|
|
469
|
+
const decorationModel = this.activePreviewerMap
|
|
470
|
+
.get(codeBlock.relativePath)
|
|
471
|
+
?.getNode()?.livePreviewDiffDecorationModel;
|
|
331
472
|
if (!decorationModel) {
|
|
332
473
|
throw new Error('No active previewer found');
|
|
333
474
|
}
|
|
@@ -341,32 +482,27 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
341
482
|
this.updateCodeBlock(codeBlock);
|
|
342
483
|
}
|
|
343
484
|
|
|
344
|
-
protected listenPartialEdit(
|
|
485
|
+
protected listenPartialEdit(model: ITextModel, codeBlock: CodeBlockData) {
|
|
345
486
|
const deferred = new Deferred<{ diff: string; diagnosticInfos: IMarker[] }>();
|
|
487
|
+
const uriString = model.uri.toString();
|
|
346
488
|
const toDispose = this.inlineDiffService.onPartialEdit((event) => {
|
|
347
489
|
// TODO 支持自动保存
|
|
348
|
-
if (
|
|
490
|
+
if (
|
|
491
|
+
event.totalPartialEditCount === event.resolvedPartialEditCount &&
|
|
492
|
+
event.uri.path === model.uri.path.toString()
|
|
493
|
+
) {
|
|
349
494
|
if (event.acceptPartialEditCount > 0) {
|
|
350
495
|
codeBlock.status = 'success';
|
|
351
|
-
const appliedResult =
|
|
352
|
-
const
|
|
353
|
-
.
|
|
354
|
-
|
|
355
|
-
.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
.map((line) => {
|
|
359
|
-
if (line.startsWith('@@')) {
|
|
360
|
-
const [, , , start, end] = line.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)!;
|
|
361
|
-
return new Range(parseInt(start, 10), 0, parseInt(end, 10), 0);
|
|
362
|
-
}
|
|
363
|
-
return null;
|
|
364
|
-
})
|
|
365
|
-
.filter((range) => range !== null);
|
|
366
|
-
const diagnosticInfos = this.getDiagnosticInfos(editor.getModel()!.uri.toString(), rangesFromDiffHunk);
|
|
496
|
+
const appliedResult = model.getValue();
|
|
497
|
+
const { diff, rangesFromDiffHunk } = this.getDiffResult(
|
|
498
|
+
codeBlock.originalCode,
|
|
499
|
+
appliedResult,
|
|
500
|
+
codeBlock.relativePath,
|
|
501
|
+
);
|
|
502
|
+
const diagnosticInfos = this.getDiagnosticInfos(model.uri.toString(), rangesFromDiffHunk);
|
|
367
503
|
// 移除开头的几个固定信息,避免浪费 tokens
|
|
368
504
|
deferred.resolve({
|
|
369
|
-
diff
|
|
505
|
+
diff,
|
|
370
506
|
diagnosticInfos,
|
|
371
507
|
});
|
|
372
508
|
} else {
|
|
@@ -374,12 +510,31 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
374
510
|
codeBlock.status = 'cancelled';
|
|
375
511
|
deferred.resolve();
|
|
376
512
|
}
|
|
377
|
-
|
|
513
|
+
this.editorListenerMap.disposeKey(uriString);
|
|
378
514
|
}
|
|
379
515
|
});
|
|
516
|
+
this.editorListenerMap.set(uriString, toDispose);
|
|
380
517
|
return deferred.promise;
|
|
381
518
|
}
|
|
382
519
|
|
|
520
|
+
protected getDiffResult(originalContent: string, appliedResult: string, relativePath: string) {
|
|
521
|
+
const diffResult = createPatch(relativePath, originalContent, appliedResult).split('\n').slice(4).join('\n');
|
|
522
|
+
const rangesFromDiffHunk = diffResult
|
|
523
|
+
.split('\n')
|
|
524
|
+
.map((line) => {
|
|
525
|
+
if (line.startsWith('@@')) {
|
|
526
|
+
const [, , , start, end] = line.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)!;
|
|
527
|
+
return new Range(parseInt(start, 10), 0, parseInt(end, 10), 0);
|
|
528
|
+
}
|
|
529
|
+
return null;
|
|
530
|
+
})
|
|
531
|
+
.filter((range) => range !== null);
|
|
532
|
+
return {
|
|
533
|
+
diff: diffResult,
|
|
534
|
+
rangesFromDiffHunk,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
383
538
|
/**
|
|
384
539
|
* Apply changes of a code block, return stream to render inline diff in stream mode, result to render inline diff directly
|
|
385
540
|
* range is optional, if not provided, the result will be applied to the the full file
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
useInjectable,
|
|
14
14
|
} from '@opensumi/ide-core-browser';
|
|
15
15
|
import { Loading } from '@opensumi/ide-core-browser/lib/components/ai-native';
|
|
16
|
+
import { WorkbenchEditorService } from '@opensumi/ide-editor';
|
|
16
17
|
import { ILanguageService } from '@opensumi/monaco-editor-core/esm/vs/editor/common/languages/language';
|
|
17
18
|
import { IModelService } from '@opensumi/monaco-editor-core/esm/vs/editor/common/services/model';
|
|
18
19
|
import { StandaloneServices } from '@opensumi/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices';
|
|
@@ -30,6 +31,7 @@ export const EditFileToolComponent = (props: IMCPServerToolComponentProps) => {
|
|
|
30
31
|
const labelService = useInjectable(LabelService);
|
|
31
32
|
const appConfig = useInjectable<AppConfig>(AppConfig);
|
|
32
33
|
const applyService = useInjectable<BaseApplyService>(BaseApplyService);
|
|
34
|
+
const editorService = useInjectable<WorkbenchEditorService>(WorkbenchEditorService);
|
|
33
35
|
const { target_file = '', code_edit, instructions } = args || {};
|
|
34
36
|
const absolutePath = path.join(appConfig.workspaceDir, target_file);
|
|
35
37
|
const [codeBlockData, setCodeBlockData] = useState<CodeBlockData | undefined>(
|
|
@@ -55,15 +57,20 @@ export const EditFileToolComponent = (props: IMCPServerToolComponentProps) => {
|
|
|
55
57
|
|
|
56
58
|
useEffect(() => {
|
|
57
59
|
const disposable = applyService.onCodeBlockUpdate((codeBlockData) => {
|
|
58
|
-
|
|
60
|
+
if (codeBlockData.toolCallId === toolCallId) {
|
|
61
|
+
setCodeBlockData({ ...codeBlockData });
|
|
62
|
+
}
|
|
59
63
|
});
|
|
60
64
|
return () => {
|
|
61
65
|
disposable.dispose();
|
|
62
66
|
};
|
|
63
67
|
}, []);
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
const handleToggleMode = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
70
|
+
e.stopPropagation();
|
|
71
|
+
setMode(mode === 'code' ? 'diff' : 'code');
|
|
72
|
+
};
|
|
73
|
+
|
|
67
74
|
if (!args || !codeBlockData) {
|
|
68
75
|
return null;
|
|
69
76
|
}
|
|
@@ -77,7 +84,7 @@ export const EditFileToolComponent = (props: IMCPServerToolComponentProps) => {
|
|
|
77
84
|
})}
|
|
78
85
|
onClick={() => {
|
|
79
86
|
if (codeBlockData.status === 'pending') {
|
|
80
|
-
|
|
87
|
+
editorService.open(URI.file(absolutePath));
|
|
81
88
|
} else if (codeBlockData.status === 'success') {
|
|
82
89
|
applyService.revealApplyPosition(codeBlockData);
|
|
83
90
|
}
|
|
@@ -89,15 +96,15 @@ export const EditFileToolComponent = (props: IMCPServerToolComponentProps) => {
|
|
|
89
96
|
{codeBlockData.iterationCount > 1 && (
|
|
90
97
|
<span className={styles['edit-file-tool-iteration-count']}>{codeBlockData.iterationCount}/3</span>
|
|
91
98
|
)}
|
|
92
|
-
{renderStatus(codeBlockData)}
|
|
99
|
+
{renderStatus(codeBlockData, props.result)}
|
|
93
100
|
</div>
|
|
94
101
|
<div className={styles.right}>
|
|
95
102
|
<Popover title={'Show Code'} id={'edit-file-tool-show-code'}>
|
|
96
|
-
<Icon iconClass='codicon codicon-file-code' onClick={
|
|
103
|
+
<Icon iconClass='codicon codicon-file-code' onClick={handleToggleMode} />
|
|
97
104
|
</Popover>
|
|
98
105
|
{codeBlockData.applyResult?.diff && (
|
|
99
106
|
<Popover title={'Show Diff'} id={'edit-file-tool-show-diff'}>
|
|
100
|
-
<Icon iconClass='codicon codicon-diff-multiple' onClick={
|
|
107
|
+
<Icon iconClass='codicon codicon-diff-multiple' onClick={handleToggleMode} />
|
|
101
108
|
</Popover>
|
|
102
109
|
)}
|
|
103
110
|
</div>
|
|
@@ -131,7 +138,7 @@ export const EditFileToolComponent = (props: IMCPServerToolComponentProps) => {
|
|
|
131
138
|
];
|
|
132
139
|
};
|
|
133
140
|
|
|
134
|
-
const renderStatus = (codeBlockData: CodeBlockData) => {
|
|
141
|
+
const renderStatus = (codeBlockData: CodeBlockData, error?: string) => {
|
|
135
142
|
const status = codeBlockData.status;
|
|
136
143
|
switch (status) {
|
|
137
144
|
case 'generating':
|
|
@@ -150,7 +157,7 @@ const renderStatus = (codeBlockData: CodeBlockData) => {
|
|
|
150
157
|
);
|
|
151
158
|
case 'failed':
|
|
152
159
|
return (
|
|
153
|
-
<Popover title=
|
|
160
|
+
<Popover title={`Failed (${error})`} id={'edit-file-tool-status-failed'}>
|
|
154
161
|
<Icon iconClass='codicon codicon-error' style={{ color: 'var(--debugConsole-errorForeground)' }} />
|
|
155
162
|
</Popover>
|
|
156
163
|
);
|