## Summary - move delete-worktree failures out of transient toast-only UX and keep them inline in the delete modal - add parsed diagnostics for common failure modes, including a short summary, likely cause, and suggested next step - make the raw error easier to review and share with raw and sanitized copy actions Closes #301. ## BEFORE: <img width="1127" height="860" alt="image" src="https://github.com/user-attachments/assets/dd09ba1e-be8c-450c-a1dd-f1cde2a48802" /> ## AFTER: <img width="1384" height="835" alt="image" src="https://github.com/user-attachments/assets/6b0d1459-21fa-4264-9e54-45540f584538" /> ## Problem Before this change, delete-worktree failures were difficult to work with: 1. The failure message was effectively raw backend or git output. 2. Users had to infer the meaning of the error themselves. 3. The UI did not explain what likely went wrong or what to do next. 4. Sharing the error for debugging was awkward when it included machine-local absolute paths. 5. The confirmation modal was not being used as the primary diagnostic surface for a destructive action that frequently fails for understandable reasons. This was especially frustrating for common cases such as: - modified or untracked files in the worktree - a process still using the worktree directory - permission errors on Windows - missing worktree directories or stale worktree records ## What changed ### Modal failure UX - keep delete failures inline inside `packages/ui/src/components/worktree-selector.tsx` - clear modal-local error state when opening or closing the dialog - keep the success toast on successful deletion, but use the modal itself for failure presentation ### Human-readable diagnostics - parse JSON-shaped backend error payloads such as `{"error":"..."}` before classification - classify common delete failure patterns into: - `localChanges` - `inUse` - `notFound` - `permissionDenied` - `unknown` - render three user-facing lines above the raw error: - summary - likely cause - suggested next step ### Copy flows - add `Copy error` for the original failure text - add `Copy sanitized` to redact common absolute path and username patterns before copying ### Modal content and sizing - present the target worktree in a simpler two-line summary block - update the delete description text to plain English: `Deletes this branch worktree and its local folder.` - size the delete modal deliberately for desktop use while allowing vertical expansion to the viewport limit before scrolling ### i18n coverage - add the new delete diagnostic strings across all currently supported locales touched by this area: - `en` - `es` - `fr` - `he` - `ja` - `ru` - `zh-Hans` ## Why this approach - It keeps the backend contract unchanged and solves the UX problem where it occurs. - It preserves access to the raw failure text instead of hiding implementation detail entirely. - It gives users immediate guidance without forcing them to translate git errors into next actions. - It improves bug reporting without requiring a separate logging or export workflow. ## Not included - server-side preflight guards that block delete when the worktree is still assigned or in use - process-aware worktree locking detection - automatic retry or force-delete-and-retry flows Those are useful follow-ups, but this PR is intentionally scoped to failure presentation and debuggability. ## Files changed - `packages/ui/src/components/worktree-selector.tsx` - `packages/ui/src/lib/i18n/messages/en/instance.ts` - `packages/ui/src/lib/i18n/messages/es/instance.ts` - `packages/ui/src/lib/i18n/messages/fr/instance.ts` - `packages/ui/src/lib/i18n/messages/he/instance.ts` - `packages/ui/src/lib/i18n/messages/ja/instance.ts` - `packages/ui/src/lib/i18n/messages/ru/instance.ts` - `packages/ui/src/lib/i18n/messages/zh-Hans/instance.ts` ## Validation - `npm run typecheck --workspace @codenomad/ui` - `npm run build --workspace @codenomad/ui` - `npm run typecheck --workspace @neuralnomads/codenomad-electron-app` ## Notes for reviewers - The error classifier is intentionally heuristic and string-based. It is meant to improve the common cases without increasing backend coupling. - The sanitized copy flow is conservative and focused on path and username redaction, not full structured log scrubbing. --------- Co-authored-by: Shantur Rathore <i@shantur.com>
215 lines
14 KiB
TypeScript
215 lines
14 KiB
TypeScript
export const instanceMessages = {
|
||
"instanceTabs.new.title": "新建实例 (Cmd/Ctrl+N)",
|
||
"instanceTabs.new.ariaLabel": "新建实例",
|
||
"instanceTabs.remote.title": "远程连接",
|
||
"instanceTabs.remote.ariaLabel": "远程连接",
|
||
|
||
"instanceInfo.title": "实例信息",
|
||
"instanceInfo.labels.folder": "文件夹",
|
||
"instanceInfo.labels.project": "项目",
|
||
"instanceInfo.labels.versionControl": "版本控制",
|
||
"instanceInfo.labels.opencodeVersion": "OpenCode 版本",
|
||
"instanceInfo.labels.binaryPath": "可执行文件路径",
|
||
"instanceInfo.labels.environmentVariables": "环境变量({count})",
|
||
"instanceInfo.loading": "正在加载...",
|
||
"instanceInfo.server.title": "服务器",
|
||
"instanceInfo.server.port": "端口:",
|
||
"instanceInfo.server.pid": "PID:",
|
||
"instanceInfo.server.status": "状态:",
|
||
|
||
"instanceTab.status.permission": "等待权限",
|
||
"instanceTab.status.compacting": "压缩中",
|
||
"instanceTab.status.working": "工作中",
|
||
"instanceTab.status.idle": "空闲",
|
||
"instanceTab.status.ariaLabel": "实例状态:{status}",
|
||
"instanceTab.actions.close.ariaLabel": "关闭实例",
|
||
|
||
"instanceShell.leftPanel.sessionsTitle": "会话",
|
||
"instanceShell.leftPanel.instanceInfo": "实例信息",
|
||
"instanceShell.leftDrawer.pin": "固定左侧抽屉",
|
||
"instanceShell.leftDrawer.unpin": "取消固定左侧抽屉",
|
||
"instanceShell.leftDrawer.toggle.pinned": "左侧抽屉已固定",
|
||
"instanceShell.leftDrawer.toggle.open": "打开左侧抽屉",
|
||
"instanceShell.leftDrawer.toggle.close": "关闭左侧抽屉",
|
||
|
||
"instanceShell.rightDrawer.pin": "固定右侧抽屉",
|
||
"instanceShell.rightDrawer.unpin": "取消固定右侧抽屉",
|
||
"instanceShell.rightDrawer.toggle.pinned": "右侧抽屉已固定",
|
||
"instanceShell.rightDrawer.toggle.open": "打开右侧抽屉",
|
||
"instanceShell.rightDrawer.toggle.close": "关闭右侧抽屉",
|
||
|
||
"instanceShell.fullscreen.enter": "全屏",
|
||
"instanceShell.fullscreen.exit": "退出全屏",
|
||
|
||
"instanceShell.metrics.usedLabel": "已用",
|
||
"instanceShell.metrics.availableLabel": "可用",
|
||
|
||
"instanceShell.commandPalette.openAriaLabel": "打开命令面板",
|
||
"instanceShell.commandPalette.button": "命令面板",
|
||
|
||
"instanceShell.connection.ariaLabel": "连接 {status}",
|
||
"instanceShell.connection.connected": "已连接",
|
||
"instanceShell.connection.connecting": "连接中...",
|
||
"instanceShell.connection.disconnected": "已断开",
|
||
"instanceShell.connection.unknown": "未知",
|
||
|
||
"instanceWelcome.shortcuts.newSession": "新建会话",
|
||
"instanceWelcome.empty.title": "没有历史会话",
|
||
"instanceWelcome.empty.description": "在下方创建新会话以开始使用",
|
||
"instanceWelcome.loading.title": "正在加载会话",
|
||
"instanceWelcome.loading.description": "正在获取你的历史会话...",
|
||
"instanceWelcome.resume.title": "继续会话",
|
||
"instanceWelcome.resume.subtitle.one": "{count} 个会话可用",
|
||
"instanceWelcome.resume.subtitle.other": "{count} 个会话可用",
|
||
"instanceWelcome.session.untitled": "未命名会话",
|
||
"instanceWelcome.new.title": "开始新会话",
|
||
"instanceWelcome.new.subtitle": "将自动沿用上一次的智能体/模型",
|
||
"instanceWelcome.new.createButton": "创建会话",
|
||
"instanceWelcome.overlay.close": "关闭",
|
||
"instanceWelcome.actions.viewInstanceInfo": "查看实例信息",
|
||
"instanceWelcome.actions.renameTitle": "重命名会话",
|
||
"instanceWelcome.actions.deleteTitle": "删除会话",
|
||
"instanceWelcome.hints.navigate": "导航",
|
||
"instanceWelcome.hints.jump": "跳转",
|
||
"instanceWelcome.hints.firstLast": "首/末",
|
||
"instanceWelcome.hints.resume": "继续",
|
||
"instanceWelcome.hints.delete": "删除",
|
||
"instanceWelcome.toasts.renameError": "无法重命名会话",
|
||
|
||
"instanceDisconnected.title": "实例已断开连接",
|
||
"instanceDisconnected.folderFallback": "此工作区",
|
||
"instanceDisconnected.reasonFallback": "服务器停止响应",
|
||
"instanceDisconnected.description": "{folder} 已无法访问。关闭此标签页以继续工作。",
|
||
"instanceDisconnected.details.title": "详情",
|
||
"instanceDisconnected.details.folderLabel": "文件夹:",
|
||
"instanceDisconnected.actions.closeInstance": "关闭实例",
|
||
|
||
"instanceShell.empty.title": "未选择会话",
|
||
"instanceShell.empty.description": "选择会话以查看消息",
|
||
|
||
"instanceShell.rightPanel.title": "状态面板",
|
||
"instanceShell.rightPanel.tabs.changes": "更改",
|
||
"instanceShell.rightPanel.tabs.gitChanges": "Git 更改",
|
||
"instanceShell.rightPanel.tabs.files": "文件",
|
||
"instanceShell.rightPanel.tabs.status": "状态",
|
||
"instanceShell.rightPanel.tabs.ariaLabel": "右侧面板标签页",
|
||
"instanceShell.rightPanel.actions.refresh": "刷新",
|
||
"instanceShell.rightPanel.actions.save": "保存 (Ctrl+S)",
|
||
"instanceShell.rightPanel.actions.saveConfirm.message": "切换前是否保存对 \"{path}\" 的更改?",
|
||
"instanceShell.rightPanel.actions.saveConfirm.confirmLabel": "保存",
|
||
"instanceShell.rightPanel.actions.saveConfirm.cancelLabel": "放弃更改",
|
||
"instanceShell.rightPanel.actions.conflict.message": "文件已被代理修改。是否覆盖代理的更改?",
|
||
"instanceShell.rightPanel.actions.conflict.confirmLabel": "覆盖",
|
||
"instanceShell.rightPanel.actions.conflict.cancelLabel": "取消",
|
||
"instanceShell.rightPanel.actions.refreshDirty.message": "文件有未保存的更改。刷新将放弃您的编辑。继续?",
|
||
"instanceShell.rightPanel.actions.refreshDirty.confirmLabel": "刷新",
|
||
"instanceShell.rightPanel.actions.refreshDirty.cancelLabel": "取消",
|
||
"instanceShell.rightPanel.toast.saveSuccess": "文件保存成功",
|
||
"instanceShell.rightPanel.toast.saveError": "保存文件失败",
|
||
"instanceShell.rightPanel.sections.yoloMode": "Yolo 模式",
|
||
"instanceShell.rightPanel.sections.yoloMode.tooltip": "自动批准当前会话的权限请求。仅在你信任正在运行的工具时启用。",
|
||
"instanceShell.rightPanel.sections.sessionChanges": "会话更改",
|
||
"instanceShell.rightPanel.sections.sessionChanges.tooltip": "当前会话中修改的文件。显示每个文件的添加和删除。",
|
||
"instanceShell.rightPanel.sections.plan": "计划",
|
||
"instanceShell.rightPanel.sections.plan.tooltip": "代理的路线图。跟踪任务、子任务及其完成状态。",
|
||
"instanceShell.rightPanel.sections.backgroundProcesses": "后台 Shell",
|
||
"instanceShell.rightPanel.sections.backgroundProcesses.tooltip": "代理启动的后台进程。您可以监控其输出、停止或终止它们。",
|
||
"instanceShell.rightPanel.sections.mcp": "MCP 服务器",
|
||
"instanceShell.rightPanel.sections.mcp.tooltip": "模型上下文协议服务器,使用外部工具和服务扩展代理能力。",
|
||
"instanceShell.rightPanel.sections.lsp": "LSP 服务器",
|
||
"instanceShell.rightPanel.sections.lsp.tooltip": "语言服务器协议服务器,提供代码智能、诊断和语言特定的功能。",
|
||
"instanceShell.rightPanel.sections.plugins": "插件",
|
||
"instanceShell.rightPanel.sections.plugins.tooltip": "自定义 UI 和服务器行为的插件,添加超出 MCP 和 LSP 的功能。",
|
||
|
||
"instanceShell.sessionChanges.noSessionSelected": "选择会话以查看更改。",
|
||
"instanceShell.sessionChanges.loading": "正在获取会话更改...",
|
||
"instanceShell.sessionChanges.empty": "暂无会话更改。",
|
||
"instanceShell.sessionChanges.filesChanged": "已更改 {count} 个文件",
|
||
"instanceShell.sessionChanges.actions.show": "显示更改",
|
||
|
||
"instanceShell.gitChanges.loading": "正在加载 Git 更改...",
|
||
"instanceShell.gitChanges.empty": "暂无 Git 更改。",
|
||
"instanceShell.gitChanges.deleted": "已删除",
|
||
"instanceShell.gitChanges.binaryViewer": "无法显示二进制文件",
|
||
"instanceShell.gitChanges.sections.staged": "已暂存的更改",
|
||
"instanceShell.gitChanges.sections.unstaged": "更改",
|
||
"instanceShell.gitChanges.actions.insertContext": "添加到提示词",
|
||
"instanceShell.gitChanges.actions.stage": "暂存文件",
|
||
"instanceShell.gitChanges.actions.unstage": "取消暂存",
|
||
"instanceShell.gitChanges.commit.placeholder": "输入提交信息",
|
||
"instanceShell.gitChanges.commit.submit": "提交",
|
||
"instanceShell.gitChanges.commit.submitting": "正在提交...",
|
||
"instanceShell.gitChanges.commit.success": "提交已成功创建",
|
||
"instanceShell.gitChanges.commit.error": "无法创建提交",
|
||
|
||
"instanceShell.filesShell.fileListTitle": "文件列表",
|
||
"instanceShell.filesShell.mobileSelectorLabel": "选择文件",
|
||
"instanceShell.filesShell.mobileSelectorEmpty": "请选择文件",
|
||
"instanceShell.filesShell.viewerTitle": "更改查看器",
|
||
"instanceShell.filesShell.viewerPlaceholder": "详细更改渲染将在下一步中添加。",
|
||
"instanceShell.filesShell.viewerEmpty": "未选择文件。",
|
||
|
||
"instanceShell.plan.noSessionSelected": "选择会话以查看计划。",
|
||
"instanceShell.plan.empty": "暂无计划。",
|
||
|
||
"instanceShell.yoloMode.noSessionSelected": "请选择一个会话来配置 Yolo 模式。",
|
||
"instanceShell.yoloMode.title": "Yolo 模式",
|
||
"instanceShell.yoloMode.description": "自动批准此会话的权限请求。默认关闭。",
|
||
"instanceShell.yoloMode.badge": "Yolo",
|
||
"instanceShell.yoloMode.badgeAriaLabel": "Yolo 模式已启用",
|
||
|
||
"instanceShell.backgroundProcesses.empty": "没有后台进程。",
|
||
"instanceShell.backgroundProcesses.status": "状态:{status}",
|
||
"instanceShell.backgroundProcesses.output": "输出:{sizeKb}KB",
|
||
"instanceShell.backgroundProcesses.notify.enabled": "已启用完成通知",
|
||
"instanceShell.backgroundProcesses.notify.disabled": "已禁用完成通知",
|
||
"instanceShell.backgroundProcesses.actions.output": "输出",
|
||
"instanceShell.backgroundProcesses.actions.stop": "停止",
|
||
"instanceShell.backgroundProcesses.actions.terminate": "终止",
|
||
"instanceShell.worktree.delete.error.title": "删除失败",
|
||
"instanceShell.worktree.delete.error.fallback": "删除 worktree 失败",
|
||
"instanceShell.worktree.delete.error.causeLabel": "可能原因:",
|
||
"instanceShell.worktree.delete.error.nextStepLabel": "建议的下一步:",
|
||
"instanceShell.worktree.delete.error.summary.localChanges": "Git 拒绝删除这个 worktree,因为其中包含已修改或未跟踪的文件。",
|
||
"instanceShell.worktree.delete.error.summary.inUse": "CodeNomad 无法删除这个 worktree,因为目录中的文件仍在被某些进程使用。",
|
||
"instanceShell.worktree.delete.error.summary.notFound": "CodeNomad 无法删除这个 worktree,因为目录或 worktree 记录未找到。",
|
||
"instanceShell.worktree.delete.error.summary.permissionDenied": "CodeNomad 无法删除这个 worktree,因为目录访问被拒绝。",
|
||
"instanceShell.worktree.delete.error.summary.unknown": "CodeNomad 无法删除这个 worktree。",
|
||
"instanceShell.worktree.delete.error.cause.localChanges": "本地更改",
|
||
"instanceShell.worktree.delete.error.cause.inUse": "另一个进程正在使用这个 worktree",
|
||
"instanceShell.worktree.delete.error.cause.notFound": "worktree 目录或记录缺失",
|
||
"instanceShell.worktree.delete.error.cause.permissionDenied": "文件系统权限不足",
|
||
"instanceShell.worktree.delete.error.cause.unknown": "后端返回了未分类的删除错误",
|
||
"instanceShell.worktree.delete.error.nextStep.localChanges": "如果你想丢弃本地更改,请启用强制删除,或者先清理 worktree 后再重试。",
|
||
"instanceShell.worktree.delete.error.nextStep.inUse": "关闭正在使用这个 worktree 的终端、编辑器、watcher 或后台进程,然后再试一次。",
|
||
"instanceShell.worktree.delete.error.nextStep.notFound": "刷新 worktree 列表后再试一次。如果仍然失败,请检查磁盘上的 worktree 路径。",
|
||
"instanceShell.worktree.delete.error.nextStep.permissionDenied": "检查文件系统权限,并关闭可能锁定此目录的应用程序,然后再试一次。",
|
||
"instanceShell.worktree.delete.error.nextStep.unknown": "查看下方原始错误详情,并在处理提示的问题后再次重试。",
|
||
"instanceShell.worktree.delete.error.copyRaw": "复制错误",
|
||
"instanceShell.worktree.delete.error.copySanitized": "复制脱敏内容",
|
||
"instanceShell.worktree.delete.error.copySuccess": "已复制删除错误",
|
||
"instanceShell.worktree.delete.error.copySanitizedSuccess": "已复制脱敏后的删除错误",
|
||
"instanceShell.worktree.delete.error.copyFailure": "复制删除错误失败",
|
||
|
||
"versionPill.appWithVersion": "应用 {version}",
|
||
"versionPill.ui": "UI",
|
||
"versionPill.uiWithVersion": "UI {version}",
|
||
"versionPill.source": " ({source})",
|
||
|
||
"opencodeBinarySelector.title": "OpenCode 可执行文件",
|
||
"opencodeBinarySelector.subtitle": "选择 OpenCode 要运行的可执行文件",
|
||
"opencodeBinarySelector.customPath.placeholder": "输入 opencode 可执行文件路径…",
|
||
"opencodeBinarySelector.actions.add": "添加",
|
||
"opencodeBinarySelector.actions.browse": "浏览可执行文件…",
|
||
"opencodeBinarySelector.actions.removeTitle": "移除可执行文件",
|
||
"opencodeBinarySelector.badge.systemPath": "使用系统 PATH 中的可执行文件",
|
||
"opencodeBinarySelector.status.checkingVersions": "正在检查版本…",
|
||
"opencodeBinarySelector.status.checking": "正在检查…",
|
||
"opencodeBinarySelector.dialog.title": "选择 OpenCode 可执行文件",
|
||
"opencodeBinarySelector.dialog.description": "浏览 CLI 服务器暴露的文件。",
|
||
"opencodeBinarySelector.validation.invalidBinary": "无效的 OpenCode 可执行文件",
|
||
"opencodeBinarySelector.validation.alreadyValidating": "正在验证中",
|
||
"opencodeBinarySelector.display.systemPath": "{name}(系统 PATH)",
|
||
"opencodeBinarySelector.versionLabel": "v{version}",
|
||
} as const
|