Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 14 additions & 13 deletions .github/swift-file-length-budget.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,36 @@
# Reduce counts as files shrink. CI fails if tracked files exceed this budget.
20362 CLI/cmux.swift
17115 Sources/TerminalController.swift
15872 Sources/ContentView.swift
15902 Sources/ContentView.swift
14164 Sources/AppDelegate.swift
13686 Sources/Workspace.swift
13354 Sources/GhosttyTerminalView.swift
13403 Sources/GhosttyTerminalView.swift
10600 Sources/Panels/BrowserPanel.swift
8412 Sources/cmuxApp.swift
7495 Sources/TabManager.swift
7492 Sources/TabManager.swift
6811 Sources/Panels/BrowserPanelView.swift
5218 cmuxTests/AppDelegateShortcutRoutingTests.swift
4646 cmuxTests/WorkspaceUnitTests.swift
4639 cmuxTests/TerminalAndGhosttyTests.swift
4503 cmuxTests/GhosttyConfigTests.swift
4469 cmuxTests/WorkspaceRemoteConnectionTests.swift
4324 Sources/BrowserWindowPortal.swift
4557 cmuxTests/GhosttyConfigTests.swift
4465 cmuxTests/WorkspaceRemoteConnectionTests.swift
4226 Sources/BrowserWindowPortal.swift
3840 Sources/Feed/FeedPanelView.swift
3660 cmuxTests/BrowserConfigTests.swift
3229 cmuxTests/BrowserPanelTests.swift
3588 cmuxTests/BrowserConfigTests.swift
3121 cmuxTests/BrowserPanelTests.swift
2826 Sources/CmuxConfig.swift
2679 Sources/KeyboardShortcutSettings.swift
2609 Sources/SessionIndexView.swift
2491 Sources/Panels/CmuxWebView.swift
2292 cmuxTests/TabManagerUnitTests.swift
2188 Sources/TerminalWindowPortal.swift
2171 Sources/TerminalWindowPortal.swift
2153 cmuxTests/SessionPersistenceTests.swift
2112 Sources/Update/UpdateTitlebarAccessory.swift
2026 cmuxTests/CJKIMEInputTests.swift
2035 cmuxTests/WindowAndDragTests.swift
1949 Sources/FileExplorerView.swift
1949 Sources/Panels/BrowserWebAuthnSupport.swift
1941 Sources/SessionIndexStore.swift
1951 cmuxTests/WindowAndDragTests.swift
1784 cmuxTests/ShortcutAndCommandPaletteTests.swift
1692 cmuxTests/CmuxConfigTests.swift
1676 Sources/KeyboardShortcutSettingsFileStore.swift
Expand All @@ -51,13 +51,13 @@
987 Sources/CommandPalette/CommandPaletteSearch.swift
976 cmuxTests/OmnibarAndToolsTests.swift
908 cmuxTests/CommandPaletteSearchEngineTests.swift
844 Sources/WorkspaceContentView.swift
846 Sources/WorkspaceContentView.swift
834 Sources/RestorableAgentSession.swift
829 cmuxUITests/TerminalCmdClickUITests.swift
807 Sources/TerminalSSHSessionDetector.swift
809 cmuxUITests/BonsplitTabDragUITests.swift
701 cmuxUITests/BonsplitTabDragUITests.swift
785 Sources/App/TerminalDirectoryOpenSupport.swift
761 Sources/GhosttyConfig.swift
784 Sources/GhosttyConfig.swift
754 Sources/MainWindowFocusController.swift
752 cmuxUITests/CloseWorkspaceCmdDUITests.swift
719 cmuxUITests/BrowserOmnibarSuggestionsUITests.swift
Expand All @@ -71,6 +71,7 @@
586 Sources/Update/UpdateViewModel.swift
582 cmuxTests/TerminalControllerSocketSecurityTests.swift
573 Sources/Feed/FeedTextEditorDebugWindowController.swift
546 Sources/Windowing/WindowGlassEffect.swift
536 Sources/SessionPersistence.swift
510 Sources/Update/UpdatePopoverView.swift
505 cmuxUITests/DisplayResolutionRegressionUITests.swift
9 changes: 4 additions & 5 deletions Sources/BrowserWindowPortal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2433,13 +2433,12 @@ final class WindowBrowserPortal: NSObject {
}

