@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.
- package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
- package/lib/browser/components/ChatMentionInput.js +18 -3
- package/lib/browser/components/ChatMentionInput.js.map +1 -1
- package/lib/browser/components/components.module.less +2 -7
- package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
- package/lib/browser/components/mention-input/mention-input.js +39 -34
- package/lib/browser/components/mention-input/mention-input.js.map +1 -1
- package/lib/browser/components/mention-input/mention-input.module.less +0 -1
- package/lib/browser/components/mention-input/mention-select.d.ts +28 -0
- package/lib/browser/components/mention-input/mention-select.d.ts.map +1 -0
- package/lib/browser/components/mention-input/mention-select.js +136 -0
- package/lib/browser/components/mention-input/mention-select.js.map +1 -0
- package/lib/browser/components/mention-input/mention-select.module.less +297 -0
- package/lib/browser/components/mention-input/types.d.ts +7 -6
- package/lib/browser/components/mention-input/types.d.ts.map +1 -1
- package/lib/browser/components/mention-input/types.js.map +1 -1
- package/lib/browser/rules/rules.module.less +1 -0
- package/lib/browser/rules/rules.view.js +1 -1
- package/lib/browser/rules/rules.view.js.map +1 -1
- package/package.json +25 -25
- package/src/browser/components/ChatMentionInput.tsx +26 -4
- package/src/browser/components/components.module.less +2 -7
- package/src/browser/components/mention-input/mention-input.module.less +0 -1
- package/src/browser/components/mention-input/mention-input.tsx +36 -26
- package/src/browser/components/mention-input/mention-select.module.less +297 -0
- package/src/browser/components/mention-input/mention-select.tsx +256 -0
- package/src/browser/components/mention-input/types.ts +8 -7
- package/src/browser/rules/rules.module.less +1 -0
- package/src/browser/rules/rules.view.tsx +1 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import cls from 'classnames';
|
|
2
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { ClickOutside } from '@opensumi/ide-components/lib/click-outside';
|
|
5
|
+
import { Icon, getIcon } from '@opensumi/ide-core-browser/lib/components';
|
|
6
|
+
|
|
7
|
+
import styles from './mention-select.module.less';
|
|
8
|
+
|
|
9
|
+
export interface ExtendedModelOption {
|
|
10
|
+
label: string;
|
|
11
|
+
value: string;
|
|
12
|
+
icon?: string;
|
|
13
|
+
iconClass?: string;
|
|
14
|
+
tags?: string[];
|
|
15
|
+
features?: string[];
|
|
16
|
+
description?: string;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
badge?: string;
|
|
19
|
+
badgeColor?: string;
|
|
20
|
+
selected?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface MentionSelectProps {
|
|
24
|
+
options: ExtendedModelOption[];
|
|
25
|
+
value?: string;
|
|
26
|
+
onChange?: (value: string) => void;
|
|
27
|
+
placeholder?: string;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
className?: string;
|
|
30
|
+
size?: 'small' | 'medium' | 'large';
|
|
31
|
+
showThinking?: boolean;
|
|
32
|
+
thinkingEnabled?: boolean;
|
|
33
|
+
onThinkingChange?: (enabled: boolean) => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ThinkingToggle: React.FC<{
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
onChange: (enabled: boolean) => void;
|
|
39
|
+
}> = ({ enabled, onChange }) => (
|
|
40
|
+
<div className={styles.thinking_toggle} onClick={() => onChange(!enabled)}>
|
|
41
|
+
<Icon
|
|
42
|
+
iconClass={getIcon(enabled ? 'check' : 'circle-outline')}
|
|
43
|
+
className={cls(styles.thinking_icon, {
|
|
44
|
+
[styles.enabled]: enabled,
|
|
45
|
+
})}
|
|
46
|
+
/>
|
|
47
|
+
<span className={styles.thinking_label}>Thinking</span>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
export const MentionSelect: React.FC<MentionSelectProps> = ({
|
|
52
|
+
options,
|
|
53
|
+
value,
|
|
54
|
+
onChange,
|
|
55
|
+
placeholder,
|
|
56
|
+
disabled = false,
|
|
57
|
+
className,
|
|
58
|
+
size = 'small',
|
|
59
|
+
showThinking = false,
|
|
60
|
+
thinkingEnabled = false,
|
|
61
|
+
onThinkingChange,
|
|
62
|
+
}) => {
|
|
63
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
64
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
65
|
+
const [dropdownDirection, setDropdownDirection] = useState<'up' | 'down'>('up');
|
|
66
|
+
const selectRef = useRef<HTMLDivElement>(null);
|
|
67
|
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
68
|
+
|
|
69
|
+
const selectedOption = options.find((option) => option.selected) || options.find((option) => option.value === value);
|
|
70
|
+
|
|
71
|
+
const handleToggle = () => {
|
|
72
|
+
if (!disabled) {
|
|
73
|
+
setIsOpen(!isOpen);
|
|
74
|
+
setActiveIndex(-1);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const handleSelect = (option: ExtendedModelOption) => {
|
|
79
|
+
if (!option.disabled) {
|
|
80
|
+
onChange?.(option.value);
|
|
81
|
+
setIsOpen(false);
|
|
82
|
+
setActiveIndex(-1);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
87
|
+
if (disabled) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
switch (e.key) {
|
|
92
|
+
case 'Enter':
|
|
93
|
+
case ' ':
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
if (!isOpen) {
|
|
96
|
+
setIsOpen(true);
|
|
97
|
+
} else if (activeIndex >= 0) {
|
|
98
|
+
handleSelect(options[activeIndex]);
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
case 'Escape':
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
setIsOpen(false);
|
|
104
|
+
setActiveIndex(-1);
|
|
105
|
+
break;
|
|
106
|
+
case 'ArrowDown':
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
if (!isOpen) {
|
|
109
|
+
setIsOpen(true);
|
|
110
|
+
} else {
|
|
111
|
+
setActiveIndex((prev) => (prev < options.length - 1 ? prev + 1 : 0));
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
case 'ArrowUp':
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
if (isOpen) {
|
|
117
|
+
setActiveIndex((prev) => (prev > 0 ? prev - 1 : options.length - 1));
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const handleClickOutside = () => {
|
|
124
|
+
setIsOpen(false);
|
|
125
|
+
setActiveIndex(-1);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (isOpen && selectRef.current) {
|
|
130
|
+
const selectRect = selectRef.current.getBoundingClientRect();
|
|
131
|
+
const viewportHeight = window.innerHeight;
|
|
132
|
+
const dropdownHeight = Math.min(400, options.length * 60);
|
|
133
|
+
|
|
134
|
+
const spaceAbove = selectRect.top;
|
|
135
|
+
const spaceBelow = viewportHeight - selectRect.bottom;
|
|
136
|
+
|
|
137
|
+
if (spaceAbove < dropdownHeight && spaceBelow > spaceAbove) {
|
|
138
|
+
setDropdownDirection('down');
|
|
139
|
+
} else {
|
|
140
|
+
setDropdownDirection('up');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}, [isOpen, options.length]);
|
|
144
|
+
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (isOpen && activeIndex >= 0 && dropdownRef.current) {
|
|
147
|
+
const activeElement = dropdownRef.current.children[activeIndex] as HTMLElement;
|
|
148
|
+
if (activeElement) {
|
|
149
|
+
activeElement.scrollIntoView({
|
|
150
|
+
behavior: 'smooth',
|
|
151
|
+
block: 'nearest',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}, [isOpen, activeIndex]);
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<ClickOutside onOutsideClick={handleClickOutside}>
|
|
159
|
+
<div
|
|
160
|
+
ref={selectRef}
|
|
161
|
+
className={cls(
|
|
162
|
+
styles.mention_select,
|
|
163
|
+
styles[`size_${size}`],
|
|
164
|
+
{
|
|
165
|
+
[styles.disabled]: disabled,
|
|
166
|
+
[styles.open]: isOpen,
|
|
167
|
+
[styles.dropdown_up]: dropdownDirection === 'up',
|
|
168
|
+
[styles.dropdown_down]: dropdownDirection === 'down',
|
|
169
|
+
},
|
|
170
|
+
className,
|
|
171
|
+
)}
|
|
172
|
+
onClick={handleToggle}
|
|
173
|
+
onKeyDown={handleKeyDown}
|
|
174
|
+
tabIndex={disabled ? -1 : 0}
|
|
175
|
+
role='combobox'
|
|
176
|
+
aria-expanded={isOpen}
|
|
177
|
+
aria-haspopup='listbox'
|
|
178
|
+
>
|
|
179
|
+
<div className={styles.select_trigger}>
|
|
180
|
+
<div className={styles.select_content}>
|
|
181
|
+
{selectedOption ? (
|
|
182
|
+
<div className={styles.selected_option}>
|
|
183
|
+
<span className={styles.option_label}>{selectedOption.label}</span>
|
|
184
|
+
{selectedOption.badge && (
|
|
185
|
+
<span className={styles.option_badge} style={{ backgroundColor: selectedOption.badgeColor }}>
|
|
186
|
+
{selectedOption.badge}
|
|
187
|
+
</span>
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
) : (
|
|
191
|
+
<span className={styles.placeholder}>{placeholder}</span>
|
|
192
|
+
)}
|
|
193
|
+
</div>
|
|
194
|
+
<Icon
|
|
195
|
+
iconClass={getIcon('down-arrow')}
|
|
196
|
+
className={cls(styles.dropdown_arrow, {
|
|
197
|
+
[styles.open]: isOpen,
|
|
198
|
+
})}
|
|
199
|
+
/>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
{isOpen && (
|
|
203
|
+
<div ref={dropdownRef} className={styles.dropdown} role='listbox'>
|
|
204
|
+
{showThinking && onThinkingChange && (
|
|
205
|
+
<div className={styles.thinking_section}>
|
|
206
|
+
<ThinkingToggle enabled={thinkingEnabled} onChange={onThinkingChange} />
|
|
207
|
+
<div className={styles.divider} />
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
{options.map((option, index) => (
|
|
212
|
+
<div
|
|
213
|
+
key={option.value}
|
|
214
|
+
className={cls(styles.option, {
|
|
215
|
+
[styles.active]: index === activeIndex,
|
|
216
|
+
[styles.selected]: option.selected || option.value === value,
|
|
217
|
+
[styles.disabled]: option.disabled,
|
|
218
|
+
})}
|
|
219
|
+
onClick={() => handleSelect(option)}
|
|
220
|
+
role='option'
|
|
221
|
+
aria-selected={option.selected || option.value === value}
|
|
222
|
+
>
|
|
223
|
+
<div className={styles.option_main}>
|
|
224
|
+
<div className={styles.option_header}>
|
|
225
|
+
<div className={styles.option_title}>
|
|
226
|
+
{option.icon && <Icon icon={option.icon} className={styles.option_icon} />}
|
|
227
|
+
{option.iconClass && <Icon iconClass={option.iconClass} className={styles.option_icon} />}
|
|
228
|
+
<span className={styles.option_label}>{option.label}</span>
|
|
229
|
+
{option.badge && (
|
|
230
|
+
<span className={styles.option_badge} style={{ backgroundColor: option.badgeColor }}>
|
|
231
|
+
{option.badge}
|
|
232
|
+
</span>
|
|
233
|
+
)}
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
{option.description && <div className={styles.option_description}>{option.description}</div>}
|
|
238
|
+
|
|
239
|
+
{option.tags && option.tags.length > 0 && (
|
|
240
|
+
<div className={styles.option_tags}>
|
|
241
|
+
{option.tags.map((tag, idx) => (
|
|
242
|
+
<span key={idx} className={styles.tag}>
|
|
243
|
+
{tag}
|
|
244
|
+
</span>
|
|
245
|
+
))}
|
|
246
|
+
</div>
|
|
247
|
+
)}
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
))}
|
|
251
|
+
</div>
|
|
252
|
+
)}
|
|
253
|
+
</div>
|
|
254
|
+
</ClickOutside>
|
|
255
|
+
);
|
|
256
|
+
};
|
|
@@ -46,21 +46,19 @@ export interface MentionState {
|
|
|
46
46
|
interface ModelOption {
|
|
47
47
|
label: string;
|
|
48
48
|
value: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface ExtendedModelOption {
|
|
52
|
-
label: string;
|
|
53
|
-
value: string;
|
|
54
49
|
icon?: string;
|
|
55
50
|
iconClass?: string;
|
|
56
51
|
tags?: string[];
|
|
57
|
-
features?: string[];
|
|
58
52
|
description?: string;
|
|
59
|
-
disabled?: boolean;
|
|
60
53
|
badge?: string;
|
|
61
54
|
badgeColor?: string;
|
|
62
55
|
}
|
|
63
56
|
|
|
57
|
+
export interface ExtendedModelOption extends ModelOption {
|
|
58
|
+
disabled?: boolean;
|
|
59
|
+
selected?: boolean; // 由外部控制选中状态
|
|
60
|
+
}
|
|
61
|
+
|
|
64
62
|
export enum FooterButtonPosition {
|
|
65
63
|
LEFT = 'left',
|
|
66
64
|
RIGHT = 'right',
|
|
@@ -89,6 +87,9 @@ export interface FooterConfig {
|
|
|
89
87
|
buttons?: FooterButton[];
|
|
90
88
|
showModelSelector?: boolean;
|
|
91
89
|
disableModelSelector?: boolean;
|
|
90
|
+
showThinking?: boolean;
|
|
91
|
+
thinkingEnabled?: boolean;
|
|
92
|
+
onThinkingChange?: (enabled: boolean) => void;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
export interface MentionInputProps {
|
|
@@ -37,7 +37,7 @@ export const RulesView: React.FC = () => {
|
|
|
37
37
|
[rulesService],
|
|
38
38
|
);
|
|
39
39
|
|
|
40
|
-
const getFileNameFromPath = (path: string) => path.split('/').pop() || path;
|
|
40
|
+
const getFileNameFromPath = (path: string) => decodeURIComponent(path.split('/').pop() || path);
|
|
41
41
|
|
|
42
42
|
const hasWarning = (rule: ProjectRule) => !rule.description && !rule.alwaysApply && !rule.globs;
|
|
43
43
|
|