diff --git a/crates/tty7-core/src/core/session.rs b/crates/tty7-core/src/core/session.rs index 1409b9c5..0118858c 100644 --- a/crates/tty7-core/src/core/session.rs +++ b/crates/tty7-core/src/core/session.rs @@ -162,6 +162,20 @@ impl RemoteTarget { } } + /// Whether the far end is served by a tty7 daemon this computer installed + /// and can therefore restart. SSH machines and WSL distros both are; a + /// `--stdio` program is whatever the user named, and stopping it is its + /// workspace's business. + pub fn hosts_our_server(&self) -> bool { + match self { + RemoteTarget::Profile { .. } + | RemoteTarget::Alias { .. } + | RemoteTarget::Direct { .. } + | RemoteTarget::Wsl { .. } => true, + RemoteTarget::LocalStdio { .. } => false, + } + } + pub fn host_id(&self) -> crate::host::HostId { crate::host::HostId::from_connection_key(&self.connection_key()) } @@ -491,7 +505,7 @@ mod tests { } #[test] - fn only_ssh_machines_have_a_server_to_restart() { + fn only_ssh_machines_are_reached_over_ssh() { assert!( RemoteTarget::Profile { id: uuid::Uuid::nil() @@ -510,7 +524,7 @@ mod tests { distro: "Ubuntu".into() } .is_ssh(), - "a distribution's server is started by this client" + "a distribution is reached through wsl.exe, not a connection" ); assert!( !RemoteTarget::LocalStdio { @@ -522,6 +536,38 @@ mod tests { ); } + #[test] + fn every_machine_but_a_stdio_one_has_a_server_to_restart() { + assert!( + RemoteTarget::Profile { + id: uuid::Uuid::nil() + } + .hosts_our_server() + ); + assert!( + RemoteTarget::Alias { + alias: "devbox".into() + } + .hosts_our_server() + ); + assert!(RemoteTarget::direct("me", "box.local", 22).hosts_our_server()); + assert!( + RemoteTarget::Wsl { + distro: "Ubuntu".into() + } + .hosts_our_server(), + "a distribution's server is installed and launched from here, like an SSH one" + ); + assert!( + !RemoteTarget::LocalStdio { + program: "tty7-server".into(), + args: vec!["--stdio".into()], + } + .hosts_our_server(), + "a stdio program is whatever the user named, not a daemon of ours" + ); + } + #[test] fn direct_targets_normalize_and_reuse_the_quick_connect_parser() { assert_eq!( diff --git a/crates/tty7-core/src/daemon/install/wsl.rs b/crates/tty7-core/src/daemon/install/wsl.rs index cc71e51c..8de2d50b 100644 --- a/crates/tty7-core/src/daemon/install/wsl.rs +++ b/crates/tty7-core/src/daemon/install/wsl.rs @@ -603,6 +603,20 @@ pub fn restart_wsl_daemon(distro: &str) -> io::Result<()> { Ok(()) } +/// Restart the distro's daemon after making sure the binary it launches is this +/// build's. The bundled server is already on this computer, so unlike SSH there +/// is nothing to download — the copy is the whole install. +pub fn replace_wsl_server(distro: &str) -> io::Result<()> { + validate_distro(distro)?; + let ops = WslRemoteOps::new(distro); + let source = BundledServerBinary::discover(); + let confirm = install_confirm(); + let lock = install_lock(distro); + let _held = lock.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + Installer::with_source(&ops, &source, confirm.as_ref(), host_label(distro)).replace()?; + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/tty7-core/src/daemon/router.rs b/crates/tty7-core/src/daemon/router.rs index 58ac80fc..b414e8af 100644 --- a/crates/tty7-core/src/daemon/router.rs +++ b/crates/tty7-core/src/daemon/router.rs @@ -718,8 +718,25 @@ async fn restart_server( .restart_remote_server(spec, setup) .await } + // A distro's server is installed and launched from here too, so both + // moves mean the same thing they do over SSH — only the transport is + // different. + (RouteTarget::Wsl { distro }, RouteAction::ReplaceServer) => { + let distro = distro.clone(); + setup + .blocking(move || crate::daemon::install::wsl::replace_wsl_server(&distro)) + .await??; + Ok(()) + } + (RouteTarget::Wsl { distro }, _) => { + let distro = distro.clone(); + setup + .blocking(move || crate::daemon::install::wsl::restart_wsl_daemon(&distro)) + .await??; + Ok(()) + } _ => Err(anyhow::anyhow!( - "restarting tty7's server is only supported for SSH machines, not {}", + "restarting tty7's server is only supported for machines it serves, not {}", header.describe() )), } @@ -1233,19 +1250,22 @@ mod tests { assert!(forwarded.performed(RouteAction::Forward)); } + /// SSH and WSL machines both run a daemon this side installed, so both can + /// be restarted. A `--stdio` program is whatever the user named — there is + /// no daemon of ours behind it to stop. #[tokio::test] async fn a_restart_is_refused_for_a_machine_that_has_no_remote_daemon() { - for header in [ - RouteHeader::local_stdio("cat", &[]).restart_server(), - RouteHeader::wsl("Ubuntu-22.04").restart_server(), - ] { - let describe = header.describe(); - let setup = RouteSetup::unattended(header.channel); - let Err(err) = perform(&header, &setup).await else { - panic!("a restart must be refused for {describe}"); - }; - assert!(err.to_string().contains("only supported for SSH"), "{err}"); - } + let header = RouteHeader::local_stdio("cat", &[]).restart_server(); + let describe = header.describe(); + let setup = RouteSetup::unattended(header.channel); + let Err(err) = perform(&header, &setup).await else { + panic!("a restart must be refused for {describe}"); + }; + assert!( + err.to_string() + .contains("only supported for machines it serves"), + "{err}" + ); } #[test] @@ -1259,7 +1279,11 @@ mod tests { let mut client = client; let err = RouteAck::read(&mut client).expect_err("a `cat` has no daemon to restart"); - assert!(err.to_string().contains("only supported for SSH"), "{err}"); + assert!( + err.to_string() + .contains("only supported for machines it serves"), + "{err}" + ); assert!(routed.join().unwrap().is_err()); } diff --git a/src/ui/app.rs b/src/ui/app.rs index f18ae116..81e0e5c8 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -1160,9 +1160,9 @@ impl Tty7App { }; let target = remote.target.clone(); let label = crate::ui::remote_connect::label_for(&target, cx); - if !target.is_ssh() { + if !target.hosts_our_server() { window.push_notification( - t_fmt(L10nKey::AppRestartServerNotSsh, &[("label", &label)]), + t_fmt(L10nKey::AppRestartServerNoServer, &[("label", &label)]), cx, ); return; diff --git a/src/ui/i18n/en.rs b/src/ui/i18n/en.rs index 46fbf2e1..bc0b88e8 100644 --- a/src/ui/i18n/en.rs +++ b/src/ui/i18n/en.rs @@ -1120,8 +1120,8 @@ pub fn translate_en(key: L10nKey) -> &'static str { "The server holding your shells predates the version handshake, so this app can't tell what it speaks.\n\nQuit: nothing changes — the server and your shells keep running.\nRestart: tabs come back with fresh shells, and anything running now is killed." } L10nKey::AppRestart => "Restart", - L10nKey::AppRestartServerNotSsh => { - "tty7 can only restart the server on machines it reaches over SSH. {label} is served from this computer — stop its workspace instead." + L10nKey::AppRestartServerNoServer => { + "tty7 has no server of its own to restart on {label} — it is a program this computer runs over --stdio. Stop its workspace instead." } L10nKey::AppRestartServerBody => { "This stops every running shell on this computer — anything still running in them will be terminated. Your tabs and layout are kept and reopened with fresh shells." diff --git a/src/ui/i18n/ja.rs b/src/ui/i18n/ja.rs index 6fb172db..ccd25dff 100644 --- a/src/ui/i18n/ja.rs +++ b/src/ui/i18n/ja.rs @@ -1155,8 +1155,8 @@ pub fn translate_ja(key: L10nKey) -> Option<&'static str> { "サーバーはバージョン照合より前のもので、何を話すか分かりません。\n\n終了:何も変わりません。サーバーもシェルも動き続けます。\n再起動:タブは新しいシェルで戻り、いま実行中のものは終了します" } L10nKey::AppRestart => "再起動", - L10nKey::AppRestartServerNotSsh => { - "tty7 は SSH で到達できるマシン上のサーバーしか再起動できません。{label} はこのコンピュータで実行されています。代わりにそのワークスペースを止めてください" + L10nKey::AppRestartServerNoServer => { + "{label} には再起動できる tty7 自身のサーバーがありません。これはこのコンピュータが --stdio で実行しているプログラムです。代わりにそのワークスペースを止めてください" } L10nKey::AppRestartServerBody => { "このコンピュータで実行中のすべてのシェルが停止します。タブとレイアウトは保持され、新しいシェルで開きます" diff --git a/src/ui/i18n/mod.rs b/src/ui/i18n/mod.rs index 9a6c16be..cab1b2de 100644 --- a/src/ui/i18n/mod.rs +++ b/src/ui/i18n/mod.rs @@ -929,7 +929,7 @@ pub enum L10nKey { AppRestartServerDialectNewerDetail, AppRestartServerOldDetail, AppRestart, - AppRestartServerNotSsh, + AppRestartServerNoServer, AppRestartServerBody, AppWorktreeRemoveDetailDirty, AppWorktreeRemoveDetailClean, @@ -1956,7 +1956,7 @@ mod tests { L10nKey::AppRestartServerDialectNewerDetail, L10nKey::AppRestartServerOldDetail, L10nKey::AppRestart, - L10nKey::AppRestartServerNotSsh, + L10nKey::AppRestartServerNoServer, L10nKey::AppRestartServerBody, L10nKey::AppWorktreeRemoveDetailDirty, L10nKey::AppWorktreeRemoveDetailClean, diff --git a/src/ui/i18n/zh.rs b/src/ui/i18n/zh.rs index 02e5e430..633ad61e 100644 --- a/src/ui/i18n/zh.rs +++ b/src/ui/i18n/zh.rs @@ -24,7 +24,7 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { L10nKey::HomeSplitRight => "向右分屏", L10nKey::HomeSplitDown => "向下分屏", L10nKey::HomeSettings => "设置…", - L10nKey::TrayQuitStopServer => "退出并停止服务器…", + L10nKey::TrayQuitStopServer => "退出并停止 server…", L10nKey::Reconnect => "重新连接", L10nKey::None => "无。", L10nKey::TryAgain => "重试", @@ -47,9 +47,9 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { L10nKey::RememberKeychain => "记住(钥匙串)", L10nKey::Cancel => "取消", L10nKey::Close => "关闭", - L10nKey::QuitStopServerTitle => "退出并停止服务器?", + L10nKey::QuitStopServerTitle => "退出并停止 server?", L10nKey::QuitStopServerBody => { - "这会退出 tty7 并停止后台服务器,所有仍在运行的 shell 都会被终止。你的标签页和布局会被保留,下次启动时以全新的 shell 重新打开。(普通退出会保持 shell 运行。)" + "这会退出 tty7 并停止后台 server,所有仍在运行的 shell 都会被终止。你的标签页和布局会被保留,下次启动时以全新的 shell 重新打开。(普通退出会保持 shell 运行。)" } L10nKey::QuitAndStop => "退出并停止", L10nKey::CloseSshConnectionTitle => "关闭这个 SSH 连接?", @@ -435,17 +435,17 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { } L10nKey::SettingsUpdateChannelStable => "Stable", L10nKey::SettingsUpdateChannelNightly => "Nightly", - L10nKey::SettingsDaemonStale => "后台服务仍运行在 {build}。", + L10nKey::SettingsDaemonStale => "后台 server 仍运行在 {build}。", L10nKey::SettingsDaemonStaleDesc => { - "tty7 是原地升级的,界面已经是新版本,但各个 pane 仍由旧版本的后台服务托管。重启服务才能用上新版本,代价是 pane 里正在跑的进程全部结束——shell、agent、SSH 会话都算。不急,挑个 pane 空闲的时候再重启。" + "tty7 是原地升级的,界面已经是新版本,但各个 pane 仍由旧版本的后台 server 托管。重启 server 才能用上新版本,代价是 pane 里正在跑的进程全部结束——shell、agent、SSH 会话都算。不急,挑个 pane 空闲的时候再重启。" } - L10nKey::SettingsDaemonStaleRestart => "重启服务", + L10nKey::SettingsDaemonStaleRestart => "重启 server", L10nKey::UpdateDialogTitle => "有可用更新", L10nKey::UpdateDialogDetail => { - "tty7 {version} 已发布,你现在是 {current}。安装会重启应用;后台服务不动,pane 里开着的东西都还在。" + "tty7 {version} 已发布,你现在是 {current}。安装会重启应用;后台 server 不动,pane 里开着的东西都还在。" } L10nKey::UpdateDialogDetailWindows => { - "tty7 {version} 已发布,你现在是 {current}。安装会重启应用和后台服务:pane 里正在运行的进程会被结束,标签页和布局会以全新的 shell 恢复。" + "tty7 {version} 已发布,你现在是 {current}。安装会重启应用和后台 server:pane 里正在运行的进程会被结束,标签页和布局会以全新的 shell 恢复。" } L10nKey::UpdateDialogDetailManual => "tty7 {version} 已发布,你现在是 {current}。{hint}", L10nKey::UpdateDialogCannotSelfUpdate => "这份安装无法自行更新。", @@ -484,11 +484,11 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { "启动时将自带的 `tty7` 命令加入 PATH,让脚本和编码 agent 可在任意终端驱动 tty7。在 tty7 窗格内两种情况都可用。如果你自己构建或安装了 `tty7` 且不希望被遮蔽,请关闭此选项。下次启动时生效。" } L10nKey::SettingsInstallCliOnPath => "将 `tty7` 命令安装到 PATH", - L10nKey::SettingsServer => "服务器", + L10nKey::SettingsServer => "Server", L10nKey::SettingsServerDesc => { - "重启在后台维持 shell 运行的服务器。这会结束这台计算机上所有正在运行的 shell;你的标签页和布局会以全新的 shell 重新打开。" + "重启在后台维持 shell 运行的 server。这会结束这台计算机上所有正在运行的 shell;你的标签页和布局会以全新的 shell 重新打开。" } - L10nKey::SettingsRestartServer => "重启服务器…", + L10nKey::SettingsRestartServer => "重启 server…", L10nKey::SettingsAppHttpProxy => "更新代理", L10nKey::SettingsAppHttpProxyDesc => { "仅用于 tty7 自身的更新检查和下载,不影响面板中运行的程序。留空则跟随系统代理。" @@ -683,7 +683,7 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { L10nKey::SwitcherNoMatch => "没有匹配的工作区或机器。", L10nKey::AddSshHost => "添加 SSH 主机…", L10nKey::ClickForNewWindow => "点击打开新窗口", - L10nKey::RestartServer => "重启服务器", + L10nKey::RestartServer => "重启 server", L10nKey::OtherMachines => "其他机器", L10nKey::Ok => "确定", L10nKey::SftpNoTransfers => "还没有传输任务。", @@ -857,26 +857,26 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { 请检查其 SSH 主机配置或 ~/.ssh/config 条目是否仍然存在。" } L10nKey::RemoteThisComputer => "本机", - L10nKey::RemoteRestartTitle => "重启 \"{machine}\" 上的 tty7 服务器?", + L10nKey::RemoteRestartTitle => "重启 \"{machine}\" 上的 tty7 server?", L10nKey::RemoteRestartBody => { "这将停止 {machine} 上的所有 shell——其中仍在运行的任何内容都会被终止,\ 包括此窗口未显示的 shell。工作区和布局会被保留,并以全新的 shell 恢复。" } L10nKey::RemoteReplaceBody => { - "tty7 会在 {machine} 上安装匹配的服务器端并启动它。\n\ + "tty7 会在 {machine} 上安装匹配的 server 并启动它。\n\ \n\ {machine} 上运行的所有会话都会结束,包括此窗口未连接的会话。" } - L10nKey::RemoteRestartFailedTitle => "\"{machine}\" 上的 tty7 服务器未被重启", + L10nKey::RemoteRestartFailedTitle => "\"{machine}\" 上的 tty7 server 未被重启", L10nKey::RemoteRestartFailedBody => { "{error}\n\ \n\ - 那里仍在运行的会话用的还是旧版本。如果它们已经结束,重新连接就会启动此版本的服务器。" + 那里仍在运行的会话用的还是旧版本。如果它们已经结束,重新连接就会启动此版本的 server。" } L10nKey::RemoteHostUnreachable => "无法连接到 {machine}:{error}", - L10nKey::RemoteInstallTitle => "在 \"{machine}\" 上安装 tty7 服务器?", + L10nKey::RemoteInstallTitle => "在 \"{machine}\" 上安装 tty7 server?", L10nKey::RemoteInstallDetail => { - "tty7 会将其服务器二进制文件写入 {machine},以便本机可以在那里托管\ + "tty7 会将其 server 二进制文件写入 {machine},以便本机可以在那里托管\ 工作区。{machine} 上的其他内容不会被修改,也不会使用 sudo。\n\ \n\ {path_label}\u{2003}{path}\n\ @@ -894,50 +894,50 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { L10nKey::RemoteInstallShaLabel => "SHA-256", L10nKey::RemoteInstallSilentUpgrades => "此后在该机器上的升级将静默安装。", L10nKey::RemoteInstallBytes => "字节", - L10nKey::RemoteMismatchTitle => "更新 \"{machine}\" 上的 tty7 服务器端?", + L10nKey::RemoteMismatchTitle => "更新 \"{machine}\" 上的 tty7 server?", L10nKey::RemoteMismatchDetail => { "{machine} 正在使用 {running} 提供 tty7 会话,该版本使用的协议无法被\ - 此客户端({wanted})识别。tty7 已在那里安装了匹配的服务器端,\ + 此客户端({wanted})识别。tty7 已在那里安装了匹配的 server,\ 但正在运行的是你当前会话所在的版本。\n\ \n\ {replace_server}\u{2003}会将其替换为 {wanted} 并结束其托管的所有会话。\n\ {cancel}\u{2003}会保持 {machine} 现状不变。此窗口将不会连接。" } - L10nKey::RemoteMismatchReplaceServer => "更新服务器端", + L10nKey::RemoteMismatchReplaceServer => "更新 server", L10nKey::RemoteMismatchUnknownBuild => "未知构建", L10nKey::RemoteMismatchUnknownBuildFromExe => "未知构建(来自 {exe})", L10nKey::RemoteServerOutdated => { - "{machine} 上的 tty7 服务器端太旧({build}),当前这份 tty7 连不上它。\ + "{machine} 上的 tty7 server 太旧({build}),当前这份 tty7 连不上它。\ 更新它才能连接。" } L10nKey::RemoteServerTooNew => { - "{machine} 上的 tty7 服务器端({build})比当前这份 tty7 还新。\ - 请更新本机的 tty7,或把那边的服务器端替换成匹配的版本。" + "{machine} 上的 tty7 server({build})比当前这份 tty7 还新。\ + 请更新本机的 tty7,或把那边的 server 替换成匹配的版本。" } - L10nKey::RemoteDaemonStartFailed => "无法启动 tty7 本地服务器:{error}", - L10nKey::RemoteDaemonUnreachable => "无法连接到 tty7 本地服务器:{error}", + L10nKey::RemoteDaemonStartFailed => "无法启动 tty7 本地 server:{error}", + L10nKey::RemoteDaemonUnreachable => "无法连接到 tty7 本地 server:{error}", L10nKey::RemoteDaemonTooOld => { - "此机器上的 tty7 守护进程版本较旧,无法重启 {machine} 上的服务器。\ + "此机器上的 tty7 守护进程版本较旧,无法重启 {machine} 上的 server。\ 请退出 tty7(这会停止守护进程)并重新打开,然后重试。" } L10nKey::RemoteProfileMissing => "该已保存的 SSH 主机配置已不存在", L10nKey::RemoteAliasMissing => "`{alias}` 已不再位于 ~/.ssh/config 中", L10nKey::RemoteWslNoSsh => "WSL 工作区没有 SSH 连接", L10nKey::RemoteLocalStdioNoSsh => "本地 --stdio 工作区没有 SSH 连接", - L10nKey::RemoteHostNotTty7 => "{machine} 已响应,但并非作为 tty7 服务器:{error}", + L10nKey::RemoteHostNotTty7 => "{machine} 已响应,但并非作为 tty7 server:{error}", L10nKey::RemoteWorkspaceListFailed => "已连接到 {machine},但其工作区列表获取失败:{error}", - L10nKey::RemoteServerRestartFailed => "无法重启 {machine} 上的 tty7 服务器:{error}", + L10nKey::RemoteServerRestartFailed => "无法重启 {machine} 上的 tty7 server:{error}", L10nKey::RemoteNoRouteToHost => "tty7 已无法到达 {machine}", - L10nKey::RemoteMachineTreeUnexpectedReply => "服务器用 {reply} 回复了机器树请求", + L10nKey::RemoteMachineTreeUnexpectedReply => "server 用 {reply} 回复了机器树请求", L10nKey::RemoteMismatchVersionFromExe => "{version}(来自 {exe})", L10nKey::AppNoRunningCodingAgent => { "未找到运行中的编码 agent——请先在某个窗格中启动一个(claude、codex 等)。" } L10nKey::SwitcherThisComputer => "本机", - L10nKey::SwitcherRestartingServer => "正在重启 tty7 服务器…", - L10nKey::SwitcherDownloadingServerWithTotal => "正在下载 tty7 服务器… {done} / {total}", - L10nKey::SwitcherDownloadingServerNoTotal => "正在下载 tty7 服务器… {done}", - L10nKey::SwitcherCopyingServer => "正在复制 tty7 服务器… {done} / {total}", + L10nKey::SwitcherRestartingServer => "正在重启 tty7 server…", + L10nKey::SwitcherDownloadingServerWithTotal => "正在下载 tty7 server… {done} / {total}", + L10nKey::SwitcherDownloadingServerNoTotal => "正在下载 tty7 server… {done}", + L10nKey::SwitcherCopyingServer => "正在复制 tty7 server… {done} / {total}", L10nKey::SwitcherThisWindow => "当前窗口", L10nKey::SwitcherOpen => "已打开", L10nKey::SwitcherDisconnect => "断开连接", @@ -961,7 +961,7 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { L10nKey::SshPromptSubmit => "提交", L10nKey::HostOpsError => "{context}:{error}", L10nKey::TreeWindowOpenedEmpty => { - "这个窗口的服务器没有交出标签页,所以窗口是空的。什么都没丢,它一响应就会回来。如果一直不回来,在命令面板里执行「重启服务器」。" + "这个窗口的 server 没有交出标签页,所以窗口是空的。什么都没丢,它一响应就会回来。如果一直不回来,在命令面板里执行「重启 server」。" } L10nKey::CmdGroupTabsPanes => "标签页与窗格", L10nKey::CmdGroupWorkspaces => "工作区", @@ -1046,29 +1046,29 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { L10nKey::CmdDocumentation => "文档", L10nKey::CmdJoinDiscord => "加入 Discord", L10nKey::CmdReportIssue => "报告问题…", - L10nKey::CmdRestartServer => "重启服务器…", + L10nKey::CmdRestartServer => "重启 server…", L10nKey::CmdRestartServerSubtitle => "结束所有运行中的 shell;保留布局", L10nKey::CmdQuitTty7 => "退出 tty7", L10nKey::CmdQuitTty7Subtitle => "shell 保持运行", L10nKey::CmdQuickConnect => "连接到 \"{target}\"", L10nKey::CmdQuickConnectSaveProfile => "将 \"{target}\" 保存为主机配置…", L10nKey::CmdRecent => "最近使用", - L10nKey::AppRestartServerTitle => "重启服务器?", + L10nKey::AppRestartServerTitle => "重启 server?", L10nKey::AppRestartServerMismatchDetail => { - "服务器是 v{build},协议 {protocol};此应用使用 {ours}。两者无法对话,标签页取不出来。\n\n退出:什么都不变,服务器和 shell 继续运行。\n重启:标签页带全新 shell 回来,现在跑着的东西会被杀掉。" + "server 是 v{build},协议 {protocol};此应用使用 {ours}。两者无法对话,标签页取不出来。\n\n退出:什么都不变,server 和 shell 继续运行。\n重启:标签页带全新 shell 回来,现在跑着的东西会被杀掉。" } L10nKey::AppRestartServerDialectDetail => { - "服务器是 v{build}:control 方言 v{dialect},而此应用使用 v{ours}。它交不出标签页,所以每个窗口都开成空的。\n\n退出:什么都不变,服务器和 shell 继续运行。\n重启:标签页带全新 shell 回来,现在跑着的东西会被杀掉。" + "server 是 v{build}:control 方言 v{dialect},而此应用使用 v{ours}。它交不出标签页,所以每个窗口都开成空的。\n\n退出:什么都不变,server 和 shell 继续运行。\n重启:标签页带全新 shell 回来,现在跑着的东西会被杀掉。" } L10nKey::AppRestartServerDialectNewerDetail => { - "服务器是 v{build}:control 方言 v{dialect},而此应用使用 v{ours}。它交不出标签页,所以每个窗口都开成空的。\n\n退出并装上更新的构建:真正的解法,shell 全都还在。\n重启:标签页带全新 shell 回来,现在跑着的东西会被杀掉。" + "server 是 v{build}:control 方言 v{dialect},而此应用使用 v{ours}。它交不出标签页,所以每个窗口都开成空的。\n\n退出并装上更新的构建:真正的解法,shell 全都还在。\n重启:标签页带全新 shell 回来,现在跑着的东西会被杀掉。" } L10nKey::AppRestartServerOldDetail => { - "服务器早于版本握手,此应用无从得知它说的是什么。\n\n退出:什么都不变,服务器和 shell 继续运行。\n重启:标签页带全新 shell 回来,现在跑着的东西会被杀掉。" + "server 早于版本握手,此应用无从得知它说的是什么。\n\n退出:什么都不变,server 和 shell 继续运行。\n重启:标签页带全新 shell 回来,现在跑着的东西会被杀掉。" } L10nKey::AppRestart => "重启", - L10nKey::AppRestartServerNotSsh => { - "tty7 只能重启通过 SSH 连接的机器上的服务器。{label} 由本机提供服务——请改为停止其工作区。" + L10nKey::AppRestartServerNoServer => { + "{label} 上没有 tty7 自己的 server 可重启——它是本机通过 --stdio 运行的程序。请改为停止其工作区。" } L10nKey::AppRestartServerBody => { "这会停止本机上所有正在运行的 shell——其中仍在运行的任何内容都会被终止。你的标签页和布局会被保留,并以全新的 shell 重新打开。" @@ -1126,7 +1126,7 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { L10nKey::AppKeybindingDisplacedNote => { "{action} 占用了原属于 {previous} 的快捷键,{previous} 现在没有快捷键了。" } - L10nKey::AppLocalServerName => "本地服务器", + L10nKey::AppLocalServerName => "本地 server", L10nKey::AppSshParseUnbalancedQuotes => "SSH 命令中的引号不匹配", L10nKey::AppSshParseNoRemoteCommands => "此处不支持远程命令", L10nKey::AppSshParseFlagNeedsValue => "-{flag} 需要一个值", @@ -1206,7 +1206,7 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { L10nKey::AppMenuKeyboardShortcuts => "键盘快捷键", L10nKey::AppMenuJoinDiscord => "加入 Discord", L10nKey::AppMenuReportIssue => "报告问题…", - L10nKey::AppMenuRestartServer => "重启服务器…", + L10nKey::AppMenuRestartServer => "重启 server…", L10nKey::WindowUntitled => "未命名", L10nKey::TrayShowTty7 => "显示 tty7", L10nKey::TrayNotifications => "通知", diff --git a/src/ui/switcher.rs b/src/ui/switcher.rs index 8658ae13..3215ee94 100644 --- a/src/ui/switcher.rs +++ b/src/ui/switcher.rs @@ -2242,7 +2242,7 @@ fn group_menu( return menu; }; let connected = group.link == Link::Connected; - let restartable = target.is_ssh(); + let restartable = target.hosts_our_server(); let (label, for_restart) = (group.label.clone(), target.clone()); let menu = menu.separator().item( PopupMenuItem::new(t(L10nKey::SwitcherDisconnect))