feat: trae cn support web&plugin (#5466)

* feat: trae cn web support

* feat: support trae-cn

* chore: prettier adjust

* chore: prettier adjust again
This commit is contained in:
Alex Lee
2025-03-13 19:23:27 +08:00
committed by GitHub
parent 1a873f59a4
commit 1c5e2dc7da
8 changed files with 190 additions and 69 deletions
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"semi": false
}
@@ -6,6 +6,12 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
## [Unreleased]
## [1.4.0] - 2025-03-13
### Added
- Support Trae-CN.
## [1.3.4] - 2025-02-18
### Fixed
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "devbox-aio",
"displayName": "%displayName%",
"description": "%description%",
"version": "1.3.4",
"version": "1.4.0",
"keywords": [
"devbox",
"remote development",
@@ -256,16 +256,13 @@ export class RemoteSSHConnector extends Disposable {
vscode.env.uriScheme === 'vscode' ||
vscode.env.uriScheme === 'vscode-insiders' ||
vscode.env.uriScheme === 'cursor'
const isTrae = vscode.env.uriScheme === 'trae'
// windsurf has remote-ssh inside already
if (!isOfficialVscode && !isTrae) {
// windsurf and trae has remote-ssh inside already
if (!isOfficialVscode) {
return true
}
const remoteSSHId = isOfficialVscode
? 'ms-vscode-remote.remote-ssh'
: 'labring.open-remote-ssh-for-trae'
const remoteSSHId = 'ms-vscode-remote.remote-ssh'
const msVscodeRemoteExt = vscode.extensions.getExtension(remoteSSHId)
@@ -3,9 +3,6 @@ import { GlobalStateManager } from './globalStateManager'
import { Logger } from '../common/logger'
const message = {
cursorDevboxNotLatest: vscode.l10n.t(
"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)."
),
sshPortNotCorrect: vscode.l10n.t(
`SSH Port is not correct,maybe your devbox's nodeport is over the limit`
),
@@ -21,15 +18,12 @@ export class UriHandler {
uri.scheme !== 'cursor' &&
uri.scheme !== 'vscode-insiders' &&
uri.scheme !== 'windsurf' &&
uri.scheme !== 'trae'
uri.scheme !== 'trae' &&
uri.scheme !== 'trae-cn'
) {
return
}
if (uri.scheme === 'cursor') {
vscode.window.showInformationMessage(message.cursorDevboxNotLatest)
}
const queryParams = new URLSearchParams(uri.query)
const params = this.extractParams(queryParams)
@@ -1,37 +1,37 @@
import * as vscode from "vscode";
import { Logger } from "../common/logger";
import * as vscode from 'vscode'
import { Logger } from '../common/logger'
// update Remote-SSH config
export const modifiedRemoteSSHConfig = async (sshHostLabel: string) => {
Logger.info(`Modifying Remote-SSH config for ${sshHostLabel}`);
Logger.info(`Modifying Remote-SSH config for ${sshHostLabel}`)
const existingSSHHostPlatforms = vscode.workspace
.getConfiguration("remote.SSH")
.get<{ [host: string]: string }>("remotePlatform", {});
.getConfiguration('remote.SSH')
.get<{ [host: string]: string }>('remotePlatform', {})
// delete repeated remotePlatform by sshDomain_namespace_devboxName
const newSSHHostPlatforms = Object.keys(existingSSHHostPlatforms).reduce(
(acc: { [host: string]: string }, host: string) => {
if (host.startsWith(sshHostLabel)) {
return acc;
return acc
}
acc[host] = existingSSHHostPlatforms[host];
return acc;
acc[host] = existingSSHHostPlatforms[host]
return acc
},
{}
);
)
// add new ssh host label
newSSHHostPlatforms[sshHostLabel] = "linux";
newSSHHostPlatforms[sshHostLabel] = 'linux'
const appName = vscode.env.appName;
if (appName !== "Windsurf" && appName !== "Trae") {
const appName = vscode.env.appName
if (appName !== 'Windsurf' && appName !== 'Trae' && appName !== 'Trae-CN') {
await vscode.workspace
.getConfiguration("remote.SSH")
.getConfiguration('remote.SSH')
.update(
"remotePlatform",
'remotePlatform',
newSSHHostPlatforms,
vscode.ConfigurationTarget.Global
);
)
}
// await vscode.workspace
@@ -44,5 +44,5 @@ export const modifiedRemoteSSHConfig = async (sshHostLabel: string) => {
// .getConfiguration('remote.SSH')
// .update('useLocalServer', true, vscode.ConfigurationTarget.Global)
Logger.info(`Modified Remote-SSH config for ${sshHostLabel}`);
};
Logger.info(`Modified Remote-SSH config for ${sshHostLabel}`)
}
@@ -9,7 +9,8 @@ import {
MenuButton,
MenuItem,
MenuList,
Tooltip
Tooltip,
Text
} from '@chakra-ui/react';
import { useMessage } from '@sealos/ui';
import { useTranslations } from 'next-intl';
@@ -44,6 +45,13 @@ export interface JetBrainsGuideData {
configHost: string;
}
interface MenuItem {
value: string;
menuLabel: string;
group?: string;
options?: { value: string; menuLabel: string }[];
}
const IDEButton = ({
devboxName,
runtimeType,
@@ -138,13 +146,13 @@ const IDEButton = ({
>
{isBigButton ? (
<Flex alignItems={'center'} w={'100%'} justifyContent={'center'}>
<MyIcon name={currentIDE} w={'25%'} />
<Box w={'75%'} textAlign={'center'} px={'7px'}>
<MyIcon name={getIconName(currentIDE)} w={'25%'} />
<Box w={'75%'} textAlign={'center'} px={'7px'} whiteSpace="nowrap">
{ideObj[currentIDE]?.label}
</Box>
</Flex>
) : (
<MyIcon name={currentIDE} w={'16px'} />
<MyIcon name={getIconName(currentIDE)} w={'16px'} />
)}
</Button>
</Tooltip>
@@ -182,32 +190,90 @@ const IDEButton = ({
fontWeight={500}
fontSize={'12px'}
defaultValue={currentIDE}
px={1}
p={'6px'}
w={'186px'}
display={'flex'}
flexDirection={'column'}
gap={'4px'}
>
{menuItems.map((item) => (
<MenuItem
key={item.value}
value={item.value}
onClick={() => {
updateDevboxIDE(item.value as IDEType, devboxName);
handleGotoIDE(item.value as IDEType);
}}
icon={<MyIcon name={item.value as IDEType} w={'16px'} />}
_hover={{
bg: '#1118240D',
borderRadius: 4
}}
_focus={{
bg: '#1118240D',
borderRadius: 4
}}
>
<Flex justifyContent="space-between" alignItems="center" width="100%">
{item?.menuLabel}
{currentIDE === item.value && <MyIcon name="check" w={'16px'} />}
</Flex>
</MenuItem>
))}
{menuItems.map((item) => {
if (item.group === 'trae') {
return (
<Flex key={item.value} gap={'4px'}>
{item.options?.map((option) => (
<MenuItem
h={'30px'}
w={'90px'}
borderRadius={'4px'}
bg={'grayModern.50'}
key={option.value}
value={option.value}
onClick={() => {
updateDevboxIDE(option.value as IDEType, devboxName);
handleGotoIDE(option.value as IDEType);
}}
_hover={{
bg: 'grayModern.150',
borderRadius: 4
}}
_focus={{
bg: '#1118240D',
borderRadius: 4
}}
{...(currentIDE === option.value && {
bg: 'grayModern.50',
borderWidth: '1px',
borderColor: 'brightBlue.500',
color: 'brightBlue.600'
})}
>
<Flex alignItems="center" w={'100%'}>
<MyIcon name="trae" w={'16px'} mr={'6px'} />
<Text whiteSpace="nowrap" mr={'2px'}>
{option.menuLabel}
</Text>
</Flex>
</MenuItem>
))}
</Flex>
);
} else {
return (
<MenuItem
borderRadius={'4px'}
key={item.value}
value={item.value}
bg={'grayModern.50'}
fontWeight={500}
onClick={() => {
updateDevboxIDE(item.value as IDEType, devboxName);
handleGotoIDE(item.value as IDEType);
}}
_hover={{
bg: 'grayModern.150',
borderRadius: 4
}}
_focus={{
bg: '#1118240D',
borderRadius: 4
}}
{...(currentIDE === item.value && {
bg: 'grayModern.50',
borderWidth: '1px',
borderColor: 'brightBlue.500',
color: 'brightBlue.600'
})}
>
<Flex alignItems="center" w={'100%'}>
<MyIcon name={getIconName(item.value as IDEType)} w={'16px'} mr={'6px'} />
<Text whiteSpace="nowrap" overflow="hidden" textOverflow="ellipsis" mr={'4px'}>
{item?.menuLabel}
</Text>
</Flex>
</MenuItem>
);
}
})}
</MenuList>
</Menu>
{!!onOpenJetbrainsModal && !!jetbrainsGuideData && (
@@ -228,7 +294,8 @@ export const ideObj = {
icon: 'vscode',
prefix: 'vscode://',
value: 'vscode',
sortId: 0
sortId: 0,
group: ''
},
vscodeInsiders: {
label: 'Insiders',
@@ -236,7 +303,8 @@ export const ideObj = {
icon: 'vscodeInsiders',
prefix: 'vscode-insiders://',
value: 'vscodeInsiders',
sortId: 1
sortId: 1,
group: ''
},
cursor: {
label: 'Cursor',
@@ -244,7 +312,8 @@ export const ideObj = {
icon: 'cursor',
prefix: 'cursor://',
value: 'cursor',
sortId: 2
sortId: 2,
group: ''
},
windsurf: {
label: 'Windsurf',
@@ -252,7 +321,8 @@ export const ideObj = {
icon: 'windsurf',
prefix: 'windsurf://',
value: 'windsurf',
sortId: 3
sortId: 3,
group: ''
},
trae: {
label: 'Trae',
@@ -260,7 +330,17 @@ export const ideObj = {
icon: 'trae',
prefix: 'trae://',
value: 'trae',
sortId: 4
sortId: 4,
group: 'trae'
},
traeCN: {
label: 'Trae CN',
menuLabel: 'Trae CN',
icon: 'trae',
prefix: 'trae-cn://',
value: 'traeCN',
sortId: 4,
group: 'trae'
},
jetbrains: {
label: 'JetBrains',
@@ -268,12 +348,45 @@ export const ideObj = {
menuLabel: 'JetBrains',
prefix: '-',
value: 'jetbrains',
sortId: 4
sortId: 5,
group: ''
}
} as const;
const getIconName = (
ide: IDEType
):
| 'link'
| 'search'
| 'template'
| 'ellipse'
| 'cursor'
| 'vscode'
| 'vscodeInsiders'
| 'windsurf'
| 'trae'
| 'jetbrains' => {
if (ide === 'traeCN') return 'trae';
return ide;
};
const menuItems = Object.values(ideObj)
.sort((a, b) => a.sortId - b.sortId)
.map(({ value, menuLabel }) => ({ value, menuLabel }));
.reduce((acc, item) => {
if (item.group === 'trae' && !acc.some((i) => i.group === 'trae')) {
acc.push({
value: 'trae-group',
menuLabel: 'Trae',
group: 'trae',
options: [
{ value: 'trae', menuLabel: 'Trae' },
{ value: 'traeCN', menuLabel: 'Trae CN' }
]
});
} else if (item.group === '') {
acc.push({ value: item.value, menuLabel: item.menuLabel });
}
return acc;
}, [] as MenuItem[]);
export default IDEButton;
+8 -1
View File
@@ -2,7 +2,14 @@ import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
export type IDEType = 'vscode' | 'cursor' | 'vscodeInsiders' | 'windsurf' | 'jetbrains';
export type IDEType =
| 'vscode'
| 'cursor'
| 'vscodeInsiders'
| 'windsurf'
| 'jetbrains'
| 'trae'
| 'traeCN';
type State = {
devboxIDEList: { ide: IDEType; devboxName: string }[];