feat: 添加工作空间面板 - 右侧终端/文件浏览器

- 新增主进程 workspace.ts:spawn 进程管理,流式输出,安全检查
- IPC 双向通信:workspace:exec(on/send 模式,无超时限制)
- preload 暴露 workspace API
- 渲染进程 workspace-panel.ts:多终端 Tab、ANSI 颜色渲染、文件浏览器
- 设置面板添加工作空间目录配置
- 启动时自动创建 workspace 目录
- 窗口关闭时自动清理所有子进程
- 帮助文档更新
This commit is contained in:
thzxx
2026-04-07 18:30:45 +08:00
parent 9485b4c09e
commit 0e1f216aa4
10 changed files with 1555 additions and 0 deletions
+47
View File
@@ -167,6 +167,53 @@ export function initSettingsModal(): void {
logSetting('Agent 记忆', toggleMemory.checked ? '开启' : '关闭');
});
}
// ── 工作空间目录设置 ──
const inputWorkspaceDir = document.querySelector('#inputWorkspaceDir') as HTMLInputElement;
const btnBrowseWorkspace = document.querySelector('#btnBrowseWorkspace') as HTMLButtonElement;
if (inputWorkspaceDir) {
// 加载当前工作空间目录
const bridge = (window as any).metonaDesktop;
if (bridge?.isDesktop) {
bridge.workspace.getDir().then((info: { dir: string }) => {
inputWorkspaceDir.value = info.dir;
}).catch(() => {});
}
}
if (btnBrowseWorkspace) {
btnBrowseWorkspace.addEventListener('click', async () => {
const bridge = (window as any).metonaDesktop;
if (!bridge?.isDesktop) return;
const paths = await bridge.dialog.openFile({
filters: [{ name: '文件夹', extensions: ['*'] }]
});
// Electron openFile 对话框不能直接选文件夹,这里用 saveFile 代替
// 或者让用户手动输入路径
showToast('请在输入框中直接修改路径', 'info');
});
// 双击输入框解锁编辑
inputWorkspaceDir.addEventListener('dblclick', () => {
inputWorkspaceDir.removeAttribute('readonly');
inputWorkspaceDir.focus();
});
inputWorkspaceDir.addEventListener('blur', async () => {
inputWorkspaceDir.setAttribute('readonly', '');
const newDir = inputWorkspaceDir.value.trim();
if (!newDir) return;
const bridge = (window as any).metonaDesktop;
if (!bridge?.isDesktop) return;
const result = await bridge.workspace.setDir(newDir);
if (result.success) {
showToast('工作空间目录已更新', 'success');
logSetting('工作空间目录', result.dir);
} else {
showToast(`设置失败: ${result.error}`, 'error');
// 恢复原值
const info = await bridge.workspace.getDir();
inputWorkspaceDir.value = info.dir;
}
});
}
}
export function openSettingsModal(): void {