diff --git a/.github/AUTHOR_MAP b/.github/AUTHOR_MAP index 12fb658d65..01ac73ef95 100644 --- a/.github/AUTHOR_MAP +++ b/.github/AUTHOR_MAP @@ -40,6 +40,7 @@ Duducoco = Duducoco <69681789+Duducoco@users.noreply.github.com> cyq1017 = cyq1017 <61975706+cyq1017@users.noreply.github.com> cyq = cyq1017 <61975706+cyq1017@users.noreply.github.com> 15000851237@163.com = cyq1017 <61975706+cyq1017@users.noreply.github.com> +cy2311 = CY <29836092+cy2311@users.noreply.github.com> zlh124 = zlh124 <56312993+zlh124@users.noreply.github.com> LeoLin990405 = LeoLin990405 <101193087+LeoLin990405@users.noreply.github.com> THINKER-ONLY = THINKER-ONLY <181556007+THINKER-ONLY@users.noreply.github.com> diff --git a/crates/tui/locales/zh-Hans.json b/crates/tui/locales/zh-Hans.json new file mode 100644 index 0000000000..a4c6d95844 --- /dev/null +++ b/crates/tui/locales/zh-Hans.json @@ -0,0 +1,443 @@ +{ + "ComposerPlaceholder": "编写任务或使用 /。", + "HistorySearchPlaceholder": "搜索提示历史...", + "HistorySearchTitle": "历史搜索", + "HistoryHintMove": "Up/Down 移动", + "HistoryHintAccept": "Enter 接受", + "HistoryHintRestore": "Esc 还原", + "HistoryNoMatches": " 无匹配", + "StatusPickerTitle": " 状态行 ", + "StatusPickerInstruction": "选择要在底部显示的项目:", + "StatusPickerActionToggle": "切换 ", + "StatusPickerActionAll": "全部 ", + "StatusPickerActionNone": "无 ", + "StatusPickerActionSave": "保存 ", + "StatusPickerActionCancel": "取消 ", + "ConfigTitle": "会话配置", + "ConfigModalTitle": " 配置 ", + "ConfigSearchPlaceholder": "输入以筛选", + "ConfigNoSettings": " 没有可用设置。", + "ConfigNoMatchesPrefix": " 没有匹配设置: ", + "ConfigFilteredSettings": " 已筛选设置", + "ConfigShowing": " 显示", + "ConfigFooterDefault": " 输入=筛选, Up/Down=选择, Enter/e=编辑, Esc/q=关闭 ", + "ConfigFooterScrollable": " 输入=筛选, Up/Down=选择, Enter/e=编辑, PgUp/PgDn=滚动, Esc/q=关闭 ", + "ConfigFooterFiltered": " 输入=筛选, Backspace=删除, Ctrl+U/Esc=清除, Enter=编辑 ", + "ConfigSectionProvider": "提供商", + "ConfigSectionModel": "模型", + "ConfigSectionPermissions": "权限", + "ConfigSectionNetwork": "网络", + "ConfigSectionDisplay": "显示", + "ConfigSectionComposer": "编辑器", + "ConfigSectionSidebar": "侧边栏", + "ConfigSectionHistory": "历史", + "ConfigSectionMcp": "MCP", + "ConfigSectionFleet": "舰队", + "ConfigSectionExperimental": "实验", + "ConfigScopeSession": "会话", + "ConfigScopeSaved": "已保存", + "ConfigEditCancelled": "编辑已取消", + "ConfigEditTitlePrefix": "编辑 ", + "ConfigEditScopeLabel": "范围: ", + "ConfigEditCurrentLabel": "当前: ", + "ConfigEditHintLabel": "提示: ", + "ConfigEditNewLabel": "新值: ", + "ConfigEditFooter": " Enter=应用, Esc=取消, Ctrl+U=清除, Ctrl+A=全选, ←/→=移动 ", + "ConfigRowEffective": " (实际 {currency})", + "ConfigDefaultValue": "(默认)", + "ConfigDefaultReasoning": "(配置/默认)", + "ConfigUnavailable": "(不可用)", + "HelpTitle": "帮助", + "HelpFilterPlaceholder": "输入以筛选", + "HelpFilterPrefix": "筛选: ", + "HelpNoMatches": " 无匹配。", + "HelpSlashCommands": "斜杠命令", + "HelpKeybindings": "快捷键", + "HelpFooterTypeFilter": " 输入以筛选 ", + "HelpFooterMove": " Up/Down 移动 ", + "HelpFooterJump": " PgUp/PgDn 跳转 ", + "HelpFooterClose": " Esc 关闭 ", + "CmdAnchorDescription": "钉选关键事实,在压缩后自动注入上下文", + "CmdAttachDescription": "附加图片或视频媒体;文本文件或目录请使用 @path", + "CmdCacheDescription": "显示最近 N 轮的 DeepSeek 前缀缓存命中/未命中统计", + "CmdChangeDescription": "显示最新的更新日志", + "CmdChangeHeader": "最新更新日志", + "CmdChangeTranslationQueued": "下面显示英文发布说明。接下来会请求模型翻译;如果当前提供商不可用,这段英文内容就是备用结果。", + "CmdChangeTranslationUnavailable": "下面显示英文发布说明。当前会话没有 API Key 或处于离线状态,无法翻译。", + "CmdChangePreviousVersion": "上一个版本: {version} —— 输入 `/change {version}` 查看", + "CmdBalanceDescription": "查看当前提供商账户余额", + "CmdClearDescription": "清除对话历史", + "CmdCompactDescription": "触发上下文压缩以释放空间", + "CmdPurgeDescription": "让 Agent 分析对话历史,精确保留有用信息并移除冗余内容", + "CmdConfigDescription": "打开交互式配置编辑器", + "CmdContextDescription": "打开紧凑会话上下文检查器", + "CmdCostDescription": "显示本次会话的费用明细", + "CmdDiffDescription": "显示会话开始以来的文件变更", + "CmdEditDescription": "修改并重新提交最后一条消息", + "CmdExitDescription": "退出应用", + "CmdExportDescription": "将对话导出为 Markdown", + "CmdFeedbackDescription": "生成 GitHub 反馈链接", + "CmdHfDescription": "检查 Hugging Face MCP 设置和概念", + "CmdHelpDescription": "显示帮助信息", + "CmdProfileDescription": "切换到命名配置配置文件", + "CmdHomeDescription": "显示主页面板,含统计与快捷操作", + "CmdHooksDescription": "列出已配置的生命周期钩子(只读)", + "CmdAgentDescription": "打开持久子代理会话:/agent [0-3] ", + "CmdGoalDescription": "设置带有可选令牌预算的会话目标", + "CmdInitDescription": "为项目生成 AGENTS.md", + "CmdLspDescription": "切换 LSP 诊断的开启或关闭", + "CmdShareDescription": "将当前会话导出为可共享的 Web URL", + "CmdJobsDescription": "查看并管理后台 shell 作业", + "CmdLinksDescription": "显示服务商令牌、控制台与文档链接", + "CmdLoadDescription": "从文件加载会话", + "CmdLogoutDescription": "清除 API 密钥并返回设置", + "CmdMcpDescription": "打开或管理 MCP 服务器", + "CmdPluginDescription": "List discovered plugin tools or show details for one", + "CmdPluginNoneFound": "No plugin tools discovered in {dir}", + "CmdPluginNotFound": "Plugin '{name}' not found", + "CmdPluginListHeader": "Plugin tools ({count}):", + "CmdPluginDetailDescription": "Description: {description}", + "CmdPluginDetailSchema": "Schema:\n{schema}", + "CmdPluginDetailApproval": "Approval: {approval}", + "CmdPluginDetailPath": "Path: {path}", + "CmdMemoryDescription": "查看或管理持久用户记忆文件", + "CmdModeDescription": "切换运行模式或打开选择器:/mode [agent|plan|yolo|1|2|3]", + "CmdModelDescription": "切换或查看当前模型", + "CmdModelsDescription": "列出 API 中可用的模型", + "CmdNetworkDescription": "管理网络允许和拒绝规则", + "CmdNoteDescription": "添加、列出、编辑或删除工作区笔记", + "CmdThemeDescription": "切换主题:深色、浅色、灰度或系统", + "CmdProviderDescription": "切换或查看当前 LLM 后端(deepseek | nvidia-nim | ollama)", + "CmdQueueDescription": "查看或编辑已排队的消息", + "CmdQueueUsage": "用法: /queue [list|send |edit |drop |clear]", + "CmdQueueDraftHeader": "正在编辑已排队的消息:", + "CmdQueueNoMessages": "没有已排队的消息", + "CmdQueueListHeader": "已排队的消息 ({count}):", + "CmdQueueTip": "提示: /queue send 立即发送, /queue drop 删除", + "CmdQueueAlreadyEditing": "已在编辑一条已排队的消息。请先发送或使用 /queue clear 放弃。", + "CmdQueueNotFound": "未找到已排队的消息", + "CmdQueueEditingStatus": "正在编辑已排队的消息 {index}", + "CmdQueueEditingMessage": "正在编辑已排队的消息 {index}(按 Enter 重新排队/发送)", + "CmdQueueDropped": "已删除已排队的消息 {index}", + "CmdQueueAlreadyEmpty": "队列已空", + "CmdQueueCleared": "队列已清空", + "CmdQueueMissingIndex": "缺少索引。用法: /queue edit 或 /queue drop ", + "CmdQueueIndexPositive": "索引必须为正数", + "CmdQueueIndexMin": "索引必须 >= 1", + "CmdRelayDescription": "为新线程创建会话接力摘要", + "CmdRenameDescription": "重命名当前会话", + "CmdRestoreDescription": "将工作区回滚到此前的轮次前/后快照。不带参数时列出最近的快照。", + "CmdRetryDescription": "重试上一次请求", + "CmdReviewDescription": "对文件、diff 或 PR 进行结构化代码审查", + "CmdRlmDescription": "打开持久 RLM 上下文:/rlm [0-3] ", + "CmdSaveDescription": "将会话保存到文件", + "CmdForkDescription": "将当前对话分叉为兄弟会话", + "CmdNewDescription": "开始一个新的已保存会话", + "CmdSessionsDescription": "打开会话历史选择器", + "CmdSettingsDescription": "显示持久化设置", + "CmdSkillDescription": "激活技能,或安装/更新/卸载/信任社区技能", + "CmdSidebarDescription": "Toggle or focus the right sidebar", + "CmdSkillsDescription": "列出本地技能(用 `/skills ` 按名称前缀过滤,--remote 浏览精选注册表)", + "CmdSlopDescription": "Inspect or export the debt ledger", + "CmdStashDescription": "暂存或恢复输入草稿(Ctrl+S 暂存,/stash list|pop)", + "CmdStatusDescription": "显示当前运行状态", + "CmdStatuslineDescription": "配置底栏要显示哪些条目", + "CmdFleetDescription": "打开 Fleet 设置或工作器状态", + "CmdSubagentsDescription": "/fleet status 的兼容快捷方式", + "CmdSwarmDescription": "运行多代理扇出轮次(sequential | mixture | distill | deliberate)", + "CmdSystemDescription": "显示当前系统提示词", + "CmdTaskDescription": "管理后台任务", + "CmdTokensDescription": "显示本次会话的 token 用量", + "CmdTranslateDescription": "切换输出翻译为当前系统语言的开/关状态", + "CmdTranslateOff": "输出翻译已关闭(显示原始模型输出)", + "CmdTranslateOn": "输出翻译已开启:模型回复将以当前系统语言显示", + "TranslationInProgress": "正在翻译助手输出...", + "TranslationComplete": "翻译完成", + "TranslationFailed": "翻译失败", + "CmdTrustDescription": "管理工作区信任与按路径的白名单(`/trust add `、`/trust list`、`/trust on|off`)", + "CmdWorkspaceDescription": "显示或切换当前工作空间", + "CmdUndoDescription": "移除最后一组消息对", + "CmdVerboseDescription": "切换实时思考内容的完整显示", + "CmdCacheAdvice": "第 3 轮起命中率稳定在 ~70% 以上即表示前缀缓存稳定;\n长会话中明显偏低则意味着前缀有抖动,值得排查(#263)。", + "CmdCacheFootnote": "* 当提供方未单独上报未命中时,由「输入 − 命中」推算。\n", + "CmdCacheHeader": "缓存遥测 —— 最近 {count} / {total} 轮(模型:{model})\n", + "CmdCacheNoData": "缓存历史:尚未记录任何轮次。\n\nDeepSeek 在受支持的模型(V4 系列)每个 API 轮次都会返回 `prompt_cache_hit_tokens` / `prompt_cache_miss_tokens`。请先运行一个轮次再试 /cache。", + "CmdCacheTotals": "Σ 输入:{sum_in} Σ 命中:{sum_hit} Σ 未命中:{sum_miss} 平均命中率:{avg}\n", + "CmdCostReport": "会话费用:\n─────────────────────────────\n预估累计消耗:{cost}\n\n费用为估算值;如有提供方用量遥测会优先使用。\n\nDeepSeek API 计费:\n─────────────────────────────\n此 CLI 中未配置详细计费规则。", + "CmdTokensCacheBoth": "命中 {hit} / 未命中 {miss}", + "CmdTokensCacheHitOnly": "命中 {hit} / 未命中未上报", + "CmdTokensCacheMissOnly": "命中未上报 / 未命中 {miss}", + "CmdTokensContextUnknownWindow": "~{estimated} / 窗口未知", + "CmdTokensContextWithWindow": "~{used} / {window}({percent}%)", + "FooterAgentSingular": "1 个子代理", + "FooterAgentsPlural": "{count} 个子代理", + "FooterPressCtrlCAgain": "再次按 Ctrl+C 退出", + "FooterWorking": "工作中", + "FooterBalancePrefix": "余额", + "HelpSectionActions": "操作", + "HelpSectionClipboard": "剪贴板", + "HelpSectionEditing": "输入编辑", + "HelpSectionHelp": "帮助", + "HelpSectionModes": "模式", + "HelpSectionNavigation": "导航", + "HelpSectionSessions": "会话", + "CmdTokensNotReported": "未上报", + "CmdTokensReport": "令牌用量:\n─────────────────────────────\n活动上下文: {active}\n上次 API 输入: {input}(来自轮次遥测;多轮工具调用中相同前缀可能被重复计入)\n上次 API 输出: {output}\n缓存命中/未命中: {cache}(仅用于遥测/计费)\n累计令牌: {total}(会话用量遥测)\n预估会话费用: {cost}\nAPI 消息数: {api_messages}\n聊天消息数: {chat_messages}\n模型: {model}", + "KbScrollTranscript": "滚动对话记录、浏览输入历史或选择附件", + "KbNavigateHistory": "浏览输入历史", + "KbBrowseHistory": "浏览对话历史", + "KbScrollTranscriptAlt": "滚动对话记录", + "KbScrollPage": "按页滚动对话记录", + "KbJumpTopBottom": "跳转到对话顶部/底部", + "KbJumpTopBottomEmpty": "跳转到顶部/底部(输入框为空时)", + "KbJumpToolBlocks": "在工具输出块之间跳转", + "KbMoveCursor": "在输入框中移动光标", + "KbJumpLineStartEnd": "跳转到行首/行尾", + "KbDeleteChar": "删除光标前/后的字符,或移除已选附件", + "KbClearDraft": "清空当前草稿", + "KbStashDraft": "暂存当前草稿(用 `/stash pop` 恢复)", + "KbSearchHistory": "搜索提示历史并恢复本地草稿", + "KbInsertNewline": "在输入框中插入换行", + "KbSendDraft": "发送当前草稿", + "KbCloseMenu": "关闭菜单、取消请求、丢弃草稿或清空输入", + "KbCancelOrExit": "取消请求,或空闲时退出", + "KbShellControls": "将正在运行的前台命令转入后台", + "KbExitEmpty": "输入框为空时退出", + "KbCommandPalette": "打开命令面板", + "KbCancelBackgroundShellJobs": "取消所有正在运行的后台 shell 作业(Tasks 侧边栏)", + "KbFuzzyFilePicker": "打开模糊文件选择器(按 Enter 插入 @path)", + "KbCompactInspector": "打开紧凑会话上下文检查器", + "KbLastMessagePager": "打开最后一条消息的分页器(输入框为空时)", + "KbSelectedDetails": "打开选中工具或消息的详情(输入框为空时)", + "KbToolDetailsPager": "打开工具详情分页器", + "KbThinkingPager": "打开 Activity Detail", + "KbLiveTranscript": "打开实时对话覆盖层(自动滚动尾随)", + "KbBacktrackMessage": "回退到之前的用户消息(左右键步进,Enter 回退)", + "KbCompleteCycleModes": "补全 /command、排队运行轮次跟进、切换模式;Shift+Tab 切换推理强度", + "KbJumpPlanAgentYolo": "触发快捷栏槽位", + "KbAltJumpPlanAgentYolo": "替代快捷键跳转到 Plan / Agent / YOLO 模式", + "KbFocusSidebar": "聚焦 Pinned / 任务 / 代理 / Context / 自动 / 隐藏侧边栏", + "KbSessionPicker": "打开会话选择器", + "KbPasteAttach": "粘贴文本或附加剪贴板图片", + "KbCopySelection": "复制当前选中内容(macOS 为 Cmd+C)", + "KbContextMenu": "打开上下文操作菜单,用于粘贴、选择、消息详情、上下文和帮助", + "KbAttachPath": "添加本地文本文件或目录到上下文", + "KbHelpOverlay": "打开此帮助覆盖层(输入框为空时)", + "KbToggleHelp": "切换帮助覆盖层", + "KbToggleHelpSlash": "切换帮助覆盖层", + "HelpUsageLabel": "用法:", + "HelpAliasesLabel": "别名:", + "SettingsTitle": "设置:", + "SettingsConfigFile": "配置文件:", + "ClearConversation": "对话已清空", + "ClearConversationBusy": "对话已清空(Plan 状态忙碌;如需再次清空请运行 /clear)", + "ModelChanged": "模型已切换:{old} → {new}", + "LinksTitle": "服务商链接:", + "LinksDashboard": "控制台:", + "LinksDocs": "文档:", + "LinksTip": "提示:使用所显示服务商的环境变量,或通过 `codewhale auth set --provider ` 保存密钥。", + "SubagentsFetching": "正在获取 Fleet 工作器状态...", + "HelpUnknownCommand": "未知命令:{topic}", + "HomeDashboardTitle": "codewhale 主面板", + "HomeModel": "模型:", + "HomeMode": "模式:", + "HomeWorkspace": "工作区:", + "HomeHistory": "历史:", + "HomeTokens": "令牌:", + "HomeQueued": "队列:", + "HomeSubagents": "Fleet 工作器:", + "HomeSkill": "技能:", + "HomeQuickActions": "快捷操作", + "HomeQuickLinks": "/links - 控制台与 API 链接", + "HomeQuickSkills": "/skills - 列出可用技能", + "HomeQuickConfig": "/config - 打开交互式配置编辑器", + "HomeQuickSettings": "/settings - 显示持久化设置", + "HomeQuickModel": "/model - 切换或查看模型", + "HomeQuickSubagents": "/fleet status - Fleet 工作器状态", + "HomeQuickTaskList": "/task list - 显示后台任务队列", + "HomeQuickHelp": "/help - 显示帮助", + "HomeModeTips": "模式提示", + "HomeAgentModeTip": "Agent 模式 - 使用工具执行自主任务", + "HomeAgentModeReviewTip": " 输入 /mode plan 可在执行前审查", + "HomeAgentModeYoloTip": " 输入 /mode yolo 启用完整工具访问", + "HomeYoloModeTip": "YOLO 模式 - 完整工具访问,无需审批", + "HomeYoloModeCaution": " 请小心破坏性操作!", + "HomePlanModeTip": "Plan 模式 - 先设计再实现", + "HomePlanModeChecklistTip": " 使用 /mode plan 创建结构化检查清单", + "HomeGoalModeTip": "Goal 跟踪 - 设置 /goal <目标> 以跟踪持久目标", + "OnboardLanguageTitle": "选择语言", + "OnboardLanguageBlurb": "选择界面语言。可随时使用 `/settings set locale ` 修改。", + "OnboardLanguageFooter": "按 1-7 选择,或按 Enter 保留当前设置", + "OnboardApiKeyTitle": "连接你的 DeepSeek API 密钥", + "OnboardApiKeyStep1": "步骤 1. 打开 https://platform.deepseek.com/api_keys 创建一个密钥。", + "OnboardApiKeyStep2": "步骤 2. 把密钥粘贴到下方并按 Enter。", + "OnboardApiKeySavedHint": "保存到 ~/.codewhale/config.toml,因此在任何目录下都生效。", + "OnboardApiKeyFormatHint": "请完整粘贴密钥(不要含空格或换行)。", + "OnboardApiKeyPlaceholder": "(在此粘贴密钥)", + "OnboardApiKeyLabel": "密钥: ", + "OnboardApiKeyFooter": "Enter 保存,Esc 返回。", + "OnboardTrustTitle": "信任工作目录", + "OnboardTrustQuestion": "你信任此目录中的内容吗?", + "OnboardTrustLocationPrefix": "当前位置:", + "OnboardTrustRiskHint": "处理不受信任的内容会增加提示词注入的风险。", + "OnboardTrustEffectHint": "信任此目录会记录在全局配置中,并启用受信任工作区模式。", + "OnboardTrustFooterPrefix": "按 ", + "OnboardTrustFooterMiddle": " 信任并继续,", + "OnboardTrustFooterSuffix": " 退出", + "OnboardTipsTitle": "从简开始", + "OnboardTipsLine1": "用自然语言描述任务。需要命令时使用 /help 或 Ctrl+K。", + "OnboardTipsLine2": "底部输入框支持多行:Enter 发送,Alt+Enter 或 Ctrl+J 换行。", + "OnboardTipsLine3": "按需切换模式:Plan 适合先审后行,Agent 用于执行,YOLO 启用自动批准。", + "OnboardTipsLine4": "Ctrl+R 恢复历史会话,Esc 退出当前输入或弹层。", + "OnboardTipsFooterEnter": "按 Enter", + "OnboardTipsFooterAction": " 进入工作区", + "CtxMenuTitle": " 右键菜单 ", + "CtxMenuCopySelection": "复制所选", + "CtxMenuCopySelectionDesc": "将选中的记录区域文本写入剪贴板", + "CtxMenuOpenSelection": "打开所选", + "CtxMenuOpenSelectionDesc": "在翻阅器中查看选中文本", + "CtxMenuClearSelection": "清除选择", + "CtxMenuOpenDetails": "打开详情", + "CtxMenuCopyMessage": "复制消息", + "CtxMenuCopyMessageDesc": "将点击的记录条目写入剪贴板", + "CtxMenuOpenInEditor": "在编辑器中打开", + "CtxMenuOpenInEditorDesc": "在 $EDITOR 中打开 file:line", + "CtxMenuShowCell": "显示条目", + "CtxMenuShowCellDesc": "取消隐藏此记录条目", + "CtxMenuHideCell": "隐藏条目", + "CtxMenuHideCellDesc": "折叠此记录条目", + "CtxMenuShowHidden": "显示已隐藏", + "CtxMenuShowHiddenDesc": "取消隐藏所有已折叠条目", + "CtxMenuPaste": "粘贴", + "CtxMenuPasteDesc": "将剪贴板插入输入框", + "CtxMenuCmdPalette": "命令面板", + "CtxMenuCmdPaletteDesc": "命令、技能和工具", + "CtxMenuContextInspector": "上下文检查器", + "CtxMenuContextInspectorDesc": "活动上下文和缓存提示", + "CtxMenuHelp": "帮助", + "CtxMenuHelpDesc": "快捷键和命令", + "FanoutCounts": "{done} 已完成 · {running} 运行中 · {failed} 失败 · {pending} 等待中", + "ModePickerPrompt": "选择 CodeWhale 的运行方式:", + "AppModeAgent": "智能体", + "AppModeYolo": "YOLO", + "AppModePlan": "计划", + "AppModeAgentHint": "正常执行,变更前需批准", + "AppModePlanHint": "先规划,再执行", + "AppModeYoloHint": "自动批准;启用 shell(完全访问)", + "VimModeNormal": "-- 普通 --", + "VimModeInsert": "-- 插入 --", + "VimModeVisual": "-- 可视 --", + "ApprovalRiskReview": "审查", + "ApprovalRiskDestructive": "破坏性", + "ApprovalCategorySafe": "安全", + "ApprovalCategoryFileWrite": "文件写入", + "ApprovalCategoryShell": "Bash", + "ApprovalCategoryNetwork": "网络", + "ApprovalCategoryMcpRead": "MCP 读取", + "ApprovalCategoryMcpAction": "MCP 操作", + "ApprovalCategoryUnknown": "未知", + "ApprovalFieldType": "类型:", + "ApprovalFieldAbout": "说明:", + "ApprovalFieldImpact": "影响:", + "ApprovalFieldParams": "参数:", + "ApprovalOptionApproveOnce": "仅本次批准", + "ApprovalOptionApproveAlways": "本会话同类自动批准", + "ApprovalOptionDeny": "拒绝本次调用", + "ApprovalOptionAbortTurn": "终止本轮", + "ApprovalBlockTitle": "审批", + "ApprovalControlsHint": " · v:完整参数 · Esc:终止", + "ApprovalChooseHint": "选择:", + "ApprovalChooseAction": "Enter 执行选中项,或直接按 y/a/d", + "ApprovalIntentLabel": "意图:", + "ApprovalMoreLines": " … (还有 {count} 行)", + "ElevationTitleSandboxDenied": " ⚠ 沙箱拒绝 ", + "ElevationTitleRequired": " 沙箱提权 ", + "ElevationFieldTool": " 工具:", + "ElevationFieldCmd": " 命令:", + "ElevationFieldReason": " 原因:", + "ElevationImpactHeader": " 批准后的影响:", + "ElevationImpactNetwork": " - 网络重试允许外部下载和 HTTP 请求", + "ElevationImpactWrite": " - 写入重试扩大此工具调用的文件系统写入范围", + "ElevationImpactFullAccess": " - 完全访问解除沙箱限制", + "ElevationPromptProceed": " 请选择处理方式:", + "ElevationOptionNetwork": "允许外部网络访问", + "ElevationOptionWrite": "允许额外写入权限", + "ElevationOptionFullAccess": "完全访问(文件系统 + 网络)", + "ElevationOptionAbort": "中止", + "ElevationOptionNetworkDesc": "使用外部网络访问重试此工具调用(下载和 HTTP 请求)", + "ElevationOptionWriteDesc": "重试此工具调用,扩大可写入的文件系统范围", + "ElevationOptionFullAccessDesc": "无沙箱限制重试(授予无限制的文件系统和网络访问权限)", + "ElevationOptionAbortDesc": "取消此工具调用", + "CtxInspTitle": "上下文检查器", + "CtxInspSessionContext": "会话上下文", + "CtxInspSystemPrompt": "系统提示结构", + "CtxInspReferences": "引用", + "CtxInspRecentTools": "最近使用的工具", + "CtxInspModel": "模型", + "CtxInspWorkspace": "工作区", + "CtxInspSession": "会话", + "CtxInspContext": "上下文", + "CtxInspTranscript": "记录", + "CtxInspWorkspaceStatus": "工作区状态", + "CtxInspNotSampledYet": "尚未采样", + "CtxInspOk": "正常", + "CtxInspHigh": "较高", + "CtxInspCritical": "严重", + "CtxInspIncluded": "已包含", + "CtxInspAttached": "已附加", + "CtxInspNotIncluded": "未包含", + "CtxInspOutputCaptured": "已捕获输出", + "CtxInspNoOutputYet": "尚无输出", + "CtxInspNoSystemPrompt": "未设置系统提示。", + "CtxInspNoReferences": "尚未记录任何文件、目录或媒体引用。", + "CtxInspNoToolActivity": "尚未记录任何工具活动。", + "CtxInspVHint": "打开对应的卡片并按 v 查看详细信息。", + "CtxInspCells": "单元格", + "CtxInspApiMessages": "API 消息", + "CtxInspActive": "活动中", + "CtxInspCell": "单元格", + "CtxInspMoreReferences": "更多引用", + "CtxInspStablePrefix": "稳定前缀", + "CtxInspVolatileWorkingSet": "易变工作集", + "CtxInspFirstLine": "第一行", + "CtxInspTotal": "总计", + "CtxInspTextPromptLayers": "文本提示层", + "CtxInspSingleTextBlob": "单一文本块", + "CtxInspBlocks": "个区块", + "CtxInspBlock": "个区块", + "CtxInspTokens": "个 token", + "CtxInspLayers": "个层", + "CtxInspNone": "无", + "CtxInspEmpty": "(空)", + "CtxInspCacheFriendly": "缓存友好", + "CtxInspChangesByTurn": "按会话/轮次变化", + "CtxInspStablePrefixOnly": "仅稳定前缀", + "CtxInspCacheTip": "提示:稳定前缀区块符合 DeepSeek V4 前缀缓存条件。易变工作集的更改仅会破坏缓存尾部。", + "ToolFamilyRead": "读取", + "ToolFamilyPatch": "修补", + "ToolFamilyRun": "运行", + "ToolFamilyFind": "搜索", + "ToolFamilyDelegate": "委派", + "ToolFamilyFanout": "扇出", + "ToolFamilyRlm": "rlm", + "ToolFamilyVerify": "验证", + "ToolFamilyThink": "思考", + "ToolFamilyGeneric": "工具", + "CmdVoiceDescription": "切换语音输入:录制语音并转录为文字", + "CmdVoiceSendDescription": "切换语音自动发送:转录以「发送」或「send it」结尾时自动提交", + "CmdVoiceControlDescription": "切换语音控制:AI 辅助的语音听写(结合当前输入内容)", + "VoiceEnabled": "语音输入已开启,开始说话即可录制", + "VoiceDisabled": "语音输入已关闭", + "VoiceSendEnabled": "语音自动发送已开启", + "VoiceSendDisabled": "语音自动发送已关闭", + "VoiceControlEnabled": "语音控制已开启", + "VoiceControlDisabled": "语音控制已关闭", + "VoiceErrNoAuth": "语音:当前提供商未配置 API 密钥", + "VoiceErrNoRecorder": "语音:未找到录音工具,请安装 sox、arecord 或 rec", + "VoiceErrNetwork": "语音:转录请求失败", + "VoiceErrEmptySend": "语音:没有可发送的内容", + "VoiceErrTooShort": "语音:未检测到有效语音,录制时间过短", + "VoiceRecording": "🎙 正在录音...请说话", + "VoiceProcessing": "🎙 正在转录...", + "VoiceTranscribed": "🎙 转录完成" +} diff --git a/crates/tui/src/localization.rs b/crates/tui/src/localization.rs index 2594b823da..a3218386d3 100644 --- a/crates/tui/src/localization.rs +++ b/crates/tui/src/localization.rs @@ -431,6 +431,7 @@ pub enum MessageId { KbFuzzyFilePicker, KbCompactInspector, KbLastMessagePager, + KbSelectedDetails, KbToolDetailsPager, KbThinkingPager, KbLiveTranscript, @@ -625,7 +626,7 @@ pub enum MessageId { CtxInspNoSystemPrompt, CtxInspNoReferences, CtxInspNoToolActivity, - CtxInspAltVHint, + CtxInspVHint, CtxInspCells, CtxInspApiMessages, CtxInspActive, @@ -886,6 +887,7 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[ MessageId::KbFuzzyFilePicker, MessageId::KbCompactInspector, MessageId::KbLastMessagePager, + MessageId::KbSelectedDetails, MessageId::KbToolDetailsPager, MessageId::KbThinkingPager, MessageId::KbLiveTranscript, @@ -1069,7 +1071,7 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[ MessageId::CtxInspNoSystemPrompt, MessageId::CtxInspNoReferences, MessageId::CtxInspNoToolActivity, - MessageId::CtxInspAltVHint, + MessageId::CtxInspVHint, MessageId::CtxInspCells, MessageId::CtxInspApiMessages, MessageId::CtxInspActive, @@ -1601,6 +1603,9 @@ fn english(id: MessageId) -> &'static str { MessageId::KbFuzzyFilePicker => "Open the fuzzy file picker (insert @path on Enter)", MessageId::KbCompactInspector => "Open compact session context inspector", MessageId::KbLastMessagePager => "Open pager for the last message (when input is empty)", + MessageId::KbSelectedDetails => { + "Open details for the selected tool or message (when input is empty)" + } MessageId::KbToolDetailsPager => "Open tool-details pager", MessageId::KbThinkingPager => "Open Activity Detail", MessageId::KbLiveTranscript => "Open live transcript overlay (sticky-tail auto-scroll)", @@ -1842,7 +1847,7 @@ fn english(id: MessageId) -> &'static str { MessageId::CtxInspNoSystemPrompt => "No system prompt set.", MessageId::CtxInspNoReferences => "No file, directory, or media references recorded yet.", MessageId::CtxInspNoToolActivity => "No tool activity recorded yet.", - MessageId::CtxInspAltVHint => "Open the matching card and press Alt+V for full details.", + MessageId::CtxInspVHint => "Open the matching card and press v for full details.", MessageId::CtxInspCells => "cells", MessageId::CtxInspApiMessages => "API messages", MessageId::CtxInspActive => "active", @@ -2242,6 +2247,9 @@ fn vietnamese(id: MessageId) -> Option<&'static str> { MessageId::KbLastMessagePager => { "Mở trang xem cho tin nhắn cuối cùng (khi khung nhập trống)" } + MessageId::KbSelectedDetails => { + "Mở chi tiết cho công cụ hoặc tin nhắn được chọn (khi khung nhập trống)" + } MessageId::KbToolDetailsPager => "Mở trang xem chi tiết công cụ", MessageId::KbThinkingPager => "Mở Chi Tiết Hoạt Động (Activity Detail)", MessageId::KbLiveTranscript => "Mở lớp phủ bản ghi trực tiếp (tự động cuộn theo đuôi)", @@ -2492,7 +2500,7 @@ fn vietnamese(id: MessageId) -> Option<&'static str> { MessageId::CtxInspNoSystemPrompt => "Chưa có lời nhắc hệ thống.", MessageId::CtxInspNoReferences => "Chưa có tham chiếu tệp, thư mục hoặc phương tiện nào.", MessageId::CtxInspNoToolActivity => "Chưa có hoạt động công cụ nào.", - MessageId::CtxInspAltVHint => "Mở thẻ phù hợp và nhấn Alt+V để biết chi tiết.", + MessageId::CtxInspVHint => "Mở thẻ phù hợp và nhấn v để biết chi tiết.", MessageId::CtxInspCells => "ô", MessageId::CtxInspApiMessages => "tin nhắn API", MessageId::CtxInspActive => "đang hoạt động", @@ -2661,7 +2669,7 @@ fn traditional_chinese(id: MessageId) -> Option<&'static str> { MessageId::CtxInspNoSystemPrompt => "未設定系統提示。", MessageId::CtxInspNoReferences => "尚未記錄任何檔案、目錄或媒體引用。", MessageId::CtxInspNoToolActivity => "尚未記錄任何工具活動。", - MessageId::CtxInspAltVHint => "開啟對應的卡片並按 Alt+V 檢視詳細資訊。", + MessageId::CtxInspVHint => "開啟對應的卡片並按 v 檢視詳細資訊。", MessageId::CtxInspCells => "儲存格", MessageId::CtxInspApiMessages => "API 訊息", MessageId::CtxInspActive => "作用中", @@ -3058,6 +3066,9 @@ fn japanese(id: MessageId) -> Option<&'static str> { MessageId::KbFuzzyFilePicker => "ファジーファイルピッカーを開く(Enter で @path を挿入)", MessageId::KbCompactInspector => "コンパクトなセッションコンテキスト検査ツールを開く", MessageId::KbLastMessagePager => "最後のメッセージのページャーを開く(入力が空の時)", + MessageId::KbSelectedDetails => { + "選択中のツールまたはメッセージの詳細を開く(入力が空の時)" + } MessageId::KbToolDetailsPager => "ツール詳細のページャーを開く", MessageId::KbThinkingPager => "Activity Detail を開く", MessageId::KbLiveTranscript => "ライブ会話履歴オーバーレイを開く(自動追尾スクロール)", @@ -3302,7 +3313,7 @@ fn japanese(id: MessageId) -> Option<&'static str> { "ファイル、ディレクトリ、メディアの参照はまだ記録されていません。" } MessageId::CtxInspNoToolActivity => "ツールアクティビティはまだ記録されていません。", - MessageId::CtxInspAltVHint => "該当するカードを開き、Alt+V を押すと詳細が表示されます。", + MessageId::CtxInspVHint => "該当するカードを開き、v を押すと詳細が表示されます。", MessageId::CtxInspCells => "セル", MessageId::CtxInspApiMessages => "API メッセージ", MessageId::CtxInspActive => "アクティブ", @@ -3366,546 +3377,18 @@ fn japanese(id: MessageId) -> Option<&'static str> { } fn chinese_simplified(id: MessageId) -> Option<&'static str> { - Some(match id { - MessageId::ComposerPlaceholder => "编写任务或使用 /。", - MessageId::HistorySearchPlaceholder => "搜索提示历史...", - MessageId::HistorySearchTitle => "历史搜索", - MessageId::HistoryHintMove => "Up/Down 移动", - MessageId::HistoryHintAccept => "Enter 接受", - MessageId::HistoryHintRestore => "Esc 还原", - MessageId::HistoryNoMatches => " 无匹配", - MessageId::StatusPickerTitle => " 状态行 ", - MessageId::StatusPickerInstruction => "选择要在底部显示的项目:", - MessageId::StatusPickerActionToggle => "切换 ", - MessageId::StatusPickerActionAll => "全部 ", - MessageId::StatusPickerActionNone => "无 ", - MessageId::StatusPickerActionSave => "保存 ", - MessageId::StatusPickerActionCancel => "取消 ", - MessageId::ConfigTitle => "会话配置", - MessageId::ConfigModalTitle => " 配置 ", - MessageId::ConfigSearchPlaceholder => "输入以筛选", - MessageId::ConfigNoSettings => " 没有可用设置。", - MessageId::ConfigNoMatchesPrefix => " 没有匹配设置: ", - MessageId::ConfigFilteredSettings => " 已筛选设置", - MessageId::ConfigShowing => " 显示", - MessageId::ConfigFooterDefault => " 输入=筛选, Up/Down=选择, Enter/e=编辑, Esc/q=关闭 ", - MessageId::ConfigFooterScrollable => { - " 输入=筛选, Up/Down=选择, Enter/e=编辑, PgUp/PgDn=滚动, Esc/q=关闭 " - } - MessageId::ConfigFooterFiltered => { - " 输入=筛选, Backspace=删除, Ctrl+U/Esc=清除, Enter=编辑 " - } - MessageId::ConfigSectionProvider => "提供商", - MessageId::ConfigSectionModel => "模型", - MessageId::ConfigSectionPermissions => "权限", - MessageId::ConfigSectionNetwork => "网络", - MessageId::ConfigSectionDisplay => "显示", - MessageId::ConfigSectionComposer => "编辑器", - MessageId::ConfigSectionSidebar => "侧边栏", - MessageId::ConfigSectionHistory => "历史", - MessageId::ConfigSectionMcp => "MCP", - MessageId::ConfigSectionFleet => "舰队", - MessageId::ConfigSectionExperimental => "实验", - MessageId::ConfigScopeSession => "会话", - MessageId::ConfigScopeSaved => "已保存", - MessageId::ConfigEditCancelled => "编辑已取消", - MessageId::ConfigEditTitlePrefix => "编辑 ", - MessageId::ConfigEditScopeLabel => "范围: ", - MessageId::ConfigEditCurrentLabel => "当前: ", - MessageId::ConfigEditHintLabel => "提示: ", - MessageId::ConfigEditNewLabel => "新值: ", - MessageId::ConfigEditFooter => { - " Enter=应用, Esc=取消, Ctrl+U=清除, Ctrl+A=全选, \u{2190}/\u{2192}=移动 " - } - MessageId::ConfigRowEffective => " (实际 {currency})", - MessageId::ConfigDefaultValue => "(默认)", - MessageId::ConfigDefaultReasoning => "(配置/默认)", - MessageId::ConfigUnavailable => "(不可用)", - MessageId::HelpTitle => "帮助", - MessageId::HelpFilterPlaceholder => "输入以筛选", - MessageId::HelpFilterPrefix => "筛选: ", - MessageId::HelpNoMatches => " 无匹配。", - MessageId::HelpSlashCommands => "斜杠命令", - MessageId::HelpKeybindings => "快捷键", - MessageId::HelpFooterTypeFilter => " 输入以筛选 ", - MessageId::HelpFooterMove => " Up/Down 移动 ", - MessageId::HelpFooterJump => " PgUp/PgDn 跳转 ", - MessageId::HelpFooterClose => " Esc 关闭 ", - MessageId::CmdAnchorDescription => "钉选关键事实,在压缩后自动注入上下文", - MessageId::CmdAttachDescription => "附加图片或视频媒体;文本文件或目录请使用 @path", - MessageId::CmdCacheDescription => "显示最近 N 轮的 DeepSeek 前缀缓存命中/未命中统计", - MessageId::CmdChangeDescription => "显示最新的更新日志", - MessageId::CmdChangeHeader => "最新更新日志", - MessageId::CmdChangeTranslationQueued => { - "下面显示英文发布说明。接下来会请求模型翻译;如果当前提供商不可用,这段英文内容就是备用结果。" - } - MessageId::CmdChangeTranslationUnavailable => { - "下面显示英文发布说明。当前会话没有 API Key 或处于离线状态,无法翻译。" - } - MessageId::CmdChangePreviousVersion => { - "上一个版本: {version} —— 输入 `/change {version}` 查看" - } - MessageId::CmdBalanceDescription => "查看当前提供商账户余额", - MessageId::CmdClearDescription => "清除对话历史", - MessageId::CmdCompactDescription => "触发上下文压缩以释放空间", - MessageId::CmdPurgeDescription => "让 Agent 分析对话历史,精确保留有用信息并移除冗余内容", - MessageId::CmdConfigDescription => "打开交互式配置编辑器", - MessageId::CmdContextDescription => "打开紧凑会话上下文检查器", - MessageId::CmdCostDescription => "显示本次会话的费用明细", - MessageId::CmdDiffDescription => "显示会话开始以来的文件变更", - MessageId::CmdEditDescription => "修改并重新提交最后一条消息", - MessageId::CmdExitDescription => "退出应用", - MessageId::CmdExportDescription => "将对话导出为 Markdown", - MessageId::CmdFeedbackDescription => "生成 GitHub 反馈链接", - MessageId::CmdHfDescription => "检查 Hugging Face MCP 设置和概念", - MessageId::CmdHelpDescription => "显示帮助信息", - MessageId::CmdProfileDescription => "切换到命名配置配置文件", - MessageId::CmdHomeDescription => "显示主页面板,含统计与快捷操作", - MessageId::CmdHooksDescription => "列出已配置的生命周期钩子(只读)", - MessageId::CmdAgentDescription => "打开持久子代理会话:/agent [0-3] ", - MessageId::CmdGoalDescription => "设置带有可选令牌预算的会话目标", - MessageId::CmdInitDescription => "为项目生成 AGENTS.md", - MessageId::CmdLspDescription => "切换 LSP 诊断的开启或关闭", - MessageId::CmdShareDescription => "将当前会话导出为可共享的 Web URL", - MessageId::CmdJobsDescription => "查看并管理后台 shell 作业", - MessageId::CmdLinksDescription => "显示服务商令牌、控制台与文档链接", - MessageId::CmdLoadDescription => "从文件加载会话", - MessageId::CmdLogoutDescription => "清除 API 密钥并返回设置", - MessageId::CmdMcpDescription => "打开或管理 MCP 服务器", - MessageId::CmdPluginDescription => "List discovered plugin tools or show details for one", - MessageId::CmdPluginNoneFound => "No plugin tools discovered in {dir}", - MessageId::CmdPluginNotFound => "Plugin '{name}' not found", - MessageId::CmdPluginListHeader => "Plugin tools ({count}):", - MessageId::CmdPluginDetailDescription => "Description: {description}", - MessageId::CmdPluginDetailSchema => "Schema:\n{schema}", - MessageId::CmdPluginDetailApproval => "Approval: {approval}", - MessageId::CmdPluginDetailPath => "Path: {path}", - MessageId::CmdMemoryDescription => "查看或管理持久用户记忆文件", - MessageId::CmdModeDescription => "切换运行模式或打开选择器:/mode [agent|plan|yolo|1|2|3]", - MessageId::CmdModelDescription => "切换或查看当前模型", - MessageId::CmdModelsDescription => "列出 API 中可用的模型", - MessageId::CmdNetworkDescription => "管理网络允许和拒绝规则", - MessageId::CmdNoteDescription => "添加、列出、编辑或删除工作区笔记", - MessageId::CmdThemeDescription => "切换主题:深色、浅色、灰度或系统", - MessageId::CmdProviderDescription => { - "切换或查看当前 LLM 后端(deepseek | nvidia-nim | ollama)" - } - MessageId::CmdQueueDescription => "查看或编辑已排队的消息", - MessageId::CmdQueueUsage => "用法: /queue [list|send |edit |drop |clear]", - MessageId::CmdQueueDraftHeader => "正在编辑已排队的消息:", - MessageId::CmdQueueNoMessages => "没有已排队的消息", - MessageId::CmdQueueListHeader => "已排队的消息 ({count}):", - MessageId::CmdQueueTip => "提示: /queue send 立即发送, /queue drop 删除", - MessageId::CmdQueueAlreadyEditing => { - "已在编辑一条已排队的消息。请先发送或使用 /queue clear 放弃。" - } - MessageId::CmdQueueNotFound => "未找到已排队的消息", - MessageId::CmdQueueEditingStatus => "正在编辑已排队的消息 {index}", - MessageId::CmdQueueEditingMessage => { - "正在编辑已排队的消息 {index}(按 Enter 重新排队/发送)" - } - MessageId::CmdQueueDropped => "已删除已排队的消息 {index}", - MessageId::CmdQueueAlreadyEmpty => "队列已空", - MessageId::CmdQueueCleared => "队列已清空", - MessageId::CmdQueueMissingIndex => "缺少索引。用法: /queue edit 或 /queue drop ", - MessageId::CmdQueueIndexPositive => "索引必须为正数", - MessageId::CmdQueueIndexMin => "索引必须 >= 1", - MessageId::CmdRelayDescription => "为新线程创建会话接力摘要", - MessageId::CmdRenameDescription => "重命名当前会话", - MessageId::CmdRestoreDescription => { - "将工作区回滚到此前的轮次前/后快照。不带参数时列出最近的快照。" - } - MessageId::CmdRetryDescription => "重试上一次请求", - MessageId::CmdReviewDescription => "对文件、diff 或 PR 进行结构化代码审查", - MessageId::CmdRlmDescription => "打开持久 RLM 上下文:/rlm [0-3] ", - MessageId::CmdSaveDescription => "将会话保存到文件", - MessageId::CmdForkDescription => "将当前对话分叉为兄弟会话", - MessageId::CmdNewDescription => "开始一个新的已保存会话", - MessageId::CmdSessionsDescription => "打开会话历史选择器", - MessageId::CmdSettingsDescription => "显示持久化设置", - MessageId::CmdSkillDescription => "激活技能,或安装/更新/卸载/信任社区技能", - MessageId::CmdSidebarDescription => "Toggle or focus the right sidebar", - MessageId::CmdSkillsDescription => { - "列出本地技能(用 `/skills ` 按名称前缀过滤,--remote 浏览精选注册表)" - } - MessageId::CmdSlopDescription => "Inspect or export the debt ledger", - MessageId::CmdStashDescription => "暂存或恢复输入草稿(Ctrl+S 暂存,/stash list|pop)", - MessageId::CmdStatusDescription => "显示当前运行状态", - MessageId::CmdStatuslineDescription => "配置底栏要显示哪些条目", - MessageId::CmdFleetDescription => "打开 Fleet 设置或工作器状态", - MessageId::CmdSubagentsDescription => "/fleet status 的兼容快捷方式", - MessageId::CmdSwarmDescription => { - "运行多代理扇出轮次(sequential | mixture | distill | deliberate)" - } - MessageId::CmdSystemDescription => "显示当前系统提示词", - MessageId::CmdTaskDescription => "管理后台任务", - MessageId::CmdTokensDescription => "显示本次会话的 token 用量", - MessageId::CmdTranslateDescription => "切换输出翻译为当前系统语言的开/关状态", - MessageId::CmdTranslateOff => "输出翻译已关闭(显示原始模型输出)", - MessageId::CmdTranslateOn => "输出翻译已开启:模型回复将以当前系统语言显示", - MessageId::TranslationInProgress => "正在翻译助手输出...", - MessageId::TranslationComplete => "翻译完成", - MessageId::TranslationFailed => "翻译失败", - MessageId::CmdTrustDescription => { - "管理工作区信任与按路径的白名单(`/trust add `、`/trust list`、`/trust on|off`)" - } - MessageId::CmdWorkspaceDescription => "显示或切换当前工作空间", - MessageId::CmdUndoDescription => "移除最后一组消息对", - MessageId::CmdVerboseDescription => "切换实时思考内容的完整显示", - MessageId::CmdCacheAdvice => { - "第 3 轮起命中率稳定在 ~70% 以上即表示前缀缓存稳定;\n\ - 长会话中明显偏低则意味着前缀有抖动,值得排查(#263)。" - } - MessageId::CmdCacheFootnote => "* 当提供方未单独上报未命中时,由「输入 − 命中」推算。\n", - MessageId::CmdCacheHeader => "缓存遥测 —— 最近 {count} / {total} 轮(模型:{model})\n", - MessageId::CmdCacheNoData => { - "缓存历史:尚未记录任何轮次。\n\n\ - DeepSeek 在受支持的模型(V4 系列)每个 API 轮次都会返回 `prompt_cache_hit_tokens` / \ - `prompt_cache_miss_tokens`。请先运行一个轮次再试 /cache。" - } - MessageId::CmdCacheTotals => { - "Σ 输入:{sum_in} Σ 命中:{sum_hit} Σ 未命中:{sum_miss} 平均命中率:{avg}\n" - } - MessageId::CmdCostReport => { - "会话费用:\n\ - ─────────────────────────────\n\ - 预估累计消耗:{cost}\n\n\ - 费用为估算值;如有提供方用量遥测会优先使用。\n\n\ - DeepSeek API 计费:\n\ - ─────────────────────────────\n\ - 此 CLI 中未配置详细计费规则。" - } - MessageId::CmdTokensCacheBoth => "命中 {hit} / 未命中 {miss}", - MessageId::CmdTokensCacheHitOnly => "命中 {hit} / 未命中未上报", - MessageId::CmdTokensCacheMissOnly => "命中未上报 / 未命中 {miss}", - MessageId::CmdTokensContextUnknownWindow => "~{estimated} / 窗口未知", - MessageId::CmdTokensContextWithWindow => "~{used} / {window}({percent}%)", - MessageId::FooterAgentSingular => "1 个子代理", - MessageId::FooterAgentsPlural => "{count} 个子代理", - MessageId::FooterPressCtrlCAgain => "再次按 Ctrl+C 退出", - MessageId::FooterWorking => "工作中", - MessageId::FooterBalancePrefix => "余额", - MessageId::HelpSectionActions => "操作", - MessageId::HelpSectionClipboard => "剪贴板", - MessageId::HelpSectionEditing => "输入编辑", - MessageId::HelpSectionHelp => "帮助", - MessageId::HelpSectionModes => "模式", - MessageId::HelpSectionNavigation => "导航", - MessageId::HelpSectionSessions => "会话", - MessageId::CmdTokensNotReported => "未上报", - MessageId::CmdTokensReport => { - "令牌用量:\n\ - ─────────────────────────────\n\ - 活动上下文: {active}\n\ - 上次 API 输入: {input}(来自轮次遥测;多轮工具调用中相同前缀可能被重复计入)\n\ - 上次 API 输出: {output}\n\ - 缓存命中/未命中: {cache}(仅用于遥测/计费)\n\ - 累计令牌: {total}(会话用量遥测)\n\ - 预估会话费用: {cost}\n\ - API 消息数: {api_messages}\n\ - 聊天消息数: {chat_messages}\n\ - 模型: {model}" - } - MessageId::KbScrollTranscript => "滚动对话记录、浏览输入历史或选择附件", - MessageId::KbNavigateHistory => "浏览输入历史", - MessageId::KbBrowseHistory => "浏览对话历史", - MessageId::KbScrollTranscriptAlt => "滚动对话记录", - MessageId::KbScrollPage => "按页滚动对话记录", - MessageId::KbJumpTopBottom => "跳转到对话顶部/底部", - MessageId::KbJumpTopBottomEmpty => "跳转到顶部/底部(输入框为空时)", - MessageId::KbJumpToolBlocks => "在工具输出块之间跳转", - MessageId::KbMoveCursor => "在输入框中移动光标", - MessageId::KbJumpLineStartEnd => "跳转到行首/行尾", - MessageId::KbDeleteChar => "删除光标前/后的字符,或移除已选附件", - MessageId::KbClearDraft => "清空当前草稿", - MessageId::KbStashDraft => "暂存当前草稿(用 `/stash pop` 恢复)", - MessageId::KbSearchHistory => "搜索提示历史并恢复本地草稿", - MessageId::KbInsertNewline => "在输入框中插入换行", - MessageId::KbSendDraft => "发送当前草稿", - MessageId::KbCloseMenu => "关闭菜单、取消请求、丢弃草稿或清空输入", - MessageId::KbCancelOrExit => "取消请求,或空闲时退出", - MessageId::KbShellControls => "将正在运行的前台命令转入后台", - MessageId::KbExitEmpty => "输入框为空时退出", - MessageId::KbCommandPalette => "打开命令面板", - MessageId::KbCancelBackgroundShellJobs => { - "取消所有正在运行的后台 shell 作业(Tasks 侧边栏)" - } - MessageId::KbFuzzyFilePicker => "打开模糊文件选择器(按 Enter 插入 @path)", - MessageId::KbCompactInspector => "打开紧凑会话上下文检查器", - MessageId::KbLastMessagePager => "打开最后一条消息的分页器(输入框为空时)", - MessageId::KbToolDetailsPager => "打开工具详情分页器", - MessageId::KbThinkingPager => "打开 Activity Detail", - MessageId::KbLiveTranscript => "打开实时对话覆盖层(自动滚动尾随)", - MessageId::KbBacktrackMessage => "回退到之前的用户消息(左右键步进,Enter 回退)", - MessageId::KbCompleteCycleModes => { - "补全 /command、排队运行轮次跟进、切换模式;Shift+Tab 切换推理强度" - } - MessageId::KbJumpPlanAgentYolo => "触发快捷栏槽位", - MessageId::KbAltJumpPlanAgentYolo => "替代快捷键跳转到 Plan / Agent / YOLO 模式", - MessageId::KbFocusSidebar => "聚焦 Pinned / 任务 / 代理 / Context / 自动 / 隐藏侧边栏", - MessageId::KbSessionPicker => "打开会话选择器", - MessageId::KbPasteAttach => "粘贴文本或附加剪贴板图片", - MessageId::KbCopySelection => "复制当前选中内容(macOS 为 Cmd+C)", - MessageId::KbContextMenu => "打开上下文操作菜单,用于粘贴、选择、消息详情、上下文和帮助", - MessageId::KbAttachPath => "添加本地文本文件或目录到上下文", - MessageId::KbHelpOverlay => "打开此帮助覆盖层(输入框为空时)", - MessageId::KbToggleHelp => "切换帮助覆盖层", - MessageId::KbToggleHelpSlash => "切换帮助覆盖层", - MessageId::HelpUsageLabel => "用法:", - MessageId::HelpAliasesLabel => "别名:", - MessageId::SettingsTitle => "设置:", - MessageId::SettingsConfigFile => "配置文件:", - MessageId::ClearConversation => "对话已清空", - MessageId::ClearConversationBusy => { - "对话已清空(Plan 状态忙碌;如需再次清空请运行 /clear)" - } - MessageId::ModelChanged => "模型已切换:{old} \u{2192} {new}", - MessageId::LinksTitle => "服务商链接:", - MessageId::LinksDashboard => "控制台:", - MessageId::LinksDocs => "文档:", - MessageId::LinksTip => { - "提示:使用所显示服务商的环境变量,或通过 `codewhale auth set --provider ` 保存密钥。" - } - MessageId::SubagentsFetching => "正在获取 Fleet 工作器状态...", - MessageId::HelpUnknownCommand => "未知命令:{topic}", - MessageId::HomeDashboardTitle => "codewhale 主面板", - MessageId::HomeModel => "模型:", - MessageId::HomeMode => "模式:", - MessageId::HomeWorkspace => "工作区:", - MessageId::HomeHistory => "历史:", - MessageId::HomeTokens => "令牌:", - MessageId::HomeQueued => "队列:", - MessageId::HomeSubagents => "Fleet 工作器:", - MessageId::HomeSkill => "技能:", - MessageId::HomeQuickActions => "快捷操作", - MessageId::HomeQuickLinks => "/links - 控制台与 API 链接", - MessageId::HomeQuickSkills => "/skills - 列出可用技能", - MessageId::HomeQuickConfig => "/config - 打开交互式配置编辑器", - MessageId::HomeQuickSettings => "/settings - 显示持久化设置", - MessageId::HomeQuickModel => "/model - 切换或查看模型", - MessageId::HomeQuickSubagents => "/fleet status - Fleet 工作器状态", - MessageId::HomeQuickTaskList => "/task list - 显示后台任务队列", - MessageId::HomeQuickHelp => "/help - 显示帮助", - MessageId::HomeModeTips => "模式提示", - MessageId::HomeAgentModeTip => "Agent 模式 - 使用工具执行自主任务", - MessageId::HomeAgentModeReviewTip => " 输入 /mode plan 可在执行前审查", - MessageId::HomeAgentModeYoloTip => " 输入 /mode yolo 启用完整工具访问", - MessageId::HomeYoloModeTip => "YOLO 模式 - 完整工具访问,无需审批", - MessageId::HomeYoloModeCaution => " 请小心破坏性操作!", - MessageId::HomePlanModeTip => "Plan 模式 - 先设计再实现", - MessageId::HomePlanModeChecklistTip => " 使用 /mode plan 创建结构化检查清单", - MessageId::HomeGoalModeTip => "Goal 跟踪 - 设置 /goal <目标> 以跟踪持久目标", - // Onboarding — language picker. - MessageId::OnboardLanguageTitle => "选择语言", - MessageId::OnboardLanguageBlurb => { - "选择界面语言。可随时使用 `/settings set locale ` 修改。" - } - MessageId::OnboardLanguageFooter => "按 1-7 选择,或按 Enter 保留当前设置", - // Onboarding — API key entry. - MessageId::OnboardApiKeyTitle => "连接你的 DeepSeek API 密钥", - MessageId::OnboardApiKeyStep1 => { - "步骤 1. 打开 https://platform.deepseek.com/api_keys 创建一个密钥。" - } - MessageId::OnboardApiKeyStep2 => "步骤 2. 把密钥粘贴到下方并按 Enter。", - MessageId::OnboardApiKeySavedHint => { - "保存到 ~/.codewhale/config.toml,因此在任何目录下都生效。" - } - MessageId::OnboardApiKeyFormatHint => "请完整粘贴密钥(不要含空格或换行)。", - MessageId::OnboardApiKeyPlaceholder => "(在此粘贴密钥)", - MessageId::OnboardApiKeyLabel => "密钥: ", - MessageId::OnboardApiKeyFooter => "Enter 保存,Esc 返回。", - // Onboarding — workspace trust. - MessageId::OnboardTrustTitle => "信任工作目录", - MessageId::OnboardTrustQuestion => "你信任此目录中的内容吗?", - MessageId::OnboardTrustLocationPrefix => "当前位置:", - MessageId::OnboardTrustRiskHint => "处理不受信任的内容会增加提示词注入的风险。", - MessageId::OnboardTrustEffectHint => { - "信任此目录会记录在全局配置中,并启用受信任工作区模式。" - } - MessageId::OnboardTrustFooterPrefix => "按 ", - MessageId::OnboardTrustFooterMiddle => " 信任并继续,", - MessageId::OnboardTrustFooterSuffix => " 退出", - // Onboarding — final tips. - MessageId::OnboardTipsTitle => "从简开始", - MessageId::OnboardTipsLine1 => "用自然语言描述任务。需要命令时使用 /help 或 Ctrl+K。", - MessageId::OnboardTipsLine2 => "底部输入框支持多行:Enter 发送,Alt+Enter 或 Ctrl+J 换行。", - MessageId::OnboardTipsLine3 => { - "按需切换模式:Plan 适合先审后行,Agent 用于执行,YOLO 启用自动批准。" - } - MessageId::OnboardTipsLine4 => "Ctrl+R 恢复历史会话,Esc 退出当前输入或弹层。", - MessageId::OnboardTipsFooterEnter => "按 Enter", - MessageId::OnboardTipsFooterAction => " 进入工作区", - // Context menu. - MessageId::CtxMenuTitle => " 右键菜单 ", - MessageId::CtxMenuCopySelection => "复制所选", - MessageId::CtxMenuCopySelectionDesc => "将选中的记录区域文本写入剪贴板", - MessageId::CtxMenuOpenSelection => "打开所选", - MessageId::CtxMenuOpenSelectionDesc => "在翻阅器中查看选中文本", - MessageId::CtxMenuClearSelection => "清除选择", - MessageId::CtxMenuOpenDetails => "打开详情", - MessageId::CtxMenuCopyMessage => "复制消息", - MessageId::CtxMenuCopyMessageDesc => "将点击的记录条目写入剪贴板", - MessageId::CtxMenuOpenInEditor => "在编辑器中打开", - MessageId::CtxMenuOpenInEditorDesc => "在 $EDITOR 中打开 file:line", - MessageId::CtxMenuShowCell => "显示条目", - MessageId::CtxMenuShowCellDesc => "取消隐藏此记录条目", - MessageId::CtxMenuHideCell => "隐藏条目", - MessageId::CtxMenuHideCellDesc => "折叠此记录条目", - MessageId::CtxMenuShowHidden => "显示已隐藏", - MessageId::CtxMenuShowHiddenDesc => "取消隐藏所有已折叠条目", - MessageId::CtxMenuPaste => "粘贴", - MessageId::CtxMenuPasteDesc => "将剪贴板插入输入框", - MessageId::CtxMenuCmdPalette => "命令面板", - MessageId::CtxMenuCmdPaletteDesc => "命令、技能和工具", - MessageId::CtxMenuContextInspector => "上下文检查器", - MessageId::CtxMenuContextInspectorDesc => "活动上下文和缓存提示", - MessageId::CtxMenuHelp => "帮助", - MessageId::CtxMenuHelpDesc => "快捷键和命令", - MessageId::FanoutCounts => { - "{done} 已完成 · {running} 运行中 · {failed} 失败 · {pending} 等待中" - } + use std::collections::HashMap; + use std::sync::LazyLock; - // App mode picker (prompt, names, hints) and composer vim indicator. - MessageId::ModePickerPrompt => "选择 CodeWhale 的运行方式:", - MessageId::AppModeAgent => "智能体", - MessageId::AppModeYolo => "YOLO", - MessageId::AppModePlan => "计划", - MessageId::AppModeAgentHint => "正常执行,变更前需批准", - MessageId::AppModePlanHint => "先规划,再执行", - MessageId::AppModeYoloHint => "自动批准;启用 shell(完全访问)", - MessageId::VimModeNormal => "-- 普通 --", - MessageId::VimModeInsert => "-- 插入 --", - MessageId::VimModeVisual => "-- 可视 --", + static MAP: LazyLock> = LazyLock::new(|| { + serde_json::from_str(include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/locales/zh-Hans.json" + ))) + .expect("valid zh-Hans.json translations") + }); - // Approval dialog. - MessageId::ApprovalRiskReview => "审查", - MessageId::ApprovalRiskDestructive => "破坏性", - MessageId::ApprovalCategorySafe => "安全", - MessageId::ApprovalCategoryFileWrite => "文件写入", - MessageId::ApprovalCategoryShell => "Bash", - MessageId::ApprovalCategoryNetwork => "网络", - MessageId::ApprovalCategoryMcpRead => "MCP 读取", - MessageId::ApprovalCategoryMcpAction => "MCP 操作", - MessageId::ApprovalCategoryUnknown => "未知", - MessageId::ApprovalFieldType => "类型:", - MessageId::ApprovalFieldAbout => "说明:", - MessageId::ApprovalFieldImpact => "影响:", - MessageId::ApprovalFieldParams => "参数:", - MessageId::ApprovalOptionApproveOnce => "仅本次批准", - MessageId::ApprovalOptionApproveAlways => "本会话同类自动批准", - MessageId::ApprovalOptionDeny => "拒绝本次调用", - MessageId::ApprovalOptionAbortTurn => "终止本轮", - MessageId::ApprovalBlockTitle => "审批", - MessageId::ApprovalControlsHint => " · v:完整参数 · Esc:终止", - MessageId::ApprovalChooseHint => "选择:", - MessageId::ApprovalChooseAction => "Enter 执行选中项,或直接按 y/a/d", - MessageId::ApprovalIntentLabel => "意图:", - MessageId::ApprovalMoreLines => " … (还有 {count} 行)", - // Sandbox elevation dialog. - // Sandbox elevation dialog. - MessageId::ElevationTitleSandboxDenied => " \u{26a0} 沙箱拒绝 ", - MessageId::ElevationTitleRequired => " 沙箱提权 ", - MessageId::ElevationFieldTool => " 工具:", - MessageId::ElevationFieldCmd => " 命令:", - MessageId::ElevationFieldReason => " 原因:", - MessageId::ElevationImpactHeader => " 批准后的影响:", - MessageId::ElevationImpactNetwork => " - 网络重试允许外部下载和 HTTP 请求", - MessageId::ElevationImpactWrite => " - 写入重试扩大此工具调用的文件系统写入范围", - MessageId::ElevationImpactFullAccess => " - 完全访问解除沙箱限制", - MessageId::ElevationPromptProceed => " 请选择处理方式:", - MessageId::ElevationOptionNetwork => "允许外部网络访问", - MessageId::ElevationOptionWrite => "允许额外写入权限", - MessageId::ElevationOptionFullAccess => "完全访问(文件系统 + 网络)", - MessageId::ElevationOptionAbort => "中止", - MessageId::ElevationOptionNetworkDesc => { - "使用外部网络访问重试此工具调用(下载和 HTTP 请求)" - } - MessageId::ElevationOptionWriteDesc => "重试此工具调用,扩大可写入的文件系统范围", - MessageId::ElevationOptionFullAccessDesc => { - "无沙箱限制重试(授予无限制的文件系统和网络访问权限)" - } - MessageId::ElevationOptionAbortDesc => "取消此工具调用", - - MessageId::CtxInspTitle => "上下文检查器", - MessageId::CtxInspSessionContext => "会话上下文", - MessageId::CtxInspSystemPrompt => "系统提示结构", - MessageId::CtxInspReferences => "引用", - MessageId::CtxInspRecentTools => "最近使用的工具", - MessageId::CtxInspModel => "模型", - MessageId::CtxInspWorkspace => "工作区", - MessageId::CtxInspSession => "会话", - MessageId::CtxInspContext => "上下文", - MessageId::CtxInspTranscript => "记录", - MessageId::CtxInspWorkspaceStatus => "工作区状态", - MessageId::CtxInspNotSampledYet => "尚未采样", - MessageId::CtxInspOk => "正常", - MessageId::CtxInspHigh => "较高", - MessageId::CtxInspCritical => "严重", - MessageId::CtxInspIncluded => "已包含", - MessageId::CtxInspAttached => "已附加", - MessageId::CtxInspNotIncluded => "未包含", - MessageId::CtxInspOutputCaptured => "已捕获输出", - MessageId::CtxInspNoOutputYet => "尚无输出", - MessageId::CtxInspNoSystemPrompt => "未设置系统提示。", - MessageId::CtxInspNoReferences => "尚未记录任何文件、目录或媒体引用。", - MessageId::CtxInspNoToolActivity => "尚未记录任何工具活动。", - MessageId::CtxInspAltVHint => "打开对应的卡片并按 Alt+V 查看详细信息。", - MessageId::CtxInspCells => "单元格", - MessageId::CtxInspApiMessages => "API 消息", - MessageId::CtxInspActive => "活动中", - MessageId::CtxInspCell => "单元格", - MessageId::CtxInspMoreReferences => "更多引用", - MessageId::CtxInspStablePrefix => "稳定前缀", - MessageId::CtxInspVolatileWorkingSet => "易变工作集", - MessageId::CtxInspFirstLine => "第一行", - MessageId::CtxInspTotal => "总计", - MessageId::CtxInspTextPromptLayers => "文本提示层", - MessageId::CtxInspSingleTextBlob => "单一文本块", - MessageId::CtxInspBlocks => "个区块", - MessageId::CtxInspBlock => "个区块", - MessageId::CtxInspTokens => "个 token", - MessageId::CtxInspLayers => "个层", - MessageId::CtxInspNone => "无", - MessageId::CtxInspEmpty => "(空)", - MessageId::CtxInspCacheFriendly => "缓存友好", - MessageId::CtxInspChangesByTurn => "按会话/轮次变化", - MessageId::CtxInspStablePrefixOnly => "仅稳定前缀", - MessageId::CtxInspCacheTip => { - "提示:稳定前缀区块符合 DeepSeek V4 前缀缓存条件。易变工作集的更改仅会破坏缓存尾部。" - } - MessageId::ToolFamilyRead => "读取", - MessageId::ToolFamilyPatch => "修补", - MessageId::ToolFamilyRun => "运行", - MessageId::ToolFamilyFind => "搜索", - MessageId::ToolFamilyDelegate => "委派", - MessageId::ToolFamilyFanout => "扇出", - MessageId::ToolFamilyRlm => "rlm", - MessageId::ToolFamilyVerify => "验证", - MessageId::ToolFamilyThink => "思考", - MessageId::ToolFamilyGeneric => "工具", - // Voice commands - MessageId::CmdVoiceDescription => "切换语音输入:录制语音并转录为文字", - MessageId::CmdVoiceSendDescription => { - "切换语音自动发送:转录以「发送」或「send it」结尾时自动提交" - } - MessageId::CmdVoiceControlDescription => { - "切换语音控制:AI 辅助的语音听写(结合当前输入内容)" - } - MessageId::VoiceEnabled => "语音输入已开启,开始说话即可录制", - MessageId::VoiceDisabled => "语音输入已关闭", - MessageId::VoiceSendEnabled => "语音自动发送已开启", - MessageId::VoiceSendDisabled => "语音自动发送已关闭", - MessageId::VoiceControlEnabled => "语音控制已开启", - MessageId::VoiceControlDisabled => "语音控制已关闭", - MessageId::VoiceErrNoAuth => "语音:当前提供商未配置 API 密钥", - MessageId::VoiceErrNoRecorder => "语音:未找到录音工具,请安装 sox、arecord 或 rec", - MessageId::VoiceErrNetwork => "语音:转录请求失败", - MessageId::VoiceErrEmptySend => "语音:没有可发送的内容", - MessageId::VoiceErrTooShort => "语音:未检测到有效语音,录制时间过短", - MessageId::VoiceRecording => "🎙 正在录音...请说话", - MessageId::VoiceProcessing => "🎙 正在转录...", - MessageId::VoiceTranscribed => "🎙 转录完成", - }) + MAP.get(&format!("{id:?}")).map(String::as_str) } fn portuguese_brazil(id: MessageId) -> Option<&'static str> { @@ -4230,6 +3713,9 @@ fn portuguese_brazil(id: MessageId) -> Option<&'static str> { MessageId::KbLastMessagePager => { "Abrir paginador para última mensagem (quando entrada vazia)" } + MessageId::KbSelectedDetails => { + "Abrir detalhes da ferramenta ou mensagem selecionada (quando entrada vazia)" + } MessageId::KbToolDetailsPager => "Abrir paginador de detalhes da ferramenta", MessageId::KbThinkingPager => "Abrir Activity Detail", MessageId::KbLiveTranscript => "Abrir sobreposição de transcrição ao vivo (auto-scroll)", @@ -4480,8 +3966,8 @@ fn portuguese_brazil(id: MessageId) -> Option<&'static str> { "Nenhuma referência de arquivo, diretório ou mídia registrada ainda." } MessageId::CtxInspNoToolActivity => "Nenhuma atividade de ferramenta registrada ainda.", - MessageId::CtxInspAltVHint => { - "Abra o cartão correspondente e pressione Alt+V para detalhes completos." + MessageId::CtxInspVHint => { + "Abra o cartão correspondente e pressione v para detalhes completos." } MessageId::CtxInspCells => "células", MessageId::CtxInspApiMessages => "mensagens da API", @@ -4877,6 +4363,9 @@ fn spanish_latin_america(id: MessageId) -> Option<&'static str> { MessageId::KbLastMessagePager => { "Abrir paginador para el último mensaje (cuando la entrada está vacía)" } + MessageId::KbSelectedDetails => { + "Abrir detalles de la herramienta o mensaje seleccionado (cuando la entrada está vacía)" + } MessageId::KbToolDetailsPager => "Abrir paginador de detalles de la herramienta", MessageId::KbThinkingPager => "Abrir paginador de razonamiento", MessageId::KbLiveTranscript => "Abrir superposición de transcripción en vivo (auto-scroll)", @@ -5125,8 +4614,8 @@ fn spanish_latin_america(id: MessageId) -> Option<&'static str> { "Aún no se han registrado referencias de archivos, directorios o medios." } MessageId::CtxInspNoToolActivity => "Aún no se ha registrado actividad de herramientas.", - MessageId::CtxInspAltVHint => { - "Abra la tarjeta correspondiente y presione Alt+V para ver los detalles completos." + MessageId::CtxInspVHint => { + "Abra la tarjeta correspondiente y presione v para ver los detalles completos." } MessageId::CtxInspCells => "celdas", MessageId::CtxInspApiMessages => "mensajes de API", diff --git a/crates/tui/src/tui/app.rs b/crates/tui/src/tui/app.rs index 8c5ff01a14..f059897590 100644 --- a/crates/tui/src/tui/app.rs +++ b/crates/tui/src/tui/app.rs @@ -3511,7 +3511,7 @@ impl App { .find(|detail| self.tool_cells.get(&detail.tool_id).copied() == Some(index)) } - /// Whether a virtual transcript cell can open a meaningful Alt+V detail + /// Whether a virtual transcript cell can open a meaningful `v` detail /// view. Thinking cells render their own raw text inline so there is no /// separate "raw" target — only tool / sub-agent cells get the hint. #[must_use] @@ -3524,7 +3524,7 @@ impl App { } /// Pick the detail target for the current viewport. This is used by the - /// transcript highlight and footer hint so they agree with Alt+V. + /// transcript highlight and footer hint so they agree with `v`. #[must_use] pub fn detail_cell_index_for_viewport( &self, diff --git a/crates/tui/src/tui/context_inspector.rs b/crates/tui/src/tui/context_inspector.rs index 179fcba2b0..b4a4c21d96 100644 --- a/crates/tui/src/tui/context_inspector.rs +++ b/crates/tui/src/tui/context_inspector.rs @@ -419,7 +419,7 @@ fn push_tools(out: &mut String, app: &App, locale: Locale) { if rendered == 0 { let _ = writeln!(out, "- {}", tr(locale, MessageId::CtxInspNoToolActivity)); } else { - let _ = writeln!(out, "- {}", tr(locale, MessageId::CtxInspAltVHint)); + let _ = writeln!(out, "- {}", tr(locale, MessageId::CtxInspVHint)); } } diff --git a/crates/tui/src/tui/history.rs b/crates/tui/src/tui/history.rs index fd7c787443..0e1b30e64a 100644 --- a/crates/tui/src/tui/history.rs +++ b/crates/tui/src/tui/history.rs @@ -1416,7 +1416,7 @@ impl GenericToolCell { // `checklist_update` always do on a successful match — render // only the changed item plus a `M/N · pct%` summary instead of // dumping the full list every time. The full list is still - // reachable via Alt+V on the tool detail record. This keeps the + // reachable via `v` on the tool detail record. This keeps the // transcript scannable in long sessions. if matches!(mode, RenderMode::Live) && let Some(change) = parse_update_prefix(output) diff --git a/crates/tui/src/tui/history/tool_run.rs b/crates/tui/src/tui/history/tool_run.rs index 976a27c97f..9ccf085eba 100644 --- a/crates/tui/src/tui/history/tool_run.rs +++ b/crates/tui/src/tui/history/tool_run.rs @@ -54,7 +54,7 @@ impl ToolRunActivitySummary { /// /// Failed, running, patch, review, diff, and plan-update cells split runs so /// important state never disappears into a summary row. Successful command -/// cells can join dense runs; Alt+V / expansion keeps their raw details +/// cells can join dense runs; `v` / expansion keeps their raw details /// available without making routine verifier/shell work dominate the default /// transcript. pub fn detect_tool_runs(history: &[HistoryCell], min_size: usize) -> Vec { diff --git a/crates/tui/src/tui/key_shortcuts.rs b/crates/tui/src/tui/key_shortcuts.rs index 17138c7653..13692c203e 100644 --- a/crates/tui/src/tui/key_shortcuts.rs +++ b/crates/tui/src/tui/key_shortcuts.rs @@ -2,8 +2,8 @@ //! //! These helpers normalise the cross-platform variations between //! `Ctrl+…` (Linux/Windows) and `Cmd+…` (macOS), legacy `Ctrl+H`-as- -//! backspace handling, and the macOS Option-Latin-character escapes -//! (`Option+V` produces `\u{221A}` instead of `v`). Centralising them +//! backspace handling, and the macOS Option-Latin-character escapes. +//! Centralising them //! keeps the composer / transcript event loops in `ui.rs` short and //! lets us add a new platform without touching the call sites. @@ -57,11 +57,7 @@ pub(super) fn is_file_tree_toggle_shortcut(key: &KeyEvent) -> bool { } pub(super) fn tool_details_shortcut_label() -> &'static str { - if cfg!(target_os = "macos") { - "Option+V" - } else { - "Alt+V" - } + "v" } pub(super) fn tool_details_shortcut_action_hint(noun: &str) -> String { @@ -73,7 +69,7 @@ pub(super) fn activity_shortcut_label() -> &'static str { } /// Modifier predicate for the v0.8.30 family of `Alt+` transcript- -/// nav shortcuts (`Alt+G` / `Alt+[` / `Alt+]` / `Alt+?` / `Alt+L` / `Alt+V`). Requires +/// nav shortcuts (`Alt+G` / `Alt+[` / `Alt+]` / `Alt+?` / `Alt+L`). Requires /// `Alt` and disallows `Ctrl` / `Super` so the /// bindings don't collide with platform clipboard / window-management /// shortcuts. `Shift` is permitted so the capital-letter forms work on diff --git a/crates/tui/src/tui/keybindings.rs b/crates/tui/src/tui/keybindings.rs index 1ffcba073f..5a6faa525c 100644 --- a/crates/tui/src/tui/keybindings.rs +++ b/crates/tui/src/tui/keybindings.rs @@ -211,8 +211,8 @@ pub const KEYBINDINGS: &[KeybindingEntry] = &[ section: KeybindingSection::Submission, }, KeybindingEntry { - chord: "Alt+V", - description_id: crate::localization::MessageId::KbToolDetailsPager, + chord: "v", + description_id: crate::localization::MessageId::KbSelectedDetails, section: KeybindingSection::Submission, }, KeybindingEntry { @@ -365,16 +365,23 @@ mod tests { } #[test] - fn tool_details_help_documents_alt_v_without_bare_v() { - let details = KEYBINDINGS + fn tool_details_help_documents_bare_v_without_alt_v() { + let selected_details = KEYBINDINGS .iter() .filter(|entry| { - entry.description_id == crate::localization::MessageId::KbToolDetailsPager + entry.description_id == crate::localization::MessageId::KbSelectedDetails }) .map(|entry| entry.chord) .collect::>(); - assert_eq!(details, vec!["Alt+V"]); + assert_eq!(selected_details, vec!["v"]); + let legacy_modified_details_chord = ["Alt", "V"].join("+"); + assert!( + KEYBINDINGS + .iter() + .all(|entry| entry.chord != legacy_modified_details_chord), + "help should advertise the bare v details shortcut" + ); } #[test] diff --git a/crates/tui/src/tui/ui.rs b/crates/tui/src/tui/ui.rs index 9927eaead3..b0aa66d84d 100644 --- a/crates/tui/src/tui/ui.rs +++ b/crates/tui/src/tui/ui.rs @@ -3964,11 +3964,17 @@ async fn run_event_loop( { continue; } - // Bare `v` / `V` no longer opens the tool-details pager — that - // path is owned exclusively by `Alt+V` at the lower arm, so - // the letter `v` is freely usable as the first character of - // a message. `details_shortcut_modifiers` previously allowed - // empty/Shift here, eating the keystroke on empty composers. + // This detail shortcut intentionally precedes vim-normal-mode + // handling: visual selection has no useful empty-composer + // target, while selected tool cards do. + KeyCode::Char('v') + if key.modifiers == KeyModifiers::NONE + && app.input.is_empty() + && detail_target_cell_index(app).is_some() => + { + open_tool_details_pager(app); + continue; + } KeyCode::Char('o') if key.modifiers.contains(KeyModifiers::CONTROL) && app.input.is_empty() @@ -4407,12 +4413,12 @@ async fn run_event_loop( KeyCode::BackTab => { app.cycle_effort(); } - // Transcript-nav shortcuts now require Alt, leaving the bare + // Transcript-nav shortcuts now require Alt, leaving most bare // letters free to insert as text. Before v0.8.30, bare `g`, - // `G`, `[`, `]`, `?`, `l`, and `v` on an empty composer were + // `G`, `[`, `]`, `?`, and `l` on an empty composer were // hijacked for navigation — typing "good" yielded "ood" with // no whale and no warning. The Alt-prefixed shortcuts mirror - // the Alt+R / Alt+V / Alt+C pattern already in use. Shift is + // the Alt+R / Alt+C pattern already in use. Shift is // permitted for most capital-letter forms. KeyCode::Char('g') if key_shortcuts::alt_nav_modifiers(key.modifiers) @@ -10493,7 +10499,7 @@ fn open_thinking_pager(app: &mut App) -> bool { /// then a live activity in the current turn, then the most recent meaningful /// activity across history + active cells. Tool activity is intentionally /// rendered through the compact live view so Activity Detail does not become -/// an accidental raw-output dump; Alt+V remains the direct full tool-detail +/// an accidental raw-output dump; `v` remains the direct full tool-detail /// surface. fn open_activity_detail_pager(app: &mut App) -> bool { let Some(idx) = activity_target_cell_index(app) else { @@ -10924,19 +10930,19 @@ fn activity_detail_handle_line(app: &App, cell_index: usize, cell: &HistoryCell) .find(|artifact| artifact.tool_call_id == detail.tool_id) { return Some(format!( - "Detail handle: {} (retrieve_tool_result ref={}; Alt+V raw details)", + "Detail handle: {} (retrieve_tool_result ref={}; v raw details)", artifact.id, artifact.id )); } return Some(format!( - "Detail handle: tool:{} (Alt+V raw details)", + "Detail handle: tool:{} (v raw details)", detail.tool_id )); } match cell { - HistoryCell::Tool(_) => Some("Detail handle: Alt+V details".to_string()), - HistoryCell::SubAgent(_) => Some("Detail handle: Alt+V details".to_string()), + HistoryCell::Tool(_) => Some("Detail handle: v details".to_string()), + HistoryCell::SubAgent(_) => Some("Detail handle: v details".to_string()), _ => None, } } diff --git a/crates/tui/src/tui/ui/tests.rs b/crates/tui/src/tui/ui/tests.rs index 2350d1a8c2..dd4bfa8deb 100644 --- a/crates/tui/src/tui/ui/tests.rs +++ b/crates/tui/src/tui/ui/tests.rs @@ -8719,8 +8719,8 @@ fn activity_detail_includes_tool_handle_and_neighbor_context() { body.contains("retrieve_tool_result ref=art_call-read"), "{body}" ); - assert!(body.contains("Alt+V"), "{body}"); - assert!(body.contains("raw details"), "{body}"); + assert!(body.contains("v raw"), "{body}"); + assert!(body.contains("details)"), "{body}"); } #[test] @@ -8777,9 +8777,9 @@ fn activity_detail_fallback_uses_recent_meaningful_activity_without_full_tool_du assert!(body.contains("Activity: read")); assert!(body.contains("Status: done")); - assert!(body.contains("Detail handle: Alt+V details"), "{body}"); + assert!(body.contains("Detail handle: v details"), "{body}"); assert!( - !body.contains("Detail handle: Alt+V raw details"), + !body.contains("Detail handle: v raw details"), "fallback tool details should not be labeled raw: {body}" ); assert!(