diff --git a/CLI/CLI+Hooks.swift b/CLI/CLI+Hooks.swift index b18291729..0d3238f2e 100644 --- a/CLI/CLI+Hooks.swift +++ b/CLI/CLI+Hooks.swift @@ -2368,8 +2368,6 @@ extension ProgramaCLI { return pruned(.table(root)) } - - private func codexParseTOML(_ source: String, path: String) throws -> CodexTOMLValue { let decoder = TOMLDecoder() decoder.limits = .init( diff --git a/Sources/CommandPaletteSearchEngine.swift b/Sources/CommandPaletteSearchEngine.swift index 9c57a0ba7..ff630e1a3 100644 --- a/Sources/CommandPaletteSearchEngine.swift +++ b/Sources/CommandPaletteSearchEngine.swift @@ -1012,4 +1012,3 @@ enum CommandPaletteSearchEngine { return max(fuzzyScore, titleScore + titleMatchBonus) } } - diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 63d961537..7360d7d7a 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -4058,7 +4058,6 @@ struct ContentView: View { } } - for target in TerminalDirectoryOpenTarget.commandPaletteShortcutTargets { registry.register(commandId: target.commandPaletteCommandId) { if !openFocusedDirectory(in: target) { diff --git a/Sources/FileDropOverlayView.swift b/Sources/FileDropOverlayView.swift index 1d463c086..ba2938580 100644 --- a/Sources/FileDropOverlayView.swift +++ b/Sources/FileDropOverlayView.swift @@ -3,7 +3,6 @@ import AppKit import Bonsplit - /// Transparent NSView installed on the window's theme frame (above the NSHostingView) to /// handle file/URL drags from Finder. Nested NSHostingController layers (created by bonsplit's /// SinglePaneWrapper) prevent AppKit's NSDraggingDestination routing from reaching deeply diff --git a/Sources/GhosttySurfaceScrollView.swift b/Sources/GhosttySurfaceScrollView.swift index 328857763..26c7503a6 100644 --- a/Sources/GhosttySurfaceScrollView.swift +++ b/Sources/GhosttySurfaceScrollView.swift @@ -219,7 +219,6 @@ final class GhosttySurfaceScrollView: NSView { } private(set) var searchFocusTarget: SearchFocusTarget = .searchField - #if DEBUG private var lastDropZoneOverlayLogSignature: String? private var lastDragGeometryLogSignature: String? diff --git a/Sources/GhosttyTerminalView+Accessibility.swift b/Sources/GhosttyTerminalView+Accessibility.swift index 4d48e420f..f7cc920c9 100644 --- a/Sources/GhosttyTerminalView+Accessibility.swift +++ b/Sources/GhosttyTerminalView+Accessibility.swift @@ -66,7 +66,6 @@ extension GhosttyNSView { } } - override func accessibilitySelectedTextRange() -> NSRange { selectedRange() } diff --git a/Sources/Panels/ReviewPanel.swift b/Sources/Panels/ReviewPanel.swift index e4cd110b3..bed6956c8 100644 --- a/Sources/Panels/ReviewPanel.swift +++ b/Sources/Panels/ReviewPanel.swift @@ -98,8 +98,6 @@ final class ReviewPanel: Panel, ObservableObject { focusFlashToken += 1 } - // MARK: - Mode - // MARK: - Refresh /// Kicks off `ReviewDiffProber.diffSnapshot` on a background queue and publishes the diff --git a/Sources/ProgramaConfigExecutor.swift b/Sources/ProgramaConfigExecutor.swift index be573c957..afcb6e097 100644 --- a/Sources/ProgramaConfigExecutor.swift +++ b/Sources/ProgramaConfigExecutor.swift @@ -137,8 +137,6 @@ struct ProgramaConfigExecutor { } } - - /// The security decision, isolated from the UI so it can be tested directly. /// /// | directory | `confirm:` | prompt? | @@ -324,7 +322,6 @@ struct ProgramaConfigExecutor { private static func describeSurface(_ surface: ProgramaSurfaceDefinition) -> String? { var parts: [String] = [] - if let command = surface.command, !command.isEmpty { parts.append(sanitizeForDisplay(command)) } diff --git a/Sources/ProgramaSettingsFileStore.swift b/Sources/ProgramaSettingsFileStore.swift index 5610eaad4..84eed652a 100644 --- a/Sources/ProgramaSettingsFileStore.swift +++ b/Sources/ProgramaSettingsFileStore.swift @@ -522,7 +522,6 @@ final class ProgramaSettingsFileStore { } } - private func parseSidebarAppearanceSection( _ section: [String: Any], sourcePath: String, diff --git a/Sources/SessionPersistence.swift b/Sources/SessionPersistence.swift index e34c70261..640deb7bf 100644 --- a/Sources/SessionPersistence.swift +++ b/Sources/SessionPersistence.swift @@ -262,7 +262,6 @@ struct SessionTerminalPanelSnapshot: Codable, Sendable { var scrollback: String? } - struct SessionMarkdownPanelSnapshot: Codable, Sendable { var filePath: String } @@ -1002,7 +1001,6 @@ enum SessionPersistenceStore { } } - if let markdown = panel.markdown, !isValidString( markdown.filePath, @@ -1042,7 +1040,6 @@ enum SessionPersistenceStore { ) } - private static func isValidString( _ value: String?, maxBytes: Int = SessionPersistencePolicy.maxMetadataStringBytes diff --git a/Sources/SettingsComponents.swift b/Sources/SettingsComponents.swift index 9ccd5e491..42e30e885 100644 --- a/Sources/SettingsComponents.swift +++ b/Sources/SettingsComponents.swift @@ -4,7 +4,6 @@ import Darwin import Bonsplit import UniformTypeIdentifiers - struct SettingsSectionHeader: View { let title: String diff --git a/Sources/SettingsModels.swift b/Sources/SettingsModels.swift index 60106aa01..d17a506da 100644 --- a/Sources/SettingsModels.swift +++ b/Sources/SettingsModels.swift @@ -4,7 +4,6 @@ import Darwin import Bonsplit import UniformTypeIdentifiers - enum WorkspacePresentationModeSettings { static let modeKey = "workspacePresentationMode" @@ -79,8 +78,6 @@ enum AppearanceSettings { } } - - enum QuitWarningSettings { static let warnBeforeQuitKey = "warnBeforeQuitShortcut" static let defaultWarnBeforeQuit = true @@ -224,7 +221,6 @@ enum PreferredEditorSettings { } } - /// The tabs the settings window is split across. /// /// Tabs group settings by the diff --git a/Sources/SettingsView.swift b/Sources/SettingsView.swift index 0e5936a0a..6b9318b01 100644 --- a/Sources/SettingsView.swift +++ b/Sources/SettingsView.swift @@ -6,7 +6,6 @@ import UniformTypeIdentifiers import CoreImage import CoreImage.CIFilterBuiltins - /// Height tracks the selected tab's content; width is free above a floor derived from /// the tab labels, so the six segments never truncate. enum SettingsWindowMetrics { @@ -464,7 +463,6 @@ struct SettingsView: View { } .managedBySettingsFile(settingsFileStatus.isManaged(MenuBarExtraSettings.showInMenuBarKey)) - SettingsCardDivider() SettingsCardRow( @@ -993,7 +991,6 @@ struct SettingsView: View { shortcutResetToken = UUID() } - } /// Which Settings rows settings.json currently controls, and the file's parse error if its last @@ -1548,7 +1545,6 @@ private struct ThemePickerRow: View { } } - private struct ShortcutSettingRow: View { let action: KeyboardShortcutSettings.Action @State private var shortcut: StoredShortcut diff --git a/Sources/SidebarVisuals.swift b/Sources/SidebarVisuals.swift index fa0780f5b..04ddd1628 100644 --- a/Sources/SidebarVisuals.swift +++ b/Sources/SidebarVisuals.swift @@ -1,8 +1,8 @@ // Sidebar visual chrome. -// Holds sidebar footer/help-menu/scrim/blur/material/tint/preset enums, +// Holds sidebar footer/help-menu/blur/material/tint/preset enums, // visual-effect/backdrop views, and the NSColor extension. // -// SidebarFooter, SidebarTopScrim, SidebarScrollViewResolver, SidebarEmptyArea, +// SidebarFooter, SidebarScrollViewResolver, SidebarEmptyArea, // ClearScrollBackground, DraggableFolderIcon, TitlebarLeadingInsetReader, and // SidebarBackdrop are internal because VerticalTabsSidebar and ContentView // (in ContentView.swift) construct/reference them. @@ -623,71 +623,6 @@ enum SidebarTerminalAppearance { } } -struct SidebarTopScrim: View { - let height: CGFloat - @AppStorage("sidebarMatchTerminalBackground") private var matchTerminalBackground = false - @Environment(\.accessibilityReduceTransparency) private var accessibilityReduceTransparency - @State private var terminalColor = Color(nsColor: GhosttyBackgroundTheme.currentColor()) - - private var usesNativeGlassSidebar: Bool { - WindowGlassEffect.isAvailable && !accessibilityReduceTransparency - } - - var body: some View { - Group { - if usesNativeGlassSidebar { - // The glass surface provides its own top treatment; a material - // scrim reads as an opaque gray band on it (Maps has none). - Color.clear - } else if matchTerminalBackground { - // The system material stays light over a terminal-coloured sidebar, which - // reads as a grey wash across the top. Fade the terminal colour instead. - LinearGradient( - colors: [ - terminalColor, - terminalColor.opacity(0.85), - terminalColor.opacity(0.4), - Color.clear - ], - startPoint: .top, - endPoint: .bottom - ) - } else { - SidebarTopBlurEffect() - .mask( - LinearGradient( - colors: [ - Color.black.opacity(0.95), - Color.black.opacity(0.75), - Color.black.opacity(0.35), - Color.clear - ], - startPoint: .top, - endPoint: .bottom - ) - ) - } - } - .frame(height: height) - .onReceive(NotificationCenter.default.publisher(for: .ghosttyDefaultBackgroundDidChange)) { _ in - terminalColor = Color(nsColor: GhosttyBackgroundTheme.currentColor()) - } - } -} - -private struct SidebarTopBlurEffect: NSViewRepresentable { - func makeNSView(context: Context) -> NSVisualEffectView { - let view = NSVisualEffectView() - view.blendingMode = .withinWindow - view.material = .underWindowBackground - view.state = .active - view.isEmphasized = false - return view - } - - func updateNSView(_ nsView: NSVisualEffectView, context: Context) {} -} - struct SidebarScrollViewResolver: NSViewRepresentable { let onResolve: (NSScrollView?) -> Void @@ -890,7 +825,6 @@ enum SidebarTrailingAccessoryWidthPolicy { } } - struct MiddleClickCapture: NSViewRepresentable { let onMiddleClick: () -> Void diff --git a/Sources/TabItemView.swift b/Sources/TabItemView.swift index 28357a8c7..cf5ead030 100644 --- a/Sources/TabItemView.swift +++ b/Sources/TabItemView.swift @@ -101,7 +101,6 @@ private enum SidebarWorktreeCreationResult: Sendable { case failure(message: String) } - // PERF: TabItemView is Equatable so SwiftUI skips body re-evaluation when // the parent rebuilds with unchanged values. Without this, every TabManager // or NotificationStore publish causes ALL tab items to re-evaluate (~18% of @@ -1586,18 +1585,6 @@ struct TabItemView: View, Equatable { } } - private func shortenPath(_ path: String, home: String) -> String { - let trimmed = path.trimmingCharacters(in: .whitespacesAndNewlines) - guard !trimmed.isEmpty else { return path } - if trimmed == home { - return "~" - } - if trimmed.hasPrefix(home + "/") { - return "~" + trimmed.dropFirst(home.count) - } - return trimmed - } - private struct PullRequestStatusIcon: View { let status: SidebarPullRequestStatus let color: Color diff --git a/Sources/TabManager+UITestHarness.swift b/Sources/TabManager+UITestHarness.swift index 1daf126f1..54a112366 100644 --- a/Sources/TabManager+UITestHarness.swift +++ b/Sources/TabManager+UITestHarness.swift @@ -440,7 +440,6 @@ extension TabManager { tab.focusPanel(bottomRight.id) _ = tab.closePanel(bottomRight.id, force: true) - // Capture final state after Bonsplit/AppKit/Ghostty geometry reconciliation. // We avoid sleep-based timing and converge over a few main-actor turns. @MainActor func collectSplitCloseRightState() -> (data: [String: String], settled: Bool) { diff --git a/Sources/TabManager.swift b/Sources/TabManager.swift index 2272529ab..32c828a93 100644 --- a/Sources/TabManager.swift +++ b/Sources/TabManager.swift @@ -542,7 +542,6 @@ final class NotificationBurstCoalescer { } } - @MainActor class TabManager: ObservableObject { struct LifecycleResourceSnapshot { @@ -2818,7 +2817,6 @@ class TabManager: ObservableObject { workspace(withId: tabId)?.focusedPanelId } - /// Returns the focused panel if it's a MarkdownPanel, nil otherwise var focusedMarkdownPanel: MarkdownPanel? { guard let tab = selectedWorkspace, @@ -2826,12 +2824,6 @@ class TabManager: ObservableObject { return tab.panels[panelId] as? MarkdownPanel } - - - - - - /// Backwards compatibility: returns the focused surface ID func focusedSurfaceId(for tabId: UUID) -> UUID? { focusedPanelId(for: tabId) diff --git a/Sources/TerminalController+Debug.swift b/Sources/TerminalController+Debug.swift index 295f0590b..8d51a2e11 100644 --- a/Sources/TerminalController+Debug.swift +++ b/Sources/TerminalController+Debug.swift @@ -4,70 +4,6 @@ import Carbon.HIToolbox @preconcurrency import Foundation import Bonsplit -#if DEBUG -@MainActor -private final class TerminalSurfaceWaitState { - let terminalSurface: TerminalSurface - let finish: () -> Void - var readyObserver: NSObjectProtocol? - var hostedViewObserver: NSObjectProtocol? - private var completed = false - - init(terminalSurface: TerminalSurface, finish: @escaping () -> Void) { - self.terminalSurface = terminalSurface - self.finish = finish - } - - func complete() { - guard !completed else { return } - completed = true - if let readyObserver { - NotificationCenter.default.removeObserver(readyObserver) - self.readyObserver = nil - } - if let hostedViewObserver { - NotificationCenter.default.removeObserver(hostedViewObserver) - self.hostedViewObserver = nil - } - finish() - } - - func installReadyObserver(_ observer: NSObjectProtocol) { - if completed { - NotificationCenter.default.removeObserver(observer) - } else { - readyObserver = observer - } - } - - func installHostedViewObserver(_ observer: NSObjectProtocol) { - if completed { - NotificationCenter.default.removeObserver(observer) - } else { - hostedViewObserver = observer - } - } - - func completeIfSurfaceReady() { - guard terminalSurface.surface != nil else { return } - complete() - } - - func cancel() { - guard !completed else { return } - completed = true - if let readyObserver { - NotificationCenter.default.removeObserver(readyObserver) - self.readyObserver = nil - } - if let hostedViewObserver { - NotificationCenter.default.removeObserver(hostedViewObserver) - self.hostedViewObserver = nil - } - } -} -#endif - extension TerminalController { #if DEBUG // MARK: - V2 Debug / Test-only Methods @@ -337,8 +273,6 @@ extension TerminalController { } } - - nonisolated func v2DebugSidebarVisible(params: [String: Any]) -> V2CallResult { guard let windowId = v2UUID(params, "window_id") else { return v2InvalidParam("window_id") @@ -1444,7 +1378,6 @@ extension TerminalController { ) } - return LayoutDebugSelectedPanel( paneId: paneIdStr, paneFrame: paneFrame, @@ -1621,115 +1554,6 @@ extension TerminalController { return result } - private func resolveTerminalPanel(from arg: String, tabManager: TabManager) -> TerminalPanel? { - guard let tabId = tabManager.selectedTabId, - let tab = tabManager.tabs.first(where: { $0.id == tabId }) else { - return nil - } - - if let uuid = UUID(uuidString: arg) { - return tab.terminalPanel(for: uuid) - } - - if let index = Int(arg), index >= 0 { - let panels = orderedPanels(in: tab) - guard index < panels.count else { return nil } - return panels[index] as? TerminalPanel - } - - return nil - } - -#if DEBUG - func v2AwaitCallback( - timeout: TimeInterval, - start: (@escaping (T) -> Void) -> Void - ) -> T? { - if Thread.isMainThread { - // Each nested wait polls its own flag, so another client's completion cannot end it. - var resolved = false - var result: T? - start { value in - guard !resolved else { return } - resolved = true - result = value - } - let deadline = Date().addingTimeInterval(timeout) - while !resolved && Date() < deadline { - CFRunLoopRunInMode(.defaultMode, 0.05, true) - } - guard resolved else { - resolved = true // a callback arriving after the deadline is dropped - return nil - } - return result - } - - let semaphore = DispatchSemaphore(value: 0) - let lock = NSLock() - var result: T? - start { value in - lock.lock() - result = value - lock.unlock() - semaphore.signal() - } - guard semaphore.wait(timeout: .now() + timeout) == .success else { - return nil - } - lock.lock() - defer { lock.unlock() } - return result - } - - private func resolveTerminalSurface(from arg: String, tabManager: TabManager, waitUpTo timeout: TimeInterval = 0.6) -> ghostty_surface_t? { - guard let terminalPanel = resolveTerminalPanel(from: arg, tabManager: tabManager) else { return nil } - return waitForTerminalSurface(terminalPanel, waitUpTo: timeout) - } - - private func waitForTerminalSurface(_ terminalPanel: TerminalPanel, waitUpTo timeout: TimeInterval = 0.6) -> ghostty_surface_t? { - if let surface = terminalPanel.surface.surface { return surface } - - let terminalSurface = terminalPanel.surface - terminalSurface.requestBackgroundSurfaceStartIfNeeded() - var waitState: TerminalSurfaceWaitState? - _ = v2AwaitCallback(timeout: timeout) { finish in - let state = TerminalSurfaceWaitState( - terminalSurface: terminalSurface, - finish: { finish(()) } - ) - waitState = state - let readyObserver = NotificationCenter.default.addObserver( - forName: .terminalSurfaceDidBecomeReady, - object: terminalSurface, - queue: .main - ) { [state] _ in - MainActor.assumeIsolated { - state.complete() - } - } - state.installReadyObserver(readyObserver) - let hostedViewObserver = NotificationCenter.default.addObserver( - forName: .terminalSurfaceHostedViewDidMoveToWindow, - object: terminalSurface, - queue: .main - ) { [state] _ in - MainActor.assumeIsolated { - state.completeIfSurfaceReady() - } - } - state.installHostedViewObserver(hostedViewObserver) - - if terminalSurface.surface != nil { - state.complete() - } - } - waitState?.cancel() - - return terminalPanel.surface.surface - } -#endif - private func resolveSurfaceId(from arg: String, tab: Workspace) -> UUID? { if let uuid = UUID(uuidString: arg), tab.panels[uuid] != nil { return uuid @@ -1754,8 +1578,4 @@ extension TerminalController { return nil } } - - - // MARK: - Bonsplit Pane Commands - } diff --git a/Sources/TerminalController+Surface.swift b/Sources/TerminalController+Surface.swift index 810c7a0f9..2f08839c4 100644 --- a/Sources/TerminalController+Surface.swift +++ b/Sources/TerminalController+Surface.swift @@ -132,7 +132,7 @@ extension TerminalController { let panels = orderedPanels(in: ws) let surfaces: [[String: Any]] = panels.enumerated().map { index, panel in let paneUUID = paneByPanelId[panel.id] - var item: [String: Any] = [ + let item: [String: Any] = [ "id": panel.id.uuidString, "ref": v2Ref(kind: .surface, uuid: panel.id), "index": index, diff --git a/Sources/TerminalController.swift b/Sources/TerminalController.swift index 7c1f109b0..7c8399b27 100644 --- a/Sources/TerminalController.swift +++ b/Sources/TerminalController.swift @@ -1665,9 +1665,8 @@ class TerminalController { let trimmed = command.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return "ERROR: Empty command" } - // The v1 line-based protocol was removed; v2 JSON-RPC is the only protocol. - // Stale clients still speaking v1 get a terse, identifiable error rather than - // a confusing JSON parse failure. + // Only v2 JSON-RPC is accepted. Stale clients still speaking the v1 line protocol + // get a terse, identifiable error rather than a confusing JSON parse failure. guard trimmed.hasPrefix("{") else { return v2Error( id: nil, @@ -2393,7 +2392,6 @@ class TerminalController { return result } - nonisolated func v2HasNonNullParam(_ params: [String: Any], _ key: String) -> Bool { guard let raw = params[key] else { return false } return !(raw is NSNull) diff --git a/Sources/TerminalSurface.swift b/Sources/TerminalSurface.swift index 0775fa269..56188e75e 100644 --- a/Sources/TerminalSurface.swift +++ b/Sources/TerminalSurface.swift @@ -462,7 +462,6 @@ final class TerminalSurface: Identifiable, ObservableObject { } } - func updateWorkspaceId(_ newTabId: UUID) { tabId = newTabId attachedView?.tabId = newTabId diff --git a/Sources/UITestRecorder.swift b/Sources/UITestRecorder.swift index b11441aae..4592217d1 100644 --- a/Sources/UITestRecorder.swift +++ b/Sources/UITestRecorder.swift @@ -43,4 +43,3 @@ enum UITestRecorder { } } #endif - diff --git a/Sources/VerticalTabsSidebar.swift b/Sources/VerticalTabsSidebar.swift index fa824a64d..db5d449bd 100644 --- a/Sources/VerticalTabsSidebar.swift +++ b/Sources/VerticalTabsSidebar.swift @@ -183,8 +183,7 @@ struct VerticalTabsSidebar: View { private var usesBackdropSidebar: Bool { WindowGlassEffect.isAvailable && !accessibilityReduceTransparency } - /// Resolved from `ChromeDensity.sidebarRowSpacing` -- was a fixed `2pt` - /// constant before density tokens (see `WindowChrome.swift`). + /// Resolved from `ChromeDensity.sidebarRowSpacing` (see `WindowChrome.swift`). private var tabRowSpacing: CGFloat { ChromeDensity.sidebarRowSpacing } #if DEBUG // Subscribing here makes the whole sidebar (rows, spacing, header/panel diff --git a/Sources/WindowSwizzles.swift b/Sources/WindowSwizzles.swift index b71e52eb3..d8f6a1e38 100644 --- a/Sources/WindowSwizzles.swift +++ b/Sources/WindowSwizzles.swift @@ -5,20 +5,6 @@ import CoreServices import ObjectiveC.runtime private var programaFirstResponderGuardHitViewContext: NSView? -private var programaWindowFirstResponderBypassDepth = 0 - -@discardableResult -func programaWithWindowFirstResponderBypass(_ body: () -> T) -> T { - programaWindowFirstResponderBypassDepth += 1 - defer { - programaWindowFirstResponderBypassDepth = max(0, programaWindowFirstResponderBypassDepth - 1) - } - return body() -} - -func programaIsWindowFirstResponderBypassActive() -> Bool { - programaWindowFirstResponderBypassDepth > 0 -} #if DEBUG // DEBUG-only: the swizzle exists solely for typing-timing instrumentation. @@ -53,16 +39,6 @@ extension NSApplication { extension NSWindow { @objc func programa_makeFirstResponder(_ responder: NSResponder?) -> Bool { - if programaIsWindowFirstResponderBypassActive() { -#if DEBUG - dlog( - "focus.guard bypassFirstResponder responder=\(String(describing: responder.map { type(of: $0) })) " + - "window=\(ObjectIdentifier(self))" - ) -#endif - return false - } - if AppDelegate.shared?.shouldBlockFirstResponderChangeWhileCommandPaletteVisible( window: self, responder: responder @@ -438,7 +414,6 @@ extension NSWindow { } } - } // User-initiated main-window close (red button, File > Close, Close Window shortcut) diff --git a/Sources/Workspace+Bonsplit.swift b/Sources/Workspace+Bonsplit.swift index 9b815c81d..17f5eb11a 100644 --- a/Sources/Workspace+Bonsplit.swift +++ b/Sources/Workspace+Bonsplit.swift @@ -123,7 +123,6 @@ extension Workspace: @preconcurrency BonsplitDelegate { } } - private func applyTabSelectionNow( tabId: TabID, inPane pane: PaneID, @@ -203,7 +202,6 @@ extension Workspace: @preconcurrency BonsplitDelegate { p.unfocus() } - if let focusWindow = activationWindow(for: panel) { yieldForeignOwnedFocusIfNeeded( in: focusWindow, @@ -313,7 +311,6 @@ extension Workspace: @preconcurrency BonsplitDelegate { return } - if reassertAppKitFocus { panel.focus() } @@ -356,8 +353,6 @@ extension Workspace: @preconcurrency BonsplitDelegate { } } - - private func shouldRestoreFocusIntentAfterActivation(_ intent: PanelFocusIntent) -> Bool { switch intent { case .terminal(.findField): diff --git a/Sources/Workspace+FocusGeometry.swift b/Sources/Workspace+FocusGeometry.swift index feb370b6c..0210ef5ac 100644 --- a/Sources/Workspace+FocusGeometry.swift +++ b/Sources/Workspace+FocusGeometry.swift @@ -218,7 +218,6 @@ extension Workspace { ) } - if trigger == .terminalFirstResponder, panels[panelId] is TerminalPanel { beginEventDrivenLayoutFollowUp( @@ -228,7 +227,6 @@ extension Workspace { } } - func moveFocus(direction: NavigationDirection) { // If a pane is zoomed, un-zoom before navigating so the target // pane becomes visible — matches tmux behavior (#1605). @@ -294,7 +292,6 @@ extension Workspace { } } - func reconcileFocusState() { guard !isReconcilingFocusState else { return } isReconcilingFocusState = true @@ -501,9 +498,6 @@ extension Workspace { } } - - - func terminalFocusNeedsFollowUp() -> Bool { guard let panelId = layoutFollowUpTerminalFocusPanelId, let terminalPanel = terminalPanel(for: panelId) else { @@ -512,7 +506,6 @@ extension Workspace { return focusedPanelId != panelId || !terminalPanel.hostedView.isSurfaceViewFirstResponder() } - func attemptEventDrivenLayoutFollowUp() { guard layoutFollowUpTimeoutWorkItem != nil, !isAttemptingLayoutFollowUp else { return } isAttemptingLayoutFollowUp = true @@ -543,9 +536,6 @@ extension Workspace { reconcileTerminalPortalVisibilityForCurrentRenderedLayout() let terminalPortalPending = terminalPortalVisibilityNeedsFollowUp() - - - let terminalFocusPending = terminalFocusNeedsFollowUp() let needsMoreWork = layoutFollowUpNeedsGeometryPass || @@ -686,8 +676,6 @@ extension Workspace { return false } - - func scheduleMovedTerminalRefresh(panelId: UUID) { guard terminalPanel(for: panelId) != nil else { return } diff --git a/Sources/Workspace+Layout.swift b/Sources/Workspace+Layout.swift index b0afd12cc..3d80f3cd4 100644 --- a/Sources/Workspace+Layout.swift +++ b/Sources/Workspace+Layout.swift @@ -94,7 +94,6 @@ extension Workspace { ) } - // Markdown panels have no `ProgramaSurfaceType` counterpart yet (v1 cut). return nil } diff --git a/Sources/Workspace+SidebarTelemetry.swift b/Sources/Workspace+SidebarTelemetry.swift index 9eee358c2..abbef6130 100644 --- a/Sources/Workspace+SidebarTelemetry.swift +++ b/Sources/Workspace+SidebarTelemetry.swift @@ -361,7 +361,6 @@ extension Workspace { metadataBlocks.removeAll() } - @discardableResult func updatePanelTitle(panelId: UUID, title: String) -> Bool { let trimmed = title.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/Workspace+Surfaces.swift b/Sources/Workspace+Surfaces.swift index c64576e01..97fc8d119 100644 --- a/Sources/Workspace+Surfaces.swift +++ b/Sources/Workspace+Surfaces.swift @@ -412,8 +412,6 @@ extension Workspace { return newPanel } - - func newMarkdownSplit( from panelId: UUID, orientation: SplitOrientation, diff --git a/Sources/Workspace.swift b/Sources/Workspace.swift index 971f58457..4926ebe82 100644 --- a/Sources/Workspace.swift +++ b/Sources/Workspace.swift @@ -76,7 +76,6 @@ final class Workspace: Identifiable, ObservableObject { weak var owningTabManager: TabManager? - // Closing tabs mutates split layout immediately; terminal views handle their own AppKit // layout/size synchronization. @@ -300,7 +299,6 @@ final class Workspace: Identifiable, ObservableObject { return controller } - init( title: String = String(localized: "workspace.defaultTitle.terminal", defaultValue: "Terminal"), workingDirectory: String? = nil, @@ -805,7 +803,6 @@ final class Workspace: Identifiable, ObservableObject { panels[panelId] as? TerminalPanel } - func markdownPanel(for panelId: UUID) -> MarkdownPanel? { panels[panelId] as? MarkdownPanel } @@ -1290,19 +1287,6 @@ final class Workspace: Identifiable, ObservableObject { return bonsplitController.tabs(inPane: paneId).firstIndex(where: { $0.id == tabId }) } - - - - - - - - - - - - - @discardableResult func moveSurface(panelId: UUID, toPane paneId: PaneID, atIndex index: Int? = nil, focus: Bool = true) -> Bool { guard let tabId = surfaceIdFromPanelId(panelId) else { return false } @@ -1727,8 +1711,6 @@ final class Workspace: Identifiable, ObservableObject { _ = reorderSurface(panelId: newPanel.id, toIndex: targetIndex) } - - func promptRenamePanel(tabId: TabID) { guard let panelId = panelIdFromSurfaceId(tabId), let panel = panels[panelId] else { return } diff --git a/Sources/WorkspaceContentView.swift b/Sources/WorkspaceContentView.swift index bb86c2bf8..074787027 100644 --- a/Sources/WorkspaceContentView.swift +++ b/Sources/WorkspaceContentView.swift @@ -61,7 +61,6 @@ struct WorkspaceContentView: View { // AppKit-backed views can still intercept drags. Disable drop acceptance for them. let _ = { workspace.bonsplitController.isInteractive = isWorkspaceInputActive }() - // Wire up file drop handling so bonsplit's PaneDragContainerView can forward // Finder file drops to the correct terminal panel. let _ = { diff --git a/programaTests/AppDelegateShortcutRoutingTests.swift b/programaTests/AppDelegateShortcutRoutingTests.swift index e8187b2f2..9b05f6394 100644 --- a/programaTests/AppDelegateShortcutRoutingTests.swift +++ b/programaTests/AppDelegateShortcutRoutingTests.swift @@ -8,8 +8,6 @@ import Combine @testable import Programa #endif -private let appDelegateLastSurfaceCloseShortcutDefaultsKey = "closeWorkspaceOnLastSurfaceShortcut" -private final class FakeWKInspectorContainerView: NSView {} private final class FocusableTestView: NSView { override var acceptsFirstResponder: Bool { true } } @@ -2138,7 +2136,6 @@ final class AppDelegateShortcutRoutingTests: XCTestCase { return } - let windowId = appDelegate.createMainWindow() defer { closeWindow(withId: windowId) } @@ -2882,7 +2879,6 @@ final class AppDelegateShortcutRoutingTests: XCTestCase { XCTAssertEqual(observedPaletteWindow?.windowNumber, window.windowNumber) } - func testCmdPhysicalWWithDvorakCharactersDoesNotTriggerClosePanelShortcut() { guard let appDelegate = AppDelegate.shared else { XCTFail("Expected AppDelegate.shared") @@ -4665,11 +4661,6 @@ final class AppDelegateShortcutRoutingTests: XCTestCase { // MARK: - Non-Latin keyboard layout shortcut tests - - - - - func testCmdTWorksWithRussianKeyboardLayout() { guard let appDelegate = AppDelegate.shared else { XCTFail("Expected AppDelegate.shared") diff --git a/programaTests/GhosttyConfigTests.swift b/programaTests/GhosttyConfigTests.swift index fafa571cb..da827cf65 100644 --- a/programaTests/GhosttyConfigTests.swift +++ b/programaTests/GhosttyConfigTests.swift @@ -1422,7 +1422,6 @@ final class GhosttyDefaultBackgroundNotificationDispatcherTests: XCTestCase { } } - final class SocketControlSettingsTests: XCTestCase { func testMigrateModeSupportsExpandedSocketModes() { XCTAssertEqual(SocketControlSettings.migrateMode("off"), .off) @@ -3526,56 +3525,6 @@ final class ZshShellIntegrationHandoffTests: XCTestCase { try contents.write(to: url, atomically: true, encoding: .utf8) try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: url.path) } - - private func bindUnixSocket(at path: String) throws -> Int32 { - unlink(path) - - let fd = socket(AF_UNIX, SOCK_STREAM, 0) - guard fd >= 0 else { - throw NSError( - domain: NSPOSIXErrorDomain, - code: Int(errno), - userInfo: [NSLocalizedDescriptionKey: "Failed to create Unix socket"] - ) - } - - var addr = sockaddr_un() - addr.sun_family = sa_family_t(AF_UNIX) - let maxPathLength = MemoryLayout.size(ofValue: addr.sun_path) - path.withCString { ptr in - withUnsafeMutablePointer(to: &addr.sun_path) { pathPtr in - let pathBuf = UnsafeMutableRawPointer(pathPtr).assumingMemoryBound(to: CChar.self) - strncpy(pathBuf, ptr, maxPathLength - 1) - } - } - - let bindResult = withUnsafePointer(to: &addr) { ptr in - ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockaddrPtr in - Darwin.bind(fd, sockaddrPtr, socklen_t(MemoryLayout.size)) - } - } - guard bindResult == 0 else { - let code = Int(errno) - Darwin.close(fd) - throw NSError( - domain: NSPOSIXErrorDomain, - code: code, - userInfo: [NSLocalizedDescriptionKey: "Failed to bind Unix socket"] - ) - } - - guard Darwin.listen(fd, 1) == 0 else { - let code = Int(errno) - Darwin.close(fd) - throw NSError( - domain: NSPOSIXErrorDomain, - code: code, - userInfo: [NSLocalizedDescriptionKey: "Failed to listen on Unix socket"] - ) - } - - return fd - } } final class FishShellIntegrationHandoffTests: XCTestCase { diff --git a/programaTests/InactivePaneFirstClickFocusTests.swift b/programaTests/InactivePaneFirstClickFocusTests.swift index cec50687e..84bce3879 100644 --- a/programaTests/InactivePaneFirstClickFocusTests.swift +++ b/programaTests/InactivePaneFirstClickFocusTests.swift @@ -33,7 +33,6 @@ final class InactivePaneFirstClickFocusTests: XCTestCase { XCTAssertFalse(view.acceptsFirstMouse(for: nil)) } - func testMarkdownPointerObserverRejectsFirstMouse() { let view = MarkdownPanelPointerObserverView(frame: .zero) diff --git a/programaTests/NotificationAndMenuBarTests.swift b/programaTests/NotificationAndMenuBarTests.swift index a8362d9b3..b13592c76 100644 --- a/programaTests/NotificationAndMenuBarTests.swift +++ b/programaTests/NotificationAndMenuBarTests.swift @@ -532,7 +532,6 @@ final class NotificationDockBadgeTests: XCTestCase { } } - final class MenuBarBadgeLabelFormatterTests: XCTestCase { func testBadgeLabelFormatting() { XCTAssertNil(MenuBarBadgeLabelFormatter.badgeText(for: 0)) @@ -647,7 +646,6 @@ final class FocusedNotificationIndicatorTests: XCTestCase { } } - final class NotificationMenuSnapshotBuilderTests: XCTestCase { func testSnapshotCountsUnreadAndLimitsRecentItems() { let notifications = (0..<8).map { index in @@ -682,7 +680,6 @@ final class NotificationMenuSnapshotBuilderTests: XCTestCase { } } - final class MenuBarBuildHintFormatterTests: XCTestCase { func testReleaseBuildShowsNoHint() { XCTAssertNil(MenuBarBuildHintFormatter.menuTitle(appName: "Programa DEV menubar-extra", isDebugBuild: false)) @@ -703,7 +700,6 @@ final class MenuBarBuildHintFormatterTests: XCTestCase { } } - final class MenuBarNotificationLineFormatterTests: XCTestCase { func testPlainTitleContainsUnreadDotBodyAndTab() { let notification = TerminalNotification( @@ -786,7 +782,6 @@ final class MenuBarNotificationLineFormatterTests: XCTestCase { } } - final class MenuBarIconDebugSettingsTests: XCTestCase { func testDisplayedUnreadCountUsesPreviewOverrideWhenEnabled() { let suiteName = "MenuBarIconDebugSettingsTests.\(UUID().uuidString)" @@ -839,7 +834,6 @@ final class MenuBarIconDebugSettingsTests: XCTestCase { @MainActor - final class MenuBarIconRendererTests: XCTestCase { func testImageWidthDoesNotShiftWhenBadgeAppears() { let noBadge = MenuBarIconRenderer.makeImage(unreadCount: 0) diff --git a/programaTests/OmnibarAndToolsTests.swift b/programaTests/OmnibarAndToolsTests.swift index 57e3826b3..9d85b0840 100644 --- a/programaTests/OmnibarAndToolsTests.swift +++ b/programaTests/OmnibarAndToolsTests.swift @@ -155,7 +155,6 @@ final class FinderServicePathResolverTests: XCTestCase { } } - final class CanonicalSubprocessRunnerTests: XCTestCase { func testSeparatelyCapturesBoundedStandardOutputAndError() { let result = CanonicalSubprocessRunner.run( diff --git a/programaTests/ProgramaConfigTests.swift b/programaTests/ProgramaConfigTests.swift index 7a14475b9..61f55cf20 100644 --- a/programaTests/ProgramaConfigTests.swift +++ b/programaTests/ProgramaConfigTests.swift @@ -308,7 +308,6 @@ final class ProgramaConfigDecodingTests: XCTestCase { } } - func testDecodeMultipleSurfacesInPane() throws { let json = """ { diff --git a/programaTests/SessionPersistenceTests.swift b/programaTests/SessionPersistenceTests.swift index fac168bfc..56d6f7365 100644 --- a/programaTests/SessionPersistenceTests.swift +++ b/programaTests/SessionPersistenceTests.swift @@ -926,8 +926,6 @@ final class SessionPersistenceTests: XCTestCase { XCTAssertEqual(try XCTUnwrap(object["height"]), 704.5, accuracy: 0.001) } - - func testFreshSpawnScrollbackSeedPreparesText() { let prepared = SessionFreshSpawnScrollbackSeed.preparedText(for: "line one\nline two\n") XCTAssertEqual(prepared, "line one\nline two\n") diff --git a/programaTests/ShellFixSweepSHELTests.swift b/programaTests/ShellFixSweepSHELTests.swift index 843dc2e89..26902455d 100644 --- a/programaTests/ShellFixSweepSHELTests.swift +++ b/programaTests/ShellFixSweepSHELTests.swift @@ -113,11 +113,6 @@ final class ExternalOpenPolicySHELTests: XCTestCase { } } - - -// MARK: - SHEL-12: design mode payload bounds - - // MARK: - SHEL-15: notification reorder keeps children under their parent @MainActor diff --git a/programaTests/ShortcutAndCommandPaletteTests.swift b/programaTests/ShortcutAndCommandPaletteTests.swift index 2d53b6bd2..dcde91ef8 100644 --- a/programaTests/ShortcutAndCommandPaletteTests.swift +++ b/programaTests/ShortcutAndCommandPaletteTests.swift @@ -59,8 +59,6 @@ final class SplitShortcutTransientFocusGuardTests: XCTestCase { } } - - final class FullScreenShortcutTests: XCTestCase { func testMatchesCommandControlF() { XCTAssertTrue( @@ -186,7 +184,6 @@ final class FullScreenShortcutTests: XCTestCase { } } - final class CommandPaletteKeyboardNavigationTests: XCTestCase { func testArrowKeysMoveSelectionWithoutModifiers() { XCTAssertEqual( @@ -336,7 +333,6 @@ final class CommandPaletteKeyboardNavigationTests: XCTestCase { } } - final class CommandPaletteOpenShortcutConsumptionTests: XCTestCase { func testDoesNotConsumeWhenPaletteIsNotVisible() { XCTAssertFalse( @@ -434,7 +430,6 @@ final class CommandPaletteOpenShortcutConsumptionTests: XCTestCase { } } - final class CommandPaletteFocusStealerClassificationTests: XCTestCase { private final class NonViewTextDelegate: NSObject, NSTextViewDelegate {} private final class UnrelatedViewTextDelegate: NSView, NSTextViewDelegate {} @@ -495,9 +490,6 @@ final class CommandPaletteFocusStealerClassificationTests: XCTestCase { } } - - - final class CommandPaletteSelectionScrollBehaviorTests: XCTestCase { func testFirstEntryPinsToTopAnchor() { let anchor = ContentView.commandPaletteScrollPositionAnchor( @@ -532,7 +524,6 @@ final class CommandPaletteSelectionScrollBehaviorTests: XCTestCase { } } - final class ShortcutHintModifierPolicyTests: XCTestCase { func testShortcutHintRequiresCommandOnlyModifier() { XCTAssertTrue(ShortcutHintModifierPolicy.shouldShowHints(for: [.command])) @@ -637,7 +628,6 @@ final class ShortcutHintModifierPolicyTests: XCTestCase { } } - final class ShortcutHintDebugSettingsTests: XCTestCase { func testClampKeepsValuesWithinSupportedRange() { XCTAssertEqual(ShortcutHintDebugSettings.clamped(0.0), 0.0) @@ -677,7 +667,6 @@ final class ShortcutHintDebugSettingsTests: XCTestCase { } } - final class DevBuildBannerDebugSettingsTests: XCTestCase { func testShowSidebarBannerDefaultsToVisible() { let suiteName = "DevBuildBannerDebugSettingsTests.Default.\(UUID().uuidString)" @@ -707,7 +696,6 @@ final class DevBuildBannerDebugSettingsTests: XCTestCase { } } - final class ShortcutHintLanePlannerTests: XCTestCase { func testAssignLanesKeepsSeparatedIntervalsOnSingleLane() { let intervals: [ClosedRange] = [0...20, 28...40, 48...64] @@ -720,7 +708,6 @@ final class ShortcutHintLanePlannerTests: XCTestCase { } } - final class ShortcutHintHorizontalPlannerTests: XCTestCase { func testAssignRightEdgesResolvesOverlapWithMinimumSpacing() { let intervals: [ClosedRange] = [0...20, 18...34, 30...46] @@ -744,7 +731,6 @@ final class ShortcutHintHorizontalPlannerTests: XCTestCase { } } - final class AppearanceSettingsTests: XCTestCase { func testResolvedModeDefaultsToSystemWhenUnset() { let suiteName = "AppearanceSettingsTests.Default.\(UUID().uuidString)" @@ -762,7 +748,6 @@ final class AppearanceSettingsTests: XCTestCase { } } - final class QuitWarningSettingsTests: XCTestCase { func testDefaultWarnBeforeQuitIsEnabledWhenUnset() { let suiteName = "QuitWarningSettingsTests.Default.\(UUID().uuidString)" @@ -793,7 +778,6 @@ final class QuitWarningSettingsTests: XCTestCase { } } - final class UpdateChannelSettingsTests: XCTestCase { func testResolvedFeedFallsBackWhenInfoFeedMissing() { let resolved = UpdateFeedResolver.resolvedFeedURLString(infoFeedURL: nil) @@ -815,7 +799,6 @@ final class UpdateChannelSettingsTests: XCTestCase { } } - private final class UpdateRelaunchPreparationRecorder: @unchecked Sendable { private let lock = NSLock() private var events: [String] = [] @@ -847,7 +830,6 @@ private final class UpdateRelaunchPreparationRecorder: @unchecked Sendable { } } - final class UpdateRelaunchPreparationTests: XCTestCase { @MainActor func testMainThreadCallCompletesPreparationBeforeReturning() { @@ -908,7 +890,6 @@ final class UpdateRelaunchPreparationTests: XCTestCase { } } - final class UpdateSettingsTests: XCTestCase { func testApplyEnablesAutomaticChecksAndDailySchedule() { let defaults = makeDefaults() diff --git a/programaTests/SidebarOrderingTests.swift b/programaTests/SidebarOrderingTests.swift index 027b8c024..76ff650ce 100644 --- a/programaTests/SidebarOrderingTests.swift +++ b/programaTests/SidebarOrderingTests.swift @@ -47,7 +47,6 @@ final class SidebarActiveForegroundColorTests: XCTestCase { } } - final class SidebarActiveTabIndicatorSettingsTests: XCTestCase { func testDefaultStyleWhenUnset() { let suiteName = "SidebarActiveTabIndicatorSettingsTests.Default.\(UUID().uuidString)" @@ -86,7 +85,6 @@ final class SidebarActiveTabIndicatorSettingsTests: XCTestCase { } } - final class SidebarWorkspaceHierarchyTests: XCTestCase { func testCollapsedFolderHidesOnlyItsChildren() { let folder = UUID() @@ -167,7 +165,6 @@ final class SidebarWorkspaceHierarchyTests: XCTestCase { } } - final class SidebarBranchOrderingTests: XCTestCase { func testOrderedUniqueBranchesDedupesByNameAndMergesDirtyState() { @@ -566,7 +563,6 @@ final class SidebarBranchOrderingTests: XCTestCase { } } - final class SidebarDragFailsafePolicyTests: XCTestCase { func testShouldRequestClearWhenDragActiveAndMouseButtonUp() { XCTAssertTrue( @@ -629,7 +625,6 @@ final class SidebarDragFailsafePolicyTests: XCTestCase { } } - final class SidebarDropPlannerTests: XCTestCase { func testNoIndicatorForNoOpEdges() { let first = UUID() @@ -858,7 +853,6 @@ final class SidebarDropPlannerTests: XCTestCase { } - final class SidebarDragAutoScrollPlannerTests: XCTestCase { func testAutoScrollPlanTriggersNearTopAndBottomOnly() { let topPlan = SidebarDragAutoScrollPlanner.plan(distanceToTop: 4, distanceToBottom: 96, edgeInset: 44, minStep: 2, maxStep: 12) @@ -894,7 +888,6 @@ final class SidebarDragAutoScrollPlannerTests: XCTestCase { } } - final class TerminalControllerSidebarDedupeTests: XCTestCase { func testShouldReplaceStatusEntryReturnsFalseForUnchangedPayload() { let current = SidebarStatusEntry( diff --git a/programaTests/TabManagerUnitTests.swift b/programaTests/TabManagerUnitTests.swift index 8a275ff05..326b10d42 100644 --- a/programaTests/TabManagerUnitTests.swift +++ b/programaTests/TabManagerUnitTests.swift @@ -205,7 +205,6 @@ final class TabManagerChildExitCloseTests: XCTestCase { } } - @MainActor final class TabManagerWorkspaceOwnershipTests: XCTestCase { func testCloseWorkspaceIgnoresWorkspaceNotOwnedByManager() { @@ -803,7 +802,6 @@ final class TabManagerPullRequestProbeTests: XCTestCase { } } - @MainActor final class TabManagerCloseWorkspacesWithConfirmationTests: XCTestCase { func testCloseWorkspacesWithConfirmationPromptsOnceAndClosesAcceptedWorkspaces() { @@ -912,7 +910,6 @@ final class TabManagerCloseWorkspacesWithConfirmationTests: XCTestCase { } } - @MainActor final class TabManagerCloseCurrentPanelTests: XCTestCase { func testRuntimeCloseSkipsConfirmationWhenShellReportsPromptIdle() { @@ -1201,7 +1198,6 @@ final class TabManagerCloseCurrentPanelTests: XCTestCase { } } - @MainActor final class TabManagerNotificationFocusTests: XCTestCase { func testFocusTabFromNotificationClearsSplitZoomBeforeFocusingTargetPanel() { @@ -1297,7 +1293,6 @@ final class TabManagerNotificationFocusTests: XCTestCase { } } - @MainActor final class TabManagerPendingUnfocusPolicyTests: XCTestCase { func testDoesNotUnfocusWhenPendingTabIsCurrentlySelected() { @@ -1327,7 +1322,6 @@ final class TabManagerPendingUnfocusPolicyTests: XCTestCase { } } - @MainActor final class TabManagerSurfaceCreationTests: XCTestCase { func testNewSurfaceFocusesCreatedSurface() { @@ -1352,11 +1346,8 @@ final class TabManagerSurfaceCreationTests: XCTestCase { ) } - - } - @MainActor final class TabManagerEqualizeSplitsTests: XCTestCase { func testEqualizeSplitsSetsEverySplitDividerToHalf() { @@ -1637,7 +1628,6 @@ final class TabManagerResizeSplitsTests: XCTestCase { } } - @MainActor final class TabManagerWorkspaceConfigInheritanceSourceTests: XCTestCase { func testUsesFocusedTerminalWhenTerminalIsFocused() { @@ -1695,7 +1685,6 @@ final class TabManagerWorkspaceConfigInheritanceSourceTests: XCTestCase { } } - @MainActor final class TabManagerFocusedNotificationIndicatorTests: XCTestCase { func testFocusPanelDismissesUnreadNotificationWithDismissFlash() { diff --git a/programaTests/TerminalAndGhosttyTests.swift b/programaTests/TerminalAndGhosttyTests.swift index e5b644aed..58c93056c 100644 --- a/programaTests/TerminalAndGhosttyTests.swift +++ b/programaTests/TerminalAndGhosttyTests.swift @@ -431,7 +431,6 @@ final class GhosttyPasteboardHelperTests: XCTestCase { } } - final class TerminalKeyboardCopyModeActionTests: XCTestCase { func testCopyModeBypassAllowsOnlyCommandShortcuts() { XCTAssertTrue(terminalKeyboardCopyModeShouldBypassForShortcut(modifierFlags: [.command])) @@ -750,7 +749,6 @@ final class TerminalKeyboardCopyModeActionTests: XCTestCase { } } - final class TerminalKeyboardCopyModeResolveTests: XCTestCase { private func resolve( _ keyCode: UInt16, @@ -907,7 +905,6 @@ final class TerminalKeyboardCopyModeResolveTests: XCTestCase { } } - final class TerminalKeyboardCopyModeViewportRowTests: XCTestCase { func testInitialViewportRowUsesImePointBaseline() { XCTAssertEqual( @@ -965,7 +962,6 @@ final class TerminalKeyboardCopyModeViewportRowTests: XCTestCase { } } - final class GhosttyBackgroundThemeTests: XCTestCase { func testColorClampsOpacity() { let base = NSColor(srgbRed: 0.10, green: 0.20, blue: 0.30, alpha: 1.0) @@ -1027,7 +1023,6 @@ final class GhosttyBackgroundThemeTests: XCTestCase { } } - final class GhosttyResponderResolutionTests: XCTestCase { private final class FocusProbeView: NSView { override var acceptsFirstResponder: Bool { true } @@ -1052,7 +1047,6 @@ final class GhosttyResponderResolutionTests: XCTestCase { } } - final class TerminalDirectoryOpenTargetAvailabilityTests: XCTestCase { private func environment( existingPaths: Set, @@ -1146,7 +1140,6 @@ final class TerminalDirectoryOpenTargetAvailabilityTests: XCTestCase { } } - @MainActor final class TerminalNotificationDirectInteractionTests: XCTestCase { private final class FocusProbeView: NSView { @@ -1602,7 +1595,6 @@ final class TerminalNotificationDirectInteractionTests: XCTestCase { } } - @MainActor final class WindowTerminalHostViewTests: XCTestCase { private final class CapturingView: NSView { @@ -1775,7 +1767,6 @@ final class WindowTerminalHostViewTests: XCTestCase { } } - @MainActor final class GhosttySurfaceOverlayTests: XCTestCase { private final class ScrollProbeSurfaceView: GhosttyNSView { @@ -2706,7 +2697,6 @@ final class GhosttySurfaceOverlayTests: XCTestCase { } } - @MainActor final class TerminalWindowPortalLifecycleTests: XCTestCase { private final class ContentViewCountingWindow: NSWindow { @@ -2771,7 +2761,6 @@ final class TerminalWindowPortalLifecycleTests: XCTestCase { ) } - func testRegistryPrunesPortalWhenWindowCloses() { let baseline = TerminalWindowPortalRegistry.debugPortalCount() let window = NSWindow( @@ -3649,7 +3638,6 @@ final class TransientRecoveryRetryStateTests: XCTestCase { XCTAssertEqual(state.remaining, 2) } - } final class TerminalOpenURLTargetResolutionTests: XCTestCase { @@ -3722,7 +3710,6 @@ final class TerminalOpenURLTargetResolutionTests: XCTestCase { } } - final class TerminalControllerSocketTextChunkTests: XCTestCase { func testSocketTextChunksReturnsSingleChunkForPlainText() { XCTAssertEqual( @@ -3756,7 +3743,6 @@ final class TerminalControllerSocketTextChunkTests: XCTestCase { } } - final class GhosttyTerminalViewVisibilityPolicyTests: XCTestCase { func testImmediateStateUpdateAllowedWhenHostNotInWindow() { // hostedViewHasSuperview must be false here to actually represent "not in a @@ -3812,7 +3798,6 @@ final class GhosttyTerminalViewVisibilityPolicyTests: XCTestCase { } } - final class TerminalControllerSocketListenerHealthTests: XCTestCase { func testStableSocketBindPermissionFailureFallsBackToUserScopedSocket() { XCTAssertEqual( @@ -4017,7 +4002,6 @@ final class TerminalControllerSocketListenerHealthTests: XCTestCase { } } - @MainActor final class ThemeReloadCoalescerTests: XCTestCase { func testBurstOfRequestsReloadsOnceAfterTheDebounce() { diff --git a/programaTests/UpdatePillReleaseVisibilityTests.swift b/programaTests/UpdatePillReleaseVisibilityTests.swift index 18150ad9c..3ffab34fc 100644 --- a/programaTests/UpdatePillReleaseVisibilityTests.swift +++ b/programaTests/UpdatePillReleaseVisibilityTests.swift @@ -48,7 +48,6 @@ final class UpdateReleaseNotesDestinationTests: XCTestCase { } } - final class TitlebarControlsSizingPolicyTests: XCTestCase { func testSchedulePolicyRequiresMeaningfulViewSizeChange() { XCTAssertFalse(titlebarControlsShouldScheduleForViewSizeChange(previous: .zero, current: .zero)) diff --git a/programaTests/WindowAndDragTests.swift b/programaTests/WindowAndDragTests.swift index 91187beb2..423d70184 100644 --- a/programaTests/WindowAndDragTests.swift +++ b/programaTests/WindowAndDragTests.swift @@ -762,7 +762,6 @@ final class AppDelegateWindowContextRoutingTests: XCTestCase { } } - @MainActor final class AppDelegateLaunchServicesRegistrationTests: XCTestCase { // See the comment on AppDelegateWindowContextRoutingTests above: `AppDelegate()` overwrites @@ -806,7 +805,6 @@ final class AppDelegateLaunchServicesRegistrationTests: XCTestCase { } } - final class FocusFlashPatternTests: XCTestCase { func testFocusFlashPatternMatchesTerminalDoublePulseShape() { XCTAssertEqual(FocusFlashPattern.values, [0, 1, 0, 1, 0]) @@ -843,7 +841,6 @@ final class FocusFlashPatternTests: XCTestCase { } } - @available(macOS 26.0, *) private struct DragConfigurationOperationsSnapshot: Equatable { let allowCopy: Bool @@ -913,7 +910,6 @@ final class InternalTabDragConfigurationTests: XCTestCase { } } - @MainActor final class InternalTabDragBundleDeclarationTests: XCTestCase { private func exportedTypeIdentifiers(bundle: Bundle) -> Set { @@ -936,7 +932,6 @@ final class InternalTabDragBundleDeclarationTests: XCTestCase { } #endif - @MainActor final class WindowDragHandleHitTests: XCTestCase { private final class CapturingView: NSView { @@ -1308,7 +1303,6 @@ final class WindowDragHandleHitTests: XCTestCase { #if DEBUG - @MainActor final class DragOverlayRoutingPolicyPassiveMouseEventTests: XCTestCase { // Covers the WIN-1 perf fast path in WindowTerminalHostView.hitTest: passive @@ -1360,7 +1354,6 @@ final class DragOverlayRoutingPolicyPassiveMouseEventTests: XCTestCase { } } - @MainActor final class DraggableFolderHitTests: XCTestCase { func testFolderHitTestReturnsContainerWhenInsideBounds() { @@ -1387,7 +1380,6 @@ final class DraggableFolderHitTests: XCTestCase { } } - @MainActor final class TitlebarLeadingInsetPassthroughViewTests: XCTestCase { func testLeadingInsetViewDoesNotParticipateInHitTesting() { @@ -1401,7 +1393,6 @@ final class TitlebarLeadingInsetPassthroughViewTests: XCTestCase { } } - @MainActor final class FolderWindowMoveSuppressionTests: XCTestCase { private func makeWindow() -> NSWindow { @@ -1499,7 +1490,6 @@ final class FolderWindowMoveSuppressionTests: XCTestCase { } } - @MainActor final class WindowMoveSuppressionHitPathTests: XCTestCase { private func makeWindowWithContentView() -> (NSWindow, NSView) { @@ -1574,7 +1564,6 @@ final class WindowMoveSuppressionHitPathTests: XCTestCase { } } - @MainActor final class MarkdownPanelPointerObserverViewTests: XCTestCase { private func makeWindow() -> NSWindow { diff --git a/programaTests/WorkspaceFixSweepWKSPTests.swift b/programaTests/WorkspaceFixSweepWKSPTests.swift index 7c4e26c04..fb04e5622 100644 --- a/programaTests/WorkspaceFixSweepWKSPTests.swift +++ b/programaTests/WorkspaceFixSweepWKSPTests.swift @@ -167,19 +167,6 @@ final class WorkspaceFixSweepWKSPTests: XCTestCase { XCTAssertFalse(headRule.recognizes(text: headSample), "text beyond the cap must not match") } - // MARK: 16 pane cap vetoes a split before building a panel - - private func makeFourPaneWorkspace() throws -> Workspace { - let workspace = Workspace() - for _ in 0..<3 { - let source = try XCTUnwrap(workspace.focusedPanelId) - XCTAssertNotNil(workspace.newTerminalSplit(from: source, orientation: .horizontal)) - } - XCTAssertEqual(workspace.bonsplitController.allPaneIds.count, SplitPolicy.maxPanesPerWorkspace) - return workspace - } - - // MARK: 19 background tab creation leaves selection and focus alone private func twoPaneFixture() throws -> (workspace: Workspace, backgroundPane: PaneID, selectedTab: TabID, focusedPane: PaneID) { diff --git a/programaTests/WorkspaceUnitTests.swift b/programaTests/WorkspaceUnitTests.swift index b8db0738f..77d5fa868 100644 --- a/programaTests/WorkspaceUnitTests.swift +++ b/programaTests/WorkspaceUnitTests.swift @@ -87,7 +87,6 @@ final class ProgramaLayoutFileIdentityTests: XCTestCase { @testable import Programa #endif - final class SidebarSelectedWorkspaceColorTests: XCTestCase { func testLightModeUsesConfiguredSelectedWorkspaceBackgroundColor() { guard let color = sidebarSelectedWorkspaceBackgroundNSColor(for: .light).usingColorSpace(.sRGB) else { @@ -126,7 +125,6 @@ final class SidebarSelectedWorkspaceColorTests: XCTestCase { } } - final class WorkspaceRenameShortcutDefaultsTests: XCTestCase { func testRenameTabShortcutDefaultsAndMetadata() { XCTAssertEqual(KeyboardShortcutSettings.Action.renameTab.label, "Rename Tab") @@ -1159,7 +1157,6 @@ final class KeyboardShortcutSettingsFileStoreTests: XCTestCase { } } - final class TerminalThemeSettingsTests: XCTestCase { private let settingsFileBackupsDefaultsKey = "programa.settingsFile.backups.v1" @@ -1261,7 +1258,6 @@ final class TerminalThemeSettingsTests: XCTestCase { XCTAssertEqual(reloadRequestCount, 2) } - private func restoreDefaultsValue(_ value: Any?, key: String, defaults: UserDefaults) { if let value { defaults.set(value, forKey: key) @@ -1440,7 +1436,6 @@ final class TerminalThemeSettingsTests: XCTestCase { } } - final class WorkspaceShortcutMapperTests: XCTestCase { func testCommandNineMapsToLastWorkspaceIndex() { XCTAssertEqual(WorkspaceShortcutMapper.workspaceIndex(forDigit: 9, workspaceCount: 1), 0) @@ -1555,7 +1550,6 @@ final class WorkspacePlacementSettingsTests: XCTestCase { } } - @MainActor final class WorkspaceCreationPlacementTests: XCTestCase { private final class SnapshotMutatingTabManager: TabManager { @@ -1872,7 +1866,6 @@ final class WorkspaceCreationConfigSanitizationTests: XCTestCase { } } - final class WorkspaceTabColorSettingsTests: XCTestCase { func testNormalizedHexAcceptsAndNormalizesValidInput() { XCTAssertEqual(WorkspaceTabColorSettings.normalizedHex("#abc123"), "#ABC123") @@ -2249,7 +2242,6 @@ final class WorkspaceTabColorSettingsTests: XCTestCase { } } - final class WorkspaceReorderTests: XCTestCase { @MainActor func testRelativeWorkspaceMovesUseIndicesAfterRemovingTheMovingWorkspace() { @@ -2340,7 +2332,6 @@ final class WorkspaceReorderTests: XCTestCase { } } - @MainActor final class WorkspaceNotificationReorderTests: XCTestCase { func testNotificationAutoReorderDoesNotMovePinnedWorkspace() { @@ -2385,7 +2376,6 @@ final class WorkspaceNotificationReorderTests: XCTestCase { } } - @MainActor final class WorkspaceTeardownTests: XCTestCase { func testTeardownAllPanelsClearsPanelMetadataCaches() { @@ -2423,7 +2413,6 @@ final class WorkspaceTeardownTests: XCTestCase { } } - @MainActor final class WorkspaceSplitWorkingDirectoryTests: XCTestCase { private func hostTerminalPanelInWindow(_ panel: TerminalPanel) throws -> NSWindow { @@ -2665,7 +2654,6 @@ final class WorkspaceSplitWorkingDirectoryTests: XCTestCase { } } - @MainActor final class WorkspaceTerminalFocusRecoveryTests: XCTestCase { private func makeWindow() -> NSWindow { @@ -2971,7 +2959,6 @@ final class WorkspaceTerminalFocusRecoveryTests: XCTestCase { } } - @MainActor final class WorkspaceTerminalConfigInheritanceSelectionTests: XCTestCase { func testPrefersSelectedTerminalInTargetPaneOverFocusedTerminalElsewhere() { @@ -3050,7 +3037,6 @@ final class WorkspaceTerminalConfigInheritanceSelectionTests: XCTestCase { } } - @MainActor final class WorkspacePanelGitBranchTests: XCTestCase { private func drainMainQueue() { @@ -3361,7 +3347,6 @@ final class WorkspacePanelGitBranchTests: XCTestCase { assertPanelAndTabTitle("live-process", workspace: restored, panelId: restoredPanelId) } - func testTerminalSplitWithFocusFalsePreservesOriginalFocusedPanel() { let workspace = Workspace() guard let originalFocusedPanelId = workspace.focusedPanelId else { @@ -3508,10 +3493,6 @@ final class WorkspacePanelGitBranchTests: XCTestCase { XCTAssertFalse(attachedTab.hasCustomTitle) } - - - - func testAttachedTransferIgnoresStaleFinalizeAndNextDetachHasIndependentOwnership() throws { let source = Workspace() let destination = Workspace() @@ -3548,8 +3529,6 @@ final class WorkspacePanelGitBranchTests: XCTestCase { XCTAssertEqual(terminalPanel.surface.portalBindingStateLabel(), "closed") } - - func testNewTerminalSurfaceWithFocusFalsePreservesFocusedPanel() { let workspace = Workspace() guard let originalFocusedPanelId = workspace.focusedPanelId, @@ -3580,7 +3559,6 @@ final class WorkspacePanelGitBranchTests: XCTestCase { ) } - func testClosingFocusedSplitRestoresBranchForRemainingFocusedPanel() { let workspace = Workspace() guard let firstPanelId = workspace.focusedPanelId else { @@ -3732,20 +3710,8 @@ final class WorkspacePanelGitBranchTests: XCTestCase { ) } - // Renamed from testSidebarPullRequestsTrackFocusedPanelOnly. That name asserted a - // focused-only filter that never existed in production: `sidebarPullRequestsInDisplayOrder()` - // has surfaced PRs for every panel in sidebar order since it was introduced (commit - // 2d454df5, "feat(sidebar): show linked pull request metadata"), mirroring the pre-existing - // multi-panel `sidebarGitBranchesInDisplayOrder()`. The perf-only precompute in f7388715 - // ("Precompute panel ordering to reduce sidebar scroll lag") didn't change that. And the - // recent poll-candidate fix (commit d94d79ee, "fix: PR-probe filtering, poll candidates...") - // together with the green `TabManagerPullRequestProbeTests - // .testTrackedWorkspaceGitMetadataPollCandidatesIncludeMainAndMasterPanels` test explicitly - // proves background/non-main panels (master, feature, mainline splits) are polled for PR - // metadata -- which would be pointless if the sidebar only ever displayed the focused panel's - // PR. This test now documents the real contract: background panels with a valid, - // branch-matched PR are shown in sidebar order; PRs whose recorded branch no longer matches - // the panel's current git branch are filtered out. + // Contract: background panels with a valid, branch-matched PR are shown in sidebar order; + // PRs whose recorded branch no longer matches the panel's current git branch are filtered out. @MainActor func testSidebarPullRequestsShowAllPanelsInSidebarOrderFilteringBranchMismatches() { let workspace = Workspace() @@ -4018,7 +3984,6 @@ final class WorkspacePanelGitBranchTests: XCTestCase { } } - final class WorkspaceMountPolicyTests: XCTestCase { func testDefaultPolicyMountsOnlySelectedWorkspace() { let a = UUID() @@ -4295,7 +4260,6 @@ final class ReviewPanelWorkspaceTransferTests: XCTestCase { } } - @MainActor final class SidebarWorkspaceShortcutHintMetricsTests: XCTestCase { override func setUp() { diff --git a/programaUITests/MenuKeyEquivalentRoutingUITests.swift b/programaUITests/MenuKeyEquivalentRoutingUITests.swift index 7f47b3680..9d52c8959 100644 --- a/programaUITests/MenuKeyEquivalentRoutingUITests.swift +++ b/programaUITests/MenuKeyEquivalentRoutingUITests.swift @@ -1,8 +1,5 @@ import XCTest import Foundation -import CoreGraphics -import ImageIO -import Darwin private extension XCTestCase { func waitForCondition(timeout: TimeInterval, predicate: @escaping () -> Bool) -> Bool { @@ -14,7 +11,6 @@ private extension XCTestCase { } } - final class SplitCloseRightBlankRegressionUITests: XCTestCase { private var dataPath = "" private var socketPath = "" @@ -353,308 +349,6 @@ final class SplitCloseRightBlankRegressionUITests: XCTestCase { ) } - // MARK: - Screenshot-Based Blank Detection - - private struct CropStats { - let sampleCount: Int - let uniqueQuantized: Int - let lumaStdDev: Double - let modeFraction: Double - let fingerprint: UInt64 - - var isProbablyBlank: Bool { - // Tuned for "terminal went visually blank": near-uniform region, very low contrast. - // (The exact thresholds are conservative; we also require consecutive blank samples below.) - return lumaStdDev < 2.5 && modeFraction > 0.992 - } - } - - private func assertPaneRendersAndUpdates( - app: XCUIApplication, - window: XCUIElement, - paneCenter: CGVector, - blankCrop: CGRect, - updateCrop: CGRect, - label: String - ) { - // We want to catch: - // 1) pane visually blank (uniform background) - // 2) pane visually frozen (doesn't update after printing) - // - // We deliberately avoid relying on "Empty Panel" accessibility text, since the regression - // you're reporting is a blank terminal surface, not necessarily the EmptyPanelView. - - func takeStats(_ name: String, crop: CGRect) -> (String, CropStats)? { - guard let path = writeScreenshot(window: window, name: name), - let png = try? Data(contentsOf: URL(fileURLWithPath: path)), - let stats = cropStats(pngData: png, normalizedCrop: crop) else { - return nil - } - return (path, stats) - } - - // Capture a baseline frame. - guard let (preBlankPath, preBlankStats) = takeStats("\(label)-pre-blank", crop: blankCrop), - let (preUpdatePath, preUpdateStats) = takeStats("\(label)-pre-update", crop: updateCrop) else { - XCTFail("Failed to capture pre screenshot for \(label). shots=\(screenshotDir)") - return - } - - // Trigger a visible update in the pane. - window.coordinate(withNormalizedOffset: paneCenter).click() - // Print a lot of lines so the update is visible in a screenshot diff even with subsampling. - let token = String(UUID().uuidString.prefix(8)) - app.typeText("yes PROGRAMA_AFTER_CLOSE_\(label)_\(token) | head -n 30\n") - RunLoop.current.run(until: Date().addingTimeInterval(0.7)) - - guard let (postBlankPath, postBlankStats) = takeStats("\(label)-post-blank", crop: blankCrop), - let (postUpdatePath, postUpdateStats) = takeStats("\(label)-post-update", crop: updateCrop) else { - XCTFail("Failed to capture post screenshot for \(label). shots=\(screenshotDir)") - return - } - - if postBlankStats.isProbablyBlank { - addKeptScreenshot(path: preBlankPath, name: "\(label)-pre-blank") - addKeptScreenshot(path: postBlankPath, name: "\(label)-post-blank") - XCTFail("Pane looks blank after close. label=\(label) pre=\(preBlankStats) post=\(postBlankStats) shots=\(screenshotDir)") - return - } - - // Fingerprints can collide on terminal content (white glyphs on dark background with similar - // layout). Use a mean absolute luma diff threshold to detect a truly frozen surface. - // Compare only the pane area to avoid false positives from other UI movement. - if let prePng = try? Data(contentsOf: URL(fileURLWithPath: preUpdatePath)), - let postPng = try? Data(contentsOf: URL(fileURLWithPath: postUpdatePath)), - let diff = meanAbsLumaDiff(pngA: prePng, pngB: postPng, normalizedCrop: updateCrop), - diff < 1.0 { - addKeptScreenshot(path: preUpdatePath, name: "\(label)-pre-update") - addKeptScreenshot(path: postUpdatePath, name: "\(label)-post-update") - XCTFail("Pane looks frozen (no visual change after printing). label=\(label) diff=\(diff) pre=\(preUpdateStats) post=\(postUpdateStats) shots=\(screenshotDir)") - return - } - - // Also guard against a delayed blanking: watch for ~1.5s and fail if it goes blank for sustained streak. - var blankStreak = 0 - for sampleIndex in 1...9 { - guard let (path, stats) = takeStats("\(label)-watch-\(String(format: "%02d", sampleIndex))", crop: blankCrop) else { - if sampleIndex < 9 { - RunLoop.current.run(until: Date().addingTimeInterval(0.17)) - } - continue - } - if stats.isProbablyBlank { - blankStreak += 1 - } else { - blankStreak = 0 - } - if blankStreak >= 6 { // ~1s - addKeptScreenshot(path: path, name: "\(label)-watch-last") - XCTFail("Pane became blank for sustained period after close. label=\(label) stats=\(stats) shots=\(screenshotDir)") - return - } - if sampleIndex < 9 { - RunLoop.current.run(until: Date().addingTimeInterval(0.17)) - } - } - } - - @discardableResult - private func writeScreenshot(window: XCUIElement, name: String) -> String? { - let shot = window.screenshot() - let path = "\(screenshotDir)/\(name).png" - do { - try shot.pngRepresentation.write(to: URL(fileURLWithPath: path)) - return path - } catch { - return nil - } - } - - private func addKeptScreenshot(path: String, name: String) { - let attachment = XCTAttachment(contentsOfFile: URL(fileURLWithPath: path)) - attachment.name = name - attachment.lifetime = .keepAlways - add(attachment) - } - - private func meanAbsLumaDiff(pngA: Data, pngB: Data, normalizedCrop: CGRect) -> Double? { - guard let imageA = cgImage(from: pngA), - let imageB = cgImage(from: pngB) else { - return nil - } - guard imageA.width == imageB.width, imageA.height == imageB.height else { return nil } - let width = imageA.width - let height = imageA.height - if width <= 0 || height <= 0 { return nil } - - let cropPx = CGRect( - x: max(0, min(CGFloat(width - 1), normalizedCrop.origin.x * CGFloat(width))), - y: max(0, min(CGFloat(height - 1), normalizedCrop.origin.y * CGFloat(height))), - width: max(1, min(CGFloat(width), normalizedCrop.width * CGFloat(width))), - height: max(1, min(CGFloat(height), normalizedCrop.height * CGFloat(height))) - ).integral - - let x0 = Int(cropPx.minX) - let y0 = Int(cropPx.minY) - let x1 = Int(min(CGFloat(width), cropPx.maxX)) - let y1 = Int(min(CGFloat(height), cropPx.maxY)) - if x1 <= x0 || y1 <= y0 { return nil } - - guard let bufA = decodeRGBA(imageA), let bufB = decodeRGBA(imageB) else { return nil } - let bytesPerPixel = 4 - let bytesPerRow = width * bytesPerPixel - - let step = 3 - var total = 0.0 - var count = 0 - for y in stride(from: y0, to: y1, by: step) { - let row = y * bytesPerRow - for x in stride(from: x0, to: x1, by: step) { - let i = row + x * bytesPerPixel - let ar = Double(bufA[i]) - let ag = Double(bufA[i + 1]) - let ab = Double(bufA[i + 2]) - let br = Double(bufB[i]) - let bg = Double(bufB[i + 1]) - let bb = Double(bufB[i + 2]) - let al = 0.2126 * ar + 0.7152 * ag + 0.0722 * ab - let bl = 0.2126 * br + 0.7152 * bg + 0.0722 * bb - total += abs(al - bl) - count += 1 - } - } - return count > 0 ? (total / Double(count)) : nil - } - - private func cgImage(from pngData: Data) -> CGImage? { - guard let source = CGImageSourceCreateWithData(pngData as CFData, nil) else { - return nil - } - return CGImageSourceCreateImageAtIndex(source, 0, nil) - } - - private func decodeRGBA(_ image: CGImage) -> [UInt8]? { - let width = image.width - let height = image.height - let bytesPerPixel = 4 - let bytesPerRow = width * bytesPerPixel - var buf = [UInt8](repeating: 0, count: height * bytesPerRow) - - // Important: pass the *pixel buffer* pointer, not the Array object address. - // Also pin the pixel format so our [r,g,b,a] indexing matches reality. - let ok = buf.withUnsafeMutableBytes { raw -> Bool in - guard let base = raw.baseAddress else { return false } - - let bitmapInfo = CGBitmapInfo.byteOrder32Big.rawValue | CGImageAlphaInfo.premultipliedLast.rawValue - guard let ctx = CGContext( - data: base, - width: width, - height: height, - bitsPerComponent: 8, - bytesPerRow: bytesPerRow, - space: CGColorSpaceCreateDeviceRGB(), - bitmapInfo: bitmapInfo - ) else { return false } - - // Note: do not flip the context here. - // With CGImage decoded from XCUI screenshots, the bitmap memory we get from a plain - // draw() already matches the "top-left origin" expectation used by our normalized crops. - ctx.draw(image, in: CGRect(x: 0, y: 0, width: width, height: height)) - return true - } - - return ok ? buf : nil - } - - private func cropStats(pngData: Data, normalizedCrop: CGRect) -> CropStats? { - guard let image = cgImage(from: pngData) else { - return nil - } - - let width = image.width - let height = image.height - if width <= 0 || height <= 0 { return nil } - - let cropPx = CGRect( - x: max(0, min(CGFloat(width - 1), normalizedCrop.origin.x * CGFloat(width))), - y: max(0, min(CGFloat(height - 1), normalizedCrop.origin.y * CGFloat(height))), - width: max(1, min(CGFloat(width), normalizedCrop.width * CGFloat(width))), - height: max(1, min(CGFloat(height), normalizedCrop.height * CGFloat(height))) - ).integral - - // Render into a known RGBA8 buffer with top-left origin. - guard let buf = decodeRGBA(image) else { return nil } - let bytesPerPixel = 4 - let bytesPerRow = width * bytesPerPixel - - let x0 = Int(cropPx.minX) - let y0 = Int(cropPx.minY) - let x1 = Int(min(CGFloat(width), cropPx.maxX)) - let y1 = Int(min(CGFloat(height), cropPx.maxY)) - if x1 <= x0 || y1 <= y0 { return nil } - - // Sample every N pixels to keep this cheap and stable. - let step = 3 - var lumas = [Double]() - lumas.reserveCapacity(((x1 - x0) / step) * ((y1 - y0) / step)) - - // Quantize RGB to 4 bits/channel and track uniqueness + mode. - var hist = [UInt16: Int]() - hist.reserveCapacity(256) - - var count = 0 - var fnv: UInt64 = 1469598103934665603 // FNV-1a offset basis - for y in stride(from: y0, to: y1, by: step) { - let rowBase = y * bytesPerRow - for x in stride(from: x0, to: x1, by: step) { - let i = rowBase + x * bytesPerPixel - let r = Double(buf[i]) - let g = Double(buf[i + 1]) - let b = Double(buf[i + 2]) - let luma = 0.2126 * r + 0.7152 * g + 0.0722 * b - lumas.append(luma) - - let rq = UInt16(UInt8(buf[i]) >> 4) - let gq = UInt16(UInt8(buf[i + 1]) >> 4) - let bq = UInt16(UInt8(buf[i + 2]) >> 4) - let key = (rq << 8) | (gq << 4) | bq - hist[key, default: 0] += 1 - count += 1 - - // Fingerprint based on quantized luma (coarse) plus position order. - let lq = UInt8(max(0, min(63, Int(luma / 4.0)))) // ~6 bits - fnv ^= UInt64(lq) - fnv &*= 1099511628211 - } - } - - guard count > 0 else { return nil } - - // stddev of luma - let mean = lumas.reduce(0.0, +) / Double(lumas.count) - let variance = lumas.reduce(0.0) { $0 + ($1 - mean) * ($1 - mean) } / Double(lumas.count) - let stddev = sqrt(variance) - - // mode fraction - let modeCount = hist.values.max() ?? 0 - let modeFrac = Double(modeCount) / Double(count) - - return CropStats( - sampleCount: count, - uniqueQuantized: hist.count, - lumaStdDev: stddev, - modeFraction: modeFrac, - fingerprint: fnv - ) - } - - private func waitForData(keys: [String], timeout: TimeInterval) -> Bool { - waitForCondition(timeout: timeout) { - guard let data = self.loadData() else { return false } - return keys.allSatisfy { data[$0] != nil } - } - } - private func waitForAnyData(timeout: TimeInterval) -> Bool { waitForCondition(timeout: timeout) { self.loadData() != nil @@ -707,13 +401,6 @@ final class SplitCloseRightBlankRegressionUITests: XCTestCase { return (try? JSONSerialization.jsonObject(with: raw)) as? [String: String] } - private func loadDiagnostics() -> [String: String]? { - guard let raw = try? Data(contentsOf: URL(fileURLWithPath: diagnosticsPath)) else { - return nil - } - return (try? JSONSerialization.jsonObject(with: raw)) as? [String: String] - } - private func waitForVisualDone(timeout: TimeInterval) -> Bool { waitForCondition(timeout: timeout) { self.loadData()?["visualDone"] == "1" diff --git a/programaUITests/MultiWindowNotificationsUITests.swift b/programaUITests/MultiWindowNotificationsUITests.swift index dcd082b55..0ee4d35b4 100644 --- a/programaUITests/MultiWindowNotificationsUITests.swift +++ b/programaUITests/MultiWindowNotificationsUITests.swift @@ -312,7 +312,7 @@ final class MultiWindowNotificationsUITests: XCTestCase { try? FileManager.default.removeItem(atPath: commandScriptPath) } - guard let bundledCLIPath = resolveProgramaCLIPaths(strategy: .bundledOnly).first else { + guard let bundledCLIPath = resolveProgramaCLIPaths().first else { XCTFail("Failed to locate bundled programa CLI for notify regression test") return } @@ -448,60 +448,6 @@ final class MultiWindowNotificationsUITests: XCTestCase { } } - private func waitForSocketPong(timeout: TimeInterval) -> String? { - var lastResponse: String? - _ = waitForCondition(timeout: timeout) { - lastResponse = self.socketPingResponse() - return lastResponse == "PONG" - } - return lastResponse == "PONG" ? "PONG" : (socketPingResponse() ?? lastResponse) - } - - private func waitForTerminalFocus(surfaceId: String, timeout: TimeInterval) -> Bool { - waitForCondition(timeout: timeout) { - self.socketResult("debug.terminal.is_focused", params: ["surface_id": surfaceId])?["focused"] as? Bool == true - } - } - - private func waitForProgramaPing(timeout: TimeInterval) -> (stdout: String?, stderr: String?) { - var lastStdout: String? - var lastStderr: String? - let didSucceed = waitForCondition(timeout: timeout) { - let result = self.runProgramaCommand( - socketPath: self.socketPath, - arguments: ["ping"], - responseTimeoutSeconds: 2.0 - ) - let stdout = result.stdout.isEmpty ? nil : result.stdout - let stderr = result.stderr.isEmpty ? nil : result.stderr - if let stdout { - lastStdout = stdout - } - if let stderr { - lastStderr = stderr - } - if result.terminationStatus == 0, stdout == "PONG" { - return true - } - if self.isSocketPermissionFailure(stderr), - self.waitForSocketPong(timeout: 0.5) == "PONG" { - return true - } - return false - } - if didSucceed { - return ("PONG", lastStderr) - } - - let result = runProgramaCommand(socketPath: socketPath, arguments: ["ping"], responseTimeoutSeconds: 2.0) - let stdout = result.stdout.isEmpty ? nil : result.stdout - let stderr = result.stderr.isEmpty ? nil : result.stderr - if isSocketPermissionFailure(stderr), waitForSocketPong(timeout: 0.5) == "PONG" { - return ("PONG", stderr) - } - return (stdout ?? lastStdout, stderr ?? lastStderr) - } - /// Whether the app under test is the frontmost (active) application according to the /// workspace. `XCUIApplication.state` can report `.runningForeground` for an instant on the /// CI virtual display while the app itself never receives `applicationDidBecomeActive`; @@ -551,38 +497,6 @@ final class MultiWindowNotificationsUITests: XCTestCase { } } - private func firstSurfaceId(forWorkspaceId workspaceId: String) -> String? { - guard let result = socketResult("surface.list", params: ["workspace_id": workspaceId]), - let surfaces = result["surfaces"] as? [[String: Any]] else { - return nil - } - - for surface in surfaces { - if let candidate = surface["id"] as? String, UUID(uuidString: candidate) != nil { - return candidate - } - } - return nil - } - - private func waitForSurfaceId(forWorkspaceId workspaceId: String, timeout: TimeInterval) -> String? { - var surfaceId: String? - _ = waitForCondition(timeout: timeout) { - surfaceId = self.firstSurfaceId(forWorkspaceId: workspaceId) - return surfaceId != nil - } - return surfaceId ?? firstSurfaceId(forWorkspaceId: workspaceId) - } - - private func waitForSurfaceIdViaCLI(forWorkspaceId workspaceId: String, timeout: TimeInterval) -> String? { - var surfaceId: String? - _ = waitForCondition(timeout: timeout) { - surfaceId = self.firstSurfaceIdViaCLI(forWorkspaceId: workspaceId) - return surfaceId != nil - } - return surfaceId ?? firstSurfaceIdViaCLI(forWorkspaceId: workspaceId) - } - private func waitForCondition(timeout: TimeInterval, predicate: @escaping () -> Bool) -> Bool { let expectation = XCTNSPredicateExpectation( predicate: NSPredicate { _, _ in predicate() }, @@ -591,154 +505,6 @@ final class MultiWindowNotificationsUITests: XCTestCase { return XCTWaiter().wait(for: [expectation], timeout: timeout) == .completed } - private func firstSurfaceIdViaCLI(forWorkspaceId workspaceId: String) -> String? { - guard let paneId = firstPaneIdViaCLI(forWorkspaceId: workspaceId) else { - return firstSurfaceId(forWorkspaceId: workspaceId) - } - let result = runProgramaCommand( - socketPath: socketPath, - arguments: [ - "list-pane-surfaces", - "--workspace", - workspaceId, - "--pane", - paneId, - "--id-format", - "uuids" - ], - responseTimeoutSeconds: 3.0 - ) - guard result.terminationStatus == 0 else { - if isSocketPermissionFailure(result.stderr) { - return firstSurfaceId(forWorkspaceId: workspaceId) - } - return nil - } - return firstHandle(in: result.stdout) - } - - private func firstPaneIdViaCLI(forWorkspaceId workspaceId: String) -> String? { - let result = runProgramaCommand( - socketPath: socketPath, - arguments: [ - "list-panes", - "--workspace", - workspaceId, - "--id-format", - "uuids" - ], - responseTimeoutSeconds: 3.0 - ) - guard result.terminationStatus == 0 else { - if isSocketPermissionFailure(result.stderr) { - return nil - } - return nil - } - return firstHandle(in: result.stdout) - } - - private func firstHandle(in output: String) -> String? { - for rawLine in output.split(separator: "\n", omittingEmptySubsequences: true) { - var line = rawLine.trimmingCharacters(in: .whitespacesAndNewlines) - guard !line.isEmpty, !line.hasPrefix("No ") else { continue } - if line.hasPrefix("* ") || line.hasPrefix(" ") { - line = String(line.dropFirst(2)) - } - guard let token = line.split(whereSeparator: \.isWhitespace).first else { continue } - return String(token) - } - return nil - } - - private func runProgramaNotify( - socketPath: String, - workspaceId: String, - surfaceId: String, - title: String - ) -> (terminationStatus: Int32, stdout: String, stderr: String) { - runProgramaCommand( - socketPath: socketPath, - arguments: [ - "notify", - "--workspace", - workspaceId, - "--surface", - surfaceId, - "--title", - title, - "--subtitle", - "ui-test", - "--body", - "focus-regression" - ], - responseTimeoutSeconds: 4.0, - cliStrategy: .bundledOnly - ) - } - - private func runProgramaCommand( - socketPath: String, - arguments: [String], - responseTimeoutSeconds: Double = 3.0, - cliStrategy: ProgramaCLIStrategy = .any - ) -> (terminationStatus: Int32, stdout: String, stderr: String) { - var args = ["--socket", socketPath] - args.append(contentsOf: arguments) - var environment = ProcessInfo.processInfo.environment - environment["PROGRAMA_CLI_RESPONSE_TIMEOUT_SEC"] = String(responseTimeoutSeconds) - - let cliPaths = resolveProgramaCLIPaths(strategy: cliStrategy) - if cliPaths.isEmpty, cliStrategy == .bundledOnly { - return ( - terminationStatus: -1, - stdout: "", - stderr: "Failed to locate bundled programa CLI" - ) - } - - var lastPermissionFailure: (terminationStatus: Int32, stdout: String, stderr: String)? - for cliPath in cliPaths { - let result = executeProgramaCommand( - executablePath: cliPath, - arguments: args, - environment: environment - ) - if result.terminationStatus == 0 { - return result - } - if result.stderr.localizedCaseInsensitiveContains("operation not permitted") { - lastPermissionFailure = result - continue - } - return result - } - - if cliStrategy == .bundledOnly { - return lastPermissionFailure ?? ( - terminationStatus: -1, - stdout: "", - stderr: "Bundled programa CLI command failed without an executable path" - ) - } - - let fallbackArgs = ["programa"] + args - let fallbackResult = executeProgramaCommand( - executablePath: "/usr/bin/env", - arguments: fallbackArgs, - environment: environment - ) - if fallbackResult.terminationStatus == 0 || lastPermissionFailure == nil { - return fallbackResult - } - return lastPermissionFailure ?? fallbackResult - } - - private enum ProgramaCLIStrategy: Equatable { - case any - case bundledOnly - } - private func socketDiagnostics(from data: [String: String]) -> String { let pingResponse = data["socketPingResponse"].flatMap { $0.isEmpty ? nil : $0 } ?? "" return "mode=\(data["socketMode"] ?? "") running=\(data["socketIsRunning"] ?? "") " + @@ -747,20 +513,12 @@ final class MultiWindowNotificationsUITests: XCTestCase { "signals=\(data["socketFailureSignals"] ?? "")" } - private func resolveProgramaCLIPaths(strategy: ProgramaCLIStrategy) -> [String] { + private func resolveProgramaCLIPaths() -> [String] { let fileManager = FileManager.default let env = ProcessInfo.processInfo.environment var candidates: [String] = [] var productDirectories: [String] = [] - if strategy == .any { - for key in ["PROGRAMA_UI_TEST_CLI_PATH", "PROGRAMA_CLI"] { - if let value = env[key], !value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - candidates.append(value) - } - } - } - if let builtProductsDir = env["BUILT_PRODUCTS_DIR"], !builtProductsDir.isEmpty { productDirectories.append(builtProductsDir) } @@ -778,14 +536,11 @@ final class MultiWindowNotificationsUITests: XCTestCase { productDirectories.append(contentsOf: inferredBuildProductsDirectories()) for productsDir in uniquePaths(productDirectories) { - appendCLIPathCandidates(fromProductsDirectory: productsDir, strategy: strategy, to: &candidates) + appendCLIPathCandidates(fromProductsDirectory: productsDir, to: &candidates) } candidates.append("/tmp/programa-\(launchTag)/Build/Products/Debug/Programa DEV.app/Contents/Resources/bin/programa") candidates.append("/tmp/programa-\(launchTag)/Build/Products/Debug/Programa.app/Contents/Resources/bin/programa") - if strategy == .any { - candidates.append("/tmp/programa-\(launchTag)/Build/Products/Debug/programa") - } var resolvedPaths: [String] = [] for path in uniquePaths(candidates) { @@ -815,14 +570,10 @@ final class MultiWindowNotificationsUITests: XCTestCase { private func appendCLIPathCandidates( fromProductsDirectory productsDir: String, - strategy: ProgramaCLIStrategy, to candidates: inout [String] ) { candidates.append("\(productsDir)/Programa DEV.app/Contents/Resources/bin/programa") candidates.append("\(productsDir)/Programa.app/Contents/Resources/bin/programa") - if strategy == .any { - candidates.append("\(productsDir)/programa") - } guard let entries = try? FileManager.default.contentsOfDirectory(atPath: productsDir) else { return @@ -835,56 +586,6 @@ final class MultiWindowNotificationsUITests: XCTestCase { .path candidates.append(cliPath) } - if strategy == .any { - for entry in entries.sorted() where entry == "programa" { - let cliPath = URL(fileURLWithPath: productsDir) - .appendingPathComponent(entry) - .path - candidates.append(cliPath) - } - } - } - - private func executeProgramaCommand( - executablePath: String, - arguments: [String], - environment: [String: String] - ) -> (terminationStatus: Int32, stdout: String, stderr: String) { - let process = Process() - process.executableURL = URL(fileURLWithPath: executablePath) - process.arguments = arguments - process.environment = environment - - let stdoutPipe = Pipe() - let stderrPipe = Pipe() - process.standardOutput = stdoutPipe - process.standardError = stderrPipe - - do { - try process.run() - process.waitUntilExit() - } catch { - return ( - terminationStatus: -1, - stdout: "", - stderr: "Failed to run programa command: \(error.localizedDescription) (cliPath=\(executablePath))" - ) - } - - let stdoutData = stdoutPipe.fileHandleForReading.readDataToEndOfFile() - let stderrData = stderrPipe.fileHandleForReading.readDataToEndOfFile() - let stdout = String(data: stdoutData, encoding: .utf8)? - .trimmingCharacters(in: .whitespacesAndNewlines) ?? "" - let rawStderr = String(data: stderrData, encoding: .utf8)? - .trimmingCharacters(in: .whitespacesAndNewlines) ?? "" - let stderr = rawStderr.isEmpty ? "" : "\(rawStderr) (cliPath=\(executablePath))" - return (process.terminationStatus, stdout, stderr) - } - - private func isSocketPermissionFailure(_ stderr: String?) -> Bool { - guard let stderr, !stderr.isEmpty else { return false } - return stderr.localizedCaseInsensitiveContains("failed to connect to socket") && - stderr.localizedCaseInsensitiveContains("operation not permitted") } private func uniquePaths(_ paths: [String]) -> [String] { @@ -898,85 +599,6 @@ final class MultiWindowNotificationsUITests: XCTestCase { return unique } - private func socketRespondsToPing(at path: String) -> Bool { - let originalPath = socketPath - socketPath = path - defer { socketPath = originalPath } - return socketPingResponse() == "PONG" - } - - /// Returns "PONG" for a valid v2 `system.ping` response, otherwise the raw response (or nil). - private func socketPingResponse() -> String? { - guard let envelope = socketJSON(method: "system.ping") else { return nil } - if envelope["ok"] as? Bool == true, (envelope["result"] as? [String: Any])?["pong"] as? Bool == true { - return "PONG" - } - return "\(envelope)" - } - - /// Sends a v2 JSON-RPC request and returns the full response envelope. - private func socketJSON( - method: String, - params: [String: Any] = [:], - responseTimeout: TimeInterval = 2.0 - ) -> [String: Any]? { - let request: [String: Any] = ["id": UUID().uuidString, "method": method, "params": params] - guard JSONSerialization.isValidJSONObject(request), - let body = try? JSONSerialization.data(withJSONObject: request), - let line = String(data: body, encoding: .utf8), - let response = socketRequestLine(line, responseTimeout: responseTimeout), - let data = response.data(using: .utf8) else { - return nil - } - return (try? JSONSerialization.jsonObject(with: data)) as? [String: Any] - } - - /// Returns the `result` object of a successful v2 response, nil on transport failure or error. - private func socketResult(_ method: String, params: [String: Any] = [:]) -> [String: Any]? { - guard let envelope = socketJSON(method: method, params: params), - envelope["ok"] as? Bool == true else { - return nil - } - return envelope["result"] as? [String: Any] ?? [:] - } - - private func socketRequestLine(_ cmd: String, responseTimeout: TimeInterval = 2.0) -> String? { - if let response = ControlSocketClient(path: socketPath, responseTimeout: responseTimeout).sendLine(cmd) { - return response - } - return socketCommandViaNetcat(cmd, responseTimeout: responseTimeout) - } - - private func socketCommandViaNetcat(_ cmd: String, responseTimeout: TimeInterval = 2.0) -> String? { - let nc = "/usr/bin/nc" - guard FileManager.default.isExecutableFile(atPath: nc) else { return nil } - - let proc = Process() - proc.executableURL = URL(fileURLWithPath: "/bin/sh") - let timeoutSeconds = max(1, Int(ceil(responseTimeout))) - let script = "printf '%s\\n' \(shellSingleQuote(cmd)) | \(nc) -U \(shellSingleQuote(socketPath)) -w \(timeoutSeconds) 2>/dev/null" - proc.arguments = ["-lc", script] - - let outPipe = Pipe() - proc.standardOutput = outPipe - - do { - try proc.run() - } catch { - return nil - } - - proc.waitUntilExit() - - let outData = outPipe.fileHandleForReading.readDataToEndOfFile() - guard let outStr = String(data: outData, encoding: .utf8) else { return nil } - if let first = outStr.split(separator: "\n", maxSplits: 1).first { - return String(first).trimmingCharacters(in: .whitespacesAndNewlines) - } - let trimmed = outStr.trimmingCharacters(in: .whitespacesAndNewlines) - return trimmed.isEmpty ? nil : trimmed - } - private func shellSingleQuote(_ value: String) -> String { if value.isEmpty { return "''" } return "'" + value.replacingOccurrences(of: "'", with: "'\"'\"'") + "'" @@ -990,120 +612,6 @@ final class MultiWindowNotificationsUITests: XCTestCase { return value.trimmingCharacters(in: .whitespacesAndNewlines) } - private final class ControlSocketClient { - private let path: String - private let responseTimeout: TimeInterval - - init(path: String, responseTimeout: TimeInterval = 2.0) { - self.path = path - self.responseTimeout = responseTimeout - } - - func sendLine(_ line: String) -> String? { - let fd = socket(AF_UNIX, SOCK_STREAM, 0) - guard fd >= 0 else { return nil } - defer { close(fd) } - var socketTimeout = timeval( - tv_sec: Int(responseTimeout.rounded(.down)), - tv_usec: Int32(((responseTimeout - floor(responseTimeout)) * 1_000_000).rounded()) - ) - -#if os(macOS) - var noSigPipe: Int32 = 1 - _ = withUnsafePointer(to: &noSigPipe) { ptr in - setsockopt( - fd, - SOL_SOCKET, - SO_NOSIGPIPE, - ptr, - socklen_t(MemoryLayout.size) - ) - } -#endif - _ = withUnsafePointer(to: &socketTimeout) { ptr in - setsockopt( - fd, - SOL_SOCKET, - SO_RCVTIMEO, - ptr, - socklen_t(MemoryLayout.size) - ) - } - _ = withUnsafePointer(to: &socketTimeout) { ptr in - setsockopt( - fd, - SOL_SOCKET, - SO_SNDTIMEO, - ptr, - socklen_t(MemoryLayout.size) - ) - } - - var addr = sockaddr_un() - memset(&addr, 0, MemoryLayout.size) - addr.sun_family = sa_family_t(AF_UNIX) - - let maxLen = MemoryLayout.size(ofValue: addr.sun_path) - let bytes = Array(path.utf8CString) - guard bytes.count <= maxLen else { return nil } - withUnsafeMutablePointer(to: &addr.sun_path) { p in - let raw = UnsafeMutableRawPointer(p).assumingMemoryBound(to: CChar.self) - memset(raw, 0, maxLen) - for i in 0...offset(of: \.sun_path) ?? 0 - let addrLen = socklen_t(pathOffset + bytes.count) -#if os(macOS) - addr.sun_len = UInt8(min(Int(addrLen), 255)) -#endif - - let connected = withUnsafePointer(to: &addr) { ptr in - ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) { sa in - connect(fd, sa, addrLen) - } - } - guard connected == 0 else { return nil } - - let payload = line + "\n" - let wrote: Bool = payload.withCString { cstr in - var remaining = strlen(cstr) - var p = UnsafeRawPointer(cstr) - while remaining > 0 { - let n = write(fd, p, remaining) - if n <= 0 { return false } - remaining -= n - p = p.advanced(by: n) - } - return true - } - guard wrote else { return nil } - - var buf = [UInt8](repeating: 0, count: 4096) - var accum = "" - while true { - let n = read(fd, &buf, buf.count) - if n < 0 { - let code = errno - if code == EAGAIN || code == EWOULDBLOCK { - break - } - return nil - } - if n <= 0 { break } - if let chunk = String(bytes: buf[0.. [String: String]? { guard let data = try? Data(contentsOf: URL(fileURLWithPath: dataPath)) else { return nil diff --git a/scripts/gen-v2-contract.py b/scripts/gen-v2-contract.py index 12d3efedc..450644d8d 100644 --- a/scripts/gen-v2-contract.py +++ b/scripts/gen-v2-contract.py @@ -39,11 +39,6 @@ def camel_case(method: str) -> str: return out -def pascal_case(method: str) -> str: - c = camel_case(method) - return c[:1].upper() + c[1:] - - # --------------------------------------------------------------------------- # (a) Sources/V2CommandCatalog.swift # --------------------------------------------------------------------------- diff --git a/tests_v2/programa_client.py b/tests_v2/programa_client.py index 74598c128..9fbc47fd6 100755 --- a/tests_v2/programa_client.py +++ b/tests_v2/programa_client.py @@ -496,22 +496,6 @@ def close_workspace(self, workspace_id: str) -> None: wsid = self._resolve_workspace_id(workspace_id) self._call("workspace.close", {"workspace_id": wsid}) - # Backwards-compatible aliases - def list_tabs(self) -> List[Tuple[int, str, str, bool]]: - return self.list_workspaces() - - def new_tab(self) -> str: - return self.new_workspace() - - def close_tab(self, workspace_id: str) -> None: - return self.close_workspace(workspace_id) - - def select_tab(self, workspace: Union[str, int]) -> None: - return self.select_workspace(workspace) - - def current_tab(self) -> str: - return self.current_workspace() - # --------------------------------------------------------------------- # Surfaces / panes # --------------------------------------------------------------------- diff --git a/vendor/bonsplit/Sources/Bonsplit/Internal/Controllers/SplitViewController.swift b/vendor/bonsplit/Sources/Bonsplit/Internal/Controllers/SplitViewController.swift index 01d60f976..d8cb4c5f3 100644 --- a/vendor/bonsplit/Sources/Bonsplit/Internal/Controllers/SplitViewController.swift +++ b/vendor/bonsplit/Sources/Bonsplit/Internal/Controllers/SplitViewController.swift @@ -40,11 +40,6 @@ final class SplitViewController { /// Receives the dropped URLs and the pane ID where the drop occurred. @ObservationIgnored var onFileDrop: ((_ urls: [URL], _ paneId: PaneID) -> Bool)? - /// During drop, SwiftUI may keep the source tab view alive briefly (default removal animation) - /// even after we've updated the model. Hide it explicitly so it disappears immediately. - var dragHiddenSourceTabId: UUID? - var dragHiddenSourcePaneId: PaneID? - /// Current frame of the entire split view container var containerFrame: CGRect = .zero @@ -440,21 +435,6 @@ final class SplitViewController { return sorted.first?.0 } - /// Create a new tab in the focused pane - func createNewTab() { - guard let pane = focusedPane else { return } - let count = pane.tabs.count + 1 - let newTab = TabItem(title: "Untitled \(count)", icon: "doc") - pane.addTab(newTab) - } - - /// Close the currently selected tab in the focused pane - func closeSelectedTab() { - guard let pane = focusedPane, - let selectedTabId = pane.selectedTabId else { return } - closeTab(selectedTabId, inPane: pane.id) - } - /// Select the previous tab in the focused pane func selectPreviousTab() { guard let pane = focusedPane, @@ -498,18 +478,4 @@ final class SplitViewController { return findSplitRecursively(in: splitState.second, id: id) } } - - /// Get all split states in the tree - var allSplits: [SplitState] { - return collectSplits(from: rootNode) - } - - private func collectSplits(from node: SplitNode) -> [SplitState] { - switch node { - case .pane: - return [] - case .split(let splitState): - return [splitState] + collectSplits(from: splitState.first) + collectSplits(from: splitState.second) - } - } } diff --git a/vendor/bonsplit/Sources/Bonsplit/Internal/Styling/TabBarColors.swift b/vendor/bonsplit/Sources/Bonsplit/Internal/Styling/TabBarColors.swift index 418edeb43..f35c97c56 100644 --- a/vendor/bonsplit/Sources/Bonsplit/Internal/Styling/TabBarColors.swift +++ b/vendor/bonsplit/Sources/Bonsplit/Internal/Styling/TabBarColors.swift @@ -96,10 +96,6 @@ enum TabBarColors { return Color(nsColor: adjusted.withAlphaComponent(0.78)) } - static var inactiveTabBackground: Color { - .clear - } - // MARK: - Text Colors static var activeText: Color { @@ -171,10 +167,6 @@ enum TabBarColors { return dropIndicator } - static var focusRing: Color { - Color.accentColor.opacity(0.5) - } - static var dirtyIndicator: Color { Color(nsColor: .labelColor).opacity(0.6) } @@ -192,12 +184,6 @@ enum TabBarColors { _ = appearance return notificationBadge } - - // MARK: - Shadows - - static var tabShadow: Color { - Color.black.opacity(0.08) - } } private extension NSColor { diff --git a/vendor/bonsplit/Sources/Bonsplit/Internal/Views/TabBarGlassSurfaces.swift b/vendor/bonsplit/Sources/Bonsplit/Internal/Views/TabBarGlassSurfaces.swift index e3986b333..ed71add2a 100644 --- a/vendor/bonsplit/Sources/Bonsplit/Internal/Views/TabBarGlassSurfaces.swift +++ b/vendor/bonsplit/Sources/Bonsplit/Internal/Views/TabBarGlassSurfaces.swift @@ -16,7 +16,6 @@ enum TabBarGlassStyling { } } - /// Hosts compact overlay content, such as a shortcut hint, inside native glass. The /// actual tab controls never use this bridge; they are fully native AppKit controls above. struct TabPillGlassHost: NSViewRepresentable { diff --git a/vendor/bonsplit/Sources/Bonsplit/Internal/Views/TabBarView.swift b/vendor/bonsplit/Sources/Bonsplit/Internal/Views/TabBarView.swift index d129bc75c..fd877b36b 100644 --- a/vendor/bonsplit/Sources/Bonsplit/Internal/Views/TabBarView.swift +++ b/vendor/bonsplit/Sources/Bonsplit/Internal/Views/TabBarView.swift @@ -67,7 +67,6 @@ struct TabBarView: View { var showSplitButtons: Bool = true @AppStorage("workspacePresentationMode") private var presentationMode = "standard" - @AppStorage("debugFadeColorStyle") private var fadeColorStyle = 0 @State private var isHoveringTabBar = false @State private var dropTargetIndex: Int? @State private var dropLifecycle: TabDropLifecycle = .idle @@ -546,61 +545,6 @@ struct TabBarView: View { .padding(.trailing, 8) } - - private static func buttonBackdropColor( - for appearance: BonsplitConfiguration.Appearance, - focused: Bool, - style: Int - ) -> NSColor { - switch style { - case 1: // raw paneBackground forced opaque - return TabBarColors.nsColorPaneBackground(for: appearance).withAlphaComponent(1.0) - case 2: // barBackground (tab bar chrome) - let c = NSColor(TabBarColors.barBackground(for: appearance)) - return (c.usingColorSpace(.sRGB) ?? c).withAlphaComponent(1.0) - case 3: // windowBackgroundColor - return NSColor.windowBackgroundColor.withAlphaComponent(1.0) - case 4: // controlBackgroundColor - return NSColor.controlBackgroundColor.withAlphaComponent(1.0) - case 5: // pre-composited barBackground over windowBg - let chrome = NSColor(TabBarColors.barBackground(for: appearance)) - let winBg = NSColor.windowBackgroundColor - guard let fg = chrome.usingColorSpace(.sRGB), - let bk = winBg.usingColorSpace(.sRGB) else { - return chrome.withAlphaComponent(1.0) - } - let a: CGFloat = focused ? fg.alphaComponent : fg.alphaComponent * 0.95 - let oneMinusA = 1.0 - a - let r = fg.redComponent * a + bk.redComponent * oneMinusA - let g = fg.greenComponent * a + bk.greenComponent * oneMinusA - let b = fg.blueComponent * a + bk.blueComponent * oneMinusA - return NSColor(red: r, green: g, blue: b, alpha: 1.0) - default: // 0: pre-composited paneBackground over windowBg - return precompositedPaneBackground(for: appearance, focused: focused) - } - } - - /// Pre-composite the pane background over the window background to produce - /// a flat opaque color that matches what .background(barFill) looks like - /// after compositing. Avoids double-compositing mismatch on overlays. - private static func precompositedPaneBackground( - for appearance: BonsplitConfiguration.Appearance, - focused: Bool - ) -> NSColor { - let chrome = TabBarColors.nsColorPaneBackground(for: appearance) - let winBg = NSColor.windowBackgroundColor - guard let fg = chrome.usingColorSpace(.sRGB), - let bk = winBg.usingColorSpace(.sRGB) else { - return chrome.withAlphaComponent(1.0) - } - let a: CGFloat = focused ? fg.alphaComponent : fg.alphaComponent * 0.95 - let oneMinusA = 1.0 - a - let r = fg.redComponent * a + bk.redComponent * oneMinusA - let g = fg.greenComponent * a + bk.greenComponent * oneMinusA - let b = fg.blueComponent * a + bk.blueComponent * oneMinusA - return NSColor(red: r, green: g, blue: b, alpha: 1.0) - } - // MARK: - Combined Mask (scroll fades + button area) @ViewBuilder @@ -629,25 +573,6 @@ struct TabBarView: View { .animation(.easeOut(duration: 0.14), value: shouldShowButtons) } - // MARK: - Fade Overlays - - /// Mask that fades scroll content at the edges instead of overlaying - /// a colored gradient. The mask uses black (visible) → clear (hidden), - /// so the tab bar background shows through naturally with no compositing. - @ViewBuilder - private var fadeOverlays: some View { - let fadeWidth: CGFloat = 24 - HStack(spacing: 0) { - LinearGradient(colors: [.clear, .black], startPoint: .leading, endPoint: .trailing) - .frame(width: canScrollLeft ? fadeWidth : 0) - - Rectangle().fill(Color.black) - - LinearGradient(colors: [.black, .clear], startPoint: .leading, endPoint: .trailing) - .frame(width: canScrollRight ? fadeWidth : 0) - } - } - // MARK: - Background @ViewBuilder @@ -1126,7 +1051,6 @@ private final class TabControlShortcutKeyMonitor: ObservableObject { } } - /// Drop lifecycle state to prevent dropUpdated from re-setting state after performDrop enum TabDropLifecycle { case idle diff --git a/vendor/bonsplit/Sources/Bonsplit/Public/BonsplitConfiguration.swift b/vendor/bonsplit/Sources/Bonsplit/Public/BonsplitConfiguration.swift index 2688c9d29..3c5383124 100644 --- a/vendor/bonsplit/Sources/Bonsplit/Public/BonsplitConfiguration.swift +++ b/vendor/bonsplit/Sources/Bonsplit/Public/BonsplitConfiguration.swift @@ -198,19 +198,6 @@ extension BonsplitConfiguration { public static let `default` = Appearance() - public static let compact = Appearance( - tabBarHeight: 28, - tabMinWidth: 100, - tabMaxWidth: 160 - ) - - public static let spacious = Appearance( - tabBarHeight: 38, - tabMinWidth: 160, - tabMaxWidth: 280, - tabSpacing: 2 - ) - // MARK: - Initializer public init(