Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
05af4af
Add browser subframe download regression test
austinywang Jun 24, 2026
05fdea5
Fix browser downloads from subframes
austinywang Jun 24, 2026
8e36fd9
Refresh Swift file length budget
austinywang Jun 24, 2026
2ae496e
Address browser download review findings
austinywang Jun 24, 2026
53a7b18
Harden browser download completion paths
austinywang Jun 25, 2026
a4e37af
Move session download writes off main thread
austinywang Jun 25, 2026
db86301
Preserve scripted blob download filenames
austinywang Jun 25, 2026
52d149e
Bound subframe download interception
austinywang Jun 25, 2026
9262538
Handle redirected subframe downloads
austinywang Jun 25, 2026
2d06aea
Tighten subframe download intent handling
austinywang Jun 25, 2026
30c0d56
Route subframe attachment downloads through session
austinywang Jun 25, 2026
9edfadd
Merge remote-tracking branch 'origin/main' into issue-6754-browser-do…
austinywang Jun 25, 2026
b49f3f0
Use tokenized subframe download intent
austinywang Jun 25, 2026
87b327a
Stream subframe fallback downloads with WebKit
austinywang Jun 25, 2026
651fe5e
Handle redirected subframe download waits
austinywang Jun 25, 2026
e64cd2d
Share subframe download intent tracking
austinywang Jun 25, 2026
8a7020b
Transfer redirected subframe download intents
austinywang Jun 25, 2026
b69a81c
Fix subframe intent callback binding
austinywang Jun 25, 2026
4a57d5e
Fix prompted download activity accounting
austinywang Jun 25, 2026
b05d550
Merge remote-tracking branch 'origin/main' into issue-6754-browser-do…
austinywang Jun 26, 2026
951bac4
Fix download helper isolation warning
austinywang Jun 26, 2026
9e47a65
Harden browser download saves
austinywang Jun 26, 2026
bdea121
Address download autoreview findings
austinywang Jun 26, 2026
3f9a58d
Fix context download save panel activity
austinywang Jun 26, 2026
4f58615
Use response downloads for subframes
austinywang Jun 26, 2026
3eb0856
Restrict scripted downloads to main frame
austinywang Jun 26, 2026
cd14b8c
Validate scripted download frame origin
austinywang Jun 26, 2026
de1fd4f
Avoid intercepting subframe scripted downloads
austinywang Jun 26, 2026
91835cb
Split browser download helper types
austinywang Jun 26, 2026
eb22a3f
Limit subframe download intents to trusted clicks
austinywang Jun 26, 2026
209962a
Keep scripted download hook out of subframes
austinywang Jun 26, 2026
273ea5b
Merge remote-tracking branch 'origin/main' into issue-6754-browser-do…
austinywang Jun 26, 2026
9ba6ccf
Add prompted download completion regression
austinywang Jun 26, 2026
99aed4a
Post prompted download completion events
austinywang Jun 26, 2026
0ec2621
Deduplicate prompted download automation events
austinywang Jun 26, 2026
1bc8400
Address prompted download autoreview findings
austinywang Jun 26, 2026
e587a54
Merge remote-tracking branch 'origin/main' into issue-6754-browser-do…
austinywang Jun 26, 2026
23c638d
Post session download automation events
austinywang Jun 26, 2026
0cef08d
Gate subframe download actions through HTTP policy
austinywang Jun 26, 2026
a1362ed
Preserve prompted download completions
austinywang Jun 27, 2026
568e64d
Avoid consuming prompted download ready events
austinywang Jun 27, 2026
7dd6bb6
Handle popup subframe download actions
austinywang Jun 27, 2026
92c98f1
Bound browser download event queues
austinywang Jun 27, 2026
57b2ab2
Fix PDF viewer toolbar downloads
austinywang Jun 27, 2026
26f3973
Merge remote-tracking branch 'origin/main' into issue-6754-browser-do…
austinywang Jun 27, 2026
c21c441
Gate PDF print toolbar intents
austinywang Jun 27, 2026
5883354
Use MainActor for download notifications
austinywang Jun 27, 2026
10ee538
Deduplicate browser download prompt events
austinywang Jun 27, 2026
e6aa389
Avoid consuming HTTP bypass for subframe downloads
austinywang Jun 27, 2026
2c3ed06
Require trusted subframe download intents
austinywang Jun 27, 2026
72e03e1
Gate subframe action downloads
austinywang Jun 27, 2026
2101441
Record dynamic subframe download intents
austinywang Jun 27, 2026
ca34d11
Extract browser session download saver
austinywang Jun 27, 2026
feebd87
Scope PDF print intents to rendered subframes
austinywang Jun 27, 2026
949d203
Preserve download quarantine and completion events
austinywang Jun 27, 2026
89d51da
Treat final quarantine as best effort
austinywang Jun 27, 2026
146f4be
Sanitize browser download failure events
austinywang Jun 27, 2026
fec6a27
Fix browser download event test compile
austinywang Jun 27, 2026
0e6b6ba
Add browser PDF toolbar and force-download subframe attachments
austinywang Jun 29, 2026
4e5e4f2
Merge origin/main into issue-6754-browser-downloads
austinywang Jun 29, 2026
b745d98
Merge origin/main into issue-6754-browser-downloads
austinywang Jun 29, 2026
09db647
Scope PDF document toolbar to main-frame PDFs
austinywang Jun 29, 2026
020a0e5
Merge remote-tracking branch 'origin/main' into issue-6754-browser-do…
austinywang Jun 29, 2026
7d58cac
Merge remote-tracking branch 'origin/main' into issue-6754-browser-do…
austinywang Jun 29, 2026
fe5f5bf
Add Safari/Chrome-style downloads button + popover
austinywang Jun 29, 2026
d95954e
Merge remote-tracking branch 'origin/main' into issue-6754-browser-do…
austinywang Jun 29, 2026
36c3b01
Keep download-event folding off the Swift warning budget
austinywang Jun 29, 2026
2a4e131
Merge remote-tracking branch 'origin/main' into issue-6754-browser-do…
austinywang Jun 29, 2026
149b097
Make the downloads toolbar button obviously animated
austinywang Jun 30, 2026
eee0c89
Tone down downloads button: monochrome icon + notification bubble
austinywang Jun 30, 2026
c6c6b6d
Merge remote-tracking branch 'origin/main' into issue-6754-browser-do…
austinywang Jun 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions .github/swift-file-length-budget.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
34427 CLI/cmux.swift
17908 Sources/AppDelegate.swift
16491 Sources/ContentView.swift
14170 Sources/TerminalController.swift
14224 Sources/TerminalController.swift
12917 Sources/Workspace.swift
12348 cmuxTests/AppDelegateShortcutRoutingTests.swift
12214 Sources/GhosttyTerminalView.swift
11592 Sources/Panels/BrowserPanel.swift
11671 Sources/Panels/BrowserPanel.swift
9497 cmuxTests/CLINotifyProcessIntegrationRegressionTests.swift
8059 Sources/Panels/BrowserPanelView.swift
8032 Sources/Panels/BrowserPanelView.swift
8016 CLI/cmux_open.swift
7366 cmuxTests/WorkspaceUnitTests.swift
7218 cmuxTests/WorkspaceRemoteConnectionTests.swift
Expand All @@ -36,7 +36,7 @@
2876 cmuxTests/CMUXOpenCommandTests.swift
2875 Sources/SessionIndexView.swift
2606 Sources/KeyboardShortcutSettings.swift
2565 Sources/Panels/CmuxWebView.swift
2563 Sources/Panels/CmuxWebView.swift
2546 cmuxTests/WorkspaceManualUnreadTests.swift
2524 cmuxTests/CommandPaletteSearchEngineTests.swift
2395 Sources/Mobile/MobileHostService.swift
Expand All @@ -57,9 +57,9 @@
1680 cmuxUITests/BrowserPaneNavigationKeybindUITests.swift
1656 Sources/FileExplorerView.swift
1652 cmuxTests/CMUXCLIErrorOutputRegressionTests.swift
1649 cmuxTests/TerminalControllerSocketSecurityTests.swift
1581 Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/TerminalInputTextView.swift
1560 cmuxTests/TextBoxMentionCompletionTests.swift
1547 cmuxTests/TerminalControllerSocketSecurityTests.swift
1500 cmuxUITests/MultiWindowNotificationsUITests.swift
1499 cmuxTests/OmnibarAndToolsTests.swift
1447 Sources/FileExplorerStore.swift
Expand Down Expand Up @@ -90,24 +90,24 @@
1009 cmuxTests/CmuxTopSnapshotScopeTests.swift
1006 cmuxTests/CmuxSSHURLRequestTests.swift
1002 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/PairedMacBackupTests.swift
982 Packages/Shared/CmuxAgentChat/Tests/CmuxAgentChatTests/ChatConversationStoreTests.swift
953 cmuxTests/DockSocketLifecycleTests.swift
951 Sources/App/TerminalDirectoryOpenSupport.swift
982 Packages/Shared/CmuxAgentChat/Tests/CmuxAgentChatTests/ChatConversationStoreTests.swift
948 Sources/App/ShortcutRoutingSupport.swift
947 Sources/TerminalNotificationPolicy.swift
945 Sources/SessionIndexRegisteredAgents.swift
937 Sources/Panels/BrowserPopupWindowController.swift
944 Sources/CommandPalette/CommandPaletteSettingsToggle.swift
937 Sources/TextBoxMentionIndexStore.swift
928 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift
920 Sources/CommandPalette/CommandPaletteSettingsToggle.swift
755 Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift
918 Sources/Panels/BrowserPopupWindowController.swift
905 Sources/CmuxSSHURLRequest.swift
899 Sources/Panels/MarkdownWebRenderer.swift
885 Sources/Panels/TerminalPanel.swift
885 cmuxTests/SidebarWorkspaceDropPlannerTests.swift
881 Sources/DockSplitStore.swift
871 cmuxTests/ClaudeHookSurfaceResolutionSwiftTests.swift
868 Sources/Panels/BrowserScreenshotSnapshotter.swift
864 Packages/Shared/CmuxAgentChat/Sources/CmuxAgentChat/Store/ChatConversationStore.swift
859 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Workspace/ControlCommandCoordinator+Workspace.swift
847 cmuxTests/AgentSessionAutoResumeSettingsTests.swift
845 cmuxTests/SSHStartupSignalLifecycleTests.swift
Expand All @@ -124,13 +124,13 @@
773 Sources/App/MenuBarExtraController.swift
769 Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+Input.swift
768 cmuxUITests/BrowserFixtureInteractionUITests.swift
864 Packages/Shared/CmuxAgentChat/Sources/CmuxAgentChat/Store/ChatConversationStore.swift
762 Packages/iOS/CmuxMobileTransport/Sources/CmuxMobileTransport/CmxNetworkByteTransport.swift
761 Sources/Mobile/AgentChat/AgentChatSessionRegistry.swift
760 Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/AgentLaunchSanitizerTests.swift
756 Sources/Panels/AgentSessionWebRendererCoordinator.swift
754 Sources/TerminalController+ControlWorkspaceContext.swift
752 cmuxUITests/CloseWorkspaceCmdDUITests.swift
743 Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift
739 cmuxTests/CLICodexHookTimeoutRegressionTests.swift
738 Packages/macOS/CMUXProjectModel/Sources/CMUXProjectModel/XcodeProjectAdapter.swift
718 Packages/Shared/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Coordinator/AuthCoordinator.swift
Expand Down Expand Up @@ -167,7 +167,7 @@
627 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/KeyboardShortcutsSection.swift
620 cmuxTests/FinderFileDropRegressionTests.swift
620 cmuxTests/TerminalNotificationQueueTests.swift
613 Sources/SettingsNavigation.swift
615 Sources/SettingsNavigation.swift
608 cmuxUITests/FeedSidebarUITests.swift
607 Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift
607 Sources/SessionIndexModels.swift
Expand All @@ -179,6 +179,7 @@
596 cmuxTests/CmuxEventBusTests.swift
594 cmuxTests/PortalTabDragRoutingTests.swift
591 Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/DefaultsValueModelLifecycleTests.swift
590 Sources/Panels/BrowserNavigationDelegate.swift
589 Packages/macOS/CmuxWorkspaces/Sources/CmuxWorkspaces/Coordinators/WorkspaceGroupCoordinator.swift
588 cmuxTests/CommandPaletteShortcutCustomizationTests.swift
586 Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+PortScan.swift
Expand All @@ -191,14 +192,14 @@
574 Packages/macOS/CmuxWorkspaces/Tests/CmuxWorkspacesTests/WorkspaceCoordinatorTests.swift
572 Sources/Feed/FeedTextEditorDebugWindowController.swift
568 Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/MobileTerminalRenderGrid.swift
567 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserSection.swift
567 Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/ConfigDiscovery/GhosttyConfigDiscovery.swift
563 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Pane/ControlCommandCoordinator+Pane.swift
562 cmuxTests/AgentExecutableResolverTests.swift
561 cmuxTests/GhosttyConfigPathResolverTests.swift
560 cmuxTests/CLISSHPTYResizeInputTests.swift
558 Packages/macOS/CmuxGit/Sources/CmuxGit/Parsing/GitMetadataService+Config.swift
553 Sources/RightSidebarPanelView.swift
552 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserSection.swift
549 Sources/Panels/BrowserAutomation.swift
547 Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/BackingUpPairedMacStore.swift
546 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AutomationSection.swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ public struct BrowserCatalogSection: SettingCatalogSection {
userDefaultsKey: "browserHiddenWebViewDiscardDelaySeconds"
)

