Split open-wrapper interception into its own setting
This commit is contained in:
parent
2428ae5dbd
commit
0046b674aa
5 changed files with 94 additions and 13 deletions
|
|
@ -169,7 +169,11 @@ fi
|
|||
|
||||
# Respect the same settings used for terminal link clicks.
|
||||
if [[ -n "$settings_domain" ]]; then
|
||||
open_in_cmux="$("$DEFAULTS_BIN" read "$settings_domain" browserOpenTerminalLinksInCmuxBrowser 2>/dev/null || true)"
|
||||
open_in_cmux="$("$DEFAULTS_BIN" read "$settings_domain" browserInterceptTerminalOpenCommandInCmuxBrowser 2>/dev/null || true)"
|
||||
if [[ -z "$open_in_cmux" ]]; then
|
||||
# Backward compatibility for installs that predate the dedicated open-wrapper toggle.
|
||||
open_in_cmux="$("$DEFAULTS_BIN" read "$settings_domain" browserOpenTerminalLinksInCmuxBrowser 2>/dev/null || true)"
|
||||
fi
|
||||
case "${open_in_cmux,,}" in
|
||||
0|false|no)
|
||||
system_open "$@"
|
||||
|
|
|
|||
|
|
@ -127,6 +127,9 @@ enum BrowserLinkOpenSettings {
|
|||
static let openTerminalLinksInCmuxBrowserKey = "browserOpenTerminalLinksInCmuxBrowser"
|
||||
static let defaultOpenTerminalLinksInCmuxBrowser: Bool = true
|
||||
|
||||
static let interceptTerminalOpenCommandInCmuxBrowserKey = "browserInterceptTerminalOpenCommandInCmuxBrowser"
|
||||
static let defaultInterceptTerminalOpenCommandInCmuxBrowser: Bool = true
|
||||
|
||||
static let browserHostWhitelistKey = "browserHostWhitelist"
|
||||
static let defaultBrowserHostWhitelist: String = ""
|
||||
|
||||
|
|
@ -137,6 +140,19 @@ enum BrowserLinkOpenSettings {
|
|||
return defaults.bool(forKey: openTerminalLinksInCmuxBrowserKey)
|
||||
}
|
||||
|
||||
static func interceptTerminalOpenCommandInCmuxBrowser(defaults: UserDefaults = .standard) -> Bool {
|
||||
if defaults.object(forKey: interceptTerminalOpenCommandInCmuxBrowserKey) != nil {
|
||||
return defaults.bool(forKey: interceptTerminalOpenCommandInCmuxBrowserKey)
|
||||
}
|
||||
|
||||
// Migrate existing behavior for users who only had the link-click toggle.
|
||||
if defaults.object(forKey: openTerminalLinksInCmuxBrowserKey) != nil {
|
||||
return defaults.bool(forKey: openTerminalLinksInCmuxBrowserKey)
|
||||
}
|
||||
|
||||
return defaultInterceptTerminalOpenCommandInCmuxBrowser
|
||||
}
|
||||
|
||||
static func hostWhitelist(defaults: UserDefaults = .standard) -> [String] {
|
||||
let raw = defaults.string(forKey: browserHostWhitelistKey) ?? defaultBrowserHostWhitelist
|
||||
return raw
|
||||
|
|
|
|||
|
|
@ -2559,6 +2559,8 @@ struct SettingsView: View {
|
|||
@AppStorage(BrowserSearchSettings.searchSuggestionsEnabledKey) private var browserSearchSuggestionsEnabled = BrowserSearchSettings.defaultSearchSuggestionsEnabled
|
||||
@AppStorage(BrowserThemeSettings.modeKey) private var browserThemeMode = BrowserThemeSettings.defaultMode.rawValue
|
||||
@AppStorage(BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowserKey) private var openTerminalLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenTerminalLinksInCmuxBrowser
|
||||
@AppStorage(BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowserKey)
|
||||
private var interceptTerminalOpenCommandInCmuxBrowser = BrowserLinkOpenSettings.defaultInterceptTerminalOpenCommandInCmuxBrowser
|
||||
@AppStorage(BrowserLinkOpenSettings.browserHostWhitelistKey) private var browserHostWhitelist = BrowserLinkOpenSettings.defaultBrowserHostWhitelist
|
||||
@AppStorage(BrowserInsecureHTTPSettings.allowlistKey) private var browserInsecureHTTPAllowlist = BrowserInsecureHTTPSettings.defaultAllowlistText
|
||||
@AppStorage(NotificationBadgeSettings.dockBadgeEnabledKey) private var notificationDockBadgeEnabled = NotificationBadgeSettings.defaultDockBadgeEnabled
|
||||
|
|
@ -3023,13 +3025,24 @@ struct SettingsView: View {
|
|||
.controlSize(.small)
|
||||
}
|
||||
|
||||
if openTerminalLinksInCmuxBrowser {
|
||||
SettingsCardDivider()
|
||||
|
||||
SettingsCardRow(
|
||||
"Intercept open http(s) in Terminal",
|
||||
subtitle: "When off, `open https://...` and `open http://...` always use your default browser."
|
||||
) {
|
||||
Toggle("", isOn: $interceptTerminalOpenCommandInCmuxBrowser)
|
||||
.labelsHidden()
|
||||
.controlSize(.small)
|
||||
}
|
||||
|
||||
if openTerminalLinksInCmuxBrowser || interceptTerminalOpenCommandInCmuxBrowser {
|
||||
SettingsCardDivider()
|
||||
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
SettingsCardRow(
|
||||
"Hosts to Open in Embedded Browser",
|
||||
subtitle: "When you click links in terminal output, only these hosts open in cmux. Other hosts open in your default browser. One host or wildcard per line (for example: example.com, *.internal.example). Leave empty to open all links in cmux."
|
||||
subtitle: "Applies to terminal link clicks and intercepted `open https://...` calls. Only these hosts open in cmux. Others open in your default browser. One host or wildcard per line (for example: example.com, *.internal.example). Leave empty to open all hosts in cmux."
|
||||
) {
|
||||
EmptyView()
|
||||
}
|
||||
|
|
@ -3291,6 +3304,7 @@ struct SettingsView: View {
|
|||
browserSearchSuggestionsEnabled = BrowserSearchSettings.defaultSearchSuggestionsEnabled
|
||||
browserThemeMode = BrowserThemeSettings.defaultMode.rawValue
|
||||
openTerminalLinksInCmuxBrowser = BrowserLinkOpenSettings.defaultOpenTerminalLinksInCmuxBrowser
|
||||
interceptTerminalOpenCommandInCmuxBrowser = BrowserLinkOpenSettings.defaultInterceptTerminalOpenCommandInCmuxBrowser
|
||||
browserHostWhitelist = BrowserLinkOpenSettings.defaultBrowserHostWhitelist
|
||||
browserInsecureHTTPAllowlist = BrowserInsecureHTTPSettings.defaultAllowlistText
|
||||
browserInsecureHTTPAllowlistDraft = BrowserInsecureHTTPSettings.defaultAllowlistText
|
||||
|
|
|
|||
|
|
@ -4140,6 +4140,26 @@ final class BrowserLinkOpenSettingsTests: XCTestCase {
|
|||
defaults.set(true, forKey: BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowserKey)
|
||||
XCTAssertTrue(BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowser(defaults: defaults))
|
||||
}
|
||||
|
||||
func testOpenCommandInterceptionDefaultsToCmuxBrowser() {
|
||||
XCTAssertTrue(BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowser(defaults: defaults))
|
||||
}
|
||||
|
||||
func testOpenCommandInterceptionUsesStoredValue() {
|
||||
defaults.set(false, forKey: BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowserKey)
|
||||
XCTAssertFalse(BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowser(defaults: defaults))
|
||||
|
||||
defaults.set(true, forKey: BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowserKey)
|
||||
XCTAssertTrue(BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowser(defaults: defaults))
|
||||
}
|
||||
|
||||
func testOpenCommandInterceptionFallsBackToLegacyLinkToggleWhenUnset() {
|
||||
defaults.set(false, forKey: BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowserKey)
|
||||
XCTAssertFalse(BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowser(defaults: defaults))
|
||||
|
||||
defaults.set(true, forKey: BrowserLinkOpenSettings.openTerminalLinksInCmuxBrowserKey)
|
||||
XCTAssertTrue(BrowserLinkOpenSettings.interceptTerminalOpenCommandInCmuxBrowser(defaults: defaults))
|
||||
}
|
||||
}
|
||||
|
||||
final class TerminalOpenURLTargetResolutionTests: XCTestCase {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ def read_log(path: Path) -> list[str]:
|
|||
def run_wrapper(
|
||||
*,
|
||||
args: list[str],
|
||||
open_setting: str | None,
|
||||
intercept_setting: str | None,
|
||||
legacy_open_setting: str | None = None,
|
||||
whitelist: str | None,
|
||||
fail_urls: list[str] | None = None,
|
||||
) -> tuple[list[str], list[str], int, str]:
|
||||
|
|
@ -63,9 +64,16 @@ if [[ "${1:-}" != "read" ]]; then
|
|||
fi
|
||||
key="${3:-}"
|
||||
case "$key" in
|
||||
browserInterceptTerminalOpenCommandInCmuxBrowser)
|
||||
if [[ -v FAKE_DEFAULTS_INTERCEPT_OPEN ]]; then
|
||||
printf '%s\\n' "$FAKE_DEFAULTS_INTERCEPT_OPEN"
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
;;
|
||||
browserOpenTerminalLinksInCmuxBrowser)
|
||||
if [[ -v FAKE_DEFAULTS_OPEN ]]; then
|
||||
printf '%s\\n' "$FAKE_DEFAULTS_OPEN"
|
||||
if [[ -v FAKE_DEFAULTS_LEGACY_OPEN ]]; then
|
||||
printf '%s\\n' "$FAKE_DEFAULTS_LEGACY_OPEN"
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
|
|
@ -110,10 +118,15 @@ exit 0
|
|||
env["FAKE_OPEN_LOG"] = str(open_log)
|
||||
env["FAKE_CMUX_LOG"] = str(cmux_log)
|
||||
|
||||
if open_setting is None:
|
||||
env.pop("FAKE_DEFAULTS_OPEN", None)
|
||||
if intercept_setting is None:
|
||||
env.pop("FAKE_DEFAULTS_INTERCEPT_OPEN", None)
|
||||
else:
|
||||
env["FAKE_DEFAULTS_OPEN"] = open_setting
|
||||
env["FAKE_DEFAULTS_INTERCEPT_OPEN"] = intercept_setting
|
||||
|
||||
if legacy_open_setting is None:
|
||||
env.pop("FAKE_DEFAULTS_LEGACY_OPEN", None)
|
||||
else:
|
||||
env["FAKE_DEFAULTS_LEGACY_OPEN"] = legacy_open_setting
|
||||
|
||||
if whitelist is None:
|
||||
env.pop("FAKE_DEFAULTS_WHITELIST", None)
|
||||
|
|
@ -145,7 +158,7 @@ def test_toggle_disabled_passthrough(failures: list[str]) -> None:
|
|||
url = "https://example.com"
|
||||
open_log, cmux_log, code, stderr = run_wrapper(
|
||||
args=[url],
|
||||
open_setting="0",
|
||||
intercept_setting="0",
|
||||
whitelist="",
|
||||
)
|
||||
expect(code == 0, f"toggle off: wrapper exited {code}: {stderr}", failures)
|
||||
|
|
@ -157,7 +170,7 @@ def test_whitelist_miss_passthrough(failures: list[str]) -> None:
|
|||
url = "https://example.com"
|
||||
open_log, cmux_log, code, stderr = run_wrapper(
|
||||
args=[url],
|
||||
open_setting="1",
|
||||
intercept_setting="1",
|
||||
whitelist="localhost\n127.0.0.1",
|
||||
)
|
||||
expect(code == 0, f"whitelist miss: wrapper exited {code}: {stderr}", failures)
|
||||
|
|
@ -169,7 +182,7 @@ def test_whitelist_match_routes_to_cmux(failures: list[str]) -> None:
|
|||
url = "https://api.example.com/path?q=1"
|
||||
open_log, cmux_log, code, stderr = run_wrapper(
|
||||
args=[url],
|
||||
open_setting="1",
|
||||
intercept_setting="1",
|
||||
whitelist="*.example.com",
|
||||
)
|
||||
expect(code == 0, f"whitelist match: wrapper exited {code}: {stderr}", failures)
|
||||
|
|
@ -183,7 +196,7 @@ def test_partial_failures_only_fallback_failed_urls(failures: list[str]) -> None
|
|||
external = "https://outside.test"
|
||||
open_log, cmux_log, code, stderr = run_wrapper(
|
||||
args=[good, failed, external],
|
||||
open_setting="1",
|
||||
intercept_setting="1",
|
||||
whitelist="*.example.com",
|
||||
fail_urls=[failed],
|
||||
)
|
||||
|
|
@ -200,12 +213,26 @@ def test_partial_failures_only_fallback_failed_urls(failures: list[str]) -> None
|
|||
)
|
||||
|
||||
|
||||
def test_legacy_toggle_fallback_passthrough(failures: list[str]) -> None:
|
||||
url = "https://example.com"
|
||||
open_log, cmux_log, code, stderr = run_wrapper(
|
||||
args=[url],
|
||||
intercept_setting=None,
|
||||
legacy_open_setting="0",
|
||||
whitelist="",
|
||||
)
|
||||
expect(code == 0, f"legacy fallback: wrapper exited {code}: {stderr}", failures)
|
||||
expect(cmux_log == [], f"legacy fallback: cmux should not be called, got {cmux_log}", failures)
|
||||
expect(open_log == [url], f"legacy fallback: expected system open [{url}], got {open_log}", failures)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
failures: list[str] = []
|
||||
test_toggle_disabled_passthrough(failures)
|
||||
test_whitelist_miss_passthrough(failures)
|
||||
test_whitelist_match_routes_to_cmux(failures)
|
||||
test_partial_failures_only_fallback_failed_urls(failures)
|
||||
test_legacy_toggle_fallback_passthrough(failures)
|
||||
|
||||
if failures:
|
||||
print("open wrapper regression tests failed:")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue