@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.
Files changed (194) hide show
  1. package/LICENSE +21 -0
  2. package/lib/browser/ai-chat.contribution.d.ts +26 -0
  3. package/lib/browser/ai-chat.contribution.d.ts.map +1 -0
  4. package/lib/browser/ai-chat.contribution.js +228 -0
  5. package/lib/browser/ai-chat.contribution.js.map +1 -0
  6. package/lib/browser/ai-chat.module.less +174 -0
  7. package/lib/browser/ai-chat.service.d.ts +30 -0
  8. package/lib/browser/ai-chat.service.d.ts.map +1 -0
  9. package/lib/browser/ai-chat.service.js +129 -0
  10. package/lib/browser/ai-chat.service.js.map +1 -0
  11. package/lib/browser/ai-chat.view.d.ts +4 -0
  12. package/lib/browser/ai-chat.view.d.ts.map +1 -0
  13. package/lib/browser/ai-chat.view.js +292 -0
  14. package/lib/browser/ai-chat.view.js.map +1 -0
  15. package/lib/browser/ai-editor.contribution.d.ts +26 -0
  16. package/lib/browser/ai-editor.contribution.d.ts.map +1 -0
  17. package/lib/browser/ai-editor.contribution.js +430 -0
  18. package/lib/browser/ai-editor.contribution.js.map +1 -0
  19. package/lib/browser/ai-project/generate.service.d.ts +34 -0
  20. package/lib/browser/ai-project/generate.service.d.ts.map +1 -0
  21. package/lib/browser/ai-project/generate.service.js +460 -0
  22. package/lib/browser/ai-project/generate.service.js.map +1 -0
  23. package/lib/browser/ai-sumi/sumi.service.d.ts +11 -0
  24. package/lib/browser/ai-sumi/sumi.service.d.ts.map +1 -0
  25. package/lib/browser/ai-sumi/sumi.service.js +64 -0
  26. package/lib/browser/ai-sumi/sumi.service.js.map +1 -0
  27. package/lib/browser/code-widget/ai-code-document.provider.d.ts +10 -0
  28. package/lib/browser/code-widget/ai-code-document.provider.d.ts.map +1 -0
  29. package/lib/browser/code-widget/ai-code-document.provider.js +26 -0
  30. package/lib/browser/code-widget/ai-code-document.provider.js.map +1 -0
  31. package/lib/browser/code-widget/ai-code-widget.d.ts +20 -0
  32. package/lib/browser/code-widget/ai-code-widget.d.ts.map +1 -0
  33. package/lib/browser/code-widget/ai-code-widget.js +164 -0
  34. package/lib/browser/code-widget/ai-code-widget.js.map +1 -0
  35. package/lib/browser/components/AIImprove.d.ts +9 -0
  36. package/lib/browser/components/AIImprove.d.ts.map +1 -0
  37. package/lib/browser/components/AIImprove.js +34 -0
  38. package/lib/browser/components/AIImprove.js.map +1 -0
  39. package/lib/browser/components/AIInput.d.ts +5 -0
  40. package/lib/browser/components/AIInput.d.ts.map +1 -0
  41. package/lib/browser/components/AIInput.js +22 -0
  42. package/lib/browser/components/AIInput.js.map +1 -0
  43. package/lib/browser/components/ChatEditor.d.ts +5 -0
  44. package/lib/browser/components/ChatEditor.d.ts.map +1 -0
  45. package/lib/browser/components/ChatEditor.js +85 -0
  46. package/lib/browser/components/ChatEditor.js.map +1 -0
  47. package/lib/browser/components/ChatInput.d.ts +6 -0
  48. package/lib/browser/components/ChatInput.d.ts.map +1 -0
  49. package/lib/browser/components/ChatInput.js +126 -0
  50. package/lib/browser/components/ChatInput.js.map +1 -0
  51. package/lib/browser/components/ChatMoreActions.d.ts +5 -0
  52. package/lib/browser/components/ChatMoreActions.d.ts.map +1 -0
  53. package/lib/browser/components/ChatMoreActions.js +26 -0
  54. package/lib/browser/components/ChatMoreActions.js.map +1 -0
  55. package/lib/browser/components/Thinking.d.ts +3 -0
  56. package/lib/browser/components/Thinking.d.ts.map +1 -0
  57. package/lib/browser/components/Thinking.js +19 -0
  58. package/lib/browser/components/Thinking.js.map +1 -0
  59. package/lib/browser/components/components.module.less +261 -0
  60. package/lib/browser/components/lineVertical.d.ts +4 -0
  61. package/lib/browser/components/lineVertical.d.ts.map +1 -0
  62. package/lib/browser/components/lineVertical.js +22 -0
  63. package/lib/browser/components/lineVertical.js.map +1 -0
  64. package/lib/browser/content-widget/ai-content-widget.d.ts +38 -0
  65. package/lib/browser/content-widget/ai-content-widget.d.ts.map +1 -0
  66. package/lib/browser/content-widget/ai-content-widget.js +97 -0
  67. package/lib/browser/content-widget/ai-content-widget.js.map +1 -0
  68. package/lib/browser/content-widget/ai-inline-chat-panel.d.ts +6 -0
  69. package/lib/browser/content-widget/ai-inline-chat-panel.d.ts.map +1 -0
  70. package/lib/browser/content-widget/ai-inline-chat-panel.js +108 -0
  71. package/lib/browser/content-widget/ai-inline-chat-panel.js.map +1 -0
  72. package/lib/browser/content-widget/ai-inline-chat.module.less +81 -0
  73. package/lib/browser/content-widget/ai-inline-chat.service.d.ts +23 -0
  74. package/lib/browser/content-widget/ai-inline-chat.service.d.ts.map +1 -0
  75. package/lib/browser/content-widget/ai-inline-chat.service.js +47 -0
  76. package/lib/browser/content-widget/ai-inline-chat.service.js.map +1 -0
  77. package/lib/browser/diff-widget/ai-diff-document.provider.d.ts +10 -0
  78. package/lib/browser/diff-widget/ai-diff-document.provider.d.ts.map +1 -0
  79. package/lib/browser/diff-widget/ai-diff-document.provider.js +26 -0
  80. package/lib/browser/diff-widget/ai-diff-document.provider.js.map +1 -0
  81. package/lib/browser/diff-widget/ai-diff-widget.d.ts +20 -0
  82. package/lib/browser/diff-widget/ai-diff-widget.d.ts.map +1 -0
  83. package/lib/browser/diff-widget/ai-diff-widget.js +158 -0
  84. package/lib/browser/diff-widget/ai-diff-widget.js.map +1 -0
  85. package/lib/browser/index.d.ts +11 -0
  86. package/lib/browser/index.d.ts.map +1 -0
  87. package/lib/browser/index.js +82 -0
  88. package/lib/browser/index.js.map +1 -0
  89. package/lib/browser/inline-completions/constants.d.ts +62 -0
  90. package/lib/browser/inline-completions/constants.d.ts.map +1 -0
  91. package/lib/browser/inline-completions/constants.js +69 -0
  92. package/lib/browser/inline-completions/constants.js.map +1 -0
  93. package/lib/browser/inline-completions/provider.d.ts +27 -0
  94. package/lib/browser/inline-completions/provider.d.ts.map +1 -0
  95. package/lib/browser/inline-completions/provider.js +63 -0
  96. package/lib/browser/inline-completions/provider.js.map +1 -0
  97. package/lib/browser/override/ai-editor-tab.service.d.ts +6 -0
  98. package/lib/browser/override/ai-editor-tab.service.d.ts.map +1 -0
  99. package/lib/browser/override/ai-editor-tab.service.js +22 -0
  100. package/lib/browser/override/ai-editor-tab.service.js.map +1 -0
  101. package/lib/browser/override/ai-marker.service.d.ts +11 -0
  102. package/lib/browser/override/ai-marker.service.d.ts.map +1 -0
  103. package/lib/browser/override/ai-marker.service.js +56 -0
  104. package/lib/browser/override/ai-marker.service.js.map +1 -0
  105. package/lib/browser/override/layout/layout-config.d.ts +11 -0
  106. package/lib/browser/override/layout/layout-config.d.ts.map +1 -0
  107. package/lib/browser/override/layout/layout-config.js +17 -0
  108. package/lib/browser/override/layout/layout-config.js.map +1 -0
  109. package/lib/browser/override/layout/layout.module.less +261 -0
  110. package/lib/browser/override/layout/main-slot-renderer.d.ts +4 -0
  111. package/lib/browser/override/layout/main-slot-renderer.d.ts.map +1 -0
  112. package/lib/browser/override/layout/main-slot-renderer.js +23 -0
  113. package/lib/browser/override/layout/main-slot-renderer.js.map +1 -0
  114. package/lib/browser/override/layout/menu-bar/menu-bar.contribution.d.ts +8 -0
  115. package/lib/browser/override/layout/menu-bar/menu-bar.contribution.d.ts.map +1 -0
  116. package/lib/browser/override/layout/menu-bar/menu-bar.contribution.js +32 -0
  117. package/lib/browser/override/layout/menu-bar/menu-bar.contribution.js.map +1 -0
  118. package/lib/browser/override/layout/menu-bar/menu-bar.module.less +41 -0
  119. package/lib/browser/override/layout/menu-bar/menu-bar.view.d.ts +3 -0
  120. package/lib/browser/override/layout/menu-bar/menu-bar.view.d.ts.map +1 -0
  121. package/lib/browser/override/layout/menu-bar/menu-bar.view.js +62 -0
  122. package/lib/browser/override/layout/menu-bar/menu-bar.view.js.map +1 -0
  123. package/lib/browser/override/layout/tabbar.view.d.ts +19 -0
  124. package/lib/browser/override/layout/tabbar.view.d.ts.map +1 -0
  125. package/lib/browser/override/layout/tabbar.view.js +38 -0
  126. package/lib/browser/override/layout/tabbar.view.js.map +1 -0
  127. package/lib/browser/override/override.module.less +32 -0
  128. package/lib/browser/override/theme/default-theme.d.ts +315 -0
  129. package/lib/browser/override/theme/default-theme.d.ts.map +1 -0
  130. package/lib/browser/override/theme/default-theme.js +821 -0
  131. package/lib/browser/override/theme/default-theme.js.map +1 -0
  132. package/lib/browser/run/run.service.d.ts +52 -0
  133. package/lib/browser/run/run.service.d.ts.map +1 -0
  134. package/lib/browser/run/run.service.js +176 -0
  135. package/lib/browser/run/run.service.js.map +1 -0
  136. package/lib/common/command.d.ts +17 -0
  137. package/lib/common/command.d.ts.map +1 -0
  138. package/lib/common/command.js +21 -0
  139. package/lib/common/command.js.map +1 -0
  140. package/lib/common/index.d.ts +47 -0
  141. package/lib/common/index.d.ts.map +1 -0
  142. package/lib/common/index.js +33 -0
  143. package/lib/common/index.js.map +1 -0
  144. package/lib/index.d.ts +2 -0
  145. package/lib/index.d.ts.map +1 -0
  146. package/lib/index.js +5 -0
  147. package/lib/index.js.map +1 -0
  148. package/lib/node/index.d.ts +10 -0
  149. package/lib/node/index.d.ts.map +1 -0
  150. package/lib/node/index.js +32 -0
  151. package/lib/node/index.js.map +1 -0
  152. package/package.json +38 -0
  153. package/src/browser/ai-chat.contribution.ts +300 -0
  154. package/src/browser/ai-chat.module.less +174 -0
  155. package/src/browser/ai-chat.service.ts +146 -0
  156. package/src/browser/ai-chat.view.tsx +410 -0
  157. package/src/browser/ai-editor.contribution.ts +564 -0
  158. package/src/browser/ai-project/generate.service.ts +515 -0
  159. package/src/browser/ai-sumi/sumi.service.ts +65 -0
  160. package/src/browser/code-widget/ai-code-document.provider.ts +22 -0
  161. package/src/browser/code-widget/ai-code-widget.tsx +202 -0
  162. package/src/browser/components/AIImprove.tsx +45 -0
  163. package/src/browser/components/AIInput.tsx +30 -0
  164. package/src/browser/components/ChatEditor.tsx +129 -0
  165. package/src/browser/components/ChatInput.tsx +199 -0
  166. package/src/browser/components/ChatMoreActions.tsx +33 -0
  167. package/src/browser/components/Thinking.tsx +23 -0
  168. package/src/browser/components/components.module.less +261 -0
  169. package/src/browser/components/lineVertical.tsx +19 -0
  170. package/src/browser/content-widget/ai-content-widget.tsx +132 -0
  171. package/src/browser/content-widget/ai-inline-chat-panel.tsx +163 -0
  172. package/src/browser/content-widget/ai-inline-chat.module.less +81 -0
  173. package/src/browser/content-widget/ai-inline-chat.service.ts +46 -0
  174. package/src/browser/diff-widget/ai-diff-document.provider.ts +22 -0
  175. package/src/browser/diff-widget/ai-diff-widget.tsx +196 -0
  176. package/src/browser/index.ts +89 -0
  177. package/src/browser/inline-completions/constants.ts +108 -0
  178. package/src/browser/inline-completions/provider.ts +55 -0
  179. package/src/browser/override/ai-editor-tab.service.tsx +22 -0
  180. package/src/browser/override/ai-marker.service.tsx +61 -0
  181. package/src/browser/override/layout/layout-config.ts +17 -0
  182. package/src/browser/override/layout/layout.module.less +261 -0
  183. package/src/browser/override/layout/main-slot-renderer.tsx +63 -0
  184. package/src/browser/override/layout/menu-bar/menu-bar.contribution.tsx +28 -0
  185. package/src/browser/override/layout/menu-bar/menu-bar.module.less +41 -0
  186. package/src/browser/override/layout/menu-bar/menu-bar.view.tsx +92 -0
  187. package/src/browser/override/layout/tabbar.view.tsx +91 -0
  188. package/src/browser/override/override.module.less +32 -0
  189. package/src/browser/override/theme/default-theme.ts +818 -0
  190. package/src/browser/run/run.service.ts +186 -0
  191. package/src/common/command.ts +21 -0
  192. package/src/common/index.ts +50 -0
  193. package/src/index.ts +1 -0
  194. 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
+ );