@opensumi/ide-ai-native 3.8.1-next-1741092802.0 → 3.8.1-next-1741093151.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 (64) hide show
  1. package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
  2. package/lib/browser/chat/chat-manager.service.js +4 -0
  3. package/lib/browser/chat/chat-manager.service.js.map +1 -1
  4. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  5. package/lib/browser/chat/chat.view.js +7 -1
  6. package/lib/browser/chat/chat.view.js.map +1 -1
  7. package/lib/browser/components/chat-history.module.less +1 -0
  8. package/lib/browser/mcp/base-apply.service.d.ts +18 -7
  9. package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
  10. package/lib/browser/mcp/base-apply.service.js +185 -65
  11. package/lib/browser/mcp/base-apply.service.js.map +1 -1
  12. package/lib/browser/mcp/tools/components/EditFile.d.ts.map +1 -1
  13. package/lib/browser/mcp/tools/components/EditFile.js +15 -9
  14. package/lib/browser/mcp/tools/components/EditFile.js.map +1 -1
  15. package/lib/browser/mcp/tools/components/index.module.less +3 -0
  16. package/lib/browser/mcp/tools/createNewFileWithText.d.ts +1 -0
  17. package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -1
  18. package/lib/browser/mcp/tools/createNewFileWithText.js +18 -11
  19. package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -1
  20. package/lib/browser/mcp/tools/handlers/EditFile.js +1 -1
  21. package/lib/browser/mcp/tools/handlers/EditFile.js.map +1 -1
  22. package/lib/browser/model/msg-history-manager.d.ts +1 -0
  23. package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
  24. package/lib/browser/model/msg-history-manager.js +3 -0
  25. package/lib/browser/model/msg-history-manager.js.map +1 -1
  26. package/lib/browser/widget/inline-diff/inline-diff-manager.d.ts.map +1 -1
  27. package/lib/browser/widget/inline-diff/inline-diff-manager.js +68 -8
  28. package/lib/browser/widget/inline-diff/inline-diff-manager.js.map +1 -1
  29. package/lib/browser/widget/inline-diff/inline-diff-previewer.d.ts +10 -4
  30. package/lib/browser/widget/inline-diff/inline-diff-previewer.d.ts.map +1 -1
  31. package/lib/browser/widget/inline-diff/inline-diff-previewer.js +14 -3
  32. package/lib/browser/widget/inline-diff/inline-diff-previewer.js.map +1 -1
  33. package/lib/browser/widget/inline-diff/inline-diff-widget.module.less +25 -4
  34. package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts +3 -3
  35. package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts.map +1 -1
  36. package/lib/browser/widget/inline-diff/inline-diff.controller.js +10 -5
  37. package/lib/browser/widget/inline-diff/inline-diff.controller.js.map +1 -1
  38. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts +46 -17
  39. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts.map +1 -1
  40. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js +110 -53
  41. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
  42. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts +4 -0
  43. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts.map +1 -1
  44. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js +26 -1
  45. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js.map +1 -1
  46. package/lib/common/types.d.ts +1 -0
  47. package/lib/common/types.d.ts.map +1 -1
  48. package/package.json +23 -23
  49. package/src/browser/chat/chat-manager.service.ts +6 -0
  50. package/src/browser/chat/chat.view.tsx +7 -2
  51. package/src/browser/components/chat-history.module.less +1 -0
  52. package/src/browser/mcp/base-apply.service.ts +222 -67
  53. package/src/browser/mcp/tools/components/EditFile.tsx +16 -9
  54. package/src/browser/mcp/tools/components/index.module.less +3 -0
  55. package/src/browser/mcp/tools/createNewFileWithText.ts +20 -12
  56. package/src/browser/mcp/tools/handlers/EditFile.ts +1 -1
  57. package/src/browser/model/msg-history-manager.ts +4 -0
  58. package/src/browser/widget/inline-diff/inline-diff-manager.tsx +143 -21
  59. package/src/browser/widget/inline-diff/inline-diff-previewer.ts +25 -7
  60. package/src/browser/widget/inline-diff/inline-diff-widget.module.less +25 -4
  61. package/src/browser/widget/inline-diff/inline-diff.controller.ts +16 -8
  62. package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +139 -68
  63. package/src/browser/widget/inline-stream-diff/live-preview.decoration.tsx +30 -1
  64. package/src/common/types.ts +1 -0
@@ -128,6 +128,10 @@ export class MsgHistoryManager extends Disposable {
128
128
  return this.messageAdditionalMap.get(id) || {};
129
129
  }
130
130
 