/// Shows a save panel for each browser download instead of saving directly
/// to the user's Downloads folder.
public let askWhereToSaveDownloads = DefaultsKey<Bool>(
id: "browser.askWhereToSaveDownloads",
defaultValue: false,
userDefaultsKey: "browserAskWhereToSaveDownloads"
)

public let openTerminalLinksInCmuxBrowser = DefaultsKey<Bool>(
id: "browser.openTerminalLinksInCmuxBrowser",
defaultValue: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,13 @@ extension Array where Element == CuratedSettingEntry {
.init(section: .browser, id: "theme", title: "Browser Theme", synonyms: "browser.theme web page theme color scheme light dark system"),
.init(section: .browser, id: "hidden-webview-discard", title: "Browser Memory Saver", synonyms: "browser.discardHiddenWebViews memory hidden tabs webview discard unload reclaim"),
.init(section: .browser, id: "hidden-webview-discard-delay", title: "Memory Saver Delay", synonyms: "browser.hiddenWebViewDiscardDelaySeconds memory hidden tabs delay seconds discard unload"),
.init(
section: .browser,
id: "ask-where-to-save-downloads",
title: String(localized: "settings.browser.askWhereToSaveDownloads", defaultValue: "Ask Where to Save Downloads"),
detailText: String(localized: "settings.browser.askWhereToSaveDownloads.subtitle", defaultValue: "When off, browser downloads save directly to Downloads without a save panel."),
synonyms: String(localized: "settings.search.alias.setting.browser.ask-where-to-save-downloads", defaultValue: "browser.askWhereToSaveDownloads downloads save panel folder attachments files pdf gmail")
),
.init(section: .browser, id: "terminal-links", title: "Open Terminal Links in cmux Browser", synonyms: "browser.openTerminalLinksInCmuxBrowser click url terminal links open in browser href"),
.init(section: .browser, id: "intercept-open", title: "Intercept open http(s) in Terminal", synonyms: "browser.interceptTerminalOpenCommandInCmuxBrowser open command http https url terminal intercept"),
.init(section: .browser, id: "host-whitelist", title: "Hosts to Open in Embedded Browser", synonyms: "browser.hostsToOpenInEmbeddedBrowser allowlist whitelist host wildcard domain embedded browser"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public struct BrowserSection: View {
@State private var theme: DefaultsValueModel<BrowserThemeMode>
@State private var discardEnabled: DefaultsValueModel<Bool>
@State private var discardDelay: DefaultsValueModel<Double>
@State private var askWhereToSaveDownloads: DefaultsValueModel<Bool>
@State private var openTermLinks: DefaultsValueModel<Bool>
@State private var interceptOpen: DefaultsValueModel<Bool>
@State private var hosts: DefaultsValueModel<String>
Expand Down Expand Up @@ -55,6 +56,7 @@ public struct BrowserSection: View {
_theme = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.browser.theme))
_discardEnabled = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.browser.discardHiddenWebViews))
_discardDelay = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.browser.hiddenWebViewDiscardDelaySeconds))
_askWhereToSaveDownloads = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.browser.askWhereToSaveDownloads))
_openTermLinks = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.browser.openTerminalLinksInCmuxBrowser))
_interceptOpen = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.browser.interceptTerminalOpenCommandInCmuxBrowser))
_hosts = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.browser.hostsToOpenInEmbeddedBrowser))
Expand Down Expand Up @@ -83,7 +85,7 @@ public struct BrowserSection: View {
Button(String(localized: "settings.browser.history.clearDialog.cancel", defaultValue: "Cancel"), role: .cancel) {}
} message: {
Text(String(localized: "settings.browser.history.clearDialog.message", defaultValue: "This removes visited-page suggestions from the browser omnibar."))
}.task { startSettingsObservation([disabled, engine, customName, customURL, suggestions, theme, discardEnabled, discardDelay, openTermLinks, interceptOpen, hosts, external, httpAllowlist, importHint, reactGrab]) }
}.task { startSettingsObservation([disabled, engine, customName, customURL, suggestions, theme, discardEnabled, discardDelay, askWhereToSaveDownloads, openTermLinks, interceptOpen, hosts, external, httpAllowlist, importHint, reactGrab]) }
}

