@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.
Files changed (192) hide show
  1. package/lib/browser/ai-core.contribution.d.ts +0 -3
  2. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  3. package/lib/browser/ai-core.contribution.js +1 -45
  4. package/lib/browser/ai-core.contribution.js.map +1 -1
  5. package/lib/browser/chat/chat-agent.service.d.ts +8 -0
  6. package/lib/browser/chat/chat-agent.service.d.ts.map +1 -1
  7. package/lib/browser/chat/chat-agent.service.js +32 -5
  8. package/lib/browser/chat/chat-agent.service.js.map +1 -1
  9. package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
  10. package/lib/browser/chat/chat-manager.service.js +4 -0
  11. package/lib/browser/chat/chat-manager.service.js.map +1 -1
  12. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  13. package/lib/browser/chat/chat-model.js +3 -2
  14. package/lib/browser/chat/chat-model.js.map +1 -1
  15. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  16. package/lib/browser/chat/chat-proxy.service.js +5 -9
  17. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  18. package/lib/browser/chat/chat.module.less +1 -2
  19. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  20. package/lib/browser/chat/chat.view.js +13 -39
  21. package/lib/browser/chat/chat.view.js.map +1 -1
  22. package/lib/browser/components/ChatContext/index.js +2 -2
  23. package/lib/browser/components/ChatContext/index.js.map +1 -1
  24. package/lib/browser/components/ChatInput.d.ts.map +1 -1
  25. package/lib/browser/components/ChatInput.js +25 -1
  26. package/lib/browser/components/ChatInput.js.map +1 -1
  27. package/lib/browser/components/ChatToolRender.d.ts.map +1 -1
  28. package/lib/browser/components/ChatToolRender.js +2 -3
  29. package/lib/browser/components/ChatToolRender.js.map +1 -1
  30. package/lib/browser/components/chat-history.module.less +2 -1
  31. package/lib/browser/components/components.module.less +20 -0
  32. package/lib/browser/context/llm-context.service.d.ts +18 -5
  33. package/lib/browser/context/llm-context.service.d.ts.map +1 -1
  34. package/lib/browser/context/llm-context.service.js +80 -47
  35. package/lib/browser/context/llm-context.service.js.map +1 -1
  36. package/lib/browser/layout/layout.module.less +4 -4
  37. package/lib/browser/mcp/base-apply.service.d.ts +18 -7
  38. package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
  39. package/lib/browser/mcp/base-apply.service.js +185 -65
  40. package/lib/browser/mcp/base-apply.service.js.map +1 -1
  41. package/lib/browser/mcp/tools/components/EditFile.d.ts.map +1 -1
  42. package/lib/browser/mcp/tools/components/EditFile.js +15 -9
  43. package/lib/browser/mcp/tools/components/EditFile.js.map +1 -1
  44. package/lib/browser/mcp/tools/components/ExpandableFileList.d.ts +13 -0
  45. package/lib/browser/mcp/tools/components/ExpandableFileList.d.ts.map +1 -0
  46. package/lib/browser/mcp/tools/components/{SearchResult.js → ExpandableFileList.js} +29 -19
  47. package/lib/browser/mcp/tools/components/ExpandableFileList.js.map +1 -0
  48. package/lib/browser/mcp/tools/components/index.module.less +4 -0
  49. package/lib/browser/mcp/tools/createNewFileWithText.d.ts +1 -0
  50. package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -1
  51. package/lib/browser/mcp/tools/createNewFileWithText.js +19 -11
  52. package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -1
  53. package/lib/browser/mcp/tools/fileSearch.d.ts +1 -0
  54. package/lib/browser/mcp/tools/fileSearch.d.ts.map +1 -1
  55. package/lib/browser/mcp/tools/fileSearch.js +14 -5
  56. package/lib/browser/mcp/tools/fileSearch.js.map +1 -1
  57. package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts.map +1 -1
  58. package/lib/browser/mcp/tools/getDiagnosticsByPath.js +1 -0
  59. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
  60. package/lib/browser/mcp/tools/grepSearch.d.ts.map +1 -1
  61. package/lib/browser/mcp/tools/grepSearch.js +6 -3
  62. package/lib/browser/mcp/tools/grepSearch.js.map +1 -1
  63. package/lib/browser/mcp/tools/handlers/EditFile.js +1 -1
  64. package/lib/browser/mcp/tools/handlers/EditFile.js.map +1 -1
  65. package/lib/browser/mcp/tools/handlers/ListDir.d.ts +1 -0
  66. package/lib/browser/mcp/tools/handlers/ListDir.d.ts.map +1 -1
  67. package/lib/browser/mcp/tools/handlers/ListDir.js +3 -0
  68. package/lib/browser/mcp/tools/handlers/ListDir.js.map +1 -1
  69. package/lib/browser/mcp/tools/handlers/RunCommand.d.ts.map +1 -1
  70. package/lib/browser/mcp/tools/handlers/RunCommand.js +2 -0
  71. package/lib/browser/mcp/tools/handlers/RunCommand.js.map +1 -1
  72. package/lib/browser/mcp/tools/listDir.d.ts +1 -0
  73. package/lib/browser/mcp/tools/listDir.d.ts.map +1 -1
  74. package/lib/browser/mcp/tools/listDir.js +35 -4
  75. package/lib/browser/mcp/tools/listDir.js.map +1 -1
  76. package/lib/browser/mcp/tools/runTerminalCmd.d.ts.map +1 -1
  77. package/lib/browser/mcp/tools/runTerminalCmd.js +1 -0
  78. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  79. package/lib/browser/model/msg-history-manager.d.ts +1 -0
  80. package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
  81. package/lib/browser/model/msg-history-manager.js +3 -0
  82. package/lib/browser/model/msg-history-manager.js.map +1 -1
  83. package/lib/browser/preferences/schema.d.ts.map +1 -1
  84. package/lib/browser/preferences/schema.js +1 -7
  85. package/lib/browser/preferences/schema.js.map +1 -1
  86. package/lib/browser/widget/inline-diff/inline-diff-manager.d.ts.map +1 -1
  87. package/lib/browser/widget/inline-diff/inline-diff-manager.js +68 -8
  88. package/lib/browser/widget/inline-diff/inline-diff-manager.js.map +1 -1
  89. package/lib/browser/widget/inline-diff/inline-diff-previewer.d.ts +10 -4
  90. package/lib/browser/widget/inline-diff/inline-diff-previewer.d.ts.map +1 -1
  91. package/lib/browser/widget/inline-diff/inline-diff-previewer.js +14 -3
  92. package/lib/browser/widget/inline-diff/inline-diff-previewer.js.map +1 -1
  93. package/lib/browser/widget/inline-diff/inline-diff-widget.module.less +25 -4
  94. package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts +3 -3
  95. package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts.map +1 -1
  96. package/lib/browser/widget/inline-diff/inline-diff.controller.js +10 -5
  97. package/lib/browser/widget/inline-diff/inline-diff.controller.js.map +1 -1
  98. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts +46 -17
  99. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts.map +1 -1
  100. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js +110 -53
  101. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
  102. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts +4 -0
  103. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts.map +1 -1
  104. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js +26 -1
  105. package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js.map +1 -1
  106. package/lib/common/index.d.ts +0 -1
  107. package/lib/common/index.d.ts.map +1 -1
  108. package/lib/common/index.js +0 -2
  109. package/lib/common/index.js.map +1 -1
  110. package/lib/common/llm-context.d.ts +13 -9
  111. package/lib/common/llm-context.d.ts.map +1 -1
  112. package/lib/common/llm-context.js.map +1 -1
  113. package/lib/common/prompts/context-prompt-provider.d.ts +4 -3
  114. package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
  115. package/lib/common/prompts/context-prompt-provider.js +33 -22
  116. package/lib/common/prompts/context-prompt-provider.js.map +1 -1
  117. package/lib/common/types.d.ts +1 -0
  118. package/lib/common/types.d.ts.map +1 -1
  119. package/lib/node/anthropic/anthropic-language-model.d.ts +1 -3
  120. package/lib/node/anthropic/anthropic-language-model.d.ts.map +1 -1
  121. package/lib/node/anthropic/anthropic-language-model.js +2 -6
  122. package/lib/node/anthropic/anthropic-language-model.js.map +1 -1
  123. package/lib/node/base-language-model.d.ts +1 -4
  124. package/lib/node/base-language-model.d.ts.map +1 -1
  125. package/lib/node/base-language-model.js +6 -7
  126. package/lib/node/base-language-model.js.map +1 -1
  127. package/lib/node/deepseek/deepseek-language-model.d.ts +1 -3
  128. package/lib/node/deepseek/deepseek-language-model.d.ts.map +1 -1
  129. package/lib/node/deepseek/deepseek-language-model.js +2 -6
  130. package/lib/node/deepseek/deepseek-language-model.js.map +1 -1
  131. package/lib/node/openai/openai-language-model.d.ts +4 -5
  132. package/lib/node/openai/openai-language-model.d.ts.map +1 -1
  133. package/lib/node/openai/openai-language-model.js +7 -8
  134. package/lib/node/openai/openai-language-model.js.map +1 -1
  135. package/package.json +23 -24
  136. package/src/browser/ai-core.contribution.ts +1 -56
  137. package/src/browser/chat/chat-agent.service.ts +38 -7
  138. package/src/browser/chat/chat-manager.service.ts +6 -0
  139. package/src/browser/chat/chat-model.ts +11 -6
  140. package/src/browser/chat/chat-proxy.service.ts +6 -9
  141. package/src/browser/chat/chat.module.less +1 -2
  142. package/src/browser/chat/chat.view.tsx +14 -72
  143. package/src/browser/components/ChatContext/index.tsx +2 -2
  144. package/src/browser/components/ChatInput.tsx +67 -3
  145. package/src/browser/components/ChatToolRender.tsx +1 -2
  146. package/src/browser/components/chat-history.module.less +2 -1
  147. package/src/browser/components/components.module.less +20 -0
  148. package/src/browser/context/llm-context.service.ts +93 -54
  149. package/src/browser/layout/layout.module.less +4 -4
  150. package/src/browser/mcp/base-apply.service.ts +222 -67
  151. package/src/browser/mcp/tools/components/EditFile.tsx +16 -9
  152. package/src/browser/mcp/tools/components/ExpandableFileList.tsx +133 -0
  153. package/src/browser/mcp/tools/components/index.module.less +4 -0
  154. package/src/browser/mcp/tools/createNewFileWithText.ts +21 -12
  155. package/src/browser/mcp/tools/fileSearch.ts +14 -4
  156. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -0
  157. package/src/browser/mcp/tools/grepSearch.ts +6 -3
  158. package/src/browser/mcp/tools/handlers/EditFile.ts +1 -1
  159. package/src/browser/mcp/tools/handlers/ListDir.ts +4 -0
  160. package/src/browser/mcp/tools/handlers/RunCommand.ts +2 -0
  161. package/src/browser/mcp/tools/listDir.ts +36 -5
  162. package/src/browser/mcp/tools/runTerminalCmd.ts +1 -0
  163. package/src/browser/model/msg-history-manager.ts +4 -0
  164. package/src/browser/preferences/schema.ts +1 -7
  165. package/src/browser/widget/inline-diff/inline-diff-manager.tsx +143 -21
  166. package/src/browser/widget/inline-diff/inline-diff-previewer.ts +25 -7
  167. package/src/browser/widget/inline-diff/inline-diff-widget.module.less +25 -4
  168. package/src/browser/widget/inline-diff/inline-diff.controller.ts +16 -8
  169. package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +139 -68
  170. package/src/browser/widget/inline-stream-diff/live-preview.decoration.tsx +30 -1
  171. package/src/common/index.ts +0 -2
  172. package/src/common/llm-context.ts +10 -4
  173. package/src/common/prompts/context-prompt-provider.ts +38 -29
  174. package/src/common/types.ts +1 -0
  175. package/src/node/anthropic/anthropic-language-model.ts +2 -7
  176. package/src/node/base-language-model.ts +12 -10
  177. package/src/node/deepseek/deepseek-language-model.ts +2 -7
  178. package/src/node/openai/openai-language-model.ts +9 -10
  179. package/lib/browser/mcp/tools/components/SearchResult.d.ts +0 -11
  180. package/lib/browser/mcp/tools/components/SearchResult.d.ts.map +0 -1
  181. package/lib/browser/mcp/tools/components/SearchResult.js.map +0 -1
  182. package/lib/common/model.d.ts +0 -12
  183. package/lib/common/model.d.ts.map +0 -1
  184. package/lib/common/model.js +0 -83
  185. package/lib/common/model.js.map +0 -1
  186. package/lib/node/openai-compatible/openai-compatible-language-model.d.ts +0 -10
  187. package/lib/node/openai-compatible/openai-compatible-language-model.d.ts.map +0 -1
  188. package/lib/node/openai-compatible/openai-compatible-language-model.js +0 -32
  189. package/lib/node/openai-compatible/openai-compatible-language-model.js.map +0 -1
  190. package/src/browser/mcp/tools/components/SearchResult.tsx +0 -92
  191. package/src/common/model.ts +0 -90
  192. package/src/node/openai-compatible/openai-compatible-language-model.ts +0 -30
