@opensumi/ide-ai-native 2.26.9-next-1695201123.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/LICENSE +21 -0
- package/lib/browser/ai-chat.contribution.d.ts +26 -0
- package/lib/browser/ai-chat.contribution.d.ts.map +1 -0
- package/lib/browser/ai-chat.contribution.js +228 -0
- package/lib/browser/ai-chat.contribution.js.map +1 -0
- package/lib/browser/ai-chat.module.less +174 -0
- package/lib/browser/ai-chat.service.d.ts +30 -0
- package/lib/browser/ai-chat.service.d.ts.map +1 -0
- package/lib/browser/ai-chat.service.js +129 -0
- package/lib/browser/ai-chat.service.js.map +1 -0
- package/lib/browser/ai-chat.view.d.ts +4 -0
- package/lib/browser/ai-chat.view.d.ts.map +1 -0
- package/lib/browser/ai-chat.view.js +292 -0
- package/lib/browser/ai-chat.view.js.map +1 -0
- package/lib/browser/ai-editor.contribution.d.ts +26 -0
- package/lib/browser/ai-editor.contribution.d.ts.map +1 -0
- package/lib/browser/ai-editor.contribution.js +430 -0
- package/lib/browser/ai-editor.contribution.js.map +1 -0
- package/lib/browser/ai-project/generate.service.d.ts +34 -0
- package/lib/browser/ai-project/generate.service.d.ts.map +1 -0
- package/lib/browser/ai-project/generate.service.js +460 -0
- package/lib/browser/ai-project/generate.service.js.map +1 -0
- package/lib/browser/ai-sumi/sumi.service.d.ts +11 -0
- package/lib/browser/ai-sumi/sumi.service.d.ts.map +1 -0
- package/lib/browser/ai-sumi/sumi.service.js +64 -0
- package/lib/browser/ai-sumi/sumi.service.js.map +1 -0
- package/lib/browser/code-widget/ai-code-document.provider.d.ts +10 -0
- package/lib/browser/code-widget/ai-code-document.provider.d.ts.map +1 -0
- package/lib/browser/code-widget/ai-code-document.provider.js +26 -0
- package/lib/browser/code-widget/ai-code-document.provider.js.map +1 -0
- package/lib/browser/code-widget/ai-code-widget.d.ts +20 -0
- package/lib/browser/code-widget/ai-code-widget.d.ts.map +1 -0
- package/lib/browser/code-widget/ai-code-widget.js +164 -0
- package/lib/browser/code-widget/ai-code-widget.js.map +1 -0
- package/lib/browser/components/AIImprove.d.ts +9 -0
- package/lib/browser/components/AIImprove.d.ts.map +1 -0
- package/lib/browser/components/AIImprove.js +34 -0
- package/lib/browser/components/AIImprove.js.map +1 -0
- package/lib/browser/components/AIInput.d.ts +5 -0
- package/lib/browser/components/AIInput.d.ts.map +1 -0
- package/lib/browser/components/AIInput.js +22 -0
- package/lib/browser/components/AIInput.js.map +1 -0
- package/lib/browser/components/ChatEditor.d.ts +5 -0
- package/lib/browser/components/ChatEditor.d.ts.map +1 -0
- package/lib/browser/components/ChatEditor.js +85 -0
- package/lib/browser/components/ChatEditor.js.map +1 -0
- package/lib/browser/components/ChatInput.d.ts +6 -0
- package/lib/browser/components/ChatInput.d.ts.map +1 -0
- package/lib/browser/components/ChatInput.js +126 -0
- package/lib/browser/components/ChatInput.js.map +1 -0
- package/lib/browser/components/ChatMoreActions.d.ts +5 -0
- package/lib/browser/components/ChatMoreActions.d.ts.map +1 -0
- package/lib/browser/components/ChatMoreActions.js +26 -0
- package/lib/browser/components/ChatMoreActions.js.map +1 -0
- package/lib/browser/components/Thinking.d.ts +3 -0
- package/lib/browser/components/Thinking.d.ts.map +1 -0
- package/lib/browser/components/Thinking.js +19 -0
- package/lib/browser/components/Thinking.js.map +1 -0
- package/lib/browser/components/components.module.less +261 -0
- package/lib/browser/components/lineVertical.d.ts +4 -0
- package/lib/browser/components/lineVertical.d.ts.map +1 -0
- package/lib/browser/components/lineVertical.js +22 -0
- package/lib/browser/components/lineVertical.js.map +1 -0
- package/lib/browser/content-widget/ai-content-widget.d.ts +38 -0
- package/lib/browser/content-widget/ai-content-widget.d.ts.map +1 -0
- package/lib/browser/content-widget/ai-content-widget.js +97 -0
- package/lib/browser/content-widget/ai-content-widget.js.map +1 -0
- package/lib/browser/content-widget/ai-inline-chat-panel.d.ts +6 -0
- package/lib/browser/content-widget/ai-inline-chat-panel.d.ts.map +1 -0
- package/lib/browser/content-widget/ai-inline-chat-panel.js +108 -0
- package/lib/browser/content-widget/ai-inline-chat-panel.js.map +1 -0
- package/lib/browser/content-widget/ai-inline-chat.module.less +81 -0
- package/lib/browser/content-widget/ai-inline-chat.service.d.ts +23 -0
- package/lib/browser/content-widget/ai-inline-chat.service.d.ts.map +1 -0
- package/lib/browser/content-widget/ai-inline-chat.service.js +47 -0
- package/lib/browser/content-widget/ai-inline-chat.service.js.map +1 -0
- package/lib/browser/diff-widget/ai-diff-document.provider.d.ts +10 -0
- package/lib/browser/diff-widget/ai-diff-document.provider.d.ts.map +1 -0
- package/lib/browser/diff-widget/ai-diff-document.provider.js +26 -0
- package/lib/browser/diff-widget/ai-diff-document.provider.js.map +1 -0
- package/lib/browser/diff-widget/ai-diff-widget.d.ts +20 -0
- package/lib/browser/diff-widget/ai-diff-widget.d.ts.map +1 -0
- package/lib/browser/diff-widget/ai-diff-widget.js +158 -0
- package/lib/browser/diff-widget/ai-diff-widget.js.map +1 -0
- package/lib/browser/index.d.ts +11 -0
- package/lib/browser/index.d.ts.map +1 -0
- package/lib/browser/index.js +82 -0
- package/lib/browser/index.js.map +1 -0
- package/lib/browser/inline-completions/constants.d.ts +62 -0
- package/lib/browser/inline-completions/constants.d.ts.map +1 -0
- package/lib/browser/inline-completions/constants.js +69 -0
- package/lib/browser/inline-completions/constants.js.map +1 -0
- package/lib/browser/inline-completions/provider.d.ts +27 -0
- package/lib/browser/inline-completions/provider.d.ts.map +1 -0
- package/lib/browser/inline-completions/provider.js +63 -0
- package/lib/browser/inline-completions/provider.js.map +1 -0
- package/lib/browser/override/ai-editor-tab.service.d.ts +6 -0
- package/lib/browser/override/ai-editor-tab.service.d.ts.map +1 -0
- package/lib/browser/override/ai-editor-tab.service.js +22 -0
- package/lib/browser/override/ai-editor-tab.service.js.map +1 -0
- package/lib/browser/override/ai-marker.service.d.ts +11 -0
- package/lib/browser/override/ai-marker.service.d.ts.map +1 -0
- package/lib/browser/override/ai-marker.service.js +56 -0
- package/lib/browser/override/ai-marker.service.js.map +1 -0
- package/lib/browser/override/layout/layout-config.d.ts +11 -0
- package/lib/browser/override/layout/layout-config.d.ts.map +1 -0
- package/lib/browser/override/layout/layout-config.js +17 -0
- package/lib/browser/override/layout/layout-config.js.map +1 -0
- package/lib/browser/override/layout/layout.module.less +261 -0
- package/lib/browser/override/layout/main-slot-renderer.d.ts +4 -0
- package/lib/browser/override/layout/main-slot-renderer.d.ts.map +1 -0
- package/lib/browser/override/layout/main-slot-renderer.js +23 -0
- package/lib/browser/override/layout/main-slot-renderer.js.map +1 -0
- package/lib/browser/override/layout/menu-bar/menu-bar.contribution.d.ts +8 -0
- package/lib/browser/override/layout/menu-bar/menu-bar.contribution.d.ts.map +1 -0
- package/lib/browser/override/layout/menu-bar/menu-bar.contribution.js +32 -0
- package/lib/browser/override/layout/menu-bar/menu-bar.contribution.js.map +1 -0
- package/lib/browser/override/layout/menu-bar/menu-bar.module.less +41 -0
- package/lib/browser/override/layout/menu-bar/menu-bar.view.d.ts +3 -0
- package/lib/browser/override/layout/menu-bar/menu-bar.view.d.ts.map +1 -0
- package/lib/browser/override/layout/menu-bar/menu-bar.view.js +62 -0
- package/lib/browser/override/layout/menu-bar/menu-bar.view.js.map +1 -0
- package/lib/browser/override/layout/tabbar.view.d.ts +19 -0
- package/lib/browser/override/layout/tabbar.view.d.ts.map +1 -0
- package/lib/browser/override/layout/tabbar.view.js +38 -0
- package/lib/browser/override/layout/tabbar.view.js.map +1 -0
- package/lib/browser/override/override.module.less +32 -0
- package/lib/browser/override/theme/default-theme.d.ts +315 -0
- package/lib/browser/override/theme/default-theme.d.ts.map +1 -0
- package/lib/browser/override/theme/default-theme.js +821 -0
- package/lib/browser/override/theme/default-theme.js.map +1 -0
- package/lib/browser/run/run.service.d.ts +52 -0
- package/lib/browser/run/run.service.d.ts.map +1 -0
- package/lib/browser/run/run.service.js +176 -0
- package/lib/browser/run/run.service.js.map +1 -0
- package/lib/common/command.d.ts +17 -0
- package/lib/common/command.d.ts.map +1 -0
- package/lib/common/command.js +21 -0
- package/lib/common/command.js.map +1 -0
- package/lib/common/index.d.ts +47 -0
- package/lib/common/index.d.ts.map +1 -0
- package/lib/common/index.js +33 -0
- package/lib/common/index.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +5 -0
- package/lib/index.js.map +1 -0
- package/lib/node/index.d.ts +10 -0
- package/lib/node/index.d.ts.map +1 -0
- package/lib/node/index.js +32 -0
- package/lib/node/index.js.map +1 -0
- package/package.json +38 -0
- package/src/browser/ai-chat.contribution.ts +300 -0
- package/src/browser/ai-chat.module.less +174 -0
- package/src/browser/ai-chat.service.ts +146 -0
- package/src/browser/ai-chat.view.tsx +410 -0
- package/src/browser/ai-editor.contribution.ts +564 -0
- package/src/browser/ai-project/generate.service.ts +515 -0
- package/src/browser/ai-sumi/sumi.service.ts +65 -0
- package/src/browser/code-widget/ai-code-document.provider.ts +22 -0
- package/src/browser/code-widget/ai-code-widget.tsx +202 -0
- package/src/browser/components/AIImprove.tsx +45 -0
- package/src/browser/components/AIInput.tsx +30 -0
- package/src/browser/components/ChatEditor.tsx +129 -0
- package/src/browser/components/ChatInput.tsx +199 -0
- package/src/browser/components/ChatMoreActions.tsx +33 -0
- package/src/browser/components/Thinking.tsx +23 -0
- package/src/browser/components/components.module.less +261 -0
- package/src/browser/components/lineVertical.tsx +19 -0
- package/src/browser/content-widget/ai-content-widget.tsx +132 -0
- package/src/browser/content-widget/ai-inline-chat-panel.tsx +163 -0
- package/src/browser/content-widget/ai-inline-chat.module.less +81 -0
- package/src/browser/content-widget/ai-inline-chat.service.ts +46 -0
- package/src/browser/diff-widget/ai-diff-document.provider.ts +22 -0
- package/src/browser/diff-widget/ai-diff-widget.tsx +196 -0
- package/src/browser/index.ts +89 -0
- package/src/browser/inline-completions/constants.ts +108 -0
- package/src/browser/inline-completions/provider.ts +55 -0
- package/src/browser/override/ai-editor-tab.service.tsx +22 -0
- package/src/browser/override/ai-marker.service.tsx +61 -0
- package/src/browser/override/layout/layout-config.ts +17 -0
- package/src/browser/override/layout/layout.module.less +261 -0
- package/src/browser/override/layout/main-slot-renderer.tsx +63 -0
- package/src/browser/override/layout/menu-bar/menu-bar.contribution.tsx +28 -0
- package/src/browser/override/layout/menu-bar/menu-bar.module.less +41 -0
- package/src/browser/override/layout/menu-bar/menu-bar.view.tsx +92 -0
- package/src/browser/override/layout/tabbar.view.tsx +91 -0
- package/src/browser/override/override.module.less +32 -0
- package/src/browser/override/theme/default-theme.ts +818 -0
- package/src/browser/run/run.service.ts +186 -0
- package/src/common/command.ts +21 -0
- package/src/common/index.ts +50 -0
- package/src/index.ts +1 -0
- package/src/node/index.ts +24 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import React, { CSSProperties, useEffect, useRef } from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
|
|
4
|
+
import { Injectable, Autowired } from '@opensumi/di';
|
|
5
|
+
import { AppConfig, URI, useInjectable } from '@opensumi/ide-core-browser';
|
|
6
|
+
import { ConfigProvider } from '@opensumi/ide-core-browser';
|
|
7
|
+
import { EditorCollectionService } from '@opensumi/ide-editor';
|
|
8
|
+
import { getSimpleEditorOptions } from '@opensumi/ide-editor';
|
|
9
|
+
import { ICodeEditor, IEditorDocumentModelService } from '@opensumi/ide-editor/lib/browser/index';
|
|
10
|
+
import { monaco } from '@opensumi/ide-monaco/lib/browser/monaco-api';
|
|
11
|
+
import { IDiffEditorOptions } from '@opensumi/ide-monaco/lib/browser/monaco-api/editor';
|
|
12
|
+
import { ZoneWidget } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/zoneWidget/browser/zoneWidget';
|
|
13
|
+
|
|
14
|
+
const diffEditorOptions: IDiffEditorOptions = {
|
|
15
|
+
scrollBeyondLastLine: false,
|
|
16
|
+
scrollbar: {
|
|
17
|
+
verticalScrollbarSize: 14,
|
|
18
|
+
horizontal: 'auto',
|
|
19
|
+
useShadows: true,
|
|
20
|
+
verticalHasArrows: false,
|
|
21
|
+
horizontalHasArrows: false,
|
|
22
|
+
alwaysConsumeMouseWheel: false,
|
|
23
|
+
},
|
|
24
|
+
fixedOverflowWidgets: true,
|
|
25
|
+
readOnly: true,
|
|
26
|
+
minimap: {
|
|
27
|
+
enabled: false,
|
|
28
|
+
},
|
|
29
|
+
enableSplitViewResizing: true,
|
|
30
|
+
renderOverviewRuler: true,
|
|
31
|
+
ignoreTrimWhitespace: false,
|
|
32
|
+
renderSideBySide: true,
|
|
33
|
+
lineNumbers: 'on',
|
|
34
|
+
glyphMargin: false,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const CodeContentProvider = React.memo(((props: { dto: { answerValue } | undefined; onMaxLincCount: (n) => void }) => {
|
|
38
|
+
const { dto, onMaxLincCount } = props;
|
|
39
|
+
const documentModelService: IEditorDocumentModelService = useInjectable(IEditorDocumentModelService);
|
|
40
|
+
const editorCollectionService: EditorCollectionService = useInjectable(EditorCollectionService);
|
|
41
|
+
const editorRef = useRef<HTMLDivElement>(null);
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!dto) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let codeEditor: ICodeEditor;
|
|
49
|
+
|
|
50
|
+
const random = Math.random() * 10;
|
|
51
|
+
const originUri = URI.parse(`AI://origin${random}`);
|
|
52
|
+
|
|
53
|
+
Promise.all([
|
|
54
|
+
documentModelService.createModelReference(originUri),
|
|
55
|
+
]).then((data) => {
|
|
56
|
+
const [original] = data;
|
|
57
|
+
if (!original) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const { answerValue } = dto!;
|
|
62
|
+
|
|
63
|
+
codeEditor = editorCollectionService.createCodeEditor(editorRef.current!, {
|
|
64
|
+
...getSimpleEditorOptions(),
|
|
65
|
+
...diffEditorOptions,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const originalModel = original.instance.getMonacoModel();
|
|
69
|
+
|
|
70
|
+
originalModel.setMode('java');
|
|
71
|
+
|
|
72
|
+
originalModel.setValue(answerValue);
|
|
73
|
+
|
|
74
|
+
codeEditor.monacoEditor.setModel(originalModel);
|
|
75
|
+
codeEditor.monacoEditor.updateOptions({ readOnly: true });
|
|
76
|
+
|
|
77
|
+
if (onMaxLincCount) {
|
|
78
|
+
const { monacoEditor } = codeEditor;
|
|
79
|
+
|
|
80
|
+
const contentHeight = monacoEditor.getContentHeight();
|
|
81
|
+
const lineCount = contentHeight / monacoEditor.getOption(monaco.editor.EditorOption.lineHeight);
|
|
82
|
+
|
|
83
|
+
onMaxLincCount(lineCount);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return () => {
|
|
88
|
+
if (codeEditor) {
|
|
89
|
+
codeEditor.dispose();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}, [dto]);
|
|
93
|
+
|
|
94
|
+
return <div ref={editorRef} style={{ height: 'inherit', width:'100%', border:'1px solid #6666' }}></div>;
|
|
95
|
+
}));
|
|
96
|
+
|
|
97
|
+
const styles: CSSProperties = {
|
|
98
|
+
height: '100%',
|
|
99
|
+
display: 'flex',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
flexDirection: 'column',
|
|
102
|
+
backgroundColor: 'rgba(38, 79, 120, 0.25)',
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
@Injectable({ multiple: true })
|
|
106
|
+
export class AiCodeWidget extends ZoneWidget {
|
|
107
|
+
@Autowired(AppConfig)
|
|
108
|
+
private configContext: AppConfig;
|
|
109
|
+
|
|
110
|
+
private recordLine: number;
|
|
111
|
+
|
|
112
|
+
private answerValue: string;
|
|
113
|
+
private headUri: URI;
|
|
114
|
+
|
|
115
|
+
protected applyClass(): void {}
|
|
116
|
+
protected applyStyle(): void {}
|
|
117
|
+
|
|
118
|
+
protected _fillContainer(container: HTMLElement): void {
|
|
119
|
+
this.setCssClass('ai_code-widget');
|
|
120
|
+
|
|
121
|
+
ReactDOM.render(
|
|
122
|
+
<ConfigProvider value={this.configContext}>
|
|
123
|
+
<div style={styles}>
|
|
124
|
+
{
|
|
125
|
+
this.headUri &&
|
|
126
|
+
<div className={'ai_code_head'} style={{
|
|
127
|
+
display: 'flex',
|
|
128
|
+
alignItems: 'center',
|
|
129
|
+
width: '100%',
|
|
130
|
+
whiteSpace: 'nowrap',
|
|
131
|
+
padding: '3px 16px',
|
|
132
|
+
}}>
|
|
133
|
+
<span style={{
|
|
134
|
+
color: 'white',
|
|
135
|
+
marginRight: '10px',
|
|
136
|
+
}}>{this.headUri.displayName}</span>
|
|
137
|
+
<span>{this.headUri.codeUri.fsPath.replace(this.configContext.workspaceDir, '')}</span>
|
|
138
|
+
</div>
|
|
139
|
+
}
|
|
140
|
+
<CodeContentProvider dto={{ answerValue: this.answerValue }} onMaxLincCount={(n) => {
|
|
141
|
+
if (n) {
|
|
142
|
+
this._relayout(n);
|
|
143
|
+
}
|
|
144
|
+
}}/>
|
|
145
|
+
</div>
|
|
146
|
+
</ConfigProvider>,
|
|
147
|
+
container,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
constructor(editor: ICodeEditor, answerValue: string, headUri: string) {
|
|
152
|
+
// @ts-ignore
|
|
153
|
+
super(editor, {
|
|
154
|
+
showArrow: false,
|
|
155
|
+
showFrame: false,
|
|
156
|
+
arrowColor: undefined,
|
|
157
|
+
frameColor: undefined,
|
|
158
|
+
// 这里有个小坑,如果不开启这个配置,那么在调用 show 函数的时候会自动对焦并滚动到对应 range,导致在编辑 result 视图中代码时光标总是滚动在最后一个 widget 上
|
|
159
|
+
keepEditorSelection: true,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
this.answerValue = answerValue;
|
|
163
|
+
this.headUri = URI.parse(headUri);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 覆写 revealLine 函数,使其在 show 的时候编辑器不会定位到对应位置
|
|
167
|
+
protected override revealLine(lineNumber: number, isLastLine: boolean): void {
|
|
168
|
+
// not implement
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public getRecordLine(): number {
|
|
172
|
+
return this.recordLine;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public setContainerStyle(style: { [key in string]: string }): void {
|
|
176
|
+
const keys = Object.keys(style);
|
|
177
|
+
for (const key of keys) {
|
|
178
|
+
if (Object.prototype.hasOwnProperty.call(this.container?.style, key)) {
|
|
179
|
+
this.container!.style[key] = style[key];
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public addClassName(type: string): this {
|
|
185
|
+
this.setCssClass(type);
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public showByLine(line: number, lineNumber = 20): void {
|
|
190
|
+
this.recordLine = line;
|
|
191
|
+
super.hide();
|
|
192
|
+
super.show(
|
|
193
|
+
{
|
|
194
|
+
startLineNumber: line,
|
|
195
|
+
startColumn: 0,
|
|
196
|
+
endLineNumber: line,
|
|
197
|
+
endColumn: Number.MAX_SAFE_INTEGER,
|
|
198
|
+
},
|
|
199
|
+
lineNumber,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { useCallback, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { getExternalIcon, getIcon } from '@opensumi/ide-core-browser';
|
|
4
|
+
import { Icon } from '@opensumi/ide-core-browser/lib/components/index';
|
|
5
|
+
|
|
6
|
+
export const AIImprove = (props: { onClick?: (title: string) => void; lists?: { title: string; iconClass: string }[] }) => {
|
|
7
|
+
const { onClick, lists } = props;
|
|
8
|
+
|
|
9
|
+
const handleClick = useCallback(
|
|
10
|
+
(title) => {
|
|
11
|
+
if (onClick) {
|
|
12
|
+
onClick(title);
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
[onClick],
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const useLists = useMemo(() => {
|
|
19
|
+
if (lists && lists.length > 0) {
|
|
20
|
+
return lists;
|
|
21
|
+
}
|
|
22
|
+
return [
|
|
23
|
+
{ title: '采纳', iconClass: getExternalIcon('git-pull-request') },
|
|
24
|
+
{ title: '|', iconClass: ''},
|
|
25
|
+
{ title: '丢弃', iconClass: getExternalIcon('clear-all')},
|
|
26
|
+
{ title: '|', iconClass: ''},
|
|
27
|
+
{ title: '优化', iconClass: getExternalIcon('wand')},
|
|
28
|
+
{ title: '|', iconClass: ''},
|
|
29
|
+
{ title: '更多指令', iconClass: getIcon('more')},
|
|
30
|
+
];
|
|
31
|
+
}, [lists]);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<ul style={{ display: 'flex', alignItems: 'center', paddingLeft: '0', margin: '6px 0 6px 0' }}>
|
|
35
|
+
{useLists.map(({ title, iconClass }) => (
|
|
36
|
+
<li style={{ marginRight: '6px', display: 'flex', alignItems: 'center', cursor: 'pointer' }}>
|
|
37
|
+
{iconClass && <Icon className={iconClass} style={{marginRight: '6px'}}></Icon>}
|
|
38
|
+
<span onClick={() => handleClick(title)}>
|
|
39
|
+
{title}
|
|
40
|
+
</span>
|
|
41
|
+
</li>
|
|
42
|
+
))}
|
|
43
|
+
</ul>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { getIcon } from '@opensumi/ide-core-browser';
|
|
4
|
+
import { Icon, Input } from '@opensumi/ide-core-browser/lib/components/index';
|
|
5
|
+
|
|
6
|
+
import * as styles from './components.module.less';
|
|
7
|
+
|
|
8
|
+
export const AiInput = ({ onValueChange }) => {
|
|
9
|
+
const [value, setValue] = useState<string>();
|
|
10
|
+
|
|
11
|
+
const handleChange = (newValue: string) => {
|
|
12
|
+
setValue(newValue);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const emitterValueChange = useCallback(() => {
|
|
16
|
+
if (onValueChange) {
|
|
17
|
+
onValueChange(value);
|
|
18
|
+
}
|
|
19
|
+
}, [value]);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Input
|
|
23
|
+
className={styles.ai_native_input_container}
|
|
24
|
+
placeholder={'可以问我任何问题,或键入主题 "/"'}
|
|
25
|
+
value={value}
|
|
26
|
+
onValueChange={handleChange}
|
|
27
|
+
addonAfter={<Icon className={getIcon('right')} onClick={() => emitterValueChange()} />}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Schemes,
|
|
5
|
+
URI,
|
|
6
|
+
getIcon,
|
|
7
|
+
useInjectable,
|
|
8
|
+
uuid,
|
|
9
|
+
} from '@opensumi/ide-core-browser';
|
|
10
|
+
import { Icon, Popover } from '@opensumi/ide-core-browser/lib/components';
|
|
11
|
+
import { getSimpleEditorOptions, ICodeEditor } from '@opensumi/ide-editor';
|
|
12
|
+
import { EditorCollectionService } from '@opensumi/ide-editor';
|
|
13
|
+
import { IEditorDocumentModelService } from '@opensumi/ide-editor/lib/browser/index';
|
|
14
|
+
import { TextmateService } from '@opensumi/ide-editor/lib/browser/monaco-contrib/tokenizer/textmate.service';
|
|
15
|
+
|
|
16
|
+
import * as styles from './components.module.less';
|
|
17
|
+
|
|
18
|
+
const ChatEditor = ({ input, language }) => {
|
|
19
|
+
const ref = React.useRef<HTMLDivElement | null>(null);
|
|
20
|
+
const editorCollectionService = useInjectable<EditorCollectionService>(EditorCollectionService);
|
|
21
|
+
const documentService = useInjectable<IEditorDocumentModelService>(IEditorDocumentModelService);
|
|
22
|
+
const textmateTokenizer = useInjectable<TextmateService>(TextmateService);
|
|
23
|
+
|
|
24
|
+
const useUUID = useMemo(() => uuid(12), [ref, ref.current]);
|
|
25
|
+
|
|
26
|
+
const createEditor = async (container: HTMLElement): Promise<ICodeEditor> => {
|
|
27
|
+
const codeEditor: ICodeEditor = await editorCollectionService.createCodeEditor(container!, {
|
|
28
|
+
...getSimpleEditorOptions(),
|
|
29
|
+
readOnly: true,
|
|
30
|
+
lineNumbers: 'off',
|
|
31
|
+
selectOnLineNumbers: true,
|
|
32
|
+
scrollBeyondLastLine: false,
|
|
33
|
+
lineDecorationsWidth: 8,
|
|
34
|
+
dragAndDrop: false,
|
|
35
|
+
padding: { top: 8, bottom: 8 },
|
|
36
|
+
mouseWheelZoom: false,
|
|
37
|
+
scrollbar: {
|
|
38
|
+
alwaysConsumeMouseWheel: false,
|
|
39
|
+
},
|
|
40
|
+
wordWrap: 'off',
|
|
41
|
+
ariaLabel: 'Code block',
|
|
42
|
+
automaticLayout: true,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const docModel = await documentService.createModelReference(
|
|
46
|
+
new URI(`ai/chat/editor/${useUUID}`).withScheme(Schemes.walkThroughSnippet),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const model = docModel.instance.getMonacoModel();
|
|
50
|
+
model.updateOptions({ tabSize: 2 });
|
|
51
|
+
codeEditor.monacoEditor.setModel(model);
|
|
52
|
+
codeEditor.monacoEditor.getModel()?.setMode(language);
|
|
53
|
+
|
|
54
|
+
return codeEditor;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
if (ref && ref.current) {
|
|
59
|
+
textmateTokenizer.activateLanguage(language).then(async () => {
|
|
60
|
+
const codeEditor = await createEditor(ref.current!);
|
|
61
|
+
if (codeEditor) {
|
|
62
|
+
codeEditor.monacoEditor.setValue(input);
|
|
63
|
+
requestAnimationFrame(() => {
|
|
64
|
+
ref.current!.style.height = codeEditor.monacoEditor.getContentHeight() + 2 + 'px';
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}, [ref.current]);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div className={styles.monaco_wrapper}>
|
|
73
|
+
<div className={styles.action_toolbar}>
|
|
74
|
+
<Popover id={`ai-chat-inser-${useUUID}`} title='插入代码'>
|
|
75
|
+
<Icon className={getIcon('openfile')} />
|
|
76
|
+
</Popover>
|
|
77
|
+
<Popover id={`ai-chat-copy-${useUUID}`} title='复制代码'>
|
|
78
|
+
<Icon className={getIcon('file-copy')} />
|
|
79
|
+
</Popover>
|
|
80
|
+
</div>
|
|
81
|
+
<div ref={ref} className={styles.editor}></div>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const CodeBlockWrapper = ({ text }: { text: string }) => {
|
|
87
|
+
const renderText = (content) => {
|
|
88
|
+
const regexInlineCode = /`([^`]+)`/g;
|
|
89
|
+
const regexBlockCode = /```([^`]+)```/g;
|
|
90
|
+
|
|
91
|
+
return content.split(regexBlockCode).map((block: string, index) => {
|
|
92
|
+
if (index % 2 === 0) {
|
|
93
|
+
// 文本内容
|
|
94
|
+
return block.split(regexInlineCode).map((inline, index) => {
|
|
95
|
+
if (index % 2 === 0) {
|
|
96
|
+
// 非代码内容
|
|
97
|
+
return inline;
|
|
98
|
+
} else {
|
|
99
|
+
// 用 <span> 包裹代码行
|
|
100
|
+
return (
|
|
101
|
+
<span className={styles.code_inline} key={index}>
|
|
102
|
+
{inline}
|
|
103
|
+
</span>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
} else {
|
|
108
|
+
// 通常第一个换行前的字符就是 language,比如 ```typescript
|
|
109
|
+
const language = block.split('\n')[0] || 'plaintext';
|
|
110
|
+
|
|
111
|
+
// 并去掉第一个 \n 前面的字符
|
|
112
|
+
block = block.replace(/.*?\n/, '');
|
|
113
|
+
block = block.trim();
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div className={styles.code_block}>
|
|
117
|
+
<ChatEditor input={block} language={language} />
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div className={styles.ai_chat_code_wrapper}>
|
|
126
|
+
<div className={styles.render_text}>{renderText(text)}</div>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import cls from 'classnames';
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { getExternalIcon } from '@opensumi/ide-core-browser';
|
|
5
|
+
import { Icon, Input, getIcon } from '@opensumi/ide-core-browser/lib/components';
|
|
6
|
+
|
|
7
|
+
import { InstructionEnum } from '../../common';
|
|
8
|
+
|
|
9
|
+
import * as styles from './components.module.less';
|
|
10
|
+
|
|
11
|
+
interface IBlockProps {
|
|
12
|
+
icon: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const Block = ({ icon, name }: IBlockProps) => (
|
|
17
|
+
<div className={styles.block}>
|
|
18
|
+
<Icon className={icon} />
|
|
19
|
+
{name && <span className={styles.name}>{name}</span>}
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// 指令列表
|
|
24
|
+
const optionsList: IBlockProps[] = [
|
|
25
|
+
{
|
|
26
|
+
icon: getIcon('search'),
|
|
27
|
+
name: InstructionEnum.aiSearchKey,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
icon: getExternalIcon('code'),
|
|
31
|
+
name: InstructionEnum.aiSearchCodeKey,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
icon: getIcon('edit'),
|
|
35
|
+
name: InstructionEnum.aiExplainKey,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
icon: getIcon('smile'),
|
|
39
|
+
name: InstructionEnum.aiSumiKey,
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// 指令命令激活组件
|
|
44
|
+
const InstructionOptions = ({ onClick, bottom }) => {
|
|
45
|
+
const [commonlyUsed, setCommonlyUsed] = useState<IBlockProps[]>([]);
|
|
46
|
+
const [options, setOptions] = useState<IBlockProps[]>([]);
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
setOptions(optionsList);
|
|
50
|
+
|
|
51
|
+
setCommonlyUsed([
|
|
52
|
+
{
|
|
53
|
+
icon: getIcon('delete'),
|
|
54
|
+
name: '聊天',
|
|
55
|
+
},
|
|
56
|
+
]);
|
|
57
|
+
}, []);
|
|
58
|
+
|
|
59
|
+
const handleClick = useCallback(
|
|
60
|
+
(name: string | undefined) => {
|
|
61
|
+
if (onClick) {
|
|
62
|
+
onClick(name || '');
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
[onClick],
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className={styles.instruction_options_container} style={{ bottom: bottom + 'px' }}>
|
|
70
|
+
<div className={styles.options}>
|
|
71
|
+
<ul>
|
|
72
|
+
{options.map(({ icon, name }) => (
|
|
73
|
+
<li key={name} onClick={() => handleClick(name)}>
|
|
74
|
+
<Block icon={icon} />
|
|
75
|
+
<span>{name}</span>
|
|
76
|
+
</li>
|
|
77
|
+
))}
|
|
78
|
+
</ul>
|
|
79
|
+
</div>
|
|
80
|
+
{commonlyUsed.length > 0 && (
|
|
81
|
+
<div className={styles.commonly_used}>
|
|
82
|
+
<span>常用指令:</span>
|
|
83
|
+
{commonlyUsed.map(({ icon, name }, i) => (
|
|
84
|
+
<Block key={i} icon={icon} name={name} />
|
|
85
|
+
))}
|
|
86
|
+
</div>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export interface IChatInputProps {
|
|
93
|
+
onSend: (value: string) => void;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 指令命令激活组件
|
|
97
|
+
export const ChatInput = ({ onSend }: IChatInputProps) => {
|
|
98
|
+
const [value, setValue] = useState('');
|
|
99
|
+
const [isShowOptions, setIsShowOptions] = useState<boolean>(false);
|
|
100
|
+
const [wrapperHeight, setWrapperHeight] = useState<number>(40);
|
|
101
|
+
const [slashWidget, setSlashWidget] = useState('');
|
|
102
|
+
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (value.length === 1 && value.startsWith('/')) {
|
|
106
|
+
setIsShowOptions(true);
|
|
107
|
+
} else {
|
|
108
|
+
setIsShowOptions(false);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 自适应高度
|
|
112
|
+
if (inputRef && inputRef.current) {
|
|
113
|
+
inputRef.current.style.height = 0 + 'px';
|
|
114
|
+
|
|
115
|
+
const scrollHeight = inputRef.current.scrollHeight;
|
|
116
|
+
inputRef.current.style.height = Math.min(scrollHeight, 140) + 'px';
|
|
117
|
+
|
|
118
|
+
setWrapperHeight(scrollHeight + 20);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 设置 slash widget 块
|
|
122
|
+
const regex = /\/(\w+)\s/;
|
|
123
|
+
const match = value.match(regex);
|
|
124
|
+
if (match) {
|
|
125
|
+
const keyword = match[0];
|
|
126
|
+
if (optionsList.find(({ name }) => name === keyword)) {
|
|
127
|
+
setSlashWidget(keyword);
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
setSlashWidget('');
|
|
131
|
+
}
|
|
132
|
+
}, [inputRef, value]);
|
|
133
|
+
|
|
134
|
+
const handleInputChange = useCallback((value: string) => setValue(value), []);
|
|
135
|
+
|
|
136
|
+
const handleSend = useCallback(() => {
|
|
137
|
+
if (onSend) {
|
|
138
|
+
onSend(value);
|
|
139
|
+
setValue('');
|
|
140
|
+
}
|
|
141
|
+
}, [onSend, value]);
|
|
142
|
+
|
|
143
|
+
const acquireOptionsCheck = useCallback(
|
|
144
|
+
(value: string) => {
|
|
145
|
+
if (value) {
|
|
146
|
+
setValue(value);
|
|
147
|
+
setIsShowOptions(false);
|
|
148
|
+
|
|
149
|
+
if (inputRef && inputRef.current) {
|
|
150
|
+
inputRef.current.focus();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
[inputRef],
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const optionsBottomPosition = useMemo(() => Math.min(181, Math.max(61, 21 + wrapperHeight)), [wrapperHeight]);
|
|
158
|
+
|
|
159
|
+
const handleKeyDown = (event) => {
|
|
160
|
+
if (event.key === 'Enter' && !event.nativeEvent.isComposing) {
|
|
161
|
+
if (!event.shiftKey) {
|
|
162
|
+
event.preventDefault();
|
|
163
|
+
handleSend();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<div className={styles.chat_input_container}>
|
|
170
|
+
{isShowOptions && <InstructionOptions onClick={acquireOptionsCheck} bottom={optionsBottomPosition} />}
|
|
171
|
+
{/* <div className={styles.header_operate}>
|
|
172
|
+
<Block icon={getIcon('add-comments')} name={'新对话'} />
|
|
173
|
+
<Icon className={getExternalIcon('history')} />
|
|
174
|
+
</div> */}
|
|
175
|
+
<Input
|
|
176
|
+
ref={inputRef}
|
|
177
|
+
placeholder={'可以问我任何问题,或键入主题 "/"'}
|
|
178
|
+
wrapperStyle={{ height: wrapperHeight + 'px' }}
|
|
179
|
+
value={value}
|
|
180
|
+
type={'textarea'}
|
|
181
|
+
onKeyDown={handleKeyDown}
|
|
182
|
+
onValueChange={handleInputChange}
|
|
183
|
+
className={styles.input_wrapper}
|
|
184
|
+
addonBefore={
|
|
185
|
+
slashWidget && (
|
|
186
|
+
<div className={styles.slash_widget_block}>
|
|
187
|
+
<span>{slashWidget}</span>
|
|
188
|
+
</div>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
addonAfter={
|
|
192
|
+
<div className={cls(styles.send_chat_btn, value.length && styles.active)} onClick={() => handleSend()}>
|
|
193
|
+
<Icon className={getIcon('right')} />
|
|
194
|
+
</div>
|
|
195
|
+
}
|
|
196
|
+
/>
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { getExternalIcon, uuid } from '@opensumi/ide-core-browser';
|
|
4
|
+
import { Icon, Popover } from '@opensumi/ide-core-browser/lib/components';
|
|
5
|
+
|
|
6
|
+
import * as styles from './components.module.less';
|
|
7
|
+
import { LineVertical } from './lineVertical';
|
|
8
|
+
|
|
9
|
+
export const ChatMoreActions = ({ children }) => {
|
|
10
|
+
|
|
11
|
+
const useUUID = useMemo(() => uuid(12), []);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className={styles.ai_chat_more_actions_container}>
|
|
15
|
+
<div className={styles.ai_chat_message}>{children}</div>
|
|
16
|
+
<div className={styles.more_actions}>
|
|
17
|
+
<div className={styles.side}>
|
|
18
|
+
<Icon className={getExternalIcon('history')} />
|
|
19
|
+
<span>重新生成</span>
|
|
20
|
+
</div>
|
|
21
|
+
<div className={styles.side}>
|
|
22
|
+
<Popover id={`ai-chat-thumbsup-${useUUID}`} title='赞'>
|
|
23
|
+
<Icon className={getExternalIcon('thumbsup')} />
|
|
24
|
+
</Popover>
|
|
25
|
+
<LineVertical />
|
|
26
|
+
<Popover id={`ai-chat-thumbsdown-${useUUID}`} title='踩'>
|
|
27
|
+
<Icon className={getExternalIcon('thumbsdown')} />
|
|
28
|
+
</Popover>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Icon, getIcon } from '@opensumi/ide-core-browser/lib/components';
|
|
4
|
+
import { Progress } from '@opensumi/ide-core-browser/lib/progress/progress-bar';
|
|
5
|
+
|
|
6
|
+
import * as styles from './components.module.less';
|
|
7
|
+
|
|
8
|
+
export const Thinking = () => (
|
|
9
|
+
<div className={styles.thinking_container}>
|
|
10
|
+
<div className={styles.content}>
|
|
11
|
+
<span>Thinking...</span>
|
|
12
|
+
</div>
|
|
13
|
+
<div className={styles.stop}>
|
|
14
|
+
<span className={styles.progress_bar}>
|
|
15
|
+
<Progress loading={true} />
|
|
16
|
+
</span>
|
|
17
|
+
<div className={styles.block}>
|
|
18
|
+
<Icon className={getIcon('pause')}></Icon>
|
|
19
|
+
<span>停止</span>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
);
|