Skip to content

Commit 1c5e2dc

Browse files
authoredMar 13, 2025··
feat: trae cn support web&plugin (#5466)
* feat: trae cn web support * feat: support trae-cn * chore: prettier adjust * chore: prettier adjust again
1 parent 1a873f5 commit 1c5e2dc

File tree

8 files changed

+190
-69
lines changed

8 files changed

+190
-69
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"singleQuote": true,
3+
"semi": false
4+
}

‎extensions/ide/vscode/devbox/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
66

77
## [Unreleased]
88

9+
## [1.4.0] - 2025-03-13
10+
11+
### Added
12+
13+
- Support Trae-CN.
14+
915
## [1.3.4] - 2025-02-18
1016

1117
### Fixed

‎extensions/ide/vscode/devbox/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "devbox-aio",
33
"displayName": "%displayName%",
44
"description": "%description%",
5-
"version": "1.3.4",
5+
"version": "1.4.0",
66
"keywords": [
77
"devbox",
88
"remote development",

‎extensions/ide/vscode/devbox/src/commands/remoteConnector.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -256,16 +256,13 @@ export class RemoteSSHConnector extends Disposable {
256256
vscode.env.uriScheme === 'vscode' ||
257257
vscode.env.uriScheme === 'vscode-insiders' ||
258258
vscode.env.uriScheme === 'cursor'
259-
const isTrae = vscode.env.uriScheme === 'trae'
260259

261-
// windsurf has remote-ssh inside already
262-
if (!isOfficialVscode && !isTrae) {
260+
// windsurf and trae has remote-ssh inside already
261+
if (!isOfficialVscode) {
263262
return true
264263
}
265264

266-
const remoteSSHId = isOfficialVscode
267-
? 'ms-vscode-remote.remote-ssh'
268-
: 'labring.open-remote-ssh-for-trae'
265+
const remoteSSHId = 'ms-vscode-remote.remote-ssh'
269266

270267
const msVscodeRemoteExt = vscode.extensions.getExtension(remoteSSHId)
271268

‎extensions/ide/vscode/devbox/src/utils/handleUri.ts

+2-8
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ import { GlobalStateManager } from './globalStateManager'
33
import { Logger } from '../common/logger'
44

55
const message = {
6-
cursorDevboxNotLatest: vscode.l10n.t(
7-
"Cursor's Devbox is often not the latest. If there are any issues, please manually install the [plugin](https://marketplace.visualstudio.com/items?itemName=labring.devbox-aio&ssr=false#overview) referenced this [URI](https://www.cursor.com/how-to-install-extension)."
8-
),
96
sshPortNotCorrect: vscode.l10n.t(
107
`SSH Port is not correct,maybe your devbox's nodeport is over the limit`
118
),
@@ -21,15 +18,12 @@ export class UriHandler {
2118
uri.scheme !== 'cursor' &&
2219
uri.scheme !== 'vscode-insiders' &&
2320
uri.scheme !== 'windsurf' &&
24-
uri.scheme !== 'trae'
21+
uri.scheme !== 'trae' &&
22+
uri.scheme !== 'trae-cn'
2523
) {
2624
return
2725
}
2826

29-
if (uri.scheme === 'cursor') {
30-
vscode.window.showInformationMessage(message.cursorDevboxNotLatest)
31-
}
32-
3327
const queryParams = new URLSearchParams(uri.query)
3428
const params = this.extractParams(queryParams)
3529

Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
1-
import * as vscode from "vscode";
2-
import { Logger } from "../common/logger";
1+
import * as vscode from 'vscode'
2+
import { Logger } from '../common/logger'
33

44
// update Remote-SSH config
55
export const modifiedRemoteSSHConfig = async (sshHostLabel: string) => {
6-
Logger.info(`Modifying Remote-SSH config for ${sshHostLabel}`);
6+
Logger.info(`Modifying Remote-SSH config for ${sshHostLabel}`)
77

88
const existingSSHHostPlatforms = vscode.workspace
9-
.getConfiguration("remote.SSH")
10-
.get<{ [host: string]: string }>("remotePlatform", {});
9+
.getConfiguration('remote.SSH')
10+
.get<{ [host: string]: string }>('remotePlatform', {})
1111

1212
// delete repeated remotePlatform by sshDomain_namespace_devboxName
1313
const newSSHHostPlatforms = Object.keys(existingSSHHostPlatforms).reduce(
1414
(acc: { [host: string]: string }, host: string) => {
1515
if (host.startsWith(sshHostLabel)) {
16-
return acc;
16+
return acc
1717
}
18-
acc[host] = existingSSHHostPlatforms[host];
19-
return acc;
18+
acc[host] = existingSSHHostPlatforms[host]
19+
return acc
2020
},
2121
{}
22-
);
22+
)
2323
// add new ssh host label
24-
newSSHHostPlatforms[sshHostLabel] = "linux";
24+
newSSHHostPlatforms[sshHostLabel] = 'linux'
2525

26-
const appName = vscode.env.appName;
27-
if (appName !== "Windsurf" && appName !== "Trae") {
26+
const appName = vscode.env.appName
27+
if (appName !== 'Windsurf' && appName !== 'Trae' && appName !== 'Trae-CN') {
2828
await vscode.workspace
29-
.getConfiguration("remote.SSH")
29+
.getConfiguration('remote.SSH')
3030
.update(
31-
"remotePlatform",
31+
'remotePlatform',
3232
newSSHHostPlatforms,
3333
vscode.ConfigurationTarget.Global
34-
);
34+
)
3535
}
3636

3737
// await vscode.workspace
@@ -44,5 +44,5 @@ export const modifiedRemoteSSHConfig = async (sshHostLabel: string) => {
4444
// .getConfiguration('remote.SSH')
4545
// .update('useLocalServer', true, vscode.ConfigurationTarget.Global)
4646

47-
Logger.info(`Modified Remote-SSH config for ${sshHostLabel}`);
48-
};
47+
Logger.info(`Modified Remote-SSH config for ${sshHostLabel}`)
48+
}

‎frontend/providers/devbox/components/IDEButton.tsx

+149-36
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import {
99
MenuButton,
1010
MenuItem,
1111
MenuList,
12-
Tooltip
12+
Tooltip,
13+
Text
1314
} from '@chakra-ui/react';
1415
import { useMessage } from '@sealos/ui';
1516
import { useTranslations } from 'next-intl';
@@ -44,6 +45,13 @@ export interface JetBrainsGuideData {
4445
configHost: string;
4546
}
4647

48+
interface MenuItem {
49+
value: string;
50+
menuLabel: string;
51+
group?: string;
52+
options?: { value: string; menuLabel: string }[];
53+
}
54+
4755
const IDEButton = ({
4856
devboxName,
4957
runtimeType,
@@ -138,13 +146,13 @@ const IDEButton = ({
138146
>
139147
{isBigButton ? (
140148
<Flex alignItems={'center'} w={'100%'} justifyContent={'center'}>
141-
<MyIcon name={currentIDE} w={'25%'} />
142-
<Box w={'75%'} textAlign={'center'} px={'7px'}>
149+
<MyIcon name={getIconName(currentIDE)} w={'25%'} />
150+
<Box w={'75%'} textAlign={'center'} px={'7px'} whiteSpace="nowrap">
143151
{ideObj[currentIDE]?.label}
144152
</Box>
145153
</Flex>
146154
) : (
147-
<MyIcon name={currentIDE} w={'16px'} />
155+
<MyIcon name={getIconName(currentIDE)} w={'16px'} />
148156
)}
149157
</Button>
150158
</Tooltip>
@@ -182,32 +190,90 @@ const IDEButton = ({
182190
fontWeight={500}
183191
fontSize={'12px'}
184192
defaultValue={currentIDE}
185-
px={1}
193+
p={'6px'}
194+
w={'186px'}
195+
display={'flex'}
196+
flexDirection={'column'}
197+
gap={'4px'}
186198
>
187-
{menuItems.map((item) => (
188-
<MenuItem
189-
key={item.value}
190-
value={item.value}
191-
onClick={() => {
192-
updateDevboxIDE(item.value as IDEType, devboxName);
193-
handleGotoIDE(item.value as IDEType);
194-
}}
195-
icon={<MyIcon name={item.value as IDEType} w={'16px'} />}
196-
_hover={{
197-
bg: '#1118240D',
198-
borderRadius: 4
199-
}}
200-
_focus={{
201-
bg: '#1118240D',
202-
borderRadius: 4
203-
}}
204-
>
205-
<Flex justifyContent="space-between" alignItems="center" width="100%">
206-
{item?.menuLabel}
207-
{currentIDE === item.value && <MyIcon name="check" w={'16px'} />}
208-
</Flex>
209-
</MenuItem>
210-
))}
199+
{menuItems.map((item) => {
200+
if (item.group === 'trae') {
201+
return (
202+
<Flex key={item.value} gap={'4px'}>
203+
{item.options?.map((option) => (
204+
<MenuItem
205+
h={'30px'}
206+
w={'90px'}
207+
borderRadius={'4px'}
208+
bg={'grayModern.50'}
209+
key={option.value}
210+
value={option.value}
211+
onClick={() => {
212+
updateDevboxIDE(option.value as IDEType, devboxName);
213+
handleGotoIDE(option.value as IDEType);
214+
}}
215+
_hover={{
216+
bg: 'grayModern.150',
217+
borderRadius: 4
218+
}}
219+
_focus={{
220+
bg: '#1118240D',
221+
borderRadius: 4
222+
}}
223+
{...(currentIDE === option.value && {
224+
bg: 'grayModern.50',
225+
borderWidth: '1px',
226+
borderColor: 'brightBlue.500',
227+
color: 'brightBlue.600'
228+
})}
229+
>
230+
<Flex alignItems="center" w={'100%'}>
231+
<MyIcon name="trae" w={'16px'} mr={'6px'} />
232+
<Text whiteSpace="nowrap" mr={'2px'}>
233+
{option.menuLabel}
234+
</Text>
235+
</Flex>
236+
</MenuItem>
237+
))}
238+
</Flex>
239+
);
240+
} else {
241+
return (
242+
<MenuItem
243+
borderRadius={'4px'}
244+
key={item.value}
245+
value={item.value}
246+
bg={'grayModern.50'}
247+
fontWeight={500}
248+
onClick={() => {
249+
updateDevboxIDE(item.value as IDEType, devboxName);
250+
handleGotoIDE(item.value as IDEType);
251+
}}
252+
_hover={{
253+
bg: 'grayModern.150',
254+
borderRadius: 4
255+
}}
256+
_focus={{
257+
bg: '#1118240D',
258+
borderRadius: 4
259+
}}
260+
{...(currentIDE === item.value && {
261+
bg: 'grayModern.50',
262+
borderWidth: '1px',
263+
borderColor: 'brightBlue.500',
264+
color: 'brightBlue.600'
265+
})}
266+
>
267+
<Flex alignItems="center" w={'100%'}>
268+
<MyIcon name={getIconName(item.value as IDEType)} w={'16px'} mr={'6px'} />
269+
<Text whiteSpace="nowrap" overflow="hidden" textOverflow="ellipsis" mr={'4px'}>
270+
{item?.menuLabel}
271+
</Text>
272+
</Flex>
273+
</MenuItem>
274+
);
275+
}
276+
})}
211277
</MenuList>
212278
</Menu>
213279
{!!onOpenJetbrainsModal && !!jetbrainsGuideData && (
@@ -228,52 +294,99 @@ export const ideObj = {
228294
icon: 'vscode',
229295
prefix: 'vscode://',
230296
value: 'vscode',
231-
sortId: 0
297+
sortId: 0,
298+
group: ''
232299
},
233300
vscodeInsiders: {
234301
label: 'Insiders',
235302
menuLabel: 'VSCode Insiders',
236303
icon: 'vscodeInsiders',
237304
prefix: 'vscode-insiders://',
238305
value: 'vscodeInsiders',
239-
sortId: 1
306+
sortId: 1,
307+
group: ''
240308
},
241309
cursor: {
242310
label: 'Cursor',
243311
menuLabel: 'Cursor',
244312
icon: 'cursor',
245313
prefix: 'cursor://',
246314
value: 'cursor',
247-
sortId: 2
315+
sortId: 2,
316+
group: ''
248317
},
249318
windsurf: {
250319
label: 'Windsurf',
251320
menuLabel: 'Windsurf',
252321
icon: 'windsurf',
253322
prefix: 'windsurf://',
254323
value: 'windsurf',
255-
sortId: 3
324+
sortId: 3,
325+
group: ''
256326
},
257327
trae: {
258328
label: 'Trae',
259329
menuLabel: 'Trae',
260330
icon: 'trae',
261331
prefix: 'trae://',
262332
value: 'trae',
263-
sortId: 4
333+
sortId: 4,
334+
group: 'trae'
335+
},
336+
traeCN: {
337+
label: 'Trae CN',
338+
menuLabel: 'Trae CN',
339+
icon: 'trae',
340+
prefix: 'trae-cn://',
341+
value: 'traeCN',
342+
sortId: 4,
343+
group: 'trae'
264344
},
265345
jetbrains: {
266346
label: 'JetBrains',
267347
icon: 'jetbrains',
268348
menuLabel: 'JetBrains',
269349
prefix: '-',
270350
value: 'jetbrains',
271-
sortId: 4
351+
sortId: 5,
352+
group: ''
272353
}
273354
} as const;
274355

356+
const getIconName = (
357+
ide: IDEType
358+
):
359+
| 'link'
360+
| 'search'
361+
| 'template'
362+
| 'ellipse'
363+
| 'cursor'
364+
| 'vscode'
365+
| 'vscodeInsiders'
366+
| 'windsurf'
367+
| 'trae'
368+
| 'jetbrains' => {
369+
if (ide === 'traeCN') return 'trae';
370+
return ide;
371+
};
372+
275373
const menuItems = Object.values(ideObj)
276374
.sort((a, b) => a.sortId - b.sortId)
277-
.map(({ value, menuLabel }) => ({ value, menuLabel }));
375+
.reduce((acc, item) => {
376+
if (item.group === 'trae' && !acc.some((i) => i.group === 'trae')) {
377+
acc.push({
378+
value: 'trae-group',
379+
menuLabel: 'Trae',
380+
group: 'trae',
381+
options: [
382+
{ value: 'trae', menuLabel: 'Trae' },
383+
{ value: 'traeCN', menuLabel: 'Trae CN' }
384+
]
385+
});
386+
} else if (item.group === '') {
387+
acc.push({ value: item.value, menuLabel: item.menuLabel });
388+
}
389+
return acc;
390+
}, [] as MenuItem[]);
278391

279392
export default IDEButton;

‎frontend/providers/devbox/stores/ide.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@ import { create } from 'zustand';
22
import { devtools, persist } from 'zustand/middleware';
33
import { immer } from 'zustand/middleware/immer';
44

5-
export type IDEType = 'vscode' | 'cursor' | 'vscodeInsiders' | 'windsurf' | 'jetbrains';
5+
export type IDEType =
6+
| 'vscode'
7+
| 'cursor'
8+
| 'vscodeInsiders'
9+
| 'windsurf'
10+
| 'jetbrains'
11+
| 'trae'
12+
| 'traeCN';
613

714
type State = {
815
devboxIDEList: { ide: IDEType; devboxName: string }[];

0 commit comments

Comments
 (0)
Please sign in to comment.