@@ -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
 
@@ -44,10 +44,13 @@ export enum EComputerMode {
44
44
 
45
45
  const inlineStreamDiffComputer = new InlineStreamDiffComputer();
46
46
 
47
+ /**
48
+ * Abstract base class for inline streaming diff handlers
49
+ */
47
50
  @Injectable({ multiple: true })
48
- export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPreviewerNode {
51
+ export abstract class BaseInlineStreamDiffHandler extends Disposable implements IInlineDiffPreviewerNode {
49
52
  @Autowired(INJECTOR_TOKEN)
50
- private readonly injector: Injector;
53
+ protected readonly injector: Injector;
51
54
 
52
55
  protected readonly _onDidEditChange = this.registerDispose(new Emitter<void>());
53
56
  public readonly onDidEditChange: Event<void> = this._onDidEditChange.event;
@@ -57,15 +60,14 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
57
60
 
58
61
  public previewerOptions: IDiffPreviewerOptions;
59
62
 
60
- private originalModel: ITextModel;
61
- private virtualModel: ITextModel;
63
+ protected savedModel: ITextModel;
64
+ protected virtualModel: ITextModel;
62
65
 
63
- private rawOriginalTextLines: string[];
64
- private rawOriginalTextLinesTokens: LineTokens[] = [];
65
- private undoRedoGroup: UndoRedoGroup = new UndoRedoGroup();
66
+ protected rawSavedTextLines: string[];
67
+ protected rawOriginTextLinesTokens: LineTokens[] | undefined;
68
+ protected undoRedoGroup: UndoRedoGroup = new UndoRedoGroup();
66
69
 
67
- private readonly diffModel: ISettableObservable<IComputeDiffData | undefined> = observableValue(this, undefined);
68
- private readonly finallyDiffModel: ISettableObservable<IComputeDiffData | undefined> = observableValue(
70
+ protected readonly finallyDiffModel: ISettableObservable<IComputeDiffData | undefined> = observableValue(
69
71
  this,
70
72
  undefined,
71
73
  );
@@ -73,14 +75,15 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
73
75
  public livePreviewDiffDecorationModel: LivePreviewDiffDecorationModel;
74
76
 
75
77
  public get uri() {
76
- return this.originalModel.uri;
78
+ return this.savedModel.uri;
77
79
  }
78
80
 
79
- constructor(private readonly monacoEditor: ICodeEditor) {
81
+ constructor(protected readonly monacoEditor: ICodeEditor) {
80
82
  super();
81
83
 
82
84
  const modelService = StandaloneServices.get(IModelService);
83
- this.virtualModel = modelService.createModel(
85
+ const savedModel = this.monacoEditor.getModel()!;
86
+ const setModel = modelService.createModel(
84
87
  '',
85
88
  null,
86
89
  Uri.from({
@@ -89,7 +92,8 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
89
92
  }),
90
93
  true,
91
94
  );
92
- this.originalModel = this.monacoEditor.getModel()!;
95
+ this.savedModel = savedModel;
96
+ this.virtualModel = setModel;
93
97
 
94
98
  this.livePreviewDiffDecorationModel = this.injector.get(LivePreviewDiffDecorationModel, [this.monacoEditor]);
95
99
  this.addDispose(this.livePreviewDiffDecorationModel);
@@ -113,45 +117,21 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
113
117
  });
114
118
  }
115
119
 
116
- initialize(selection: Selection): void {
117
- const eol = this.originalModel.getEOL();
118
- const startPosition = selection.getStartPosition();
119
- const endPosition = selection.getEndPosition();
120
-
121
- this.rawOriginalTextLines = this.originalModel
122
- .getValueInRange(Range.fromPositions(startPosition, endPosition))
123
- .split(eol);
124
-
125
- this.rawOriginalTextLinesTokens = this.rawOriginalTextLines.map((_, index) => {
126
- const lineNumber = startPosition.lineNumber + index;
127
- this.originalModel.tokenization.forceTokenization(lineNumber);
128
- const lineTokens = this.originalModel.tokenization.getLineTokens(lineNumber);
129
- return lineTokens;
130
- });
131
-
132
- const zone = LineRange.fromRangeInclusive(
133
- Range.fromPositions(
134
- { lineNumber: selection.startLineNumber, column: 1 },
135
- { lineNumber: selection.endLineNumber, column: Number.MAX_SAFE_INTEGER },
136
- ),
137
- );
138
-
139
- this.livePreviewDiffDecorationModel.initialize(zone);
140
- }
120
+ abstract initialize(selection: Selection): void;
141
121
 
142
122
  getVirtualModelValue() {
143
123
  return this.virtualModel.getValue();
144
124
  }
145
125
 
146
126
  getOriginModelValue() {
147
- return this.rawOriginalTextLines.join('\n');
127
+ return this.rawSavedTextLines.join('\n');
148
128
  }
149
129
 
150
130
  get onPartialEditWidgetListChange() {
151
131
  return this.livePreviewDiffDecorationModel.onPartialEditWidgetListChange;
152
132
  }
153
133
 
154
- private computeDiff(
134
+ protected computeDiff(
155
135
  originalTextLines: string[],
156
136
  newTextLines: string[],
157
137
  computerMode: EComputerMode = EComputerMode.default,
@@ -285,7 +265,12 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
285
265
  },
286
266
  textLines: removedTextLines.map((text, index) => ({
287
267
  text,
288
- lineTokens: this.rawOriginalTextLinesTokens[removedLinesOriginalRange.startLineNumber - 1 + index],
268
+ lineTokens:
269
+ this.rawOriginTextLinesTokens?.[removedLinesOriginalRange.startLineNumber - 1 + index] ||
270
+ LineTokens.createEmpty(text, {
271
+ encodeLanguageId: () => 0,
272
+ decodeLanguageId: () => 'plaintext',
273
+ }),
289
274
  })),
290
275
  });
291
276
  }
@@ -298,6 +283,11 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
298
283
  });
