diff --git a/bun.lock b/bun.lock index 6d8d84b..a34b037 100644 --- a/bun.lock +++ b/bun.lock @@ -50,6 +50,11 @@ "remark-gfm": "^4.0.0", "tailwind-merge": "^2.6.0", "tailwindcss": "^4.1.8", + "xterm": "^5.3.0", + "xterm-addon-fit": "^0.8.0", + "xterm-addon-search": "^0.13.0", + "xterm-addon-unicode11": "^0.6.0", + "xterm-addon-web-links": "^0.9.0", "zod": "^3.24.1", "zustand": "^5.0.6", }, @@ -1363,6 +1368,16 @@ "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + "xterm": ["xterm@5.3.0", "https://registry.npmmirror.com/xterm/-/xterm-5.3.0.tgz", {}, "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg=="], + + "xterm-addon-fit": ["xterm-addon-fit@0.8.0", "https://registry.npmmirror.com/xterm-addon-fit/-/xterm-addon-fit-0.8.0.tgz", { "peerDependencies": { "xterm": "^5.0.0" } }, "sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw=="], + + "xterm-addon-search": ["xterm-addon-search@0.13.0", "https://registry.npmmirror.com/xterm-addon-search/-/xterm-addon-search-0.13.0.tgz", { "peerDependencies": { "xterm": "^5.0.0" } }, "sha512-sDUwG4CnqxUjSEFh676DlS3gsh3XYCzAvBPSvJ5OPgF3MRL3iHLPfsb06doRicLC2xXNpeG2cWk8x1qpESWJMA=="], + + "xterm-addon-unicode11": ["xterm-addon-unicode11@0.6.0", "https://registry.npmmirror.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0.tgz", { "peerDependencies": { "xterm": "^5.0.0" } }, "sha512-5pkb8YoS/deRtNqQRw8t640mu+Ga8B2MG3RXGQu0bwgcfr8XiXIRI880TWM49ICAHhTmnOLPzIIBIjEnCq7k2A=="], + + "xterm-addon-web-links": ["xterm-addon-web-links@0.9.0", "https://registry.npmmirror.com/xterm-addon-web-links/-/xterm-addon-web-links-0.9.0.tgz", { "peerDependencies": { "xterm": "^5.0.0" } }, "sha512-LIzi4jBbPlrKMZF3ihoyqayWyTXAwGfu4yprz1aK2p71e9UKXN6RRzVONR0L+Zd+Ik5tPVI9bwp9e8fDTQh49Q=="], + "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], "yocto-queue": ["yocto-queue@0.1.0", "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], diff --git a/package.json b/package.json index f769751..4e4df2f 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "xterm": "^5.3.0", "xterm-addon-fit": "^0.8.0", "xterm-addon-search": "^0.13.0", + "xterm-addon-unicode11": "^0.6.0", "xterm-addon-web-links": "^0.9.0", "zod": "^3.24.1", "zustand": "^5.0.6" diff --git a/src-tauri/src/commands/terminal.rs b/src-tauri/src/commands/terminal.rs index b0245c5..f0280d0 100644 --- a/src-tauri/src/commands/terminal.rs +++ b/src-tauri/src/commands/terminal.rs @@ -52,8 +52,8 @@ pub async fn create_terminal_session( // Create PTY pair with size let pty_pair = pty_system.openpty(PtySize { - rows: 24, - cols: 80, + rows: 30, + cols: 120, pixel_width: 0, pixel_height: 0, }).map_err(|e| format!("Failed to create PTY: {}", e))?; @@ -75,6 +75,16 @@ pub async fn create_terminal_session( // Set environment variables cmd.env("TERM", "xterm-256color"); cmd.env("COLORTERM", "truecolor"); + cmd.env("LANG", std::env::var("LANG").unwrap_or_else(|_| "en_US.UTF-8".to_string())); + cmd.env("LC_ALL", std::env::var("LC_ALL").unwrap_or_else(|_| "en_US.UTF-8".to_string())); + cmd.env("LC_CTYPE", std::env::var("LC_CTYPE").unwrap_or_else(|_| "en_US.UTF-8".to_string())); + + // 继承其他环境变量 + for (key, value) in std::env::vars() { + if !key.starts_with("TERM") && !key.starts_with("COLORTERM") && !key.starts_with("LC_") && !key.starts_with("LANG") { + cmd.env(&key, &value); + } + } // Spawn the shell process let _child = pty_pair.slave.spawn_command(cmd) diff --git a/src/components/Terminal.tsx b/src/components/Terminal.tsx index 880e74f..5d34cd7 100644 --- a/src/components/Terminal.tsx +++ b/src/components/Terminal.tsx @@ -60,21 +60,52 @@ export const Terminal: React.FC = ({ // 创建终端实例 const xterm = new XTerm({ theme: { - background: '#000000', - foreground: '#ffffff', + background: '#1e1e1e', + foreground: '#d4d4d4', cursor: '#ffffff', cursorAccent: '#000000', - selectionBackground: '#404040', + selectionBackground: '#264f78', + // ANSI 颜色 + black: '#000000', + red: '#cd3131', + green: '#0dbc79', + yellow: '#e5e510', + blue: '#2472c8', + magenta: '#bc3fbc', + cyan: '#11a8cd', + white: '#e5e5e5', + brightBlack: '#666666', + brightRed: '#f14c4c', + brightGreen: '#23d18b', + brightYellow: '#f5f543', + brightBlue: '#3b8eea', + brightMagenta: '#d670d6', + brightCyan: '#29b8db', + brightWhite: '#e5e5e5', }, - fontFamily: '"JetBrains Mono", "SF Mono", "Monaco", "Inconsolata", "Fira Code", "Source Code Pro", monospace', + // 使用支持 Powerline 和 Nerd Font 的字体 + fontFamily: '"MesloLGS NF", "JetBrainsMono Nerd Font", "FiraCode Nerd Font", "Hack Nerd Font", "SauceCodePro Nerd Font", "JetBrains Mono", "SF Mono", "Monaco", "Inconsolata", "Fira Code", "Source Code Pro", monospace', fontSize: 14, + fontWeight: 'normal', + fontWeightBold: 'bold', lineHeight: 1.2, + letterSpacing: 0, cols: 80, rows: 24, - allowTransparency: true, - scrollback: 1000, + allowTransparency: false, + scrollback: 10000, convertEol: true, cursorBlink: true, + cursorStyle: 'block', + drawBoldTextInBrightColors: true, + macOptionIsMeta: true, + rightClickSelectsWord: true, + // 启用提议的 API 以支持 Unicode 插件 + allowProposedApi: true, + // 使用 canvas 渲染器以获得更好的性能 + // @ts-ignore - xterm.js 类型定义可能过时 + rendererType: 'canvas', + windowsMode: false, }); // 添加插件 @@ -91,8 +122,15 @@ export const Terminal: React.FC = ({ xterm.open(terminalRef.current); } - // 适应容器大小 - setTimeout(() => fitAddon.fit(), 100); + // 适应容器大小 - 延迟一点确保容器尺寸计算正确 + setTimeout(() => { + fitAddon.fit(); + // 发送 resize 命令到后端(虽然当前未实现) + const { cols, rows } = fitAddon.proposeDimensions() || { cols: 120, rows: 30 }; + if (newSessionId) { + api.resizeTerminal(newSessionId, cols, rows).catch(console.error); + } + }, 150); // 存储引用 xtermRef.current = xterm; @@ -189,7 +227,7 @@ export const Terminal: React.FC = ({ }, [isMaximized, handleResize]); return ( -
+
{/* 终端头部 */}
@@ -238,12 +276,12 @@ export const Terminal: React.FC = ({
{/* 终端主体 */} -
+