From 0e20783f25563dabb6b873371ebd0f24496e1a2a Mon Sep 17 00:00:00 2001 From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com> Date: Mon, 26 Oct 2026 18:42:54 +0000 Subject: [PATCH 1/4] feat(desktop-gpui): show the language picker in release builds and follow the system language --- apps/desktop-gpui/src/i18n/mod.rs | 25 +++--------------------- apps/desktop-gpui/src/i18n/tests.rs | 21 +++----------------- apps/desktop-gpui/src/settings_window.rs | 4 +--- 3 files changed, 7 insertions(+), 43 deletions(-) diff --git a/apps/desktop-gpui/src/i18n/mod.rs b/apps/desktop-gpui/src/i18n/mod.rs index 447afcba3e..85873876e3 100644 --- a/apps/desktop-gpui/src/i18n/mod.rs +++ b/apps/desktop-gpui/src/i18n/mod.rs @@ -12,10 +12,6 @@ pub const SETTING_KEY: &str = "uiLanguage"; /// `CAP_GPUI_LOCALE=ja` (or `pseudo` in a debug build). pub const LOCALE_ENV: &str = "CAP_GPUI_LOCALE"; const SYSTEM_SETTING: &str = "system"; -/// Shows the language picker in a release build. Release builds hide it until -/// the main surfaces are translated; `uiLanguage` and [`LOCALE_ENV`] work -/// either way. -pub const PICKER_ENV: &str = "CAP_GPUI_LANGUAGE_PICKER"; static CURRENT: AtomicU8 = AtomicU8::new(Locale::En as u8); @@ -57,14 +53,6 @@ impl Preference { } } -/// Read once: the General page asks on every render. -pub fn picker_enabled() -> bool { - static ENABLED: OnceLock = OnceLock::new(); - *ENABLED.get_or_init(|| { - cfg!(debug_assertions) || std::env::var(PICKER_ENV).is_ok_and(|value| value == "1") - }) -} - /// Before the first lookup: `main` calls this ahead of the theme, menus and /// tray. pub fn init(setting: Option<&str>) { @@ -79,20 +67,13 @@ pub fn resolve(preference: Preference) -> Locale { { return locale; } - resolve_preference(preference, picker_enabled(), system) + resolve_preference(preference, system) } -/// Without the picker a release build stays English on a non-English system, -/// so nobody gets a half-translated app they can't switch back from. -fn resolve_preference( - preference: Preference, - picker_enabled: bool, - system: impl FnOnce() -> Locale, -) -> Locale { +fn resolve_preference(preference: Preference, system: impl FnOnce() -> Locale) -> Locale { match preference { Preference::Fixed(locale) => locale, - Preference::System if picker_enabled => system(), - Preference::System => Locale::En, + Preference::System => system(), } } diff --git a/apps/desktop-gpui/src/i18n/tests.rs b/apps/desktop-gpui/src/i18n/tests.rs index 859c5314a6..24e98f959c 100644 --- a/apps/desktop-gpui/src/i18n/tests.rs +++ b/apps/desktop-gpui/src/i18n/tests.rs @@ -573,13 +573,6 @@ fn language_picker_english() { ); } -#[test] -fn the_picker_shows_in_debug_builds() { - if cfg!(debug_assertions) { - assert!(picker_enabled()); - } -} - #[test] fn system_tags_resolve_to_catalogs() { for (tag, expected) in [ @@ -619,21 +612,13 @@ fn traditional_chinese_falls_through_to_the_next_language() { } #[test] -fn system_language_waits_for_the_picker() { +fn system_default_follows_the_system_language_and_a_choice_overrides_it() { assert_eq!( - resolve_preference(Preference::System, false, || Locale::Ja), - Locale::En - ); - assert_eq!( - resolve_preference(Preference::System, true, || Locale::Ja), + resolve_preference(Preference::System, || Locale::Ja), Locale::Ja ); assert_eq!( - resolve_preference(Preference::Fixed(Locale::De), false, || Locale::Ja), - Locale::De - ); - assert_eq!( - resolve_preference(Preference::Fixed(Locale::De), true, || Locale::Ja), + resolve_preference(Preference::Fixed(Locale::De), || Locale::Ja), Locale::De ); } diff --git a/apps/desktop-gpui/src/settings_window.rs b/apps/desktop-gpui/src/settings_window.rs index 74ef683bf0..4dbf67548b 100644 --- a/apps/desktop-gpui/src/settings_window.rs +++ b/apps/desktop-gpui/src/settings_window.rs @@ -3214,9 +3214,7 @@ impl SettingsWindow { fn render_general(&self, window: &Window, cx: &mut Context) -> Vec { let mut sections = Vec::with_capacity(10); sections.push(self.render_appearance(cx).into_any_element()); - if i18n::picker_enabled() { - sections.push(self.render_language(cx).into_any_element()); - } + sections.push(self.render_language(cx).into_any_element()); sections.extend([ self.render_app_section(cx).into_any_element(), self.render_recording(cx).into_any_element(), From 3b6ced64fce6feebd9283ef785d3446b216ba79a Mon Sep 17 00:00:00 2001 From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com> Date: Mon, 26 Oct 2026 18:42:54 +0000 Subject: [PATCH 2/4] feat(desktop-gpui): declare the app's languages to macOS and localize the permission prompts --- apps/desktop-gpui/Info.plist | 12 ++++++++++++ apps/desktop-gpui/macos/de.lproj/InfoPlist.strings | 3 +++ apps/desktop-gpui/macos/en.lproj/InfoPlist.strings | 3 +++ apps/desktop-gpui/macos/es.lproj/InfoPlist.strings | 3 +++ apps/desktop-gpui/macos/fr.lproj/InfoPlist.strings | 3 +++ apps/desktop-gpui/macos/hi.lproj/InfoPlist.strings | 3 +++ apps/desktop-gpui/macos/ja.lproj/InfoPlist.strings | 3 +++ .../macos/zh-Hans.lproj/InfoPlist.strings | 3 +++ apps/desktop-gpui/tauri.conf.json | 9 +++++++++ 9 files changed, 42 insertions(+) create mode 100644 apps/desktop-gpui/macos/de.lproj/InfoPlist.strings create mode 100644 apps/desktop-gpui/macos/en.lproj/InfoPlist.strings create mode 100644 apps/desktop-gpui/macos/es.lproj/InfoPlist.strings create mode 100644 apps/desktop-gpui/macos/fr.lproj/InfoPlist.strings create mode 100644 apps/desktop-gpui/macos/hi.lproj/InfoPlist.strings create mode 100644 apps/desktop-gpui/macos/ja.lproj/InfoPlist.strings create mode 100644 apps/desktop-gpui/macos/zh-Hans.lproj/InfoPlist.strings diff --git a/apps/desktop-gpui/Info.plist b/apps/desktop-gpui/Info.plist index dd740dcf95..4939978f03 100644 --- a/apps/desktop-gpui/Info.plist +++ b/apps/desktop-gpui/Info.plist @@ -2,6 +2,18 @@ + CFBundleDevelopmentRegion + en + CFBundleLocalizations + + en + zh-Hans + ja + fr + es + de + hi + CFBundleDocumentTypes diff --git a/apps/desktop-gpui/macos/de.lproj/InfoPlist.strings b/apps/desktop-gpui/macos/de.lproj/InfoPlist.strings new file mode 100644 index 0000000000..46f3484091 --- /dev/null +++ b/apps/desktop-gpui/macos/de.lproj/InfoPlist.strings @@ -0,0 +1,3 @@ +NSCameraUsageDescription = "Cap verwendet Ihre Kamera, um Sie in Ihren Caps aufzunehmen."; +NSMicrophoneUsageDescription = "Cap verwendet Ihr Mikrofon, um Sie in Ihren Caps aufzunehmen."; +NSScreenCaptureUsageDescription = "Cap nimmt Ihren Bildschirm als Teil Ihrer Caps auf."; diff --git a/apps/desktop-gpui/macos/en.lproj/InfoPlist.strings b/apps/desktop-gpui/macos/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..a5d10d3e48 --- /dev/null +++ b/apps/desktop-gpui/macos/en.lproj/InfoPlist.strings @@ -0,0 +1,3 @@ +NSCameraUsageDescription = "Cap uses your camera to record you in your Caps"; +NSMicrophoneUsageDescription = "Cap uses your microphone to record you in your Caps"; +NSScreenCaptureUsageDescription = "Cap captures your screen as part of recording your Caps"; diff --git a/apps/desktop-gpui/macos/es.lproj/InfoPlist.strings b/apps/desktop-gpui/macos/es.lproj/InfoPlist.strings new file mode 100644 index 0000000000..809129ea53 --- /dev/null +++ b/apps/desktop-gpui/macos/es.lproj/InfoPlist.strings @@ -0,0 +1,3 @@ +NSCameraUsageDescription = "Cap usa tu cámara para grabarte en tus Caps."; +NSMicrophoneUsageDescription = "Cap usa tu micrófono para grabarte en tus Caps."; +NSScreenCaptureUsageDescription = "Cap graba tu pantalla para crear tus Caps."; diff --git a/apps/desktop-gpui/macos/fr.lproj/InfoPlist.strings b/apps/desktop-gpui/macos/fr.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9d9dedbf32 --- /dev/null +++ b/apps/desktop-gpui/macos/fr.lproj/InfoPlist.strings @@ -0,0 +1,3 @@ +NSCameraUsageDescription = "Cap utilise votre caméra pour vous enregistrer dans vos Caps."; +NSMicrophoneUsageDescription = "Cap utilise votre micro pour vous enregistrer dans vos Caps."; +NSScreenCaptureUsageDescription = "Cap enregistre votre écran pour créer vos Caps."; diff --git a/apps/desktop-gpui/macos/hi.lproj/InfoPlist.strings b/apps/desktop-gpui/macos/hi.lproj/InfoPlist.strings new file mode 100644 index 0000000000..0f5383a83b --- /dev/null +++ b/apps/desktop-gpui/macos/hi.lproj/InfoPlist.strings @@ -0,0 +1,3 @@ +NSCameraUsageDescription = "Cap आपकी Caps में आपको रिकॉर्ड करने के लिए आपके कैमरा का उपयोग करता है।"; +NSMicrophoneUsageDescription = "Cap आपकी Caps में आपकी आवाज़ रिकॉर्ड करने के लिए आपके माइक्रोफ़ोन का उपयोग करता है।"; +NSScreenCaptureUsageDescription = "Cap आपकी Caps बनाने के लिए आपकी स्क्रीन रिकॉर्ड करता है।"; diff --git a/apps/desktop-gpui/macos/ja.lproj/InfoPlist.strings b/apps/desktop-gpui/macos/ja.lproj/InfoPlist.strings new file mode 100644 index 0000000000..52c7f6ded9 --- /dev/null +++ b/apps/desktop-gpui/macos/ja.lproj/InfoPlist.strings @@ -0,0 +1,3 @@ +NSCameraUsageDescription = "CapはCapsであなたを録画するためにカメラを使用します。"; +NSMicrophoneUsageDescription = "CapはCapsであなたの声を録音するためにマイクを使用します。"; +NSScreenCaptureUsageDescription = "CapはCapsを作成するために画面を録画します。"; diff --git a/apps/desktop-gpui/macos/zh-Hans.lproj/InfoPlist.strings b/apps/desktop-gpui/macos/zh-Hans.lproj/InfoPlist.strings new file mode 100644 index 0000000000..aa00352024 --- /dev/null +++ b/apps/desktop-gpui/macos/zh-Hans.lproj/InfoPlist.strings @@ -0,0 +1,3 @@ +NSCameraUsageDescription = "Cap 使用你的摄像头,在 Caps 中录制你。"; +NSMicrophoneUsageDescription = "Cap 使用你的麦克风,在 Caps 中录制你的声音。"; +NSScreenCaptureUsageDescription = "Cap 录制你的屏幕来制作 Caps。"; diff --git a/apps/desktop-gpui/tauri.conf.json b/apps/desktop-gpui/tauri.conf.json index 314e0f27dd..bc6696d705 100644 --- a/apps/desktop-gpui/tauri.conf.json +++ b/apps/desktop-gpui/tauri.conf.json @@ -54,6 +54,15 @@ "minimumSystemVersion": "12.3", "entitlements": "../desktop/src-tauri/Entitlements.plist", "frameworks": ["../../target/native-deps/Spacedrive.framework"], + "files": { + "Resources/en.lproj/InfoPlist.strings": "macos/en.lproj/InfoPlist.strings", + "Resources/zh-Hans.lproj/InfoPlist.strings": "macos/zh-Hans.lproj/InfoPlist.strings", + "Resources/ja.lproj/InfoPlist.strings": "macos/ja.lproj/InfoPlist.strings", + "Resources/fr.lproj/InfoPlist.strings": "macos/fr.lproj/InfoPlist.strings", + "Resources/es.lproj/InfoPlist.strings": "macos/es.lproj/InfoPlist.strings", + "Resources/de.lproj/InfoPlist.strings": "macos/de.lproj/InfoPlist.strings", + "Resources/hi.lproj/InfoPlist.strings": "macos/hi.lproj/InfoPlist.strings" + }, "dmg": { "background": "../desktop/src-tauri/assets/dmg-background.png", "appPosition": { From 5f0c0be03438dbbfa0e0ed6e4c7ef57ddc71f5c3 Mon Sep 17 00:00:00 2001 From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com> Date: Mon, 26 Oct 2026 20:10:44 +0000 Subject: [PATCH 3/4] feat(desktop-gpui): localize the update, quit and Open Recording dialogs --- apps/desktop-gpui/locales/de.toml | 26 ++++++ apps/desktop-gpui/locales/en.toml | 26 ++++++ apps/desktop-gpui/locales/es.toml | 26 ++++++ apps/desktop-gpui/locales/fr.toml | 26 ++++++ apps/desktop-gpui/locales/hi.toml | 26 ++++++ apps/desktop-gpui/locales/ja.toml | 26 ++++++ apps/desktop-gpui/locales/zh-CN.toml | 26 ++++++ .../src/i18n/dialogs_surface_tests.rs | 83 +++++++++++++++++++ apps/desktop-gpui/src/i18n/tests.rs | 1 + apps/desktop-gpui/src/installer.rs | 2 +- apps/desktop-gpui/src/menus.rs | 16 ++-- apps/desktop-gpui/src/updates.rs | 55 ++++++------ 12 files changed, 300 insertions(+), 39 deletions(-) diff --git a/apps/desktop-gpui/locales/de.toml b/apps/desktop-gpui/locales/de.toml index 4d0557cdb9..043a1f543b 100644 --- a/apps/desktop-gpui/locales/de.toml +++ b/apps/desktop-gpui/locales/de.toml @@ -30,6 +30,7 @@ website = "Website" commit = "Commit" [menu.file] +open_prompt = "Aufnahme öffnen" close_window = "Fenster schließen" title = "Ablage" open_file = "Datei öffnen…" @@ -2875,3 +2876,28 @@ three_d = { one = "{count} 3D-Segment ausgewählt", other = "{count} 3D-Segmente zoom = { one = "{count} ausgewählt", other = "{count} ausgewählt" } zoom_all = { one = "Alle {count} ausgewählt", other = "Alle {count} ausgewählt" } zoom_some = { one = "{count} von {total} ausgewählt", other = "{count} von {total} ausgewählt" } + +[dialog.quit] +canceled = "Beenden abgebrochen: {error}. Cap bleibt geöffnet, damit Sie die Aufnahme abschließen oder wiederherstellen können." +pending = "Beenden abgebrochen, weil die Aufnahme noch nicht beendet ist. Cap bleibt geöffnet. Verwenden Sie „Stoppen“, sobald es verfügbar ist, und beenden Sie Cap dann erneut." +still_open = "Cap ist noch geöffnet" +unconfirmed = "Beenden abgebrochen, weil nicht bestätigt werden konnte, dass die Aufnahme beendet wurde. Cap bleibt geöffnet. Verwenden Sie „Stoppen“ und beenden Sie Cap dann erneut." + +[dialog.update] +already_getting = "Cap bereitet Version {version} bereits vor." +available = "Version {version} von Cap ist verfügbar. Cap lädt sie im Hintergrund herunter und informiert Sie, sobald sie installiert werden kann." +busy = "Schließen Sie Ihre Aufnahme, Ihren Export, Upload, Import oder Ihre Transkription ab, bevor Sie Cap aktualisieren." +busy_title = "Cap ist beschäftigt" +check_failed = "Nach Updates konnte nicht gesucht werden: {error}" +download = "Laden" +download_failed = "Version {version} konnte nicht geladen werden: {error}" +failed = "{message}\n\nSie können die neueste Version jederzeit unter Cap.so/download laden." +install_failed = "Version {version} konnte nicht installiert werden: {error}" +later = "Später" +no_update = "Sie verwenden bereits die neueste Version von Cap." +no_update_title = "Kein Update verfügbar" +not_now = "Nicht jetzt" +package_not_installed = "Das Paket wurde nicht installiert. Bestätigen Sie die Systemabfrage oder installieren Sie es über Cap.so/download." +ready = "Cap {version} ist bereit. Starten Sie Cap neu, um das Update abzuschließen." +restart_now = "Jetzt neu starten" +title = "Cap aktualisieren" diff --git a/apps/desktop-gpui/locales/en.toml b/apps/desktop-gpui/locales/en.toml index 0297a8fcaf..f738c45262 100644 --- a/apps/desktop-gpui/locales/en.toml +++ b/apps/desktop-gpui/locales/en.toml @@ -30,6 +30,7 @@ website = "Website" commit = "Commit" [menu.file] +open_prompt = "Open Recording" close_window = "Close Window" title = "File" open_file = "Open File…" @@ -2877,3 +2878,28 @@ three_d = { one = "{count} 3D segment selected", other = "{count} 3D segments se zoom = { one = "{count} selected", other = "{count} selected", context = "Zoom segments selected when the track has only one." } zoom_all = { one = "All {count} selected", other = "All {count} selected", context = "Every zoom segment on the track is selected; only shown for two or more." } zoom_some = { one = "{count} of {total} selected", other = "{count} of {total} selected", context = "Some of the zoom segments on the track are selected." } + +[dialog.quit] +canceled = { text = "Quit canceled: {error}. Cap will stay open so you can finish or recover the recording.", context = "{error} explains why the recording could not stop." } +pending = "Quit canceled because recording shutdown is still pending. Cap will stay open. Use Stop once available, then try Quit again." +still_open = "Cap is still open" +unconfirmed = "Quit canceled because recording shutdown could not be confirmed. Cap will stay open. Use Stop, then try Quit again." + +[dialog.update] +already_getting = { text = "Cap is already getting version {version} ready.", context = "{version} is a version number such as 0.6.2." } +available = { text = "Version {version} of Cap is available. Cap will download it in the background and let you know when it's ready to install.", context = "{version} is a version number such as 0.6.2." } +busy = "Finish your recording, export, upload, import, or transcription task before updating Cap." +busy_title = "Cap is busy" +check_failed = { text = "Couldn't check for updates: {error}", context = "{error} is a technical reason, usually in English." } +download = { text = "Download", context = "Button: download the update now." } +download_failed = { text = "Couldn't download version {version}: {error}", context = "{error} is a technical reason, usually in English." } +failed = { text = "{message}\n\nYou can always download the latest version from Cap.so/download.", context = "{message} is one of the other update error sentences." } +install_failed = { text = "Couldn't install version {version}: {error}", context = "{error} is a technical reason, usually in English." } +later = "Later" +no_update = "You're already using the latest version of Cap." +no_update_title = "No Update Available" +not_now = "Not Now" +package_not_installed = "The package wasn't installed. Approve the system prompt, or install it from Cap.so/download." +ready = { text = "Cap {version} is ready. Restart Cap to finish updating.", context = "{version} is a version number such as 0.6.2." } +restart_now = "Restart Now" +title = { text = "Update Cap", context = "Title of every update dialog." } diff --git a/apps/desktop-gpui/locales/es.toml b/apps/desktop-gpui/locales/es.toml index ff6fa45abd..fb7a28a680 100644 --- a/apps/desktop-gpui/locales/es.toml +++ b/apps/desktop-gpui/locales/es.toml @@ -30,6 +30,7 @@ website = "Sitio web" commit = "Commit" [menu.file] +open_prompt = "Abrir grabación" close_window = "Cerrar ventana" title = "Archivo" open_file = "Abrir archivo…" @@ -2875,3 +2876,28 @@ three_d = { one = "{count} segmento 3D seleccionado", other = "{count} segmentos zoom = { one = "{count} seleccionado", other = "{count} seleccionados" } zoom_all = { one = "{count} seleccionado", other = "Los {count} seleccionados" } zoom_some = { one = "{count} de {total} seleccionado", other = "{count} de {total} seleccionados" } + +[dialog.quit] +canceled = "Se canceló la salida: {error}. Cap seguirá abierto para que puedas terminar o recuperar la grabación." +pending = "Se canceló la salida porque la grabación todavía se está deteniendo. Cap seguirá abierto. Usa Detener cuando esté disponible y vuelve a intentar salir." +still_open = "Cap sigue abierto" +unconfirmed = "Se canceló la salida porque no se pudo confirmar que la grabación se detuviera. Cap seguirá abierto. Usa Detener y vuelve a intentar salir." + +[dialog.update] +already_getting = "Cap ya está preparando la versión {version}." +available = "La versión {version} de Cap está disponible. Cap la descargará en segundo plano y te avisará cuando esté lista para instalarse." +busy = "Termina tu grabación, exportación, subida, importación o transcripción antes de actualizar Cap." +busy_title = "Cap está ocupado" +check_failed = "No se pudieron buscar actualizaciones: {error}" +download = "Descargar" +download_failed = "No se pudo descargar la versión {version}: {error}" +failed = "{message}\n\nSiempre puedes descargar la última versión desde Cap.so/download." +install_failed = "No se pudo instalar la versión {version}: {error}" +later = "Más tarde" +no_update = "Ya estás usando la última versión de Cap." +no_update_title = "No hay actualizaciones disponibles" +not_now = "Ahora no" +package_not_installed = "No se instaló el paquete. Acepta el aviso del sistema o instálalo desde Cap.so/download." +ready = "Cap {version} está listo. Reinicia Cap para terminar de actualizar." +restart_now = "Reiniciar ahora" +title = "Actualizar Cap" diff --git a/apps/desktop-gpui/locales/fr.toml b/apps/desktop-gpui/locales/fr.toml index 032b8eb34c..97b827f410 100644 --- a/apps/desktop-gpui/locales/fr.toml +++ b/apps/desktop-gpui/locales/fr.toml @@ -30,6 +30,7 @@ website = "Site web" commit = "Commit" [menu.file] +open_prompt = "Ouvrir l’enregistrement" close_window = "Fermer la fenêtre" title = "Fichier" open_file = "Ouvrir un fichier…" @@ -2875,3 +2876,28 @@ three_d = { one = "{count} segment 3D sélectionné", other = "{count} segments zoom = { one = "{count} sélectionné", other = "{count} sélectionnés" } zoom_all = { one = "{count} sélectionné", other = "Les {count} sélectionnés" } zoom_some = { one = "{count} sur {total} sélectionné", other = "{count} sur {total} sélectionnés" } + +[dialog.quit] +canceled = "Fermeture annulée : {error}. Cap reste ouvert pour que vous puissiez terminer ou récupérer l’enregistrement." +pending = "Fermeture annulée, car l’arrêt de l’enregistrement est toujours en cours. Cap reste ouvert. Utilisez Arrêter dès que possible, puis quittez à nouveau." +still_open = "Cap est toujours ouvert" +unconfirmed = "Fermeture annulée, car l’arrêt de l’enregistrement n’a pas pu être confirmé. Cap reste ouvert. Utilisez Arrêter, puis quittez à nouveau." + +[dialog.update] +already_getting = "Cap prépare déjà la version {version}." +available = "La version {version} de Cap est disponible. Cap la télécharge en arrière-plan et vous prévient quand elle est prête à être installée." +busy = "Terminez votre enregistrement, exportation, mise en ligne, importation ou transcription avant de mettre à jour Cap." +busy_title = "Cap est occupé" +check_failed = "Impossible de rechercher des mises à jour : {error}" +download = "Télécharger" +download_failed = "Impossible de télécharger la version {version} : {error}" +failed = "{message}\n\nVous pouvez toujours télécharger la dernière version sur Cap.so/download." +install_failed = "Impossible d’installer la version {version} : {error}" +later = "Plus tard" +no_update = "Vous utilisez déjà la dernière version de Cap." +no_update_title = "Aucune mise à jour disponible" +not_now = "Plus tard" +package_not_installed = "Le paquet n’a pas été installé. Acceptez la demande du système, ou installez-le depuis Cap.so/download." +ready = "Cap {version} est prêt. Redémarrez Cap pour terminer la mise à jour." +restart_now = "Redémarrer maintenant" +title = "Mettre à jour Cap" diff --git a/apps/desktop-gpui/locales/hi.toml b/apps/desktop-gpui/locales/hi.toml index f2bd96a33c..4eb91d5bc2 100644 --- a/apps/desktop-gpui/locales/hi.toml +++ b/apps/desktop-gpui/locales/hi.toml @@ -30,6 +30,7 @@ website = "वेबसाइट" commit = "कमिट" [menu.file] +open_prompt = "रिकॉर्डिंग खोलें" close_window = "विंडो बंद करें" title = "फ़ाइल" open_file = "फ़ाइल खोलें…" @@ -2875,3 +2876,28 @@ three_d = { one = "{count} 3D सेगमेंट चुना गया", oth zoom = { one = "{count} चुना गया", other = "{count} चुने गए" } zoom_all = { one = "{count} चुना गया", other = "सभी {count} चुने गए" } zoom_some = { one = "{total} में से {count} चुना गया", other = "{total} में से {count} चुने गए" } + +[dialog.quit] +canceled = "बाहर निकलना रद्द किया गया: {error}। Cap खुला रहेगा ताकि आप रिकॉर्डिंग पूरी या रिकवर कर सकें।" +pending = "रिकॉर्डिंग अभी बंद हो रही है, इसलिए बाहर निकलना रद्द किया गया। Cap खुला रहेगा। उपलब्ध होते ही रोकें का इस्तेमाल करें, फिर दोबारा बाहर निकलने की कोशिश करें।" +still_open = "Cap अभी भी खुला है" +unconfirmed = "रिकॉर्डिंग बंद होने की पुष्टि नहीं हो सकी, इसलिए बाहर निकलना रद्द किया गया। Cap खुला रहेगा। रोकें का इस्तेमाल करें, फिर दोबारा बाहर निकलने की कोशिश करें।" + +[dialog.update] +already_getting = "Cap पहले से ही वर्ज़न {version} तैयार कर रहा है।" +available = "Cap का वर्ज़न {version} उपलब्ध है। Cap इसे बैकग्राउंड में डाउनलोड करेगा और इंस्टॉल के लिए तैयार होने पर आपको बताएगा।" +busy = "Cap अपडेट करने से पहले अपनी रिकॉर्डिंग, एक्सपोर्ट, अपलोड, इंपोर्ट या ट्रांसक्रिप्शन पूरा करें।" +busy_title = "Cap व्यस्त है" +check_failed = "अपडेट नहीं देखे जा सके: {error}" +download = "डाउनलोड करें" +download_failed = "वर्ज़न {version} डाउनलोड नहीं हो सका: {error}" +failed = "{message}\n\nआप नया वर्ज़न कभी भी Cap.so/download से डाउनलोड कर सकते हैं।" +install_failed = "वर्ज़न {version} इंस्टॉल नहीं हो सका: {error}" +later = "बाद में" +no_update = "आप पहले से ही Cap का नया वर्ज़न इस्तेमाल कर रहे हैं।" +no_update_title = "कोई अपडेट उपलब्ध नहीं है" +not_now = "अभी नहीं" +package_not_installed = "पैकेज इंस्टॉल नहीं हुआ। सिस्टम प्रॉम्प्ट को मंज़ूरी दें, या इसे Cap.so/download से इंस्टॉल करें।" +ready = "Cap {version} तैयार है। अपडेट पूरा करने के लिए Cap रीस्टार्ट करें।" +restart_now = "अभी रीस्टार्ट करें" +title = "Cap अपडेट करें" diff --git a/apps/desktop-gpui/locales/ja.toml b/apps/desktop-gpui/locales/ja.toml index f02af39458..9f827a4d94 100644 --- a/apps/desktop-gpui/locales/ja.toml +++ b/apps/desktop-gpui/locales/ja.toml @@ -30,6 +30,7 @@ website = "ウェブサイト" commit = "コミット" [menu.file] +open_prompt = "録画を開く" close_window = "ウィンドウを閉じる" title = "ファイル" open_file = "ファイルを開く…" @@ -2875,3 +2876,28 @@ three_d = { other = "{count}個の3Dセグメントを選択中" } zoom = { other = "{count}個を選択中" } zoom_all = { other = "{count}個すべてを選択中" } zoom_some = { other = "{total}個中{count}個を選択中" } + +[dialog.quit] +canceled = "終了をキャンセルしました:{error}。録画を完了または復元できるよう、Capは開いたままになります。" +pending = "録画の停止処理がまだ終わっていないため、終了をキャンセルしました。Capは開いたままになります。「停止」が使えるようになったら停止してから、もう一度終了してください。" +still_open = "Capはまだ開いています" +unconfirmed = "録画の停止を確認できなかったため、終了をキャンセルしました。Capは開いたままになります。「停止」を使ってから、もう一度終了してください。" + +[dialog.update] +already_getting = "Capはすでにバージョン{version}を準備しています。" +available = "Capのバージョン{version}が利用可能です。バックグラウンドでダウンロードし、インストールの準備ができたらお知らせします。" +busy = "Capをアップデートする前に、録画、書き出し、アップロード、読み込み、または文字起こしを完了してください。" +busy_title = "Capは処理中です" +check_failed = "アップデートを確認できませんでした:{error}" +download = "ダウンロード" +download_failed = "バージョン{version}をダウンロードできませんでした:{error}" +failed = "{message}\n\n最新バージョンはいつでもCap.so/downloadからダウンロードできます。" +install_failed = "バージョン{version}をインストールできませんでした:{error}" +later = "後で" +no_update = "Capはすでに最新バージョンです。" +no_update_title = "アップデートはありません" +not_now = "今はしない" +package_not_installed = "パッケージはインストールされませんでした。システムの確認を承認するか、Cap.so/downloadからインストールしてください。" +ready = "Cap {version}の準備ができました。アップデートを完了するにはCapを再起動してください。" +restart_now = "今すぐ再起動" +title = "Capをアップデート" diff --git a/apps/desktop-gpui/locales/zh-CN.toml b/apps/desktop-gpui/locales/zh-CN.toml index 26ea525df4..a9473f3965 100644 --- a/apps/desktop-gpui/locales/zh-CN.toml +++ b/apps/desktop-gpui/locales/zh-CN.toml @@ -30,6 +30,7 @@ website = "网站" commit = "提交" [menu.file] +open_prompt = "打开录制" close_window = "关闭窗口" title = "文件" open_file = "打开文件…" @@ -2875,3 +2876,28 @@ three_d = { other = "已选择 {count} 个 3D 分段" } zoom = { other = "已选择 {count} 个" } zoom_all = { other = "已选择全部 {count} 个" } zoom_some = { other = "已选择 {count}/{total} 个" } + +[dialog.quit] +canceled = "已取消退出:{error}。Cap 将保持打开,以便你完成或恢复录制。" +pending = "录制仍在停止中,已取消退出。Cap 将保持打开。请在可用时点按“停止”,然后再次尝试退出。" +still_open = "Cap 仍在运行" +unconfirmed = "无法确认录制已停止,已取消退出。Cap 将保持打开。请点按“停止”,然后再次尝试退出。" + +[dialog.update] +already_getting = "Cap 已在准备版本 {version}。" +available = "Cap {version} 版本现已推出。Cap 会在后台下载,并在可以安装时通知你。" +busy = "请先完成录制、导出、上传、导入或转写任务,再更新 Cap。" +busy_title = "Cap 正忙" +check_failed = "无法检查更新:{error}" +download = "下载" +download_failed = "无法下载版本 {version}:{error}" +failed = "{message}\n\n你随时可以从 Cap.so/download 下载最新版本。" +install_failed = "无法安装版本 {version}:{error}" +later = "稍后" +no_update = "你使用的已是最新版 Cap。" +no_update_title = "没有可用的更新" +not_now = "以后再说" +package_not_installed = "软件包未安装。请批准系统提示,或从 Cap.so/download 安装。" +ready = "Cap {version} 已准备就绪。重新启动 Cap 以完成更新。" +restart_now = "立即重新启动" +title = "更新 Cap" diff --git a/apps/desktop-gpui/src/i18n/dialogs_surface_tests.rs b/apps/desktop-gpui/src/i18n/dialogs_surface_tests.rs index 1cd744f95c..b7a04b038a 100644 --- a/apps/desktop-gpui/src/i18n/dialogs_surface_tests.rs +++ b/apps/desktop-gpui/src/i18n/dialogs_surface_tests.rs @@ -54,6 +54,14 @@ const FORMATTED: &[&str] = &[ "transcription.download.failed", "transcription.download.file_progress", "transcription.download.progress", + "dialog.quit.canceled", + "dialog.update.already_getting", + "dialog.update.available", + "dialog.update.check_failed", + "dialog.update.download_failed", + "dialog.update.failed", + "dialog.update.install_failed", + "dialog.update.ready", ]; #[test] @@ -732,6 +740,34 @@ fn dialog_surfaces_match_the_literals_they_replaced() { tr::transcription::error::unsupported_language(), "The Recommended and High Accuracy models only transcribe English and 24 other European languages. Download the Small or Medium model to caption this language", ), + (tr::dialog::quit::still_open(), "Cap is still open"), + ( + tr::dialog::quit::pending(), + "Quit canceled because recording shutdown is still pending. Cap will stay open. Use Stop once available, then try Quit again.", + ), + ( + tr::dialog::quit::unconfirmed(), + "Quit canceled because recording shutdown could not be confirmed. Cap will stay open. Use Stop, then try Quit again.", + ), + (tr::dialog::update::title(), "Update Cap"), + (tr::dialog::update::busy_title(), "Cap is busy"), + ( + tr::dialog::update::busy(), + "Finish your recording, export, upload, import, or transcription task before updating Cap.", + ), + (tr::dialog::update::no_update_title(), "No Update Available"), + ( + tr::dialog::update::no_update(), + "You're already using the latest version of Cap.", + ), + (tr::dialog::update::download(), "Download"), + (tr::dialog::update::not_now(), "Not Now"), + (tr::dialog::update::restart_now(), "Restart Now"), + (tr::dialog::update::later(), "Later"), + ( + tr::dialog::update::package_not_installed(), + "The package wasn't installed. Approve the system prompt, or install it from Cap.so/download.", + ), ]; for (translated, literal) in pairs { assert_eq!(translated, literal); @@ -994,3 +1030,50 @@ fn dialog_messages_format_like_the_code_they_replaced() { ); } } + +#[test] +fn update_and_quit_messages_format_like_the_code_they_replaced() { + let error = "connection reset by peer"; + let version = semver::Version::new(0, 6, 2); + let pairs: Vec<(String, String)> = vec![ + ( + tr::dialog::quit::canceled(error), + format!( + "Quit canceled: {error}. Cap will stay open so you can finish or recover the recording." + ), + ), + ( + tr::dialog::update::already_getting(&version), + format!("Cap is already getting version {version} ready."), + ), + ( + tr::dialog::update::available(&version), + format!( + "Version {version} of Cap is available. Cap will download it in the background and let you know when it's ready to install." + ), + ), + ( + tr::dialog::update::check_failed(error), + format!("Couldn't check for updates: {error}"), + ), + ( + tr::dialog::update::download_failed(&version, error), + format!("Couldn't download version {version}: {error}"), + ), + ( + tr::dialog::update::install_failed(&version, error), + format!("Couldn't install version {version}: {error}"), + ), + ( + tr::dialog::update::ready(&version), + format!("Cap {version} is ready. Restart Cap to finish updating."), + ), + ( + tr::dialog::update::failed(error), + format!("{error}\n\nYou can always download the latest version from Cap.so/download."), + ), + ]; + for (translated, literal) in pairs { + assert_eq!(translated, literal); + } +} diff --git a/apps/desktop-gpui/src/i18n/tests.rs b/apps/desktop-gpui/src/i18n/tests.rs index 24e98f959c..db36127d61 100644 --- a/apps/desktop-gpui/src/i18n/tests.rs +++ b/apps/desktop-gpui/src/i18n/tests.rs @@ -25,6 +25,7 @@ fn english_matches_the_literals_it_replaced() { (tr::menu::app::settings(), "Settings…".into()), (tr::menu::file::close_window(), "Close Window".into()), (tr::menu::file::open_file(), "Open File…".into()), + (tr::menu::file::open_prompt(), "Open Recording".into()), (tr::menu::file::open_recent(), "Open Recent".into()), (tr::menu::file::import_media(), "Import Media…".into()), (tr::menu::file::title(), "File".into()), diff --git a/apps/desktop-gpui/src/installer.rs b/apps/desktop-gpui/src/installer.rs index 5f3eb3ec89..413d6f528b 100644 --- a/apps/desktop-gpui/src/installer.rs +++ b/apps/desktop-gpui/src/installer.rs @@ -576,7 +576,7 @@ pub(crate) fn install_deb(package: &Path) -> Result<(), String> { if status.success() { Ok(()) } else { - Err("The package wasn't installed. Approve the system prompt, or install it from cap.so/download.".to_string()) + Err(crate::i18n::tr::dialog::update::package_not_installed().to_string()) } } diff --git a/apps/desktop-gpui/src/menus.rs b/apps/desktop-gpui/src/menus.rs index 675c96b63c..3713467028 100644 --- a/apps/desktop-gpui/src/menus.rs +++ b/apps/desktop-gpui/src/menus.rs @@ -190,7 +190,7 @@ pub fn init(cx: &mut App) { files, directories, multiple: false, - prompt: Some("Open Recording".into()), + prompt: Some(tr::menu::file::open_prompt().into()), }); cx.spawn(async move |cx| { if let Ok(Ok(Some(paths))) = paths.await @@ -761,7 +761,7 @@ fn apply_quit_action(action: QuitAction, cx: &mut App) { cx.global_mut::().gate.cancel_exit(); tracing::error!(%error, "quit canceled because editor changes could not be saved"); cx.spawn(async move |_| { - platform::alert_dialog("Cap is still open", &error); + platform::alert_dialog(tr::dialog::quit::still_open(), &error); }) .detach(); return; @@ -775,17 +775,19 @@ fn apply_quit_action(action: QuitAction, cx: &mut App) { cx.global_mut::().observer = None; let message = RecordingSession::global(cx).update(cx, |session, cx| { let message = match session.error.as_deref() { - Some(error) => format!("Quit canceled: {error}. Cap will stay open so you can finish or recover the recording."), - None if action == QuitAction::TimedOut => "Quit canceled because recording shutdown is still pending. Cap will stay open. Use Stop once available, then try Quit again.".into(), - None => "Quit canceled because recording shutdown could not be confirmed. Cap will stay open. Use Stop, then try Quit again.".into(), + Some(error) => tr::dialog::quit::canceled(error), + None if action == QuitAction::TimedOut => tr::dialog::quit::pending().into(), + None => tr::dialog::quit::unconfirmed().into(), }; session.error = Some(message.clone()); cx.notify(); message }); tracing::warn!(%message); - cx.spawn(async move |_| platform::alert_dialog("Cap is still open", &message)) - .detach(); + cx.spawn(async move |_| { + platform::alert_dialog(tr::dialog::quit::still_open(), &message) + }) + .detach(); } } } diff --git a/apps/desktop-gpui/src/updates.rs b/apps/desktop-gpui/src/updates.rs index f1f97418e7..a4ccb06ad9 100644 --- a/apps/desktop-gpui/src/updates.rs +++ b/apps/desktop-gpui/src/updates.rs @@ -5,6 +5,7 @@ use gpui::{App, Global}; use semver::Version; use crate::{ + i18n::tr, installer::{self, ArtifactKind, Release}, session::RecordingSession, store::{GeneralSettings, UpdateChannel, UpdateDownloadPolicy}, @@ -14,8 +15,6 @@ const STABLE_FIRST_CHECK_DELAY: Duration = Duration::from_secs(10); const NIGHTLY_FIRST_CHECK_DELAY: Duration = Duration::from_secs(60); const NIGHTLY_CHECK_INTERVAL: Duration = Duration::from_secs(2 * 60 * 60); const BUSY_RETRY_DELAY: Duration = Duration::from_secs(5 * 60); -const BUSY_MESSAGE: &str = - "Finish your recording, export, upload, import, or transcription task before updating Cap."; #[derive(Clone, Debug, Default, PartialEq)] pub(crate) enum UpdateStatus { @@ -204,8 +203,8 @@ pub(crate) fn check_manually(cx: &mut App) { cx.spawn(async move |_| { crate::platform::activate_app(); crate::platform::alert_dialog( - "Update Cap", - &format!("Cap is already getting version {version} ready."), + tr::dialog::update::title(), + &tr::dialog::update::already_getting(version), ); }) .detach(); @@ -271,7 +270,7 @@ fn finish_manual_check(cx: &mut App, manual: bool) { fn busy_alert(cx: &mut App) { cx.spawn(async move |_| { crate::platform::activate_app(); - crate::platform::alert_dialog("Cap is busy", BUSY_MESSAGE); + crate::platform::alert_dialog(tr::dialog::update::busy_title(), tr::dialog::update::busy()); }) .detach(); } @@ -392,8 +391,7 @@ pub(crate) fn schedule_startup_check(cx: &mut App) { .background_executor() .spawn(async { GeneralSettings::load().update_channel }) .await; - let mut policy = cx - .update(|cx| cx.global::().download_policy); + let mut policy = cx.update(|cx| cx.global::().download_policy); let mut delay = if policy == UpdateDownloadPolicy::Never { None } else { @@ -501,8 +499,8 @@ pub(crate) fn schedule_startup_check(cx: &mut App) { if manual { crate::platform::activate_app(); crate::platform::alert_dialog( - "No Update Available", - "You're already using the latest version of Cap.", + tr::dialog::update::no_update_title(), + tr::dialog::update::no_update(), ); cx.update(|cx| finish_manual_check(cx, true)); } @@ -514,8 +512,8 @@ pub(crate) fn schedule_startup_check(cx: &mut App) { if manual { crate::platform::activate_app(); crate::platform::alert_dialog( - "Update Cap", - &format!("Couldn't check for updates: {error}"), + tr::dialog::update::title(), + &tr::dialog::update::check_failed(error), ); cx.update(|cx| finish_manual_check(cx, true)); } @@ -544,9 +542,8 @@ pub(crate) fn schedule_startup_check(cx: &mut App) { if let Either::Right((Err(_), _)) = select(timer, request).await { return; } - superseded = cx.update(|cx| { - supersedes_held_prompt(&cx.global::().pending) - }); + superseded = + cx.update(|cx| supersedes_held_prompt(&cx.global::().pending)); if superseded { break; } @@ -574,13 +571,10 @@ pub(crate) fn schedule_startup_check(cx: &mut App) { crate::platform::activate_app(); let accepted = crate::platform::confirm_dialog( - "Update Cap", - &format!( - "Version {} of Cap is available. Cap will download it in the background and let you know when it's ready to install.", - release.version - ), - "Download", - "Not Now", + tr::dialog::update::title(), + &tr::dialog::update::available(&release.version), + tr::dialog::update::download(), + tr::dialog::update::not_now(), false, ); cx.update(|cx| finish_manual_check(cx, manual)); @@ -680,7 +674,7 @@ fn start_download(release: Release, cx: &mut App) { } Err(error) => { tracing::warn!(%version, "update download failed: {error}"); - update_failed(&format!("Couldn't download version {version}: {error}"), cx); + update_failed(&tr::dialog::update::download_failed(&version, error), cx); } } }); @@ -690,11 +684,10 @@ fn start_download(release: Release, cx: &mut App) { fn update_failed(message: &str, cx: &mut App) { set_status(UpdateStatus::Idle, cx); - let message = - format!("{message}\n\nYou can always download the latest version from cap.so/download."); + let message = tr::dialog::update::failed(message); cx.spawn(async move |_| { crate::platform::activate_app(); - crate::platform::alert_dialog("Update Cap", &message); + crate::platform::alert_dialog(tr::dialog::update::title(), &message); }) .detach(); } @@ -717,10 +710,10 @@ fn prompt_restart(cx: &mut App) { cx.spawn(async move |cx| { crate::platform::activate_app(); if crate::platform::confirm_dialog( - "Update Cap", - &format!("Cap {version} is ready. Restart Cap to finish updating."), - "Restart Now", - "Later", + tr::dialog::update::title(), + &tr::dialog::update::ready(&version), + tr::dialog::update::restart_now(), + tr::dialog::update::later(), false, ) { cx.update(install_and_relaunch); @@ -746,7 +739,7 @@ pub(crate) fn install_and_relaunch(cx: &mut App) { } if let Err(error) = crate::app_windows::flush_pending_editor_saves(cx) { cx.spawn(async move |_| { - crate::platform::alert_dialog("Cap is still open", &error); + crate::platform::alert_dialog(tr::dialog::quit::still_open(), &error); }) .detach(); return; @@ -791,7 +784,7 @@ pub(crate) fn install_and_relaunch(cx: &mut App) { Err(error) => { tracing::error!(%version, "update install failed: {error}"); cx.global_mut::().staged = None; - update_failed(&format!("Couldn't install version {version}: {error}"), cx); + update_failed(&tr::dialog::update::install_failed(&version, error), cx); } }); }) From b6f26ae89f9a6310b58f3705ae02a6419bd35624 Mon Sep 17 00:00:00 2001 From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com> Date: Mon, 26 Oct 2026 22:24:44 +0000 Subject: [PATCH 4/4] fix(desktop-gpui): keep a running update download disabled in the menu --- apps/desktop-gpui/src/menus.rs | 25 ++++++++++++++------- apps/desktop-gpui/src/settings_window.rs | 10 +++++---- apps/desktop-gpui/src/updates.rs | 28 +++++++++++------------- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/apps/desktop-gpui/src/menus.rs b/apps/desktop-gpui/src/menus.rs index 3713467028..125471a197 100644 --- a/apps/desktop-gpui/src/menus.rs +++ b/apps/desktop-gpui/src/menus.rs @@ -65,7 +65,7 @@ impl UpdateRow { let title = match status { UpdateStatus::Idle => tr::menu::update::check(), UpdateStatus::Checking => tr::menu::update::checking(), - UpdateStatus::Downloading { fraction: None, .. } => tr::menu::update::download(), + UpdateStatus::Available { .. } => tr::menu::update::download(), UpdateStatus::Downloading { .. } | UpdateStatus::Installing { .. } => { tr::menu::update::downloading() } @@ -78,10 +78,7 @@ impl UpdateRow { enabled: !matches!( status, UpdateStatus::Checking - | UpdateStatus::Downloading { - fraction: Some(_), - .. - } + | UpdateStatus::Downloading { .. } | UpdateStatus::Installing { .. } ), badge: update_menu_badge(status), @@ -101,7 +98,8 @@ fn update_menu_badge(status: &updates::UpdateStatus) -> Option { version, (fraction * 100.0).round() as u32, )), - UpdateStatus::Downloading { version, .. } + UpdateStatus::Available { version } + | UpdateStatus::Downloading { version, .. } | UpdateStatus::Ready { version } | UpdateStatus::Installing { version } | UpdateStatus::Installed { version, .. } => Some(tr::menu::update::badge_version(version)), @@ -1356,10 +1354,15 @@ mod tests { fraction, }; + let available = UpdateRow::of(&UpdateStatus::Available { + version: version.clone(), + }); let cold = UpdateRow::of(&downloading(None)); let warm = UpdateRow::of(&downloading(Some(0.42))); - assert_eq!(cold.title, "Download update…"); - assert!(cold.enabled); + assert_eq!(available.title, "Download update…"); + assert!(available.enabled); + assert_eq!(cold.title, "Downloading update…"); + assert!(!cold.enabled); assert_eq!(warm.title, "Downloading update…"); assert!(!warm.enabled); @@ -1390,6 +1393,12 @@ mod tests { let version = Version::parse("0.4.0").unwrap(); let badge = |status| UpdateRow::of(&status).badge; + assert_eq!( + badge(UpdateStatus::Available { + version: version.clone(), + }), + Some("v0.4.0".to_string()) + ); assert_eq!( badge(UpdateStatus::Downloading { version: version.clone(), diff --git a/apps/desktop-gpui/src/settings_window.rs b/apps/desktop-gpui/src/settings_window.rs index 4dbf67548b..c0e416e5bf 100644 --- a/apps/desktop-gpui/src/settings_window.rs +++ b/apps/desktop-gpui/src/settings_window.rs @@ -2058,10 +2058,12 @@ impl SettingsWindow { fn render_account(&self, cx: &mut Context) -> impl IntoElement { let theme = self.theme; let (update_label, update_ready) = match crate::updates::status(cx) { - crate::updates::UpdateStatus::Idle => ( - tr::settings::sidebar::check_for_updates().to_string(), - false, - ), + crate::updates::UpdateStatus::Idle | crate::updates::UpdateStatus::Available { .. } => { + ( + tr::settings::sidebar::check_for_updates().to_string(), + false, + ) + } crate::updates::UpdateStatus::Checking => ( tr::settings::sidebar::checking_for_updates().to_string(), false, diff --git a/apps/desktop-gpui/src/updates.rs b/apps/desktop-gpui/src/updates.rs index a4ccb06ad9..63b57dfc2b 100644 --- a/apps/desktop-gpui/src/updates.rs +++ b/apps/desktop-gpui/src/updates.rs @@ -21,6 +21,12 @@ pub(crate) enum UpdateStatus { #[default] Idle, Checking, + /// Only the debug update simulation waits here. A real check asks in a + /// dialog first, so a real update goes straight to `Downloading`. + #[cfg_attr(not(debug_assertions), allow(dead_code))] + Available { + version: Version, + }, Downloading { version: Version, fraction: Option, @@ -71,7 +77,6 @@ struct UpdateScheduler { manual_in_flight: bool, status: UpdateStatus, download_policy: UpdateDownloadPolicy, - simulation_version: Option, staged: Option, download: Option>, download_epoch: u64, @@ -188,14 +193,7 @@ pub(crate) fn check_manually(cx: &mut App) { prompt_restart(cx); return; } - UpdateStatus::Downloading { - version, - fraction: None, - } if cx - .try_global::() - .and_then(|scheduler| scheduler.simulation_version.as_ref()) - == Some(&version) => - { + UpdateStatus::Available { version } => { simulate_download(version, cx); return; } @@ -235,7 +233,10 @@ pub(crate) fn update_channel_changed(channel: UpdateChannel, cx: &mut App) { fn channel_switch_drops_pending_package(status: &UpdateStatus) -> bool { matches!( status, - UpdateStatus::Checking | UpdateStatus::Downloading { .. } | UpdateStatus::Ready { .. } + UpdateStatus::Checking + | UpdateStatus::Available { .. } + | UpdateStatus::Downloading { .. } + | UpdateStatus::Ready { .. } ) } @@ -296,11 +297,9 @@ fn harness_update_version() -> Option { #[cfg(debug_assertions)] fn simulate_update(version: Version, cx: &mut App) { tracing::info!(%version, "CAP_GPUI_UPDATE_SIMULATION_VERSION: available"); - cx.global_mut::().simulation_version = Some(version.clone()); set_status( - UpdateStatus::Downloading { + UpdateStatus::Available { version: version.clone(), - fraction: None, }, cx, ); @@ -325,7 +324,7 @@ fn simulate_update(version: Version, cx: &mut App) { #[cfg(debug_assertions)] fn simulate_download(version: Version, cx: &mut App) { - if !matches!(status(cx), UpdateStatus::Downloading { fraction: None, .. }) { + if !matches!(status(cx), UpdateStatus::Available { .. }) { return; } tracing::info!(%version, "CAP_GPUI_UPDATE_SIMULATION_VERSION: downloading"); @@ -370,7 +369,6 @@ pub(crate) fn schedule_startup_check(cx: &mut App) { manual_in_flight: false, status: UpdateStatus::Idle, download_policy: GeneralSettings::load().update_download_policy, - simulation_version: None, staged: None, download: None, download_epoch: 0,