299
284
  }
300
285
 
286
+ /**
287
+ * Get the original model for diff operations
288
+ */
289
+ protected abstract getOriginalModel(): ITextModel;
290
+
301
291
  /**
302
292
  * 令当前的 inline diff 在流式渲染过程当中使用 pushEditOperations 进行编辑的操作都放在同一组 undo/redo 堆栈里
303
293
  * 一旦撤销到最顶层则关闭当前的 inline diff
@@ -310,22 +300,24 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
310
300
  });
311
301
  }
312
302
 
313
- private renderDiffEdits(diffModel: IComputeDiffData): void {
303
+ protected renderDiffEdits(diffModel: IComputeDiffData): void {
314
304
  const { activeLine, newFullRangeTextLines, pendingRange } = diffModel;
315
- const eol = this.originalModel.getEOL();
316
305
  const zone = this.getZone();
317
306
 
307
+ const originalModel = this.getOriginalModel();
308
+ const eol = originalModel.getEOL();
309
+
318
310
  const validZone =
319
311
  zone.startLineNumber < zone.endLineNumberExclusive
320
312
  ? new Range(
321
313
  zone.startLineNumber,
322
314
  1,
323
315
  zone.endLineNumberExclusive - 1,
324
- this.originalModel.getLineMaxColumn(zone.endLineNumberExclusive - 1),
316
+ originalModel.getLineMaxColumn(zone.endLineNumberExclusive - 1),
325
317
  )
326
318
  : new Range(zone.startLineNumber, 1, zone.startLineNumber, 1);
327
319
 
328
- const newOriginalTextLines = this.originalModel.getValueInRange(validZone).split(eol);
320
+ const newOriginalTextLines = originalModel.getValueInRange(validZone).split(eol);
329
321
  const diffComputation = linesDiffComputers.getDefault().computeDiff(newOriginalTextLines, newFullRangeTextLines, {
330
322
  computeMoves: false,
331
323
  maxComputationTimeMs: 200,
@@ -370,7 +362,7 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
370
362
  validZone.startLineNumber + change.original.startLineNumber - 1,
371
363
  1,
372
364
  validZone.startLineNumber + change.original.endLineNumberExclusive - 2,
373
- this.originalModel.getLineMaxColumn(validZone.startLineNumber + change.original.endLineNumberExclusive - 2),
365
+ originalModel.getLineMaxColumn(validZone.startLineNumber + change.original.endLineNumberExclusive - 2),
374
366
  );
375
367
  }
376
368
  const edit = {
@@ -381,7 +373,7 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
381
373
  realTimeChanges.push(edit);
382
374
  }
383
375
  }
384
- this.originalModel.pushEditOperations(null, realTimeChanges, () => null, this.undoRedoGroup);
376
+ originalModel.pushEditOperations(null, realTimeChanges, () => null, this.undoRedoGroup);
385
377
 
386
378
  /**
387
379
  * 根据 newFullRangeTextLines 内容长度重新计算 zone,避免超过最大长度,进而影响未选中的代码区域
@@ -421,17 +413,20 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
421
413
  this._onDidEditChange.fire();
422
414
  }
423
415
 
424
- public recompute(computerMode: EComputerMode, newContent?: string): IComputeDiffData {
425
- if (newContent) {
426
- this.virtualModel.setValue(newContent);
416
+ public recompute(computerMode: EComputerMode, content?: string): IComputeDiffData {
417
+ if (content) {
418
+ this.virtualModel.setValue(content);
427
419
  }
428
420
 
429
- const newTextLines = this.virtualModel.getLinesContent();
430
- return this.computeDiff(this.rawOriginalTextLines, newTextLines, computerMode);
421
+ const textLines = this.virtualModel.getLinesContent();
422
+ return this.processDiffComputation(textLines, computerMode);
431
423
  }
432
424
 
433
- private currentEditLine = 0;
434
- private isEditing = false;
425
+ protected currentEditLine = 0;
426
+ protected isEditing = false;
427
+
428
+ protected abstract processDiffComputation(currentText: string[], computerMode?: EComputerMode): IComputeDiffData;
429
+
435
430
  public async rateRenderEditController(): Promise<void> {
436
431
  if (this.isEditing === false) {
437
432
  this.isEditing = true;
@@ -443,19 +438,14 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
443
438
 
444
439
  const virtualTextLines = this.virtualModel.getLinesContent();
445
440
  const currentText = virtualTextLines.slice(0, this.currentEditLine);
446
- const currentDiffModel = this.computeDiff(this.rawOriginalTextLines, currentText);
447
- transaction((tx) => {
448
- this.diffModel.set(currentDiffModel, tx);
449
- });
450
-
451
- this.onDiffFinishedEmitter.fire(currentDiffModel);
441
+ const currentDiffModel = this.processDiffComputation(currentText);
452
442
 
453
- if (this.originalModel.id === this.monacoEditor.getModel()?.id) {
443
+ if (this.savedModel.id === this.monacoEditor.getModel()?.id) {
454
444
  this.renderDiffEdits(currentDiffModel);
455
445
  }
456
446
 
457
447
  this.currentEditLine += 1;
458
-
448
+ // 这个 sleep 会带来潜在的时序问题,如 finallyRender 时模型已经被 dispose
459
449
  await sleep(FRAME_THREE);
460
450
  }
461
451
 
@@ -469,10 +459,7 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
469
459
  }
470
460
 
471
461
  public addLinesToDiff(newText: string, computerMode: EComputerMode = EComputerMode.default): void {
472
- const diffModel = this.recompute(computerMode, newText);
473
- transaction((tx) => {
474
- this.diffModel.set(diffModel, tx);
475
- });
462
+ this.recompute(computerMode, newText);
476
463
  }
477
464
 
478
465
  public pushRateFinallyDiffStack(diffModel: IComputeDiffData): void {
@@ -488,14 +475,17 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
488
475
  public finallyRender(diffModel: IComputeDiffData): void {
489
476
  transaction((tx) => {
490
477
  this.finallyDiffModel.set(diffModel, tx);
491
- this.diffModel.set(diffModel, tx);
492
478
  });
493
479
 
494
- if (this.originalModel.id !== this.monacoEditor.getModel()?.id) {
480
+ if (this.savedModel.id !== this.monacoEditor.getModel()?.id) {
495
481
  return;
496
482
  }
497
483
 
498
484
  this.onDiffFinishedEmitter.fire(diffModel);
485
+
486
+ if (this.livePreviewDiffDecorationModel.disposed) {
487
+ return;
488
+ }
499
489
  this.renderPartialEditWidgets(diffModel);
500
490
  this.renderDiffEdits(diffModel);
501
491
  this.pushStackElement();
@@ -537,3 +527,84 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
537
527
  return this.livePreviewDiffDecorationModel.getTotalCodeInfo();
538
528
  }
539
529
  }
530
+
531
+ /**
532
+ * Regular inline stream diff handler (non-reverse mode)
533
+ */
534
+ @Injectable({ multiple: true })
535
+ export class InlineStreamDiffHandler extends BaseInlineStreamDiffHandler {
536
+ initialize(selection: Selection): void {
537
+ const eol = this.savedModel.getEOL();
538
+ const startPosition = selection.getStartPosition();
539
+ const endPosition = selection.getEndPosition();
540
+
541
+ this.rawSavedTextLines = this.savedModel
542
+ .getValueInRange(Range.fromPositions(startPosition, endPosition))
543
+ .split(eol);
544
+
545
+ const zone = LineRange.fromRangeInclusive(
546
+ Range.fromPositions(
547
+ { lineNumber: selection.startLineNumber, column: 1 },
548
+ { lineNumber: selection.endLineNumber, column: Number.MAX_SAFE_INTEGER },
549
+ ),
550
+ );
551
+
552
+ this.livePreviewDiffDecorationModel.initialize(zone);
553
+ this.rawOriginTextLinesTokens = this.rawSavedTextLines.map((_, index) => {
554
+ const lineNumber = startPosition.lineNumber + index;
555
+ this.savedModel.tokenization.forceTokenization(lineNumber);
556
+ const lineTokens = this.savedModel.tokenization.getLineTokens(lineNumber);
557
+ return lineTokens;
558
+ });
559
+ }
560
+
561
+ protected processDiffComputation(currentText: string[], computerMode?: EComputerMode): IComputeDiffData {
562
+ return this.computeDiff(this.rawSavedTextLines, currentText, computerMode);
563
+ }
564
+
565
+ protected getOriginalModel(): ITextModel {
566
+ return this.savedModel;
567
+ }
568
+ }
569
+
570
+ /**
571
+ * Reverse inline stream diff handler
572
+ * In reverse mode, the roles of savedModel and virtualModel are swapped
573
+ */
574
+ @Injectable({ multiple: true })
575
+ export class ReverseInlineStreamDiffHandler extends BaseInlineStreamDiffHandler {
576
+ recompute(computerMode: EComputerMode, content?: string): IComputeDiffData {
577
+ const result = super.recompute(computerMode, content);
578
+ this.rawOriginTextLinesTokens = this.virtualModel.getLinesContent().map((_, index) => {
579
+ const lineNumber = index + 1;
580
+ this.virtualModel.tokenization.forceTokenization(lineNumber);
581
+ const lineTokens = this.virtualModel.tokenization.getLineTokens(lineNumber);
582
+ return lineTokens;
583
+ });
584
+ return result;
585
+ }
586
+
587
+ initialize(): void {
588
+ const eol = this.savedModel.getEOL();
589
+
590
+ // reverse 模式不支持 range
591
+ this.rawSavedTextLines = this.savedModel.getValue().split(eol);
592
+
593
+ // TODO: reverse 模式暂不支持 range
594
+ const zone = LineRange.fromRangeInclusive(
595
+ Range.fromPositions(
596
+ { lineNumber: 1, column: 1 },
597
+ { lineNumber: this.virtualModel.getLineCount(), column: Number.MAX_SAFE_INTEGER },
598
+ ),
599
+ );
600
+ this.livePreviewDiffDecorationModel.initialize(zone);
601
+ }
602
+
603
+ protected getOriginalModel(): ITextModel {
604
+ return this.virtualModel;
605
+ }
606
+
607
+ protected processDiffComputation(currentText: string[], computerMode?: EComputerMode): IComputeDiffData {
608
+ return this.computeDiff(currentText, this.rawSavedTextLines, computerMode);
609
+ }
610
+ }
@@ -7,11 +7,13 @@ import {
7
7
  Emitter,
8
8
  Event,
9
9
  IAIReporter,
10
+ localize,
10
11
  runWhenIdle,
11
12
  } from '@opensumi/ide-core-common';
