@opensumi/ide-ai-native 3.9.1-next-1748943529.0 → 3.9.1-next-1749003325.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 (29) hide show
  1. package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
  2. package/lib/browser/components/ChatMentionInput.js +18 -3
  3. package/lib/browser/components/ChatMentionInput.js.map +1 -1
  4. package/lib/browser/components/components.module.less +2 -7
  5. package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
  6. package/lib/browser/components/mention-input/mention-input.js +39 -34
  7. package/lib/browser/components/mention-input/mention-input.js.map +1 -1
  8. package/lib/browser/components/mention-input/mention-input.module.less +0 -1
  9. package/lib/browser/components/mention-input/mention-select.d.ts +28 -0
  10. package/lib/browser/components/mention-input/mention-select.d.ts.map +1 -0
  11. package/lib/browser/components/mention-input/mention-select.js +136 -0
  12. package/lib/browser/components/mention-input/mention-select.js.map +1 -0
  13. package/lib/browser/components/mention-input/mention-select.module.less +297 -0
  14. package/lib/browser/components/mention-input/types.d.ts +7 -6
  15. package/lib/browser/components/mention-input/types.d.ts.map +1 -1
  16. package/lib/browser/components/mention-input/types.js.map +1 -1
  17. package/lib/browser/rules/rules.module.less +1 -0
  18. package/lib/browser/rules/rules.view.js +1 -1
  19. package/lib/browser/rules/rules.view.js.map +1 -1
  20. package/package.json +25 -25
  21. package/src/browser/components/ChatMentionInput.tsx +26 -4
  22. package/src/browser/components/components.module.less +2 -7
  23. package/src/browser/components/mention-input/mention-input.module.less +0 -1
  24. package/src/browser/components/mention-input/mention-input.tsx +36 -26
  25. package/src/browser/components/mention-input/mention-select.module.less +297 -0
  26. package/src/browser/components/mention-input/mention-select.tsx +256 -0
  27. package/src/browser/components/mention-input/types.ts +8 -7
  28. package/src/browser/rules/rules.module.less +1 -0
  29. package/src/browser/rules/rules.view.tsx +1 -1
@@ -24,6 +24,8 @@ import { FileSearchServicePath, IFileSearchService } from '@opensumi/ide-file-se
24
24
  import { OutlineCompositeTreeNode, OutlineTreeNode } from '@opensumi/ide-outline/lib/browser/outline-node.define';
25
25
  import { OutlineTreeService } from '@opensumi/ide-outline/lib/browser/services/outline-tree.service';
26
26
  import { IMessageService } from '@opensumi/ide-overlay';
27
+ import { IconType } from '@opensumi/ide-theme';
28
+ import { IconService } from '@opensumi/ide-theme/lib/browser';
27
29
  import { IWorkspaceService } from '@opensumi/ide-workspace';
28
30
 
29
31
  import { IChatInternalService } from '../../common';
@@ -80,6 +82,7 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
80
82
  const workspaceService = useInjectable<IWorkspaceService>(IWorkspaceService);
81
83
  const editorService = useInjectable<WorkbenchEditorService>(WorkbenchEditorService);
82
84
  const labelService = useInjectable<LabelService>(LabelService);
85
+ const iconService = useInjectable<IconService>(IconService);
83
86
  const messageService = useInjectable<IMessageService>(IMessageService);
84
87
  const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
85
88
  const outlineTreeService = useInjectable<OutlineTreeService>(OutlineTreeService);
@@ -418,12 +421,31 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
418
421
  },
419
422
  },
420
423
  ];