@ViewBuilder
Expand Down Expand Up @@ -214,6 +216,19 @@ public struct BrowserSection: View {
}
SettingsCardDivider()

// Download Save Prompt
SettingsCardRow(
configurationReview: .json("browser.askWhereToSaveDownloads"),
String(localized: "settings.browser.askWhereToSaveDownloads", defaultValue: "Ask Where to Save Downloads"),
subtitle: String(localized: "settings.browser.askWhereToSaveDownloads.subtitle", defaultValue: "When off, browser downloads save directly to Downloads without a save panel.")
) {
Toggle("", isOn: Binding(get: { askWhereToSaveDownloads.current }, set: { askWhereToSaveDownloads.set($0) }))
.labelsHidden()
.controlSize(.small)
.accessibilityIdentifier("SettingsBrowserAskWhereToSaveDownloadsToggle")
}
SettingsCardDivider()

// Open Terminal Links
SettingsCardRow(
configurationReview: .json("browser.openTerminalLinksInCmuxBrowser"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ struct SettingsRowAnchorResolutionTests {
"browser.defaultSearchEngine",
"browser.discardHiddenWebViews",
"browser.hiddenWebViewDiscardDelaySeconds",
"browser.askWhereToSaveDownloads",
"browser.hostsToOpenInEmbeddedBrowser",
"browser.interceptTerminalOpenCommandInCmuxBrowser",
"browser.openTerminalLinksInCmuxBrowser",
Expand Down
Loading
Loading