From 1b03d23feee6a862a93f7ac2866dbea223f5dfc7 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Thu, 26 Mar 2026 17:12:37 -0700 Subject: [PATCH] fix(sidebar): use dedicated setting for port link browser preference (#2219) Port links were reusing the PR-link preference (openSidebarPullRequestLinksInCmuxBrowser), causing inconsistent behavior when users toggled that setting. Adds a dedicated openSidebarPortLinksInCmuxBrowser setting with its own toggle in Settings so port and PR link behavior can be controlled independently. Addresses review feedback from https://github.com/manaflow-ai/cmux/pull/1844 Co-authored-by: Lawrence Chen --- Resources/Localizable.xcstrings | 339 ++++++++++++++++++++++++++++++ Sources/ContentView.swift | 4 +- Sources/Panels/BrowserPanel.swift | 10 + Sources/cmuxApp.swift | 17 ++ 4 files changed, 369 insertions(+), 1 deletion(-) diff --git a/Resources/Localizable.xcstrings b/Resources/Localizable.xcstrings index 39f8dca0..3a55ad83 100644 --- a/Resources/Localizable.xcstrings +++ b/Resources/Localizable.xcstrings @@ -44923,6 +44923,345 @@ } } }, + "settings.app.openSidebarPortLinks": { + "extractionState": "manual", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Open Sidebar Port Links in cmux Browser" + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "サイドバーのポートリンクをcmuxブラウザで開く" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "在 cmux 浏览器中打开侧边栏端口链接" + } + }, + "zh-Hant": { + "stringUnit": { + "state": "translated", + "value": "在 cmux 瀏覽器中開啟側邊欄連接埠連結" + } + }, + "ko": { + "stringUnit": { + "state": "translated", + "value": "사이드바 포트 링크를 cmux 브라우저에서 열기" + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "Seitenleisten-Port-Links im cmux-Browser öffnen" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Abrir enlaces de puerto de la barra lateral en el navegador de cmux" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Ouvrir les liens de port de la barre latérale dans le navigateur cmux" + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Apri link delle porte della barra laterale nel browser cmux" + } + }, + "da": { + "stringUnit": { + "state": "translated", + "value": "Åbn sidebjælkens portlinks i cmux-browser" + } + }, + "pl": { + "stringUnit": { + "state": "translated", + "value": "Otwieraj linki portów z paska bocznego w przeglądarce cmux" + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Открывать ссылки портов боковой панели в браузере cmux" + } + }, + "bs": { + "stringUnit": { + "state": "translated", + "value": "Otvori port linkove iz bočne trake u cmux pregledniku" + } + }, + "ar": { + "stringUnit": { + "state": "translated", + "value": "فتح روابط المنافذ في متصفح cmux" + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Åpne sidepanel-portlenker i cmux-nettleser" + } + }, + "pt-BR": { + "stringUnit": { + "state": "translated", + "value": "Abrir Links de Porta da Barra Lateral no Navegador do cmux" + } + }, + "th": { + "stringUnit": { + "state": "translated", + "value": "เปิดลิงก์พอร์ตในแถบด้านข้างด้วยเบราว์เซอร์ cmux" + } + }, + "tr": { + "stringUnit": { + "state": "translated", + "value": "Kenar Çubuğu Port Bağlantılarını cmux Tarayıcısında Aç" + } + } + } + }, + "settings.app.openSidebarPortLinks.subtitleOff": { + "extractionState": "manual", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Port clicks open in your default browser." + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "ポートをクリックするとデフォルトブラウザで開きます。" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "端口点击在默认浏览器中打开。" + } + }, + "zh-Hant": { + "stringUnit": { + "state": "translated", + "value": "連接埠點擊會在您的預設瀏覽器中開啟。" + } + }, + "ko": { + "stringUnit": { + "state": "translated", + "value": "포트를 클릭하면 기본 브라우저에서 열립니다." + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "Port-Klicks öffnen in Ihrem Standardbrowser." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Los clics en puertos abren en tu navegador predeterminado." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Les clics de port ouvrent dans votre navigateur par défaut." + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "I clic sulle porte aprono nel browser predefinito." + } + }, + "da": { + "stringUnit": { + "state": "translated", + "value": "Portklik åbner i din standardbrowser." + } + }, + "pl": { + "stringUnit": { + "state": "translated", + "value": "Kliknięcia portów otwierają w domyślnej przeglądarce." + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Ссылки портов открываются в браузере по умолчанию." + } + }, + "bs": { + "stringUnit": { + "state": "translated", + "value": "Klikovi na portove se otvaraju u podrazumijevanom pregledniku." + } + }, + "ar": { + "stringUnit": { + "state": "translated", + "value": "نقرات المنافذ تفتح في متصفحك الافتراضي." + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Portklikk åpner i standard nettleser." + } + }, + "pt-BR": { + "stringUnit": { + "state": "translated", + "value": "Cliques em portas abrem no seu navegador padrão." + } + }, + "th": { + "stringUnit": { + "state": "translated", + "value": "คลิกพอร์ตจะเปิดในเบราว์เซอร์เริ่มต้นของคุณ" + } + }, + "tr": { + "stringUnit": { + "state": "translated", + "value": "Port tıklamaları varsayılan tarayıcınızda açılır." + } + } + } + }, + "settings.app.openSidebarPortLinks.subtitleOn": { + "extractionState": "manual", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Port clicks open inside cmux browser." + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "ポートをクリックするとcmuxブラウザ内で開きます。" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "端口点击在 cmux 浏览器中打开。" + } + }, + "zh-Hant": { + "stringUnit": { + "state": "translated", + "value": "連接埠點擊會在 cmux 瀏覽器中開啟。" + } + }, + "ko": { + "stringUnit": { + "state": "translated", + "value": "포트를 클릭하면 cmux 브라우저에서 열립니다." + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "Port-Klicks öffnen im cmux-Browser." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Los clics en puertos abren dentro del navegador de cmux." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Les clics de port ouvrent dans le navigateur cmux." + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "I clic sulle porte aprono nel browser cmux." + } + }, + "da": { + "stringUnit": { + "state": "translated", + "value": "Portklik åbner i cmux-browseren." + } + }, + "pl": { + "stringUnit": { + "state": "translated", + "value": "Kliknięcia portów otwierają w przeglądarce cmux." + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Ссылки портов открываются во встроенном браузере cmux." + } + }, + "bs": { + "stringUnit": { + "state": "translated", + "value": "Klikovi na portove se otvaraju unutar cmux preglednika." + } + }, + "ar": { + "stringUnit": { + "state": "translated", + "value": "نقرات المنافذ تفتح داخل متصفح cmux." + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Portklikk åpner i cmux-nettleseren." + } + }, + "pt-BR": { + "stringUnit": { + "state": "translated", + "value": "Cliques em portas abrem dentro do navegador do cmux." + } + }, + "th": { + "stringUnit": { + "state": "translated", + "value": "คลิกพอร์ตจะเปิดในเบราว์เซอร์ cmux" + } + }, + "tr": { + "stringUnit": { + "state": "translated", + "value": "Port tıklamaları cmux tarayıcısında açılır." + } + } + } + }, "settings.app.hideAllSidebarDetails": { "extractionState": "manual", "localizations": { diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 046ce785..72b3f922 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -11015,6 +11015,8 @@ private struct TabItemView: View, Equatable { @AppStorage("sidebarShowPullRequest") private var sidebarShowPullRequest = true @AppStorage(BrowserLinkOpenSettings.openSidebarPullRequestLinksInCmuxBrowserKey) private var openSidebarPullRequestLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPullRequestLinksInCmuxBrowser + @AppStorage(BrowserLinkOpenSettings.openSidebarPortLinksInCmuxBrowserKey) + private var openSidebarPortLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPortLinksInCmuxBrowser @AppStorage("sidebarShowSSH") private var sidebarShowSSH = true @AppStorage("sidebarShowPorts") private var sidebarShowPorts = true @AppStorage("sidebarShowLog") private var sidebarShowLog = true @@ -12220,7 +12222,7 @@ private struct TabItemView: View, Equatable { private func openPortLink(_ port: Int) { guard let url = URL(string: "http://localhost:\(port)") else { return } updateSelection() - if openSidebarPullRequestLinksInCmuxBrowser { + if openSidebarPortLinksInCmuxBrowser { if tabManager.openBrowser( inWorkspace: tab.id, url: url, diff --git a/Sources/Panels/BrowserPanel.swift b/Sources/Panels/BrowserPanel.swift index 54372be7..faf39dea 100644 --- a/Sources/Panels/BrowserPanel.swift +++ b/Sources/Panels/BrowserPanel.swift @@ -527,6 +527,9 @@ enum BrowserLinkOpenSettings { static let openSidebarPullRequestLinksInCmuxBrowserKey = "browserOpenSidebarPullRequestLinksInCmuxBrowser" static let defaultOpenSidebarPullRequestLinksInCmuxBrowser: Bool = true + static let openSidebarPortLinksInCmuxBrowserKey = "browserOpenSidebarPortLinksInCmuxBrowser" + static let defaultOpenSidebarPortLinksInCmuxBrowser: Bool = true + static let interceptTerminalOpenCommandInCmuxBrowserKey = "browserInterceptTerminalOpenCommandInCmuxBrowser" static let defaultInterceptTerminalOpenCommandInCmuxBrowser: Bool = true @@ -549,6 +552,13 @@ enum BrowserLinkOpenSettings { return defaults.bool(forKey: openSidebarPullRequestLinksInCmuxBrowserKey) } + static func openSidebarPortLinksInCmuxBrowser(defaults: UserDefaults = .standard) -> Bool { + if defaults.object(forKey: openSidebarPortLinksInCmuxBrowserKey) == nil { + return defaultOpenSidebarPortLinksInCmuxBrowser + } + return defaults.bool(forKey: openSidebarPortLinksInCmuxBrowserKey) + } + static func interceptTerminalOpenCommandInCmuxBrowser(defaults: UserDefaults = .standard) -> Bool { if defaults.object(forKey: interceptTerminalOpenCommandInCmuxBrowserKey) != nil { return defaults.bool(forKey: interceptTerminalOpenCommandInCmuxBrowserKey) diff --git a/Sources/cmuxApp.swift b/Sources/cmuxApp.swift index f0de3cf0..d70a7303 100644 --- a/Sources/cmuxApp.swift +++ b/Sources/cmuxApp.swift @@ -3861,6 +3861,8 @@ struct SettingsView: View { @AppStorage("sidebarShowPullRequest") private var sidebarShowPullRequest = true @AppStorage(BrowserLinkOpenSettings.openSidebarPullRequestLinksInCmuxBrowserKey) private var openSidebarPullRequestLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPullRequestLinksInCmuxBrowser + @AppStorage(BrowserLinkOpenSettings.openSidebarPortLinksInCmuxBrowserKey) + private var openSidebarPortLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPortLinksInCmuxBrowser @AppStorage(ShortcutHintDebugSettings.showHintsOnCommandHoldKey) private var showShortcutHintsOnCommandHold = ShortcutHintDebugSettings.defaultShowHintsOnCommandHold @AppStorage("sidebarShowSSH") private var sidebarShowSSH = true @@ -4748,6 +4750,20 @@ struct SettingsView: View { SettingsCardDivider() + SettingsCardRow( + String(localized: "settings.app.openSidebarPortLinks", defaultValue: "Open Sidebar Port Links in cmux Browser"), + subtitle: openSidebarPortLinksInCmuxBrowser + ? String(localized: "settings.app.openSidebarPortLinks.subtitleOn", defaultValue: "Port clicks open inside cmux browser.") + : String(localized: "settings.app.openSidebarPortLinks.subtitleOff", defaultValue: "Port clicks open in your default browser.") + ) { + Toggle("", isOn: $openSidebarPortLinksInCmuxBrowser) + .labelsHidden() + .controlSize(.small) + } + .disabled(sidebarHideAllDetails) + + SettingsCardDivider() + SettingsCardRow( String(localized: "settings.app.showSSH", defaultValue: "Show SSH in Sidebar"), subtitle: String(localized: "settings.app.showSSH.subtitle", defaultValue: "Display the SSH target for remote workspaces in its own row.") @@ -5647,6 +5663,7 @@ struct SettingsView: View { sidebarShowBranchDirectory = true sidebarShowPullRequest = true openSidebarPullRequestLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPullRequestLinksInCmuxBrowser + openSidebarPortLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenSidebarPortLinksInCmuxBrowser showShortcutHintsOnCommandHold = ShortcutHintDebugSettings.defaultShowHintsOnCommandHold sidebarShowSSH = true sidebarShowPorts = true