421
-
422
424
  const defaultMentionInputFooterOptions: FooterConfig = useMemo(
423
425
  () => ({
424
426
  modelOptions: [
425
- { label: 'Claude 4 Sonnet', value: 'claude_sonnet4' },
426
- { label: 'DeepSeek R1', value: 'deepseek-r1' },
427
+ {
428
+ label: 'Claude 4 Sonnet',
429
+ value: 'claude_sonnet4',
430
+ iconClass: iconService.fromIcon(
431
+ '',
432
+ 'https://img.alicdn.com/imgextra/i3/O1CN01p0mziz1Nsl40lp1HO_!!6000000001626-55-tps-92-65.svg',
433
+ IconType.Background,
434
+ ),
435
+ tags: ['多模态', '长上下文理解', '思考模式'],
436
+ description: '高性能模型,支持多模态输入',
437
+ },
438
+ {
439
+ label: 'DeepSeek R1',
440
+ value: 'deepseek-r1',
441
+ iconClass: iconService.fromIcon(
442
+ '',
443
+ 'https://img.alicdn.com/imgextra/i3/O1CN01ClcK2w1JwdxcbAB3a_!!6000000001093-55-tps-30-30.svg',
444
+ IconType.Background,
445
+ ),
446
+ tags: ['思考模式', '长上下文理解'],
447
+ description: '专业创作,支持多模态输入',
448
+ },
427
449
  ],
428
450
  defaultModel:
429
451
  props.sessionModelId || preferenceService.get<string>(AINativeSettingSectionsId.ModelID) || 'deepseek-r1',
@@ -464,7 +486,7 @@ export const ChatMentionInput = (props: IChatMentionInputProps) => {
464
486
  showModelSelector: true,
465
487
  disableModelSelector: props.disableModelSelector,
466
488
  }),
467
- [handleShowMCPConfig, props.disableModelSelector, props.sessionModelId],
489
+ [iconService, handleShowMCPConfig, handleShowRules, props.disableModelSelector, props.sessionModelId],
468
490
  );
469
491
 
470
492
  const handleStop = useCallback(() => {
@@ -524,7 +524,6 @@
524
524
  display: flex;
525
525
  font-size: 11px;
526
526
  align-items: center;
527
- min-width: 150px;
528
527
  }
529
528
 
530
529
  .mcp_desc {
@@ -597,8 +596,8 @@
597
596
  align-items: center;
598
597
  padding: 0 4px;
599
598
  margin: 0 2px;
600
- background: var(--badge-background);
601
- color: var(--badge-foreground);
599
+ background-color: var(--chat-slashCommandBackground);
600
+ color: var(--chat-slashCommandForeground);
602
601
  border-radius: 3px;
603
602
  vertical-align: middle;
604
603
  font-size: 12px;
@@ -609,10 +608,6 @@
609
608
  margin-right: 3px;
610
609
  }
611
610
  }
612
- &:hover {
613
- background-color: var(--chat-slashCommandBackground);
614
- color: var(--chat-slashCommandForeground);
615
- }
616
611
  }
617
612
 