private func installationTarget(for window: NSWindow) -> (container: NSView, reference: NSView)? {
guard let contentView = window.contentView else { return nil }

if contentView.className == "NSGlassEffectView",
let foreground = contentView.subviews.first(where: { $0 !== hostView }) {
return (contentView, foreground)
if let glassTarget = WindowGlassEffect.portalInstallationTarget(for: window) {
return glassTarget
}

guard let contentView = window.contentView else { return nil }

guard let themeFrame = contentView.superview else { return nil }
return (themeFrame, contentView)
}
Expand Down
48 changes: 39 additions & 9 deletions Sources/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,21 @@ private func attachFileDropOverlay(_ overlay: FileDropOverlayView, to contentVie
])
}

private func fileDropOverlay(_ overlay: FileDropOverlayView, isAttachedTo contentView: NSView, in themeFrame: NSView) -> Bool {
guard overlay.superview === themeFrame else { return false }
let requiredAttributes: [NSLayoutConstraint.Attribute] = [.top, .bottom, .leading, .trailing]
return requiredAttributes.allSatisfy { attribute in
themeFrame.constraints.contains { constraint in
let firstView = constraint.firstItem as? NSView
let secondView = constraint.secondItem as? NSView
return firstView === overlay &&
secondView === contentView &&
constraint.firstAttribute == attribute &&
constraint.secondAttribute == attribute
}
}
}

