Files
myagents/desktop/electron/pty.ts
T
Zhengshou Lai 0c227cae19 feat(desktop): add Electron desktop pet for Claude CLI
- Electron + React + TypeScript 桌面宠物外壳
- xterm.js + node-pty 直接对接 Claude CLI 进程
- 宠物 orb(80x80)可拖拽,点击展开终端面板
- CRT 复古终端主题(扫描线、文字发光)
- 全局快捷键 Cmd+Shift+C 显隐
- 窗口位置自动保存
- 中华田园犬吉祥物 icon(AI 生成)
- Makefile 新增 `make app` 打包命令
2026-05-27 11:23:14 +08:00

66 lines
1.5 KiB
TypeScript

import * as pty from 'node-pty'
import { execSync } from 'child_process'
import { EventEmitter } from 'events'
export class PTYManager extends EventEmitter {
private ptyProcess: pty.IPty | null = null
start(shell: string = 'claude', args: string[] = []) {
const resolvedShell = this.resolveShell(shell)
const resolvedArgs = shell === 'claude' ? args : []
this.ptyProcess = pty.spawn(resolvedShell, resolvedArgs, {
name: 'xterm-color',
cols: 80,
rows: 24,
cwd: process.env.HOME,
env: process.env as { [key: string]: string },
})
this.ptyProcess.onData((data: string) => {
this.emit('data', data)
})
this.ptyProcess.onExit(({ exitCode }) => {
this.emit('exit', exitCode)
})
return this.ptyProcess
}
write(data: string) {
if (this.ptyProcess) {
this.ptyProcess.write(data)
}
}
resize(cols: number, rows: number) {
if (this.ptyProcess) {
this.ptyProcess.resize(cols, rows)
}
}
kill() {
if (this.ptyProcess) {
this.ptyProcess.kill()
this.ptyProcess = null
}
}
private resolveShell(shell: string): string {
if (shell === 'claude') {
try {
const path = execSync('which claude', { encoding: 'utf8' }).trim()
return path
} catch {
try {
return execSync('which zsh', { encoding: 'utf8' }).trim()
} catch {
return '/bin/zsh'
}
}
}
return shell
}
}