618
613
  .attachment_text {
@@ -6,7 +6,6 @@
6
6
 
7
7
  .model_selector {
8
8
  margin-right: 5px;
9
- min-width: 150px;
10
9
  }
11
10
 
12
11
  .editor_area {
@@ -2,7 +2,7 @@ import cls from 'classnames';
2
2
  import * as React from 'react';
3
3
 
4
4
  import { getSymbolIcon, localize } from '@opensumi/ide-core-browser';
5
- import { Icon, Popover, PopoverPosition, Select, getIcon } from '@opensumi/ide-core-browser/lib/components';
5
+ import { Icon, Popover, PopoverPosition, getIcon } from '@opensumi/ide-core-browser/lib/components';
6
6
  import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native';
7
7
  import { URI } from '@opensumi/ide-utils';
8
8
 
@@ -11,6 +11,7 @@ import { ProjectRule } from '../../../common/types';
11
11
 
12
12
  import styles from './mention-input.module.less';
13
13
  import { MentionPanel } from './mention-panel';
14
+ import { ExtendedModelOption, MentionSelect } from './mention-select';
14
15
  import {
15
16
  FooterButtonPosition,
16
17
  MENTION_KEYWORD,
@@ -1162,6 +1163,29 @@ export const MentionInput: React.FC<MentionInputProps> = ({
1162
1163
  [footerConfig.disableModelSelector],
1163
1164
  );
1164
1165
 
1166
+ // 转换模型选项为扩展格式
1167
+ const getExtendedModelOptions = React.useMemo((): ExtendedModelOption[] => {
1168
+ // 如果有扩展模型选项,直接使用
1169
+ if (footerConfig.extendedModelOptions) {
1170
+ return footerConfig.extendedModelOptions.map((option) => ({
1171
+ ...option,
1172
+ selected: option.value === selectedModel,
1173
+ }));
1174
+ }
1175
+
1176
+ // 否则从基础模型选项转换
1177
+ return (footerConfig.modelOptions || []).map((option): ExtendedModelOption => {
1178
+ const extendedOption: ExtendedModelOption = {
1179
+ ...option,
1180
+ };
1181
+
1182
+ // 设置选中状态:如果当前模型匹配选中的模型,则标记为选中
1183
+ extendedOption.selected = option.value === selectedModel;
1184
+
1185
+ return extendedOption;
1186
+ });
1187
+ }, [footerConfig.modelOptions, footerConfig.extendedModelOptions, selectedModel]);
1188
+
1165
1189
  const removeContext = React.useCallback(
1166
1190
  (type: MentionType, uri: URI) => {
1167
1191
  if (type === MentionType.FILE) {
@@ -1175,6 +1199,8 @@ export const MentionInput: React.FC<MentionInputProps> = ({
1175
1199
  [contextService],
1176
1200
  );
1177
1201
 
1202
+ const getFileNameFromPath = (path: string) => decodeURIComponent(path.split('/').pop() || 'Unknown Rule');
1203
+
1178
1204
  const renderContextPreview = React.useCallback(
1179
1205
  () => (
1180
1206
  <div className={styles.context_preview_container}>
@@ -1202,11 +1228,7 @@ export const MentionInput: React.FC<MentionInputProps> = ({
1202
1228
  iconClass={cls(styles.close_icon, getIcon('close'))}
1203
1229
  onClick={() => removeContext(MentionType.FILE, file.uri)}
1204
1230
  />
1205
- <span className={styles.context_preview_item_text}>
1206
- {workspaceService?.workspace
1207
- ? file.uri.toString().replace(workspaceService.workspace.uri.toString(), '').slice(1)
1208
- : file.uri.toString()}
1209
- </span>
1231
+ <span className={styles.context_preview_item_text}>{new URI(file.uri.toString()).displayName}</span>
1210
1232
  </div>
1211
1233
  ))}
1212
1234
 
@@ -1222,11 +1244,7 @@ export const MentionInput: React.FC<MentionInputProps> = ({
1222
1244
  iconClass={cls(styles.close_icon, getIcon('close'))}
1223
1245
  onClick={() => removeContext(MentionType.FOLDER, folder.uri)}
1224
1246
  />
1225
- <span className={styles.context_preview_item_text}>
1226
- {workspaceService?.workspace
1227
- ? folder.uri.toString().replace(workspaceService.workspace.uri.toString(), '').slice(1)
1228
- : folder.uri.toString()}
1229
- </span>
1247
+ <span className={styles.context_preview_item_text}>{new URI(folder.uri.toString()).displayName}</span>
1230
1248
  </div>
1231
1249
  ))}
1232
1250
 
@@ -1253,23 +1271,12 @@ export const MentionInput: React.FC<MentionInputProps> = ({
1253
1271
  iconClass={cls(styles.close_icon, getIcon('close'))}
1254
1272
  onClick={() => removeContext(MentionType.RULE, new URI(rule.path))}
1255
1273
  />
1256
- <span className={styles.context_preview_item_text}>
1257
- {rule.path.split('/').pop()?.replace('.mdc', '') || 'Unknown Rule'}
1258
- </span>
1274
+ <span className={styles.context_preview_item_text}>{getFileNameFromPath(rule.path)}</span>
1259
1275
  </div>
1260
1276
  ))}
1261
1277
  </div>
1262
1278
  ),
1263
- [
1264
- handleClearContext,
1265
- hasContext,
1266
- attachedFiles,
1267
- workspaceService,
1268
- labelService,
1269
- contextService,
1270
- handleTitleClick,
1271
- removeContext,
1272
- ],
1279
+ [handleClearContext, hasContext, attachedFiles, labelService, contextService, handleTitleClick, removeContext],
1273
1280
  );
1274
1281
 
1275
1282
  return (
@@ -1304,13 +1311,16 @@ export const MentionInput: React.FC<MentionInputProps> = ({
1304
1311
  <div className={styles.left_control}>
1305
1312
  {footerConfig.showModelSelector &&
1306
1313
  renderModelSelectorTip(
1307
- <Select
1308
- options={footerConfig.modelOptions || []}
1314
+ <MentionSelect
1315
+ options={getExtendedModelOptions}
1309
1316
  value={selectedModel}
1310
1317
  onChange={handleModelChange}
1311
1318
  className={styles.model_selector}
1312
1319
  size='small'
1313
1320
  disabled={footerConfig.disableModelSelector}
1321
+ showThinking={footerConfig.showThinking}
1322
+ thinkingEnabled={footerConfig.thinkingEnabled}
1323
+ onThinkingChange={footerConfig.onThinkingChange}
1314
1324
  />,
1315
1325
  )}
1316
1326
  {renderButtons(FooterButtonPosition.LEFT)}
@@ -0,0 +1,297 @@
1
+ .mention_select {
2
+ position: relative;
3
+ display: inline-block;
4
+
5
+ &.size_small {
6
+ .select_trigger {
7
+ height: 20px;
8
+ padding: 0 6px;
9
+ font-size: 12px;
10
+ }
11
+ }
12
+
13
+ &.size_medium {
14
+ .select_trigger {
15
+ height: 32px;
16
+ padding: 0 12px;
17
+ font-size: 14px;
18
+ }
19
+ }
20
+
21
+ &.size_large {
22
+ .select_trigger {
23
+ height: 40px;
24
+ padding: 0 16px;
25
+ font-size: 16px;
26
+ }
27
+ }
28
+
29
+ &.disabled {
30
+ opacity: 0.6;
31
+ cursor: not-allowed;
32
+
33
+ .select_trigger {
34
+ cursor: not-allowed;
35
+ background-color: var(--input-background-disabled);
36
+ }
37
+ }
38
+
39
+ .select_trigger {
40
+ display: flex;
41
+ align-items: center;
42
+ justify-content: space-between;
43
+ background-color: var(--input-background);
44
+ border: 1px solid var(--input-border);
45
+ border-radius: 4px;
46
+ cursor: pointer;
47
+ transition: all 0.2s;
48
+ opacity: 0.7;
49
+
50
+ &:hover:not(.disabled) {
51
+ opacity: 1;
52
+ }
53
+ }
54
+
55
+ .select_content {
56
+ flex: 1;
57
+ overflow: hidden;
58
+ }
59
+
60
+ .selected_option {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: 6px;
64
+ }
65
+
66
+ .option_icon {
67
+ flex-shrink: 0;
68
+ font-size: 14px;
69
+ }
70
+
71
+ .option_label {
72
+ flex: 1;
73
+ white-space: nowrap;
74
+ overflow: hidden;
75
+ text-overflow: ellipsis;
76
+ }
77
+
78
+ .option_badge {
79
+ display: inline-block;
80
+ padding: 2px 6px;
81
+ border-radius: 10px;
82
+ font-size: 10px;
83
+ color: white;
84
+ background-color: var(--badge-background);
85
+ white-space: nowrap;
86
+ }
87
+
88
+ .placeholder {
89
+ color: var(--input-placeholder-foreground);
90
+ }
91
+
92
+ .dropdown_arrow {
93
+ margin-left: 8px;
94
+ transition: transform 0.2s;
95
+ color: var(--icon-foreground);
96
+
97
+ &.open {
98
+ transform: rotate(180deg);
99
+ }
100
+ }
101
+
102
+ .dropdown {
103
+ position: absolute;
104
+ left: 0;
105
+ right: 0;
106
+ z-index: 1000;
107
+ background-color: var(--editor-background);
108
+ border: 1px solid var(--dropdown-border);
109
+ border-radius: 6px;
110
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), 0 2px 4px rgba(0, 0, 0, 0.05);
111
+ max-height: 400px;
112
+ min-width: 300px;
113
+ overflow-y: auto;
114
+ padding: 4px;
115
+ animation: dropdownFadeIn 0.15s ease-out;
116
+
117
+ // 滚动条样式
118
+ &::-webkit-scrollbar {
119
+ width: 6px;
120
+ }
121
+
122
+ &::-webkit-scrollbar-track {
123
+ background: transparent;
124
+ }
125
+
126
+ &::-webkit-scrollbar-thumb {
127
+ background: var(--scrollbar-thumb);
128
+ border-radius: 3px;
129
+ transition: background-color 0.2s;
130
+ }
131
+
132
+ &::-webkit-scrollbar-thumb:hover {
133
+ background: var(--scrollbar-thumb-hover);
134
+ }
135
+ }
136
+
137
+ @keyframes dropdownFadeIn {
138
+ from {
139
+ opacity: 0;
140
+ transform: translateY(-4px);
141
+ }
142
+
143
+ to {
144
+ opacity: 1;
145
+ transform: translateY(0);
146
+ }
147
+ }
148
+
149
+ .thinking_section {
150
+ padding: 8px 12px 4px;
151
+ }
152
+
153
+ .thinking_toggle {
154
+ display: flex;
155
+ align-items: center;
156
+ gap: 8px;
157
+ padding: 6px 0;
158
+ cursor: pointer;
159
+ border-radius: 4px;
160
+
161
+ &:hover {
162
+ background-color: var(--list-hover-background);
163
+ }
164
+ }
165
+
166
+ .thinking_icon {
167
+ flex-shrink: 0;
168
+ color: var(--icon-foreground);
169
+
170
+ &.enabled {
171
+ color: var(--list-active-selection-foreground);
172
+ }
173
+ }
174
+
175
+ .thinking_label {
176
+ font-size: 13px;
177
+ color: var(--foreground);
178
+ font-weight: 500;
179
+ }
180
+
181
+ .divider {
182
+ height: 1px;
183
+ background-color: var(--separator-border);
184
+ margin: 8px 0 4px;
185
+ }
186
+
187
+ // 向上展开(默认)
188
+ &.dropdown_up .dropdown {
189
+ bottom: 100%;
190
+ margin-bottom: 4px;
191
+ }
192
+
193
+ // 向下展开
194
+ &.dropdown_down .dropdown {
195
+ top: 100%;
196
+ margin-top: 4px;
197
+ }
198
+
199
+ .option {
200
+ padding: 8px 12px;
201
+ border-radius: 4px;
202
+ cursor: pointer;
203
+ transition: all 0.2s;
204
+ position: relative;
205
+ border: 1px solid transparent;
206
+ box-sizing: content-box;
207
+ margin-bottom: 4px;
208
+ &:last-child {
209
+ margin-bottom: 0;
210
+ }
211
+ &:hover:not(.disabled) {
212
+ border-color: var(--dropdown-border);
213
+ }
214
+
215
+ &.active {
216
+ border-color: var(--dropdown-border);
217
+ }
218
+
219
+ &.selected {
220
+ background-color: var(--dropdown-background);
221
+
222
+ &::after {
223
+ content: '✓';
224
+ position: absolute;
225
+ top: 50%;
226
+ right: 12px;
227
+ transform: translateY(-50%);
228
+ color: var(--list-active-selection-foreground);
229
+ font-weight: bold;
230
+ font-size: 14px;
231
+ z-index: 1;
232
+ }
233
+ }
234
+
235
+ &.disabled {
236
+ opacity: 0.5;
237
+ cursor: not-allowed;
238
+
239
+ &:hover {
240
+ transform: none;
241
+ box-shadow: none;
242
+ border-color: transparent;
243
+ }
244
+ }
245
+ }
246
+
247
+ .option_main {
248
+ display: flex;
249
+ flex-direction: column;
250
+ gap: 6px;
251
+ padding-right: 24px;
252
+ }
253
+
254
+ .option_header {
255
+ display: flex;
256
+ align-items: center;
257
+ justify-content: space-between;
258
+ }
259
+
260
+ .option_title {
261
+ display: flex;
262
+ align-items: center;
263
+ gap: 8px;
264
+ flex: 1;
265
+ }
266
+
267
+ .option_description {
268
+ font-size: 12px;
269
+ color: var(--descriptionForeground);
270
+ line-height: 1.4;
271
+ margin-top: 4px;
272
+ }
273
+
274
+ .option_tags {
275
+ display: flex;
276
+ flex-wrap: wrap;
277
+ gap: 4px;
278
+ margin-top: 6px;
279
+ }
280
+
281
+ .tag {
282
+ display: inline-block;
283
+ padding: 2px 6px;
284
+ border-radius: 10px;
285
+ font-size: 10px;
286
+ background-color: var(--badge-background);
287
+ color: var(--badge-foreground);
288
+ white-space: nowrap;
289
+ }
290
+
291
+ .status_indicator {
292
+ width: 8px;
293
+ height: 8px;
294
+ border-radius: 50%;
295
+ flex-shrink: 0;
296
+ }
297
+ }