@discardableResult
func installFileDropOverlay(on window: NSWindow, tabManager: TabManager) -> Bool {
guard let contentView = window.contentView,
Expand All @@ -1481,7 +1496,9 @@ func installFileDropOverlay(on window: NSWindow, tabManager: TabManager) -> Bool
if let existingOverlay {
configureFileDropOverlay(existingOverlay, tabManager: tabManager)
objc_setAssociatedObject(window, &fileDropOverlayKey, existingOverlay, .OBJC_ASSOCIATION_RETAIN)
guard existingOverlay.superview !== themeFrame else { return true }
guard !fileDropOverlay(existingOverlay, isAttachedTo: contentView, in: themeFrame) else {
return true
}
existingOverlay.removeFromSuperview()
attachFileDropOverlay(existingOverlay, to: contentView, in: themeFrame)
return true
Expand Down Expand Up @@ -3685,7 +3702,7 @@ struct ContentView: View {
removeSidebarResizerPointerMonitor()
})

view = AnyView(view.background(WindowAccessor { [appearance] window in
view = AnyView(view.background(WindowAccessor(refreshID: appearance.appKitWindowMutationID) { [appearance] window in
window.identifier = NSUserInterfaceItemIdentifier(windowIdentifier)
window.isRestorable = false
setMinimalModeSidebarTitlebarControlsAvailable(sidebarState.isVisible, in: window)
Expand Down Expand Up @@ -3733,7 +3750,7 @@ struct ContentView: View {
// terminal focus-driven updates can leave stale opaque window fills.
window.backgroundColor = NSColor.white.withAlphaComponent(0.001)
// Configure contentView hierarchy for transparency.
if let contentView = window.contentView {
if let contentView = WindowGlassEffect.originalContentView(for: window) ?? window.contentView {
makeViewHierarchyTransparent(contentView)
}
} else {
Expand All @@ -3745,17 +3762,29 @@ struct ContentView: View {

if shouldApplyWindowGlass {
// Apply liquid glass effect to the window with tint from settings
WindowGlassEffect.apply(to: window, tintColor: appearance.windowGlassSettings.tintColor)
let didChangeGlassRoot = WindowGlassEffect.apply(
to: window,
tintColor: appearance.windowGlassSettings.tintColor,
style: appearance.windowGlassSettings.style
)
if didChangeGlassRoot {
TerminalWindowPortalRegistry.scheduleExternalGeometrySynchronize(for: window)
BrowserWindowPortalRegistry.scheduleExternalGeometrySynchronize(for: window)
}
} else {
WindowGlassEffect.remove(from: window)
if WindowGlassEffect.remove(from: window) {
TerminalWindowPortalRegistry.scheduleExternalGeometrySynchronize(for: window)
BrowserWindowPortalRegistry.scheduleExternalGeometrySynchronize(for: window)
}
}
AppDelegate.shared?.attachUpdateAccessory(to: window)
AppDelegate.shared?.applyWindowDecorations(to: window)
// Let cmux supply the translucent titlebar fills. AppKit's native
// material otherwise blends a lighter strip over the terminal area.
syncNativeTitlebarBackdrop(
in: window,
enabled: true
enabled: true,
usesGlassStyle: shouldApplyWindowGlass
)
AppDelegate.shared?.registerMainWindow(
window,
Expand Down Expand Up @@ -4038,7 +4067,8 @@ struct ContentView: View {

private func syncNativeTitlebarBackdrop(
in window: NSWindow,
enabled: Bool
enabled: Bool,
usesGlassStyle: Bool
) {
guard let titlebarContainer = nativeTitlebarContainer(in: window) else { return }
let titlebarView = firstNativeDescendant(
Expand Down Expand Up @@ -4070,10 +4100,10 @@ struct ContentView: View {
}

titlebarContainer.wantsLayer = true
titlebarContainer.layer?.backgroundColor = nil
titlebarContainer.layer?.backgroundColor = usesGlassStyle ? NSColor.clear.cgColor : nil
titlebarContainer.layer?.isOpaque = false
titlebarView?.wantsLayer = true
titlebarView?.layer?.backgroundColor = nil
titlebarView?.layer?.backgroundColor = usesGlassStyle ? NSColor.clear.cgColor : nil
titlebarView?.layer?.isOpaque = false
for titlebarBackgroundView in titlebarBackgroundViews {
titlebarBackgroundView.isHidden = true
Expand Down
23 changes: 23 additions & 0 deletions Sources/GhosttyConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct GhosttyConfig {
// Colors (from theme or config)
var backgroundColor: NSColor = NSColor(hex: "#272822")!
var backgroundOpacity: Double = 1.0
var backgroundBlur: GhosttyBackgroundBlur = .disabled
var foregroundColor: NSColor = NSColor(hex: "#fdfff1")!
var cursorColor: NSColor = NSColor(hex: "#c0c1b5")!
var cursorTextColor: NSColor = NSColor(hex: "#8d8e82")!
Expand Down Expand Up @@ -416,6 +417,10 @@ struct GhosttyConfig {
if let opacity = Double(value) {
backgroundOpacity = opacity
}
case "background-blur":
if let parsedBlur = Self.parseBackgroundBlur(value) {
backgroundBlur = parsedBlur
}
case "foreground":
if let color = NSColor(hex: value) {
foregroundColor = color
Expand Down Expand Up @@ -479,6 +484,24 @@ struct GhosttyConfig {
return parsed
}

private static func parseBackgroundBlur(_ value: String) -> GhosttyBackgroundBlur? {
switch value {
case "false", "0":
return .disabled
case "true":
return .radius(20)
case "macos-glass-regular":
return .macosGlassRegular
case "macos-glass-clear":
return .macosGlassClear
default:
guard let radius = parseIntegerLiteral(value), radius > 0, radius <= Int(UInt8.max) else {
return nil
}
return .radius(radius)
}
}

mutating func loadTheme(_ name: String) {
loadTheme(
name,
Expand Down
Loading
Loading