12
13
  import { ISingleEditOperation } from '@opensumi/ide-editor';
13
14
  import { ICodeEditor, IEditorDecorationsCollection, ITextModel, Position, Range } from '@opensumi/ide-monaco';
14
15
  import { StandaloneServices } from '@opensumi/ide-monaco/lib/browser/monaco-api/services';
16
+ import { IMessageService } from '@opensumi/ide-overlay';
15
17
  import { EditOperation } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/editOperation';
16
18
  import { LineRange } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/lineRange';
17
19
  import { ModelDecorationOptions } from '@opensumi/monaco-editor-core/esm/vs/editor/common/model/textModel';
@@ -67,6 +69,9 @@ export class LivePreviewDiffDecorationModel extends Disposable {
67
69
  @Autowired(InlineDiffService)
68
70
  private readonly inlineDiffService: InlineDiffService;
69
71
 
72
+ @Autowired(IMessageService)
73
+ private readonly messageService: IMessageService;
74
+
70
75
  private activeLineDec: IEditorDecorationsCollection;
71
76
  private pendingRangeDec: IEditorDecorationsCollection;
72
77
  private aiNativeContextKey: AINativeContextKey;
@@ -87,6 +92,10 @@ export class LivePreviewDiffDecorationModel extends Disposable {
87
92
  private removedZoneWidgets: RemovedZoneWidget[] = [];
88
93
  private zone: LineRange;
89
94
 
95
+ public get partialEditWidgetCount() {
96
+ return this.partialEditWidgetList.length;
97
+ }
98
+
90
99
  constructor(private readonly monacoEditor: ICodeEditor) {
91
100
  super();
92
101
  this.model = this.monacoEditor.getModel()!;
@@ -616,7 +625,7 @@ export class LivePreviewDiffDecorationModel extends Disposable {
616
625
  if (this.options.renderRemovedWidgetImmediately) {
617
626
  run();
618
627
  } else {
619
- runWhenIdle(run);
628
+ this.addDispose(runWhenIdle(run));
620
629
  }
621
630
  }
622
631
 
@@ -681,6 +690,26 @@ export class LivePreviewDiffDecorationModel extends Disposable {
681
690
  }
682
691
  }
683
692
 
693
+ currentChangeIndex: number = 0;
694
+
695
+ revealSiblingChange(direction: 'up' | 'down') {
696
+ this.currentChangeIndex = this.currentChangeIndex + (direction === 'up' ? -1 : 1);
697
+ if (this.currentChangeIndex >= 0 && this.currentChangeIndex < this.partialEditWidgetList.length) {
698
+ const siblingChange = this.partialEditWidgetList[this.currentChangeIndex];
699
+ const pos = siblingChange.getPosition();
700
+ if (pos?.position) {
701
+ this.monacoEditor.revealLineInCenter(pos.position!.lineNumber);
702
+ return this.currentChangeIndex;
703
+ }
704
+ } else {
705
+ this.messageService.info(
706
+ direction === 'up'
707
+ ? localize('aiNative.inlineDiff.noMoreChangesUp')
708
+ : localize('aiNative.inlineDiff.noMoreChangesDown'),
709
+ );
710
+ }
711
+ }
712
+
684
713
  setPreviewerOptions(options: IModelOptions) {
685
714
  this.options = options;
686
715
  }
@@ -19,8 +19,6 @@ import { IPosition, ITextModel, InlineCompletionContext } from '@opensumi/ide-mo
19
19
  import { MCPServerDescription } from './mcp-server-manager';
20
20
  import { MCPTool } from './types';
21
21
 
22
- export * from './model';
23
-
24
22
  export const IAINativeService = Symbol('IAINativeService');
25
23
 
26
24
  /**
@@ -15,13 +15,13 @@ export interface LLMContextService {
15
15
  */
16
16
  cleanFileContext(): void;
17
17
 
18
- onDidContextFilesChangeEvent: Event<FileContext[]>;
18
+ onDidContextFilesChangeEvent: Event<{ viewed: FileContext[]; attached: FileContext[]; version: number }>;
19
19
 
20
20
  /**
21
21
  * 从 context 中移除文件
22
22
  * @param uri URI
23
23
  */
24
- removeFileFromContext(uri: URI): void;
24
+ removeFileFromContext(uri: URI, isManual?: boolean): void;
25
25
 
26
26
  /** 导出为可序列化格式 */
27
27
  serialize(): SerializedContext;
@@ -30,12 +30,18 @@ export interface LLMContextService {
30
30
  export interface FileContext {
31
31
  uri: URI;
32
32
  selection?: [number, number];
33
- isManual: boolean;
34
33
  }
35
34
 
36
35
  export const LLMContextServiceToken = Symbol('LLMContextService');
37
36
 
37
+ export interface AttachFileContext {
38
+ content: string;
39
+ lineErrors: string[];
40
+ path: string;
41
+ language: string;
42
+ }
43
+
38
44
  export interface SerializedContext {
39
45
  recentlyViewFiles: string[];
40
- attachedFiles: Array<{ content: string; lineErrors: string[]; path: string; language: string }>;
46
+ attachedFiles: Array<AttachFileContext>;
41
47
  }