feat: add support for Trae editor (#1037)

This commit is contained in:
yyyanghj
2025-11-17 10:58:33 +08:00
committed by GitHub
parent f0e21374c1
commit 0eeffc6875
5 changed files with 29 additions and 4 deletions

View File

@@ -17,7 +17,7 @@ import { terminalSetup } from '../utils/terminalSetup.js';
export const terminalSetupCommand: SlashCommand = { export const terminalSetupCommand: SlashCommand = {
name: 'terminal-setup', name: 'terminal-setup',
description: description:
'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf)', 'Configure terminal keybindings for multiline input (VS Code, Cursor, Windsurf, Trae)',
kind: CommandKind.BUILT_IN, kind: CommandKind.BUILT_IN,
action: async (): Promise<MessageActionReturn> => { action: async (): Promise<MessageActionReturn> => {

View File

@@ -25,6 +25,7 @@ export const EDITOR_DISPLAY_NAMES: Record<EditorType, string> = {
vscodium: 'VSCodium', vscodium: 'VSCodium',
windsurf: 'Windsurf', windsurf: 'Windsurf',
zed: 'Zed', zed: 'Zed',
trae: 'Trae',
}; };
class EditorSettingsManager { class EditorSettingsManager {

View File

@@ -48,7 +48,7 @@ export interface TerminalSetupResult {
requiresRestart?: boolean; requiresRestart?: boolean;
} }
type SupportedTerminal = 'vscode' | 'cursor' | 'windsurf'; type SupportedTerminal = 'vscode' | 'cursor' | 'windsurf' | 'trae';
// Terminal detection // Terminal detection
async function detectTerminal(): Promise<SupportedTerminal | null> { async function detectTerminal(): Promise<SupportedTerminal | null> {
@@ -68,6 +68,11 @@ async function detectTerminal(): Promise<SupportedTerminal | null> {
) { ) {
return 'windsurf'; return 'windsurf';
} }
if (process.env['TERM_PRODUCT']?.toLowerCase().includes('trae')) {
return 'trae';
}
// Check VS Code last since forks may also set VSCODE env vars // Check VS Code last since forks may also set VSCODE env vars
if (termProgram === 'vscode' || process.env['VSCODE_GIT_IPC_HANDLE']) { if (termProgram === 'vscode' || process.env['VSCODE_GIT_IPC_HANDLE']) {
return 'vscode'; return 'vscode';
@@ -86,6 +91,8 @@ async function detectTerminal(): Promise<SupportedTerminal | null> {
return 'cursor'; return 'cursor';
if (parentName.includes('code') || parentName.includes('Code')) if (parentName.includes('code') || parentName.includes('Code'))
return 'vscode'; return 'vscode';
if (parentName.includes('trae') || parentName.includes('Trae'))
return 'trae';
} catch (error) { } catch (error) {
// Continue detection even if process check fails // Continue detection even if process check fails
console.debug('Parent process detection failed:', error); console.debug('Parent process detection failed:', error);
@@ -287,6 +294,10 @@ async function configureWindsurf(): Promise<TerminalSetupResult> {
return configureVSCodeStyle('Windsurf', 'Windsurf'); return configureVSCodeStyle('Windsurf', 'Windsurf');
} }
async function configureTrae(): Promise<TerminalSetupResult> {
return configureVSCodeStyle('Trae', 'Trae');
}
/** /**
* Main terminal setup function that detects and configures the current terminal. * Main terminal setup function that detects and configures the current terminal.
* *
@@ -333,6 +344,8 @@ export async function terminalSetup(): Promise<TerminalSetupResult> {
return configureCursor(); return configureCursor();
case 'windsurf': case 'windsurf':
return configureWindsurf(); return configureWindsurf();
case 'trae':
return configureTrae();
default: default:
return { return {
success: false, success: false,

View File

@@ -72,6 +72,7 @@ describe('editor utils', () => {
{ editor: 'neovim', commands: ['nvim'], win32Commands: ['nvim'] }, { editor: 'neovim', commands: ['nvim'], win32Commands: ['nvim'] },
{ editor: 'zed', commands: ['zed', 'zeditor'], win32Commands: ['zed'] }, { editor: 'zed', commands: ['zed', 'zeditor'], win32Commands: ['zed'] },
{ editor: 'emacs', commands: ['emacs'], win32Commands: ['emacs.exe'] }, { editor: 'emacs', commands: ['emacs'], win32Commands: ['emacs.exe'] },
{ editor: 'trae', commands: ['trae'], win32Commands: ['trae'] },
]; ];
for (const { editor, commands, win32Commands } of testCases) { for (const { editor, commands, win32Commands } of testCases) {
@@ -171,6 +172,7 @@ describe('editor utils', () => {
}, },
{ editor: 'cursor', commands: ['cursor'], win32Commands: ['cursor'] }, { editor: 'cursor', commands: ['cursor'], win32Commands: ['cursor'] },
{ editor: 'zed', commands: ['zed', 'zeditor'], win32Commands: ['zed'] }, { editor: 'zed', commands: ['zed', 'zeditor'], win32Commands: ['zed'] },
{ editor: 'trae', commands: ['trae'], win32Commands: ['trae'] },
]; ];
for (const { editor, commands, win32Commands } of guiEditors) { for (const { editor, commands, win32Commands } of guiEditors) {
@@ -321,6 +323,7 @@ describe('editor utils', () => {
'windsurf', 'windsurf',
'cursor', 'cursor',
'zed', 'zed',
'trae',
]; ];
for (const editor of guiEditors) { for (const editor of guiEditors) {
@@ -430,6 +433,7 @@ describe('editor utils', () => {
'windsurf', 'windsurf',
'cursor', 'cursor',
'zed', 'zed',
'trae',
]; ];
for (const editor of guiEditors) { for (const editor of guiEditors) {
it(`should not call onEditorClose for ${editor}`, async () => { it(`should not call onEditorClose for ${editor}`, async () => {
@@ -481,6 +485,7 @@ describe('editor utils', () => {
'windsurf', 'windsurf',
'cursor', 'cursor',
'zed', 'zed',
'trae',
]; ];
for (const editor of guiEditors) { for (const editor of guiEditors) {
it(`should not allow ${editor} in sandbox mode`, () => { it(`should not allow ${editor} in sandbox mode`, () => {

View File

@@ -14,7 +14,8 @@ export type EditorType =
| 'vim' | 'vim'
| 'neovim' | 'neovim'
| 'zed' | 'zed'
| 'emacs'; | 'emacs'
| 'trae';
function isValidEditorType(editor: string): editor is EditorType { function isValidEditorType(editor: string): editor is EditorType {
return [ return [
@@ -26,6 +27,7 @@ function isValidEditorType(editor: string): editor is EditorType {
'neovim', 'neovim',
'zed', 'zed',
'emacs', 'emacs',
'trae',
].includes(editor); ].includes(editor);
} }
@@ -62,6 +64,7 @@ const editorCommands: Record<
neovim: { win32: ['nvim'], default: ['nvim'] }, neovim: { win32: ['nvim'], default: ['nvim'] },
zed: { win32: ['zed'], default: ['zed', 'zeditor'] }, zed: { win32: ['zed'], default: ['zed', 'zeditor'] },
emacs: { win32: ['emacs.exe'], default: ['emacs'] }, emacs: { win32: ['emacs.exe'], default: ['emacs'] },
trae: { win32: ['trae'], default: ['trae'] },
}; };
export function checkHasEditorType(editor: EditorType): boolean { export function checkHasEditorType(editor: EditorType): boolean {
@@ -73,7 +76,9 @@ export function checkHasEditorType(editor: EditorType): boolean {
export function allowEditorTypeInSandbox(editor: EditorType): boolean { export function allowEditorTypeInSandbox(editor: EditorType): boolean {
const notUsingSandbox = !process.env['SANDBOX']; const notUsingSandbox = !process.env['SANDBOX'];
if (['vscode', 'vscodium', 'windsurf', 'cursor', 'zed'].includes(editor)) { if (
['vscode', 'vscodium', 'windsurf', 'cursor', 'zed', 'trae'].includes(editor)
) {
return notUsingSandbox; return notUsingSandbox;
} }
// For terminal-based editors like vim and emacs, allow in sandbox. // For terminal-based editors like vim and emacs, allow in sandbox.
@@ -115,6 +120,7 @@ export function getDiffCommand(
case 'windsurf': case 'windsurf':
case 'cursor': case 'cursor':
case 'zed': case 'zed':
case 'trae':
return { command, args: ['--wait', '--diff', oldPath, newPath] }; return { command, args: ['--wait', '--diff', oldPath, newPath] };
case 'vim': case 'vim':
case 'neovim': case 'neovim':