From 1c25c6bd306cb7ec6b068c433c414b684c5f5a8a Mon Sep 17 00:00:00 2001
From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
Date: Tue, 10 Mar 2026 21:41:01 -0700
Subject: [PATCH] Keep internal tab drags out of Finder
---
Resources/Info.plist | 14 +-----------
Sources/ContentView.swift | 35 ++++++++++++++++++++++++++++++
Sources/WorkspaceContentView.swift | 1 +
3 files changed, 37 insertions(+), 13 deletions(-)
diff --git a/Resources/Info.plist b/Resources/Info.plist
index f1beb4f9..48d4f800 100644
--- a/Resources/Info.plist
+++ b/Resources/Info.plist
@@ -93,27 +93,15 @@
- UTExportedTypeDeclarations
+ UTImportedTypeDeclarations
UTTypeIdentifier
com.splittabbar.tabtransfer
- UTTypeDescription
- Bonsplit Tab Transfer
- UTTypeConformsTo
-
- public.data
-
UTTypeIdentifier
com.cmux.sidebar-tab-reorder
- UTTypeDescription
- cmux Sidebar Tab Reorder
- UTTypeConformsTo
-
- public.data
-
NSAppTransportSecurity
diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift
index 6c03b213..01adb67d 100644
--- a/Sources/ContentView.swift
+++ b/Sources/ContentView.swift
@@ -82,6 +82,40 @@ func sidebarSelectedWorkspaceForegroundNSColor(opacity: CGFloat) -> NSColor {
let clampedOpacity = max(0, min(opacity, 1))
return NSColor.white.withAlphaComponent(clampedOpacity)
}
+
+#if compiler(>=6.2)
+@available(macOS 26.0, *)
+enum InternalTabDragConfigurationProvider {
+ // These drags only make sense inside cmux. Outside the app, Finder should
+ // reject them instead of materializing placeholder files from the payload.
+ static let value = DragConfiguration(
+ operationsWithinApp: .init(allowCopy: false, allowMove: true, allowDelete: false),
+ operationsOutsideApp: .init(allowCopy: false, allowMove: false, allowDelete: false)
+ )
+}
+#endif
+
+private struct InternalTabDragConfigurationModifier: ViewModifier {
+ @ViewBuilder
+ func body(content: Content) -> some View {
+ #if compiler(>=6.2)
+ if #available(macOS 26.0, *) {
+ content.dragConfiguration(InternalTabDragConfigurationProvider.value)
+ } else {
+ content
+ }
+ #else
+ content
+ #endif
+ }
+}
+
+extension View {
+ func internalOnlyTabDrag() -> some View {
+ modifier(InternalTabDragConfigurationModifier())
+ }
+}
+
struct ShortcutHintPillBackground: View {
var emphasis: Double = 1.0
@@ -9662,6 +9696,7 @@ private struct TabItemView: View {
dropIndicator = nil
return SidebarTabDragPayload.provider(for: tab.id)
}
+ .internalOnlyTabDrag()
.onDrop(of: SidebarTabDragPayload.dropContentTypes, delegate: SidebarTabDropDelegate(
targetTabId: tab.id,
tabManager: tabManager,
diff --git a/Sources/WorkspaceContentView.swift b/Sources/WorkspaceContentView.swift
index 700e65c0..0b955943 100644
--- a/Sources/WorkspaceContentView.swift
+++ b/Sources/WorkspaceContentView.swift
@@ -106,6 +106,7 @@ struct WorkspaceContentView: View {
workspace.bonsplitController.focusPane(paneId)
}
}
+ .internalOnlyTabDrag()
// Split zoom swaps Bonsplit between the full split tree and a single pane view.
// Recreate the Bonsplit subtree on zoom enter/exit so stale pre-zoom pane chrome
// cannot remain stacked above portal-hosted browser content.