131
+ public get sessionAdditionals() {
132
+ return this.messageAdditionalMap;
133
+ }
134
+
131
135
  toJSON() {
132
136
  return {
133
137
  messages: this.getMessages(),
@@ -1,38 +1,160 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
2
 
3
- import { Button } from '@opensumi/ide-components';
4
- import { localize, useInjectable } from '@opensumi/ide-core-browser';
5
- import { IResource } from '@opensumi/ide-editor';
3
+ import { Icon, Popover } from '@opensumi/ide-components';
4
+ import { AppConfig, IDisposable, URI, localize, path, useInjectable } from '@opensumi/ide-core-browser';
5
+ import { IResource, WorkbenchEditorService } from '@opensumi/ide-editor';
6
+ import { Path } from '@opensumi/ide-utils/lib/path';
6
7
 
7
8
  import { BaseApplyService } from '../../mcp/base-apply.service';
8
9
 
9
10
  import styles from './inline-diff-widget.module.less';
10
11
 
12
+ const IconWithPopover = (props: {
13
+ icon: string;
14
+ content: string;
15
+ id: string;
16
+ onClick?: () => void;
17
+ disabled?: boolean;
18
+ }) => (
19
+ <Popover content={props.content} id={props.id} onClick={props.onClick}>
20
+ <Icon iconClass={props.icon} className={props.disabled ? styles.disabled : ''} />
21
+ </Popover>
22
+ );
23
+
11
24
  export const InlineDiffManager: React.FC<{ resource: IResource }> = (props) => {
25
+ const { resource } = props;
12
26
  const applyService = useInjectable<BaseApplyService>(BaseApplyService);
27
+ const editorService = useInjectable<WorkbenchEditorService>(WorkbenchEditorService);
28
+ const appConfig = useInjectable<AppConfig>(AppConfig);
13
29
  const [show, setShow] = useState(true);
30
+ const [changesCount, setChangesCount] = useState(0);
31
+ const [currentChangeIndex, setCurrentChangeIndex] = useState(0);
32
+ const [filePaths, setFilePaths] = useState<string[]>([]);
33
+
34
+ const currentFilePath = useMemo(
35
+ () => path.relative(appConfig.workspaceDir, resource.uri.path.toString()),
36
+ [resource],
37
+ );
38
+
14
39
  useEffect(() => {
15
- applyService.onCodeBlockUpdate((codeBlock) => {
16
- setShow(codeBlock.status === 'pending');
40
+ const toDispose = applyService.onCodeBlockUpdate((codeBlock) => {
41
+ if (path.relative(appConfig.workspaceDir, props.resource.uri.path.toString()) === codeBlock.relativePath) {
42
+ setShow(codeBlock.status === 'pending');
43
+ }
44
+ const pendingPaths = applyService.getPendingPaths();
45
+ setFilePaths(pendingPaths);
17
46
  });
47
+ return () => {
48
+ toDispose.dispose();
49
+ };
18
50
  }, []);
51
+
52
+ // 不同编辑器是不同实例,所以不需要监听
53
+ const decorationModelService = applyService.currentPreviewer?.getNode()?.livePreviewDiffDecorationModel;
54
+
55
+ useEffect(() => {
56
+ let toDispose: IDisposable | undefined;
57
+ if (decorationModelService) {
58
+ setChangesCount(decorationModelService.partialEditWidgetCount);
59
+ toDispose = decorationModelService.onPartialEditWidgetListChange((e) => {
60
+ setChangesCount(e.filter((item) => item.status === 'pending').length);
61
+ });
62
+ }
63
+ return () => {
64
+ toDispose?.dispose();
65
+ };
66
+ }, [decorationModelService]);
67
+
68
+ const handleSiblingChange = useCallback(
69
+ (direction: 'up' | 'down') => {
70
+ const index = decorationModelService?.revealSiblingChange(direction);
71
+ if (index !== undefined) {
72
+ setCurrentChangeIndex(index);
73
+ }
74
+ },
75
+ [decorationModelService],
76
+ );
77
+
78
+ const handleSiblingFile = useCallback(
79
+ (direction: 'up' | 'down') => {
80
+ const index = filePaths.indexOf(currentFilePath!);
81
+ if (index === -1) {
82
+ return;
83
+ }
84
+ const uri = URI.file(path.join(appConfig.workspaceDir, filePaths[index + (direction === 'up' ? -1 : 1)]));
85
+ editorService.open(uri);
86
+ },
87
+ [currentFilePath, filePaths],
88
+ );
89
+
90
+ const changeTip = useMemo(() => {
91
+ if (changesCount === 0) {
92
+ return '';
93
+ }
94
+ return ` ${currentChangeIndex + 1} of ${changesCount}`;
95
+ }, [changesCount, currentChangeIndex]);
96
+
97
+ const fileTip = useMemo(() => {
98
+ if (!currentFilePath) {
99
+ return '';
100
+ }
101
+ return ` ${filePaths.indexOf(currentFilePath) + 1} of ${filePaths.length}`;
102
+ }, [currentFilePath, filePaths]);
103
+
19
104
  return (
20
105
  <div className={styles.inlineDiffManager} style={{ display: show ? 'flex' : 'none' }}>
21
- <Button
22
- onClick={() => {
23
- applyService.processAll(props.resource.uri, 'accept');
24
- }}
25
- >
26
- {localize('aiNative.inlineDiff.acceptAll')}
27
- </Button>
28
- <Button
29
- type='ghost'
30
- onClick={() => {
31
- applyService.processAll(props.resource.uri, 'reject');
32
- }}
33
- >
34
- {localize('aiNative.inlineDiff.rejectAll')}
35
- </Button>
106
+ {/* <div className={styles.left}>
107
+ <IconWithPopover
108
+ icon='codicon codicon-issues'
109
+ content={localize('aiNative.inlineDiff.reveal')}
110
+ id='inline-diff-manager-reveal'
111
+ onClick={handleReveal}
112
+ />
113
+ </div> */}
114
+ <div className={styles.mid}>
115
+ <IconWithPopover
116
+ icon='codicon codicon-check'
117
+ onClick={() => applyService.processAll(props.resource.uri, 'accept')}
118
+ content={localize('aiNative.inlineDiff.acceptAll')}
119
+ id='inline-diff-manager-accept-all'
120
+ />
121
+ <IconWithPopover
122
+ icon='codicon codicon-close'
123
+ onClick={() => applyService.processAll(props.resource.uri, 'reject')}
124
+ content={localize('aiNative.inlineDiff.rejectAll')}
125
+ id='inline-diff-manager-reject-all'
126
+ />
127
+ <IconWithPopover
128
+ icon='codicon codicon-arrow-up'
129
+ content={localize('aiNative.inlineDiff.up') + changeTip}
130
+ id='inline-diff-manager-up'
131
+ disabled={currentChangeIndex === 0}
132
+ onClick={() => handleSiblingChange('up')}
133
+ />
134
+ <IconWithPopover
135
+ icon='codicon codicon-arrow-down'
136
+ content={localize('aiNative.inlineDiff.down') + changeTip}
137
+ id='inline-diff-manager-down'
138
+ disabled={currentChangeIndex === changesCount - 1}
139
+ onClick={() => handleSiblingChange('down')}
140
+ />
141
+ </div>
142
+ <div className={styles.right}>
143
+ <IconWithPopover
144
+ icon='codicon codicon-arrow-left'
145
+ onClick={() => handleSiblingFile('up')}
146
+ disabled={filePaths.length === 0 || filePaths[0] === currentFilePath}
147
+ content={localize('aiNative.inlineDiff.left') + fileTip}
148
+ id='inline-diff-manager-left'
149
+ />
150
+ <IconWithPopover
151
+ icon='codicon codicon-arrow-right'
152
+ onClick={() => handleSiblingFile('down')}
153
+ disabled={filePaths.length === 0 || filePaths[filePaths.length - 1] === currentFilePath}
154
+ content={localize('aiNative.inlineDiff.right') + fileTip}
155
+ id='inline-diff-manager-right'
156
+ />
157
+ </div>
36
158
  </div>
37
159
  );
38
160
  };
@@ -1,5 +1,6 @@
1
1
  import { Autowired, INJECTOR_TOKEN, Injectable, Injector } from '@opensumi/di';
2
2
  import { Disposable, ErrorResponse, IDisposable, ReplyResponse } from '@opensumi/ide-core-common';
3
+ import { WorkbenchEditorService } from '@opensumi/ide-editor';
3
4
  import { EOL, ICodeEditor, IPosition, ITextModel, Position, Selection } from '@opensumi/ide-monaco';
4
5
  import { ContentWidgetPositionPreference } from '@opensumi/ide-monaco/lib/browser/monaco-exports/editor';
5
6
  import { empty, getLeadingWhitespace } from '@opensumi/ide-utils/lib/strings';
@@ -14,7 +15,12 @@ import {
14
15
 
15
16
  import { EResultKind } from '../inline-chat/inline-chat.service';
16
17
  import { AIInlineContentWidget } from '../inline-chat/inline-content-widget';
17
- import { EComputerMode, InlineStreamDiffHandler } from '../inline-stream-diff/inline-stream-diff.handler';
18
+ import {
19
+ BaseInlineStreamDiffHandler,
20
+ EComputerMode,
21
+ InlineStreamDiffHandler,
22
+ ReverseInlineStreamDiffHandler,
23
+ } from '../inline-stream-diff/inline-stream-diff.handler';
18
24
 
19
25
  import { InlineDiffWidget } from './inline-diff-widget';
20
26
 
@@ -29,6 +35,11 @@ export interface IDiffPreviewerOptions {
29
35
  * 默认情况下,removed widget 会在 `runWhenIdle` 内被添加,如果需要立即添加,可以设置为 true
30
36
  */
31
37
  renderRemovedWidgetImmediately?: boolean;
38
+
39
+ /**
40
+ * 是否为回退模式,即 setValue 内容作为 original,编辑器 getValue 内容作为 modified
41
+ */
42
+ reverse?: boolean;
32
43
  }
33
44
 
34
45
  export interface IInlineDiffPreviewerNode extends IDisposable {
@@ -41,6 +52,8 @@ export abstract class BaseInlineDiffPreviewer<N extends IInlineDiffPreviewerNode
41
52
  @Autowired(INJECTOR_TOKEN)
42
53
  protected readonly injector: Injector;
43
54
 
55
+ @Autowired(WorkbenchEditorService)
56
+ protected readonly editorService: WorkbenchEditorService;
44
57
  protected inlineContentWidget: AIInlineContentWidget | null = null;
45
58
  protected selection: Selection;
46
59
  protected model: ITextModel;
@@ -124,7 +137,7 @@ export abstract class BaseInlineDiffPreviewer<N extends IInlineDiffPreviewerNode
124
137
  return Disposable.NULL;
125
138
  }
126
139
 
127
- protected abstract createNode(): N;
140
+ protected abstract createNode(reverse?: boolean): N;
128
141
  abstract onData(data: ReplyResponse): void;
129
142
  abstract handleAction(action: EResultKind): void;
130
143
  abstract getPosition(): IPosition;
@@ -136,7 +149,7 @@ export abstract class BaseInlineDiffPreviewer<N extends IInlineDiffPreviewerNode
136
149
  },
137
150
  ): void {
138
151
  this.selection = selection;
139
- this.node = this.createNode();
152
+ this.node = this.createNode(options.reverse);
140
153
  this.node.setPreviewerOptions(options);
141
154
  }
142
155
 
@@ -311,8 +324,8 @@ export class SideBySideInlineDiffWidget extends BaseInlineDiffPreviewer<InlineDi
311
324
  }
312
325
 
313
326
  @Injectable({ multiple: true })
314
- export class LiveInlineDiffPreviewer extends BaseInlineDiffPreviewer<InlineStreamDiffHandler> {
315
- private listenNode(node: InlineStreamDiffHandler): void {
327
+ export class LiveInlineDiffPreviewer extends BaseInlineDiffPreviewer<BaseInlineStreamDiffHandler> {
328
+ private listenNode(node: BaseInlineStreamDiffHandler): void {
316
329
  node.addDispose(node.onDidEditChange(() => this.layout()));
317
330
  node.addDispose(
318
331
  node.onPartialEditWidgetListChange((widgets) => {
@@ -331,8 +344,13 @@ export class LiveInlineDiffPreviewer extends BaseInlineDiffPreviewer<InlineStrea
331
344
  this.addDispose(node);
332
345
  }
333
346
 
334
- createNode(): InlineStreamDiffHandler {
335
- const node = this.injector.get(InlineStreamDiffHandler, [this.monacoEditor]);
347
+ createNode(reverse?: boolean): BaseInlineStreamDiffHandler {
348
+ let node: BaseInlineStreamDiffHandler;
349
+ if (reverse) {
350
+ node = this.injector.get(ReverseInlineStreamDiffHandler, [this.monacoEditor]);
351
+ } else {
352
+ node = this.injector.get(InlineStreamDiffHandler, [this.monacoEditor]);
353
+ }
336
354
  node.initialize(this.selection);
337
355
  this.listenNode(node);
338
356
  return node;
@@ -22,12 +22,33 @@
22
22
 
23
23
  .inlineDiffManager {
24
24
  display: flex;
25
- padding: 12px 16px;
26
- justify-content: center;
25
+ border-radius: 13px;
26
+ align-items: center;
27
27
  position: absolute;
28
- bottom: 0;
28
+ bottom: 18px;
29
29
  left: 50%;
30
30
  transform: translateX(-50%);
31
- gap: 12px;
31
+ overflow: hidden;
32
32
  z-index: 999;
33
+ > div {
34
+ display: flex;
35
+ align-items: center;
36
+ }
37
+ .mid {
38
+ border-right: 1px solid var(--design-borderColor);
39
+ }
40
+ :global(.codicon) {
41
+ font-size: 20px;
42
+ padding: 6px;
43
+ color: var(--kt-primaryButton-foreground);
44
+ background-color: var(--kt-primaryButton-hoverBackground);
45
+ &:hover {
46
+ background-color: var(--kt-primaryButton-background);
47
+ }
48
+ }
49
+ }
50
+ .disabled {
51
+ // color: var(--kt-button-disableForeground) !important;
52
+ background-color: var(--kt-button-disableBackground) !important;
53
+ cursor: not-allowed;
33
54
  }
@@ -3,6 +3,7 @@ import {
3
3
  AINativeSettingSectionsId,
4
4
  ChatResponse,
5
5
  Disposable,
6
+ DisposableCollection,
6
7
  Emitter,
7
8
  Event,
8
9
  IDisposable,
@@ -23,7 +24,7 @@ import { BaseAIMonacoEditorController } from '../../contrib/base';
23
24
  import { EInlineDiffPreviewMode } from '../../preferences/schema';
24
25
  import { InlineChatController } from '../inline-chat/inline-chat-controller';
25
26
  import { EResultKind } from '../inline-chat/inline-chat.service';
26
- import { InlineStreamDiffHandler } from '../inline-stream-diff/inline-stream-diff.handler';
27
+ import { BaseInlineStreamDiffHandler } from '../inline-stream-diff/inline-stream-diff.handler';
27
28
 
28
29
  import {
29
30
  BaseInlineDiffPreviewer,
@@ -33,7 +34,7 @@ import {
33
34
  } from './inline-diff-previewer';
34
35
  import { InlineDiffWidget } from './inline-diff-widget';
35
36
 
36
- type IInlineDiffPreviewer = BaseInlineDiffPreviewer<InlineDiffWidget | InlineStreamDiffHandler>;
37
+ type IInlineDiffPreviewer = BaseInlineDiffPreviewer<InlineDiffWidget | BaseInlineStreamDiffHandler>;
37
38
 
38
39
  export class InlineDiffController extends BaseAIMonacoEditorController {
39
40
  public static readonly ID = 'editor.contrib.ai.inline.diff';
@@ -118,28 +119,26 @@ export class InlineDiffController extends BaseAIMonacoEditorController {
118
119
  chatResponse?: ChatResponse | InlineChatController;
119
120
  previewerOptions?: IDiffPreviewerOptions;
120
121
  },
121
- ): BaseInlineDiffPreviewer<InlineDiffWidget | InlineStreamDiffHandler> {
122
+ ): BaseInlineDiffPreviewer<InlineDiffWidget | BaseInlineStreamDiffHandler> {
122
123
  const { crossSelection, chatResponse } = options;
123
124
 
124
125
  const disposable = new Disposable();
125
126
 
126
127
  const previewer = this.createDiffPreviewer(monacoEditor, crossSelection, options.previewerOptions);
127
- transaction((tx) => {
128
- this.currentPreviewer.set(previewer, tx);
129
- this.previewerStore.set(previewer.modelId, previewer);
130
- });
131
128
 
132
129
  const onFinish = () => {
133
130
  previewer.layout();
134
131
  disposable.dispose();
135
132
  };
136
133
 
134
+ const previewerDisposable = new DisposableCollection();
135
+
137
136
  disposable.addDispose(
138
137
  previewer.onReady(() => {
139
138
  if (InlineChatController.is(chatResponse)) {
140
139
  const controller = chatResponse as InlineChatController;
141
140
 
142
- disposable.addDispose([
141
+ previewerDisposable.pushAll([
143
142
  controller.onData((data) => {
144
143
  if (ReplyResponse.is(data)) {
145
144
  this.renderDiff(previewer, data);
@@ -167,6 +166,10 @@ export class InlineDiffController extends BaseAIMonacoEditorController {
167
166
  }),
168
167
  );
169
168
 
169
+ previewer.onDispose(() => {
170
+ previewerDisposable.dispose();
171
+ });
172
+ disposable.addDispose(previewerDisposable);
170
173
  previewer.layout();
171
174
  return previewer;
172
175
  }
@@ -196,6 +199,11 @@ export class InlineDiffController extends BaseAIMonacoEditorController {
196
199
  }),
197
200
  );
198
201
 
202
+ transaction((tx) => {
203
+ this.currentPreviewer.set(previewer, tx);
204
+ this.previewerStore.set(previewer.modelId, previewer);
205
+ });
206
+
199
207
  return previewer;
200
208
  }
201
209