diff --git a/CLI/CLI+TmuxCompat.swift b/CLI/CLI+TmuxCompat.swift index 3684ba420..480896841 100644 --- a/CLI/CLI+TmuxCompat.swift +++ b/CLI/CLI+TmuxCompat.swift @@ -9,12 +9,9 @@ import Security #endif extension ProgramaCLI { - /// The 23 tmux-emulation command names, all dispatched through the same - /// `runTmuxCompatCommand`. Help text preserves the original grouped - /// layout, including the two pipe-separated combo lines - /// ("next-window | previous-window | last-window" and - /// "bind-key | unbind-key | copy-mode") that documented three names on - /// one line while still being three independently-dispatchable commands. + /// The tmux-emulation command names, all dispatched through the same + /// `runTmuxCompatCommand`. The "next-window | previous-window | last-window" + /// help line documents three independently-dispatchable commands on one line. static func tmuxCompatDescriptors( runTmuxCompatCommand: @escaping (CommandContext) throws -> Void ) -> [CommandDescriptor] { @@ -31,8 +28,6 @@ extension ProgramaCLI { CommandDescriptor(names: ["find-window"], helpLines: ["find-window [--content] [--select] "], grammar: CLIArgumentGrammar(booleanOptions: ["content", "select"], minPositionals: 1, maxPositionals: nil), execute: runTmuxCompatCommand), CommandDescriptor(names: ["clear-history"], helpLines: ["clear-history [--workspace ] [--surface ]"], grammar: CLIArgumentGrammar(valueOptions: ["workspace", "surface"]), execute: runTmuxCompatCommand), CommandDescriptor(names: ["set-hook"], helpLines: ["set-hook [--list] [--unset ] | "], execute: runTmuxCompatCommand), - CommandDescriptor(names: ["popup"], helpLines: ["popup"], execute: runTmuxCompatCommand), - CommandDescriptor(names: ["bind-key", "unbind-key", "copy-mode"], helpLines: ["bind-key | unbind-key | copy-mode"], execute: runTmuxCompatCommand), CommandDescriptor(names: ["set-buffer"], helpLines: ["set-buffer [--name ] "], grammar: CLIArgumentGrammar(valueOptions: ["name"], minPositionals: 1, maxPositionals: nil), execute: runTmuxCompatCommand), CommandDescriptor(names: ["list-buffers"], helpLines: ["list-buffers"], grammar: CLIArgumentGrammar(), execute: runTmuxCompatCommand), CommandDescriptor(names: ["paste-buffer"], helpLines: ["paste-buffer [--name ] [--workspace ] [--surface ]"], grammar: CLIArgumentGrammar(valueOptions: ["name", "workspace", "surface"]), execute: runTmuxCompatCommand), @@ -2159,12 +2154,6 @@ extension ProgramaCLI { } print("OK") - case "popup": - throw CLIError(message: "popup is not supported yet in programa CLI parity mode") - - case "bind-key", "unbind-key", "copy-mode": - throw CLIError(message: "\(command) is not supported yet in programa CLI parity mode") - case "set-buffer": let (nameArg, rem0) = parseOption(commandArgs, name: "--name") let name = (nameArg?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false) ? nameArg! : "default" @@ -2380,18 +2369,6 @@ extension ProgramaCLI { --list List configured hooks --unset Remove a hook by event name """ - case "popup": - return """ - Usage: programa popup - - tmux compatibility placeholder. This command is currently not supported. - """ - case "bind-key", "unbind-key", "copy-mode": - return """ - Usage: programa \(command) - - tmux compatibility placeholder. This command is currently not supported. - """ case "set-buffer": return """ Usage: programa set-buffer [--name ] [--] diff --git a/CLI/SocketClient.swift b/CLI/SocketClient.swift index e3a687d7e..6cf5e4887 100644 --- a/CLI/SocketClient.swift +++ b/CLI/SocketClient.swift @@ -380,55 +380,6 @@ final class SocketClient { return client } - static func waitForFilesystemPath(_ path: String, timeout: TimeInterval) throws { - if FileManager.default.fileExists(atPath: path) { - return - } - - guard let watchDirectory = existingWatchDirectory(forPath: path) else { - throw CLIError(message: "Timed out waiting for \(path)") - } - let watchFD = open(watchDirectory, O_EVTONLY) - guard watchFD >= 0 else { - throw CLIError(message: "Timed out waiting for \(path)") - } - - let queue = DispatchQueue(label: "com.programa.cli.path-watch.\(UUID().uuidString)") - let semaphore = DispatchSemaphore(value: 0) - var found = false - let source = DispatchSource.makeFileSystemObjectSource( - fileDescriptor: watchFD, - eventMask: [.write, .rename, .delete, .attrib, .extend, .link], - queue: queue - ) - - func checkPath() { - guard !found else { return } - if FileManager.default.fileExists(atPath: path) { - found = true - semaphore.signal() - } - } - - source.setEventHandler { - checkPath() - } - source.setCancelHandler { - Darwin.close(watchFD) - } - source.resume() - queue.async { - checkPath() - } - - guard semaphore.wait(timeout: .now() + timeout) == .success else { - source.cancel() - throw CLIError(message: "Timed out waiting for \(path)") - } - - source.cancel() - } - private static func existingWatchDirectory(forPath path: String) -> String? { let fileManager = FileManager.default var candidate = URL(fileURLWithPath: (path as NSString).deletingLastPathComponent, isDirectory: true) diff --git a/CLI/V2MethodNames.swift b/CLI/V2MethodNames.swift index 12be83317..9005c8da1 100644 --- a/CLI/V2MethodNames.swift +++ b/CLI/V2MethodNames.swift @@ -57,7 +57,6 @@ enum V2MethodNames { static let debugTerminalRenderStats = "debug.terminal.render_stats" static let debugTerminals = "debug.terminals" static let debugType = "debug.type" - static let debugViewtree = "debug.viewtree" static let debugWindowScreenshot = "debug.window.screenshot" static let feedbackOpen = "feedback.open" static let feedbackSubmit = "feedback.submit" diff --git a/CLI/programa.swift b/CLI/programa.swift index e625bf9f7..7d8fabf71 100644 --- a/CLI/programa.swift +++ b/CLI/programa.swift @@ -3224,45 +3224,6 @@ struct ProgramaCLI { return (cwd as NSString).appendingPathComponent(expanded) } - func sanitizedFilenameComponent(_ raw: String) -> String { - let sanitized = raw.replacingOccurrences( - of: #"[^\p{L}\p{N}._-]+"#, - with: "-", - options: .regularExpression - ) - let trimmed = sanitized.trimmingCharacters(in: CharacterSet(charactersIn: "-.")) - return trimmed.isEmpty ? "item" : trimmed - } - - func bestEffortPruneTemporaryFiles( - in directoryURL: URL, - keepingMostRecent maxCount: Int = 50, - maxAge: TimeInterval = 24 * 60 * 60 - ) { - guard let entries = try? FileManager.default.contentsOfDirectory( - at: directoryURL, - includingPropertiesForKeys: [.isRegularFileKey, .contentModificationDateKey, .creationDateKey], - options: [.skipsHiddenFiles] - ) else { - return - } - - let now = Date() - let datedEntries = entries.compactMap { url -> (url: URL, date: Date)? in - guard let values = try? url.resourceValues(forKeys: [.isRegularFileKey, .contentModificationDateKey, .creationDateKey]), - values.isRegularFile == true else { - return nil - } - return (url, values.contentModificationDate ?? values.creationDate ?? .distantPast) - }.sorted { $0.date > $1.date } - - for (index, entry) in datedEntries.enumerated() { - if index >= maxCount || now.timeIntervalSince(entry.date) > maxAge { - try? FileManager.default.removeItem(at: entry.url) - } - } - } - /// Returns true if the argument looks like a filesystem path rather than a CLI command. func looksLikePath(_ arg: String) -> Bool { if arg == "." || arg == ".." { return true } @@ -3548,14 +3509,6 @@ struct ProgramaCLI { } } - private func parsePositiveInt(_ raw: String?, label: String) throws -> Int? { - guard let raw else { return nil } - guard let value = Int(raw) else { - throw CLIError(message: "\(label) must be an integer") - } - return value - } - func isHandleRef(_ value: String) -> Bool { let pieces = value.split(separator: ":", omittingEmptySubsequences: false) guard pieces.count == 2 else { return false } @@ -5046,10 +4999,6 @@ struct ProgramaCLI { args.contains(name) } - private func replaceToken(_ args: [String], from: String, to: String) -> [String] { - args.map { $0 == from ? to : $0 } - } - /// Unescape CLI escape sequences for send behavior. /// \n and \r → carriage return (Enter), \t → tab. private func unescapeSendText(_ text: String) -> String { @@ -5537,8 +5486,6 @@ struct ProgramaCLI { if parsed.options["list"] == nil, parsed.options["unset"] == nil, parsed.positional.count < 2 { throw CLIError(message: "set-hook requires ") } - case "popup", "bind-key", "unbind-key", "copy-mode": - throw CLIError(message: "\(command) is not supported yet in programa CLI parity mode") case "display-message": let parsed = try parse(booleans: ["print"], minPositionals: 1, maxPositionals: nil) guard parsed.positional.filter({ $0 != "-p" }).isEmpty == false else { @@ -6343,19 +6290,6 @@ struct ProgramaCLI { to ~/Library/Application Support/programa/programa.sock and never connects to tagged/debug sockets on its own. """ } - -#if DEBUG - func debugUsageTextForTesting() -> String { - usage() - } - - func debugFormatDebugTerminalsPayloadForTesting( - _ payload: [String: Any], - idFormat: CLIIDFormat = .refs - ) -> String { - formatDebugTerminalsPayload(payload, idFormat: idFormat) - } -#endif } @main diff --git a/GhosttyTabs.xcodeproj/project.pbxproj b/GhosttyTabs.xcodeproj/project.pbxproj index e7be6c5cb..d0238cdbd 100644 --- a/GhosttyTabs.xcodeproj/project.pbxproj +++ b/GhosttyTabs.xcodeproj/project.pbxproj @@ -91,7 +91,6 @@ WTMG000005 /* ProgramaLayoutStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = WTMG000006 /* ProgramaLayoutStore.swift */; }; NRTM00000002 /* TabManager+UITestHarness.swift in Sources */ = {isa = PBXBuildFile; fileRef = NRTM00000001 /* TabManager+UITestHarness.swift */; }; A5001004 /* GhosttyConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001014 /* GhosttyConfig.swift */; }; - A5001005 /* GhosttyTerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001015 /* GhosttyTerminalView.swift */; }; A5FF0051 /* GhosttyTerminalView+Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5FF0041 /* GhosttyTerminalView+Keyboard.swift */; }; A5FF0052 /* GhosttyTerminalView+Mouse.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5FF0042 /* GhosttyTerminalView+Mouse.swift */; }; A5FF0053 /* GhosttyTerminalView+DragDrop.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5FF0043 /* GhosttyTerminalView+DragDrop.swift */; }; @@ -482,7 +481,6 @@ WTMG000006 /* ProgramaLayoutStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgramaLayoutStore.swift; sourceTree = ""; }; NRTM00000001 /* TabManager+UITestHarness.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TabManager+UITestHarness.swift"; sourceTree = ""; }; A5001014 /* GhosttyConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyConfig.swift; sourceTree = ""; }; - A5001015 /* GhosttyTerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyTerminalView.swift; sourceTree = ""; }; A5FF0041 /* GhosttyTerminalView+Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GhosttyTerminalView+Keyboard.swift"; sourceTree = ""; }; A5FF0042 /* GhosttyTerminalView+Mouse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GhosttyTerminalView+Mouse.swift"; sourceTree = ""; }; A5FF0043 /* GhosttyTerminalView+DragDrop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GhosttyTerminalView+DragDrop.swift"; sourceTree = ""; }; @@ -931,7 +929,6 @@ NRWS00000000000000000031 /* Workspace+Bonsplit.swift */, A5001417 /* WorkspaceContentView.swift */, A5001014 /* GhosttyConfig.swift */, - A5001015 /* GhosttyTerminalView.swift */, A5FF0041 /* GhosttyTerminalView+Keyboard.swift */, A5FF0042 /* GhosttyTerminalView+Mouse.swift */, A5FF0043 /* GhosttyTerminalView+DragDrop.swift */, @@ -1464,7 +1461,6 @@ NRWS00000000000000000032 /* Workspace+Bonsplit.swift in Sources */, A5001407 /* WorkspaceContentView.swift in Sources */, A5001004 /* GhosttyConfig.swift in Sources */, - A5001005 /* GhosttyTerminalView.swift in Sources */, A5FF0051 /* GhosttyTerminalView+Keyboard.swift in Sources */, A5FF0052 /* GhosttyTerminalView+Mouse.swift in Sources */, A5FF0053 /* GhosttyTerminalView+DragDrop.swift in Sources */, diff --git a/Resources/Localizable.xcstrings b/Resources/Localizable.xcstrings index 8a82eda3b..b958ba7a7 100644 --- a/Resources/Localizable.xcstrings +++ b/Resources/Localizable.xcstrings @@ -902,23 +902,6 @@ } } }, - "Browser DevTools Button": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Browser DevTools Button" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "ブラウザ開発者ツールボタン" - } - } - } - }, "Button Backdrop Color": { "extractionState": "manual", "localizations": { @@ -987,23 +970,6 @@ } } }, - "Copies sidebar, background, menu bar, and browser devtools settings as one payload.": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Copies sidebar, background, menu bar, and browser devtools settings as one payload." - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "Copies sidebar, background, menu bar, and browser devtools settings as one payload." - } - } - } - }, "Copy": { "extractionState": "manual", "localizations": { @@ -1038,23 +1004,6 @@ } } }, - "Copy Button Config": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Copy Button Config" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "ボタン設定をコピー" - } - } - } - }, "Copy Config": { "extractionState": "manual", "localizations": { @@ -1769,23 +1718,6 @@ } } }, - "Reset Button": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Reset Button" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "ボタンをリセット" - } - } - } - }, "Reset Hints": { "extractionState": "manual", "localizations": { @@ -6172,23 +6104,6 @@ } } }, - "common.create": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Create" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "作成" - } - } - } - }, "common.installAndRelaunch": { "extractionState": "manual", "localizations": { @@ -6257,23 +6172,6 @@ } } }, - "common.rename": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Rename" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "名前を変更" - } - } - } - }, "common.restartLater": { "extractionState": "manual", "localizations": { @@ -7617,23 +7515,6 @@ } } }, - "dialog.closeWindow.message": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "This will close the current window and all of its workspaces." - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "現在のウインドウとそのすべてのワークスペースが閉じられます。" - } - } - } - }, "dialog.closeWindow.title": { "extractionState": "manual", "localizations": { @@ -8161,57 +8042,6 @@ } } }, - "dialog.renameWorkspace.message": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Enter a custom name for this workspace." - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "このワークスペースのカスタム名を入力してください。" - } - } - } - }, - "dialog.renameWorkspace.placeholder": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Workspace name" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "ワークスペース名" - } - } - } - }, - "dialog.renameWorkspace.title": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Rename Workspace" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "ワークスペースの名前を変更" - } - } - } - }, "dialog.clipboardConfirmation.allow": { "extractionState": "manual", "localizations": { @@ -11663,23 +11493,6 @@ } } }, - "settings.sidebarAppearance.matchTerminalBackground.subtitle": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Use the same background color and transparency as the terminal." - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "ターミナルと同じ背景色と透明度を使用します。" - } - } - } - }, "settings.sidebarAppearance.showClaudeQuota": { "extractionState": "manual", "localizations": { @@ -12496,23 +12309,6 @@ } } }, - "shortcuts.help.tooltip": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Keyboard Shortcuts" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "キーボードショートカット" - } - } - } - }, "sidebar.activeTabIndicator.leftRail": { "extractionState": "manual", "localizations": { @@ -13023,40 +12819,6 @@ } } }, - "sidebar.quota.fiveHour": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "5h" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "5時間" - } - } - } - }, - "sidebar.quota.percent": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "%@%%" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "%@%%" - } - } - } - }, "sidebar.quota.resetDays": { "extractionState": "manual", "localizations": { @@ -13125,23 +12887,6 @@ } } }, - "sidebar.quota.sevenDay": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "7d" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "7日" - } - } - } - }, "sidebar.usage.button": { "extractionState": "manual", "localizations": { @@ -13465,40 +13210,6 @@ } } }, - "sidebar.usage.title": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Provider Usage" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "プロバイダーの使用量" - } - } - } - }, - "sidebar.usage.unavailable": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Unavailable" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "利用不可" - } - } - } - }, "sidebar.usage.window.accessibility": { "extractionState": "manual", "localizations": { @@ -15386,23 +15097,6 @@ } } }, - "update.viewGitHubCommit": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "View GitHub Commit" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "GitHubのコミットを表示" - } - } - } - }, "update.viewReleaseNotes": { "extractionState": "manual", "localizations": { diff --git a/Resources/shell-integration/fish/config.fish b/Resources/shell-integration/fish/config.fish index f9c24ef04..380ad2bf7 100644 --- a/Resources/shell-integration/fish/config.fish +++ b/Resources/shell-integration/fish/config.fish @@ -368,7 +368,7 @@ end # ran the user's real ~/.config/fish/config.fish, functions/, completions/, and # conf.d/*.fish *before* this file was sourced (Swift sets # PROGRAMA_FISH_USER_CONFIG_ALREADY_LOADED=1 for that path — see -# GhosttyTerminalView.swift). This block only fires when that flag is absent, +# TerminalSurface.swift). This block only fires when that flag is absent, # e.g. a future remote-relay bootstrap that overrides HOME/XDG_CONFIG_HOME # before fish's normal startup can find the real user config. set -l _programa_user_config_home "" diff --git a/Resources/shell-integration/programa-bash-bootstrap.bash b/Resources/shell-integration/programa-bash-bootstrap.bash index 381af9bb2..5cfda7fc0 100644 --- a/Resources/shell-integration/programa-bash-bootstrap.bash +++ b/Resources/shell-integration/programa-bash-bootstrap.bash @@ -19,7 +19,7 @@ # separator and let programa-bash-integration.bash's PROMPT_COMMAND merge # prepend _programa_prompt_command. # -# This file is the single source of truth. Sources/GhosttyTerminalView.swift +# This file is the single source of truth. Sources/TerminalSurface.swift # reads it (stripping these comments) and exports it as PROMPT_COMMAND, and # programaTests/GhosttyConfigTests.swift exercises it. PROMPT_COMMAND="${PROMPT_COMMAND##*__programa_bash_bootstrap_marker__}" diff --git a/Sources/AppDelegate+UITestCmdClick.swift b/Sources/AppDelegate+UITestCmdClick.swift index 4ae783535..c51528746 100644 --- a/Sources/AppDelegate+UITestCmdClick.swift +++ b/Sources/AppDelegate+UITestCmdClick.swift @@ -1,4 +1,4 @@ -// Extracted from AppDelegate.swift (nuclear-review N3): XCUITest-only instrumentation. +// XCUITest-only instrumentation. import AppKit import SwiftUI import Bonsplit diff --git a/Sources/AppDelegate+UITestHarnesses.swift b/Sources/AppDelegate+UITestHarnesses.swift index 50ab46fcd..d459e71af 100644 --- a/Sources/AppDelegate+UITestHarnesses.swift +++ b/Sources/AppDelegate+UITestHarnesses.swift @@ -1,4 +1,4 @@ -// Extracted from AppDelegate.swift (nuclear-review N3): XCUITest-only instrumentation. +// XCUITest-only instrumentation. import AppKit @preconcurrency import Dispatch import SwiftUI diff --git a/Sources/AppDelegate+UITestStressWorkspaces.swift b/Sources/AppDelegate+UITestStressWorkspaces.swift index e5f32ffee..eea5fdaec 100644 --- a/Sources/AppDelegate+UITestStressWorkspaces.swift +++ b/Sources/AppDelegate+UITestStressWorkspaces.swift @@ -1,4 +1,4 @@ -// Extracted from AppDelegate.swift (nuclear-review N3): XCUITest-only instrumentation. +// XCUITest-only instrumentation. import AppKit import SwiftUI import Bonsplit diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index df29cdfa4..eae6f8851 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -9,8 +9,6 @@ import ObjectiveC.runtime import Darwin import Security - - private enum ProgramaThemeNotifications { static let reloadConfig = Notification.Name("com.darkroom.programa.themes.reload-config") } @@ -148,12 +146,6 @@ func isCommandPaletteFocusStealingTerminalView(_ view: NSView) -> Bool { return false } - - - - - - private extension NSScreen { var programaDisplayID: UInt32? { let key = NSDeviceDescriptionKey("NSScreenNumber") @@ -637,7 +629,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser weak var fullscreenControlsViewModel: TitlebarControlsViewModel? weak var sidebarSelectionState: SidebarSelectionState? var shortcutLayoutCharacterProvider: (UInt16, NSEvent.ModifierFlags) -> String? = KeyboardLayout.character(forKeyCode:modifierFlags:) - private var workspaceObserver: NSObjectProtocol? private var lifecycleSnapshotObservers: [NSObjectProtocol] = [] private let themeReloadCoalescer = ThemeReloadCoalescer() private var windowKeyObserver: NSObjectProtocol? @@ -1394,7 +1385,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser #endif } - private func prepareStartupSessionSnapshotIfNeeded() { guard !didPrepareStartupSessionSnapshot else { return } didPrepareStartupSessionSnapshot = true @@ -5257,10 +5247,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser } } - #endif - func attachUpdateAccessory(to window: NSWindow) { titlebarAccessoryController.start() titlebarAccessoryController.attach(to: window) @@ -5343,12 +5331,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser } } - static func installWindowResponderSwizzlesForTesting() { - _ = didInstallWindowKeyEquivalentSwizzle - _ = didInstallWindowFirstResponderSwizzle - _ = didInstallWindowSendEventSwizzle - } - private func installWindowResponderSwizzles() { #if DEBUG _ = Self.didInstallApplicationSendEventSwizzle @@ -5654,36 +5636,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser return true } - func promptRenameSelectedWorkspace() -> Bool { - guard let tabManager, - let tabId = tabManager.selectedTabId, - let tab = tabManager.tabs.first(where: { $0.id == tabId }) else { - NSSound.beep() - return false - } - - let alert = NSAlert() - alert.messageText = String(localized: "dialog.renameWorkspace.title", defaultValue: "Rename Workspace") - alert.informativeText = String(localized: "dialog.renameWorkspace.message", defaultValue: "Enter a custom name for this workspace.") - let input = NSTextField(string: tab.customTitle ?? tab.title) - input.placeholderString = String(localized: "dialog.renameWorkspace.placeholder", defaultValue: "Workspace name") - input.frame = NSRect(x: 0, y: 0, width: 240, height: 22) - alert.accessoryView = input - alert.addButton(withTitle: String(localized: "common.rename", defaultValue: "Rename")) - alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel")) - let alertWindow = alert.window - alertWindow.initialFirstResponder = input - DispatchQueue.main.async { - alertWindow.makeFirstResponder(input) - input.selectText(nil) - } - - let response = alert.runModal() - guard response == .alertFirstButtonReturn else { return true } - tabManager.setCustomTitle(tabId: tab.id, title: input.stringValue) - return true - } - // Precedence encoded by this function's evaluation order (each phase is checked // strictly after the previous one; the first phase that returns wins). Refs #95. // 1. Setup: chord-prefix bookkeeping, Ctrl+D debug probe, close-confirmation-alert @@ -5697,12 +5649,11 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser // bypass. // 4. App-shortcut (lowest precedence, only reached once nothing above claimed the // event): the flat table of ~55 `matchConfiguredShortcut`/digit/directional/tab - // checks, extracted verbatim into handleConfiguredAppShortcutActions(event:...). + // checks, in handleConfiguredAppShortcutActions(event:...). // Snapshot of whether/how the command palette is claiming keyboard input for a given - // shortcut event's routed window. Extracted from the six interdependent booleans that used - // to be computed inline at the top of handleCustomShortcut(event:) so the precedence - // decisions below can read `commandPaletteState.isEffectiveInTargetWindow` etc. declaratively - // instead of re-deriving them. Values and their derivation are unchanged from before. + // shortcut event's routed window. The precedence decisions below read + // `commandPaletteState.isEffectiveInTargetWindow` etc. declaratively instead of + // re-deriving the interdependent booleans. private struct CommandPaletteInteractionState { let targetWindow: NSWindow? let shortcutWindow: NSWindow? @@ -6760,13 +6711,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser handleSplitShortcutAction(event: event, action: .splitDown, direction: .down, debugActionName: "splitDown") } - // SplitDirection and the debug-log action name. - - - - - - // New surface: Cmd+T private func handleNewSurfaceShortcutAction(event: NSEvent) -> Bool? { guard matchConfiguredShortcut(event: event, action: .newSurface) else { return nil } @@ -6774,32 +6718,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser return true } - - - - - - - - - - - // Safari defaults: - // - Option+Command+I => Show/Toggle Web Inspector - // - Option+Command+C => Show JavaScript Console - - - - - // TabManager zoom method to invoke. - - - - - - - - private func handleFindShortcutAction(event: NSEvent) -> Bool? { guard matchConfiguredShortcut(event: event, action: .find) else { return nil } tabManager?.startSearch() @@ -6830,8 +6748,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser return true } - - private func shouldSuppressSplitShortcutForTransientTerminalFocusState(direction: SplitDirection) -> Bool { guard let tabManager, let workspace = tabManager.selectedWorkspace, @@ -6875,50 +6791,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser return true } -#if DEBUG - - - - - -#endif - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#if DEBUG - - - -#endif - - - @discardableResult func performSplitShortcut(direction: SplitDirection, preferredWindow: NSWindow? = nil) -> Bool { let targetWindow = preferredWindow ?? NSApp.keyWindow ?? NSApp.mainWindow @@ -6984,10 +6856,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser return didCreateSplit } - - - - @discardableResult func requestRenameWorkspaceViaCommandPalette(preferredWindow: NSWindow? = nil) -> Bool { let targetWindow = preferredWindow ?? NSApp.keyWindow ?? NSApp.mainWindow @@ -7263,10 +7131,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser return digit } - // `shouldRequireCharacterMatchForCommandShortcut` and `shortcutCharacterMatches` moved to - // `ShortcutRouting` — they were only ever called from `matchShortcutStroke`, which now - // forwards to `ShortcutRouting.matchStroke`. - /// Shared with `ShortcutRouting.matchStroke` (via `AppDelegate.normalizedShortcutEventCharacter`) /// and with `numberedShortcutDigit` above, so it stays here as `nonisolated static` rather than /// moving — it never reads instance state, so isolation is safe to drop explicitly. @@ -7304,9 +7168,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser } } - // `keyCodeForShortcutKey` moved to `ShortcutRouting` — it was only ever called from - // `matchShortcutStroke`, which now forwards to `ShortcutRouting.matchStroke`. - /// Shared with `ShortcutRouting.matchStroke` (via `AppDelegate.digitForNumberKeyCode`) and /// with `numberedShortcutDigit` above, so it stays here as `nonisolated static` rather than /// moving — it never reads instance state, so isolation is safe to drop explicitly. @@ -7378,7 +7239,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser updateController.validateMenuItem(item) } - private func configureUserNotifications() { let actions = [ UNNotificationAction( @@ -7560,7 +7420,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser } #endif - func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, @@ -7646,18 +7505,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser } } - - - - - - - - - - - - private func setActiveMainWindow(_ window: NSWindow) { guard let context = contextForMainTerminalWindow(window) else { return } #if DEBUG @@ -8103,9 +7950,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser /// titlebar, movability, transparency, glass effect, decorations) and registers /// it for window-context tracking, then installs the file-drop overlay. /// - /// Extracted verbatim from `ContentView`'s `WindowAccessor` trailing closure - /// (nuclear-review CV2b) so this AppKit window mutation lives with the - /// window-context layer instead of the SwiftUI view layer. `ContentView` still + /// This AppKit window mutation lives with the window-context layer, not the + /// SwiftUI view layer. `ContentView` /// owns tracking its own `@State` (`observedWindow`, `isFullScreen`, /// `titlebarPadding`) — this method returns the computed titlebar padding so /// the caller can decide whether to update that `@State`. diff --git a/Sources/BackgroundLogWriter.swift b/Sources/BackgroundLogWriter.swift index d2af385c6..326404d64 100644 --- a/Sources/BackgroundLogWriter.swift +++ b/Sources/BackgroundLogWriter.swift @@ -2,11 +2,10 @@ import Foundation // MARK: - BackgroundLogWriter (ported from upstream cmux cb2129a5a1) // -// Replaces the previous per-call FileManager.fileExists + FileHandle(forWritingTo:) -// open -> seekToEnd -> write -> close pattern, which ran synchronously under a lock -// on the calling thread (often the main thread) for every debug log line. Instruments -// showed this blocking appearance-config resolution during bursts of background log -// activity. +// Avoids a per-call FileManager.fileExists + FileHandle(forWritingTo:) +// open -> seekToEnd -> write -> close pattern, which would run synchronously under a lock +// on the calling thread (often the main thread) for every debug log line and block +// appearance-config resolution during bursts of background log activity. // // This writer serializes all file I/O onto a single serial background queue with one // long-lived FileHandle. Callers append lines asynchronously and never block. The log diff --git a/Sources/CommandPaletteController.swift b/Sources/CommandPaletteController.swift index 4df6c92a5..28d554893 100644 --- a/Sources/CommandPaletteController.swift +++ b/Sources/CommandPaletteController.swift @@ -1,4 +1,4 @@ -// Command-palette state ownership, extracted from ContentView.swift (nuclear-review #88). +// Command-palette state ownership. // // CommandPaletteController owns every @State property that used to live on // ContentView and is exclusively used by the command palette (query, mode, diff --git a/Sources/CommandPaletteSearchEngine.swift b/Sources/CommandPaletteSearchEngine.swift index c1fd76b28..1d77a9892 100644 --- a/Sources/CommandPaletteSearchEngine.swift +++ b/Sources/CommandPaletteSearchEngine.swift @@ -1,6 +1,6 @@ -// Command palette fuzzy-search engine, extracted from ContentView.swift (nuclear-review #94.1). -// Pure move: search metadata, indexer, fuzzy matcher, corpus types, and the search engine. -// Zero coupling to ContentView state — behavior-identical relocation. +// Command palette fuzzy-search engine. +// Holds the search metadata, indexer, fuzzy matcher, corpus types, and the search engine. +// Has no coupling to ContentView state. import Foundation diff --git a/Sources/ContentView+CommandPalette.swift b/Sources/ContentView+CommandPalette.swift index fc1c261dc..e50ad0f7b 100644 --- a/Sources/ContentView+CommandPalette.swift +++ b/Sources/ContentView+CommandPalette.swift @@ -1,4 +1,4 @@ -// Command palette orchestration extracted from ContentView.swift (nuclear-review CV1). +// Command palette orchestration. // // Conservative extraction: only pure/computation functions and already-`static` // helpers are moved here (fuzzy-search corpus building, result fingerprinting/ diff --git a/Sources/ContentView+SidebarResizer.swift b/Sources/ContentView+SidebarResizer.swift index 6d10fc101..6c7ba82c7 100644 --- a/Sources/ContentView+SidebarResizer.swift +++ b/Sources/ContentView+SidebarResizer.swift @@ -1,4 +1,4 @@ -// Sidebar resizer member group extracted from ContentView.swift (nuclear-review CV1 / issue #94). +// Sidebar resizer member group. // The backing @State stays on `ContentView` (SwiftUI requires stored properties on the primary // declaration); those properties, plus `SidebarResizerHandle`, `updateSidebarResizerBandState`, // `installSidebarResizerPointerMonitorIfNeeded`, `removeSidebarResizerPointerMonitor`, and diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 087ef59ca..53743a08f 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -4322,10 +4322,6 @@ struct ContentView: View { syncCommandPaletteSelectionAnchor(resultIDs: commandPaletteController.cachedCommandPaletteResults.map(\.id)) } - private func syncCommandPaletteSelectionAnchorFromVisibleResults() { - syncCommandPaletteSelectionAnchor(resultIDs: commandPaletteController.commandPaletteVisibleResults.map(\.id)) - } - private func moveCommandPaletteSelection(by delta: Int) { let count = commandPaletteController.commandPaletteVisibleResults.count guard count > 0 else { @@ -4859,8 +4855,7 @@ struct ContentView: View { private func commandPaletteRenameInputFocusPolicy() -> CommandPaletteInputFocusPolicy { // Rename always opens with the existing name selected, so typing replaces - // it. This used to be a preference. Other focus policies still use - // .caretAtEnd, so only this call site is fixed. + // it. Other focus policies still use .caretAtEnd, so only this call site is fixed. return CommandPaletteInputFocusPolicy( focusTarget: .rename, selectionBehavior: .selectAll diff --git a/Sources/FileDropOverlayView.swift b/Sources/FileDropOverlayView.swift index 406f8e3df..1d463c086 100644 --- a/Sources/FileDropOverlayView.swift +++ b/Sources/FileDropOverlayView.swift @@ -1,5 +1,4 @@ -// File-drop overlay NSView, extracted from ContentView.swift (nuclear-review #94.3). -// Pure move — behavior-identical relocation. +// File-drop overlay NSView. import AppKit import Bonsplit diff --git a/Sources/FileWatcher.swift b/Sources/FileWatcher.swift index 46447c612..f7287dad3 100644 --- a/Sources/FileWatcher.swift +++ b/Sources/FileWatcher.swift @@ -3,8 +3,8 @@ import Foundation /// Low-level primitive for watching a single filesystem path (file or directory) via /// `DispatchSource.makeFileSystemObjectSource`. /// -/// This extracts the open-fd / create-source / resume / cancel-and-close boilerplate that -/// used to be duplicated across `ProgramaConfigStore`'s local + global config watchers and +/// This holds the open-fd / create-source / resume / cancel-and-close boilerplate shared by +/// `ProgramaConfigStore`'s local + global config watchers and /// `ShortcutSettingsFileWatcher`'s primary + fallback watchers. /// /// `FileWatcher` owns exactly one active `DispatchSourceFileSystemObject` at a time; starting diff --git a/Sources/GhosttyApp.swift b/Sources/GhosttyApp.swift index f3c918701..40d93f458 100644 --- a/Sources/GhosttyApp.swift +++ b/Sources/GhosttyApp.swift @@ -11,7 +11,7 @@ import Bonsplit import IOSurface import UniformTypeIdentifiers -// MARK: - GhosttyApp (split out, Nuclear Review #97; verbatim move) +// MARK: - GhosttyApp // `GhosttySurfaceCallbackContext` widened private -> internal (also constructed from TerminalSurface.swift). private func programaRuntimeReadClipboardCallback( @@ -71,7 +71,7 @@ private enum GhosttyClipboardRequestIdentityRegistry { } // Widened from private to internal: also constructed directly from -// TerminalSurface.swift (Nuclear Review #97 split). +// TerminalSurface.swift. // // DATA-ONLY. Ghostty's IO thread invokes callbacks (clipboard, actions) that read this // object SYNCHRONOUSLY, before any main-thread hop. It must therefore never hold a @@ -378,13 +378,11 @@ class GhosttyApp { tabId: callbackTabId, surfaceId: callbackSurfaceId ) else { return } - // Deviation from the pre-fix synchronous behavior: we can no longer tell - // off-main whether the surface is still live, so this callback always - // returns `true` (accepted) below rather than synchronously falling back to - // `false` when the surface is already gone. If resolution fails here, the - // read silently completes as a no-op instead -- ghostty's clipboard-read - // request is simply never fulfilled, matching what already happened when the - // surface went away mid-flight in the old code. + // We cannot tell off-main whether the surface is still live, so this callback + // always returns `true` (accepted) below rather than synchronously falling back + // to `false` when the surface is already gone. If resolution fails here, the + // read silently completes as a no-op: ghostty's clipboard-read request is + // simply never fulfilled, as when the surface goes away mid-flight. guard let requestSurface = terminalSurface.liveSurfaceForGhosttyAccess( reason: "clipboard.read" ) else { return } @@ -1900,11 +1898,11 @@ class GhosttyApp { return true } - // The old code gated this whole switch on resolving `callbackContext?.surfaceView` - // -- a synchronous, off-main weak-reference read. That resolution is exactly the - // race that corrupted ARC's weak-ref side tables during rapid window teardown (see - // GhosttySurfaceCallbackContext's doc comment). The context is now data-only, so we - // gate on the presence of a live callback context instead; every case below + // Gating this switch on resolving `callbackContext?.surfaceView` (a synchronous, + // off-main weak-reference read) would race and corrupt ARC's weak-ref side tables + // during rapid window teardown (see GhosttySurfaceCallbackContext's doc comment). + // The context is data-only, so we gate on the presence of a live callback context + // instead; every case below // resolves any live view/surface it needs only from inside a main-thread hop, via // `callbackTabId`/`callbackSurfaceId`. guard callbackContext != nil else { return false } diff --git a/Sources/GhosttyConfig.swift b/Sources/GhosttyConfig.swift index 4af20c652..0edf9ec35 100644 --- a/Sources/GhosttyConfig.swift +++ b/Sources/GhosttyConfig.swift @@ -190,7 +190,7 @@ struct GhosttyConfig { // write) left in UserDefaults stands. // - After launch both sides are reactive and can re-fire independently: settings.json // re-applies on every file-watcher change to settings.json/its fallback (and on - // `.reload()` call sites such as GhosttyTerminalView.swift), while this method re-runs + // explicit `.reload()` call sites), while this method re-runs // whenever `GhosttyConfig.load()` is called again (WorkspaceContentView's `onAppear`, // `ghosttyConfigDidReload` notification, color-scheme changes). Whichever fires most // recently wins, per key. diff --git a/Sources/GhosttyNSView.swift b/Sources/GhosttyNSView.swift index a8c53274a..20823798c 100644 --- a/Sources/GhosttyNSView.swift +++ b/Sources/GhosttyNSView.swift @@ -11,7 +11,7 @@ import Bonsplit import IOSurface import UniformTypeIdentifiers -// MARK: - Ghostty Surface View (split out, Nuclear Review #97; verbatim move) +// MARK: - Ghostty Surface View // Keyboard/mouse/drag/accessibility/IME/render-stats live in GhosttyTerminalView+*.swift. class GhosttyNSView: NSView, NSUserInterfaceValidations { @@ -680,15 +680,6 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { abs(lhs - rhs) <= epsilon } - func expectedPixelSize(for pointsSize: CGSize) -> CGSize { - let backing = convertToBacking(NSRect(origin: .zero, size: pointsSize)).size - if backing.width > 0, backing.height > 0 { - return backing - } - let scale = max(1.0, window?.backingScaleFactor ?? layer?.contentsScale ?? 1.0) - return CGSize(width: pointsSize.width * scale, height: pointsSize.height * scale) - } - // Convenience accessor for the ghostty surface var surface: ghostty_surface_t? { terminalSurface?.surface @@ -719,8 +710,6 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { } } - - func withExternalCommittedText(_ body: () -> T) -> T { externalCommittedTextDepth += 1 defer { externalCommittedTextDepth -= 1 } @@ -752,7 +741,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { // Test-only IME point override so firstRect behavior can be regression tested. // private(set): read from the NSTextInputClient conformance in - // GhosttyTerminalView+IME.swift (Nuclear Review #97 split), written only here. + // GhosttyTerminalView+IME.swift, written only here. private(set) var imePointOverrideForTesting: (x: Double, y: Double, width: Double, height: Double)? func setIMEPointForTesting(x: Double, y: Double, width: Double, height: Double) { @@ -764,8 +753,6 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { } #endif - - func shouldSuppressCommandPathHover(for flags: NSEvent.ModifierFlags) -> Bool { guard flags.contains(.command), let surface else { return false } return ghostty_surface_has_selection(surface) @@ -813,8 +800,6 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { return ghostty_input_mods_e(rawValue: mods) } - - deinit { // Surface lifecycle is managed by TerminalSurface, not the view #if DEBUG @@ -837,7 +822,6 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { terminalSurface = nil } - private func windowDidChangeScreen(_ notification: Notification) { guard let window else { return } guard let object = notification.object as? NSWindow, window == object else { return } diff --git a/Sources/GhosttySurfaceScrollView.swift b/Sources/GhosttySurfaceScrollView.swift index 8fa1d7ca7..65e7d01a0 100644 --- a/Sources/GhosttySurfaceScrollView.swift +++ b/Sources/GhosttySurfaceScrollView.swift @@ -11,7 +11,7 @@ import Bonsplit import IOSurface import UniformTypeIdentifiers -// MARK: - Scroll View Wrapper (split out, Nuclear Review #97; verbatim move) +// MARK: - Scroll View Wrapper // Layering contract: SurfaceSearchOverlay stays mounted from this file — see // CLAUDE.md "Terminal find layering contract". @@ -147,7 +147,7 @@ final class GhosttySurfaceScrollView: NSView { private let documentView: NSView // Widened from private to internal (immutable `let`, so this only grants // read access): read from the debug-only RenderStats extension - // (GhosttyTerminalView+RenderStats.swift, Nuclear Review #97 split). + // (GhosttyTerminalView+RenderStats.swift). let surfaceView: GhosttyNSView private let inactiveOverlayView: GhosttyFlashOverlayView private let dropZoneOverlayView: GhosttyFlashOverlayView @@ -203,7 +203,7 @@ final class GhosttySurfaceScrollView: NSView { /// Threshold in points from bottom to consider "at bottom" (allows for minor float drift) private static let scrollToBottomThreshold: CGFloat = 5.0 // private(set): read from the debug-only RenderStats extension - // (GhosttyTerminalView+RenderStats.swift, Nuclear Review #97 split), written only here. + // (GhosttyTerminalView+RenderStats.swift), written only here. private(set) var isActive = true private var lastFocusRefreshAt: CFTimeInterval = 0 private var pendingAutomaticFirstResponderApply = false @@ -237,7 +237,6 @@ final class GhosttySurfaceScrollView: NSView { private static var drawCounts: [UUID: Int] = [:] private static var lastDrawTimes: [UUID: CFTimeInterval] = [:] private static var presentCounts: [UUID: Int] = [:] - private static var dropOverlayShowCounts: [UUID: Int] = [:] private static var lastPresentTimes: [UUID: CFTimeInterval] = [:] private static var lastContentsKeys: [UUID: String] = [:] @@ -268,7 +267,7 @@ final class GhosttySurfaceScrollView: NSView { } // Widened from private to internal: called from the debug-only RenderStats - // extension (GhosttyTerminalView+RenderStats.swift, Nuclear Review #97 split). + // extension (GhosttyTerminalView+RenderStats.swift). static func contentsKey(for layer: CALayer?) -> String { guard let modelLayer = layer else { return "nil" } // Prefer the presentation layer to better reflect what the user sees on screen. @@ -295,7 +294,7 @@ final class GhosttySurfaceScrollView: NSView { } // Widened from private to internal: called from the debug-only RenderStats - // extension (GhosttyTerminalView+RenderStats.swift, Nuclear Review #97 split). + // extension (GhosttyTerminalView+RenderStats.swift). static func updatePresentStats(surfaceId: UUID, layer: CALayer?) -> (count: Int, last: CFTimeInterval, key: String) { let key = contentsKey(for: layer) if lastContentsKeys[surfaceId] != key { @@ -306,38 +305,6 @@ final class GhosttySurfaceScrollView: NSView { return (presentCounts[surfaceId, default: 0], lastPresentTimes[surfaceId, default: 0], key) } - private func recordDropOverlayShowAnimation() { - guard let surfaceId = surfaceView.terminalSurface?.id else { return } - Self.dropOverlayShowCounts[surfaceId, default: 0] += 1 - } - - func debugProbeDropOverlayAnimation(useDeferredPath: Bool) -> (before: Int, after: Int, bounds: CGSize) { - guard let surfaceId = surfaceView.terminalSurface?.id else { - return (0, 0, bounds.size) - } - - let before = Self.dropOverlayShowCounts[surfaceId, default: 0] - - // Reset to a hidden baseline so each probe exercises an initial-show transition. - dropZoneOverlayAnimationGeneration &+= 1 - activeDropZone = nil - pendingDropZone = nil - dropZoneOverlayView.layer?.removeAllAnimations() - dropZoneOverlayView.isHidden = true - dropZoneOverlayView.alphaValue = 1 - - if useDeferredPath { - pendingDropZone = .left - synchronizeGeometryAndContent() - } else { - setDropZoneOverlay(zone: .left) - } - - let after = Self.dropOverlayShowCounts[surfaceId, default: 0] - setDropZoneOverlay(zone: nil) - return (before, after, bounds.size) - } - var debugSurfaceId: UUID? { surfaceView.terminalSurface?.id } @@ -389,13 +356,6 @@ final class GhosttySurfaceScrollView: NSView { ) } - func releaseOwnedPortalHost(hostId: ObjectIdentifier, reason: String) { - surfaceView.terminalSurface?.releasePortalHostIfOwned( - hostId: hostId, - reason: reason - ) - } - func prepareOwnedPortalHostForTransientReattach(hostId: ObjectIdentifier, reason: String) { surfaceView.terminalSurface?.preparePortalHostReplacementIfOwned( hostId: hostId, @@ -1489,9 +1449,6 @@ final class GhosttySurfaceScrollView: NSView { applyDropZoneOverlayFrame(targetFrame) dropZoneOverlayView.alphaValue = 0 dropZoneOverlayView.isHidden = false -#if DEBUG - recordDropOverlayShowAnimation() -#endif #if DEBUG logDropZoneOverlay(event: "show", zone: zone, frame: targetFrame) #endif @@ -1858,13 +1815,6 @@ final class GhosttySurfaceScrollView: NSView { ) } - func debugNotificationRingState() -> (isHidden: Bool, opacity: Float) { - ( - notificationRingOverlayView.isHidden, - notificationRingLayer.opacity - ) - } - struct DebugDropZoneOverlayState { let isHidden: Bool let frame: CGRect @@ -1892,10 +1842,6 @@ final class GhosttySurfaceScrollView: NSView { #endif - fileprivate var hasActiveDropZoneOverlay: Bool { - activeDropZone != nil || pendingDropZone != nil - } - /// Handle file/URL drops, forwarding to the terminal as shell-escaped paths. func handleDroppedURLs(_ urls: [URL]) -> Bool { #if DEBUG diff --git a/Sources/GhosttyTerminalSupport.swift b/Sources/GhosttyTerminalSupport.swift index 5b25229f4..5b471ccba 100644 --- a/Sources/GhosttyTerminalSupport.swift +++ b/Sources/GhosttyTerminalSupport.swift @@ -11,7 +11,7 @@ import Bonsplit import IOSurface import UniformTypeIdentifiers -// MARK: - GhosttyTerminalView Support (split out, Nuclear Review #97; verbatim move) +// MARK: - GhosttyTerminalView Support @_silgen_name("ghostty_surface_clear_selection") func ghostty_surface_clear_selection_compat(_ surface: ghostty_surface_t) -> Bool @@ -51,7 +51,7 @@ func programaShouldUseClearWindowBackground(for opacity: Double) -> Bool { } // Widened from private to internal: used by both GhosttyApp.swift and -// GhosttyNSView.swift (Nuclear Review #97 split). +// GhosttyNSView.swift. func programaTransparentWindowBaseColor() -> NSColor { // A tiny non-zero alpha matches Ghostty's window compositing behavior on macOS and // avoids visual artifacts that can happen with a fully clear window background. @@ -482,19 +482,6 @@ func programaPasteboardImagePathForTesting(_ pasteboard: NSPasteboard) -> String GhosttyPasteboardHelper.saveClipboardImageIfNeeded(from: pasteboard) } -func programaResolveQuicklookPathForTesting( - _ rawText: String, - cwd: String, - existingPaths: Set -) -> String? { - programaResolveQuicklookPath( - rawText, - cwd: cwd, - fileExists: { path in - existingPaths.contains((path as NSString).standardizingPath) - } - ) -} #endif func programaResolveQuicklookPath( diff --git a/Sources/GhosttyTerminalView+Accessibility.swift b/Sources/GhosttyTerminalView+Accessibility.swift index 0fb434c66..7894c90fa 100644 --- a/Sources/GhosttyTerminalView+Accessibility.swift +++ b/Sources/GhosttyTerminalView+Accessibility.swift @@ -16,11 +16,8 @@ import UniformTypeIdentifiers // Accessibility support for GhosttyNSView: AX text-area exposure so // voice-input tools and other assistive tech can read/insert terminal text. // -// Split out of GhosttyTerminalView.swift (Nuclear Review TC5). Moving these -// methods into a same-type extension adds zero call-site indirection. -// Method bodies are moved verbatim. withExternalCommittedText stays on the -// primary class declaration (it is called from an untouched -// NSTextInputClient extension elsewhere in the file). +// withExternalCommittedText stays on the primary class declaration (it is +// called from the NSTextInputClient extension). extension GhosttyNSView { // MARK: - Accessibility @@ -153,8 +150,7 @@ extension GhosttyNSView { } // Always notify the host app that this pane became the first responder so bonsplit - // focus/selection can converge. Previously this was gated on `surface != nil`, which - // allowed a mismatch where AppKit focus moved but the UI focus indicator (bonsplit) + // focus/selection can converge. Gating this on `surface != nil` would allow a mismatch where AppKit focus moved but the UI focus indicator (bonsplit) // stayed behind. let hiddenInHierarchy = isHiddenOrHasHiddenAncestor if isVisibleInUI && hasUsableFocusGeometry && !hiddenInHierarchy { diff --git a/Sources/GhosttyTerminalView+DragDrop.swift b/Sources/GhosttyTerminalView+DragDrop.swift index 041d72b73..d16c178e6 100644 --- a/Sources/GhosttyTerminalView+DragDrop.swift +++ b/Sources/GhosttyTerminalView+DragDrop.swift @@ -17,14 +17,7 @@ import UniformTypeIdentifiers // plan resolution, dropped-file/pasteboard insertion, and the // NSDraggingDestination overrides. // -// Split out of GhosttyTerminalView.swift (Nuclear Review TC5). Moving these -// methods into a same-type extension adds zero call-site indirection. -// Method bodies are moved verbatim. - extension GhosttyNSView { - fileprivate static func escapeDropForShell(_ value: String) -> String { - TerminalPasteboardPlanner.escapeForShell(value) - } static func dropPlanForTesting(pasteboard: NSPasteboard) -> DropPlan { switch TerminalPasteboardPlanner.plan(pasteboard: pasteboard, mode: .drop) { diff --git a/Sources/GhosttyTerminalView+IME.swift b/Sources/GhosttyTerminalView+IME.swift index cd4cc9944..087c8f0ac 100644 --- a/Sources/GhosttyTerminalView+IME.swift +++ b/Sources/GhosttyTerminalView+IME.swift @@ -18,9 +18,6 @@ import UniformTypeIdentifiers // system input methods (Japanese/Chinese/Korean IME, dead-key composition, // emoji picker, etc.). // -// Split out of GhosttyTerminalView.swift (Nuclear Review #97). Extracted -// verbatim as a same-type extension, so call-site behavior is unchanged. - extension GhosttyNSView: NSTextInputClient { /// Deliver committed text using typed-input semantics so shells and editors /// keep their normal interactive behaviors (autosuggestions, Return diff --git a/Sources/GhosttyTerminalView+Keyboard.swift b/Sources/GhosttyTerminalView+Keyboard.swift index 308afc1b2..17d23479a 100644 --- a/Sources/GhosttyTerminalView+Keyboard.swift +++ b/Sources/GhosttyTerminalView+Keyboard.swift @@ -17,11 +17,6 @@ import UniformTypeIdentifiers // actions, and the typing-latency-critical key path (performKeyEquivalent, // keyDown, keyUp, flagsChanged, and their key-encoding helpers). // -// Split out of GhosttyTerminalView.swift (Nuclear Review TC5). Moving these -// methods into a same-type extension adds zero call-site indirection, so the -// typing-latency-sensitive behavior here is unchanged from the original -// single-file version. Method bodies are moved verbatim. - extension GhosttyNSView { @discardableResult func ensureSurfaceReadyForInput() -> ghostty_surface_t? { diff --git a/Sources/GhosttyTerminalView+Mouse.swift b/Sources/GhosttyTerminalView+Mouse.swift index c2ea0abb2..c9187fdce 100644 --- a/Sources/GhosttyTerminalView+Mouse.swift +++ b/Sources/GhosttyTerminalView+Mouse.swift @@ -16,10 +16,8 @@ import UniformTypeIdentifiers // Mouse handling for GhosttyNSView: cmd-click path hover, mouse // down/dragged/up, scroll wheel, and tracking-area maintenance. // -// Split out of GhosttyTerminalView.swift (Nuclear Review TC5). Moving these -// methods into a same-type extension adds zero call-site indirection. -// Method bodies are moved verbatim. deinit and windowDidChangeScreen stay -// on the primary class declaration (deinit cannot live in an extension). +// deinit and windowDidChangeScreen stay on the primary class declaration +// (deinit cannot live in an extension). extension GhosttyNSView { // MARK: - Mouse Handling @@ -116,24 +114,6 @@ extension GhosttyNSView { _ = handleCommandClickRelease(at: point, modifierFlags: event.modifierFlags, ghosttyConsumed: consumed) } - /// Attempt to open the word under the mouse cursor as a file path, resolved - /// against the terminal panel's current working directory. - private func tryOpenWordAsPath(at point: NSPoint? = nil) { - guard let resolution = resolveWordUnderCursorPath(at: point) else { return } - - #if DEBUG - dlog("link.wordFallback resolved=\(resolution.path) source=\(resolution.source.rawValue)") - #endif - - PreferredEditorSettings.open(URL(fileURLWithPath: resolution.path)) - } - - /// Check if the word under the mouse cursor resolves to an existing file/directory - /// in the terminal panel's CWD. Returns the resolved absolute path, or nil. - private func resolveWordUnderCursorAsPath(at point: NSPoint? = nil) -> String? { - resolveWordUnderCursorPath(at: point)?.path - } - private func resolveWordUnderCursorPath(at point: NSPoint? = nil) -> WordPathResolution? { guard let surface = surface else { return nil } diff --git a/Sources/GhosttyTerminalView+RenderStats.swift b/Sources/GhosttyTerminalView+RenderStats.swift index 786b06b4b..8c51cdf04 100644 --- a/Sources/GhosttyTerminalView+RenderStats.swift +++ b/Sources/GhosttyTerminalView+RenderStats.swift @@ -17,8 +17,7 @@ import UniformTypeIdentifiers // counters exposed to the debug socket, and IOSurface-backed frame sampling used to // detect blank-frame regressions without Screen Recording permissions. // -// Split out of GhosttyTerminalView.swift (Nuclear Review #97). Extracted verbatim -// as a same-type extension (entirely #if DEBUG), so behavior is unchanged. +// Same-type extension, entirely #if DEBUG. extension GhosttySurfaceScrollView { #if DEBUG diff --git a/Sources/GhosttyTerminalView+SwiftUIWrapper.swift b/Sources/GhosttyTerminalView+SwiftUIWrapper.swift index 807a3fb7b..6b6ecc960 100644 --- a/Sources/GhosttyTerminalView+SwiftUIWrapper.swift +++ b/Sources/GhosttyTerminalView+SwiftUIWrapper.swift @@ -17,9 +17,6 @@ import UniformTypeIdentifiers // AppKit-hosted terminal portal (GhosttySurfaceScrollView/GhosttyNSView), // plus its Coordinator and the private HostContainerView it manages. // -// Split out of GhosttyTerminalView.swift (Nuclear Review #97). Extracted -// verbatim, so call-site behavior is unchanged. - // MARK: - SwiftUI Wrapper struct GhosttyTerminalView: NSViewRepresentable { diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift deleted file mode 100644 index caad725f6..000000000 --- a/Sources/GhosttyTerminalView.swift +++ /dev/null @@ -1,7 +0,0 @@ -// MARK: - GhosttyTerminalView (module root — intentionally empty) -// -// Nuclear Review #97 split this ~8,400-line module into GhosttyApp.swift, -// TerminalSurface.swift, GhosttyNSView.swift, GhosttySurfaceScrollView.swift, -// TerminalCopyMode.swift, and GhosttyTerminalSupport.swift, following the -// existing GhosttyTerminalView+Suffix.swift extension convention. Every move -// was verbatim; see each new file's header for what it contains. diff --git a/Sources/GhosttyTitleUpdateDispatcher.swift b/Sources/GhosttyTitleUpdateDispatcher.swift index 7e0846e8c..227254bf4 100644 --- a/Sources/GhosttyTitleUpdateDispatcher.swift +++ b/Sources/GhosttyTitleUpdateDispatcher.swift @@ -25,9 +25,9 @@ final class GhosttyTitleUpdateDispatcher { } private struct PendingUpdate { - // Held strongly for the (short, <=50ms) coalescing window, matching the - // pre-fix behavior where `surfaceView` was captured strongly inside the - // `DispatchQueue.main.async` closure that posted the notification. + // Held strongly for the (short, <=50ms) coalescing window, matching how + // `surfaceView` is captured strongly inside the `DispatchQueue.main.async` closure + // that posts the notification. let surfaceView: AnyObject? let tabId: UUID let surfaceId: UUID diff --git a/Sources/GitMetadataProber.swift b/Sources/GitMetadataProber.swift index 6255d789f..f7b591419 100644 --- a/Sources/GitMetadataProber.swift +++ b/Sources/GitMetadataProber.swift @@ -496,11 +496,10 @@ extension CanonicalSubprocessRunner { // MARK: - GitMetadataProber // // Stateless git/GitHub CLI probing library: given a working directory, runs `git`/`gh` -// commands and parses their output into workspace sidebar git/PR metadata. Extracted from -// TabManager (which owns the stateful scheduling/timers/dedup around these probes) so the -// probing logic itself has no dependency on TabManager instance state and can be tested and -// reasoned about independently. A `struct` (not an `enum` namespace) so TabManager can hold -// a thin owned instance; the API surface is static. The one piece of shared state is the +// commands and parses their output into workspace sidebar git/PR metadata. TabManager owns +// the stateful scheduling/timers/dedup around these probes, so the probing logic itself has +// no dependency on TabManager instance state and can be tested and reasoned about +// independently. The API surface is static. The one piece of shared state is the // process-wide pull-request lookup throttle (`PullRequestLookupThrottle`), which limits the // GitHub `gh` calls while the cheap local git probes keep their own cadence. struct GitMetadataProber { diff --git a/Sources/KeyboardShortcutSettings.swift b/Sources/KeyboardShortcutSettings.swift index f12f65539..17723fcb7 100644 --- a/Sources/KeyboardShortcutSettings.swift +++ b/Sources/KeyboardShortcutSettings.swift @@ -344,38 +344,8 @@ enum KeyboardShortcutSettings { ) } - // MARK: - Backwards-Compatible API (call-sites can migrate gradually) - - // Defaults (used by settings reset + recorder button initial title) + // Default used by the settings reset and the recorder button initial title. static let showNotificationsDefault = Action.showNotifications.defaultShortcut - static let jumpToUnreadDefault = Action.jumpToUnread.defaultShortcut - - static func showNotificationsShortcut() -> StoredShortcut { shortcut(for: .showNotifications) } - static func setShowNotificationsShortcut(_ shortcut: StoredShortcut) { setShortcut(shortcut, for: .showNotifications) } - - static func jumpToUnreadShortcut() -> StoredShortcut { shortcut(for: .jumpToUnread) } - static func setJumpToUnreadShortcut(_ shortcut: StoredShortcut) { setShortcut(shortcut, for: .jumpToUnread) } - - static func nextSidebarTabShortcut() -> StoredShortcut { shortcut(for: .nextSidebarTab) } - static func prevSidebarTabShortcut() -> StoredShortcut { shortcut(for: .prevSidebarTab) } - static func renameWorkspaceShortcut() -> StoredShortcut { shortcut(for: .renameWorkspace) } - static func closeWorkspaceShortcut() -> StoredShortcut { shortcut(for: .closeWorkspace) } - - static func focusLeftShortcut() -> StoredShortcut { shortcut(for: .focusLeft) } - static func focusRightShortcut() -> StoredShortcut { shortcut(for: .focusRight) } - static func focusUpShortcut() -> StoredShortcut { shortcut(for: .focusUp) } - static func focusDownShortcut() -> StoredShortcut { shortcut(for: .focusDown) } - - static func splitRightShortcut() -> StoredShortcut { shortcut(for: .splitRight) } - static func splitDownShortcut() -> StoredShortcut { shortcut(for: .splitDown) } - static func toggleSplitZoomShortcut() -> StoredShortcut { shortcut(for: .toggleSplitZoom) } - - static func nextSurfaceShortcut() -> StoredShortcut { shortcut(for: .nextSurface) } - static func prevSurfaceShortcut() -> StoredShortcut { shortcut(for: .prevSurface) } - static func selectSurfaceByNumberShortcut() -> StoredShortcut { shortcut(for: .selectSurfaceByNumber) } - static func newSurfaceShortcut() -> StoredShortcut { shortcut(for: .newSurface) } - static func selectWorkspaceByNumberShortcut() -> StoredShortcut { shortcut(for: .selectWorkspaceByNumber) } - } struct ShortcutStroke: Equatable { diff --git a/Sources/NotificationSoundStaging.swift b/Sources/NotificationSoundStaging.swift index 138bc201a..0e55b439d 100644 --- a/Sources/NotificationSoundStaging.swift +++ b/Sources/NotificationSoundStaging.swift @@ -5,11 +5,9 @@ import UserNotifications // MARK: - Notification Sound Settings // // System-sound picker for notification delivery: exposes the built-in macOS sound names -// available to UNNotificationSound and simple playback for settings-UI previews. The -// custom-file staging/transcoding flow that used to live here was removed; see -// docs/removed/custom-notification-sounds.md. Extracted from TerminalNotificationStore.swift, -// which owns the rest of notification delivery/state and is a heavy consumer of this -// settings surface. +// available to UNNotificationSound and simple playback for settings-UI previews. +// TerminalNotificationStore.swift owns the rest of notification delivery/state and is a +// heavy consumer of this settings surface. enum NotificationSoundSettings { static let key = "notificationSound" static let defaultValue = "default" diff --git a/Sources/Panels/ReviewPanel.swift b/Sources/Panels/ReviewPanel.swift index 3ad43c984..e4cd110b3 100644 --- a/Sources/Panels/ReviewPanel.swift +++ b/Sources/Panels/ReviewPanel.swift @@ -100,15 +100,6 @@ final class ReviewPanel: Panel, ObservableObject { // MARK: - Mode - func setMode(_ newMode: ReviewDiffMode, baseBranch newBaseBranch: String? = nil) { - mode = newMode - if let newBaseBranch { - baseBranch = newBaseBranch - } - displayTitle = Self.title(mode: newMode, baseBranch: baseBranch) - refresh() - } - // MARK: - Refresh /// Kicks off `ReviewDiffProber.diffSnapshot` on a background queue and publishes the diff --git a/Sources/Panels/TerminalPanel.swift b/Sources/Panels/TerminalPanel.swift index ca8c36d69..c1aa29512 100644 --- a/Sources/Panels/TerminalPanel.swift +++ b/Sources/Panels/TerminalPanel.swift @@ -25,8 +25,6 @@ final class TerminalPanel: Panel, ObservableObject { /// Published directory from the terminal @Published private(set) var directory: String = "" - @Published private(set) var tmuxLayoutReport: TmuxPaneLayoutReport? - /// Search state for find functionality @Published var searchState: TerminalSurface.SearchState? { didSet { @@ -124,23 +122,11 @@ final class TerminalPanel: Panel, ObservableObject { } } - func updateDirectory(_ newDirectory: String) { - let trimmed = newDirectory.trimmingCharacters(in: .whitespacesAndNewlines) - if !trimmed.isEmpty && directory != trimmed { - directory = trimmed - } - } - func updateWorkspaceId(_ newWorkspaceId: UUID) { workspaceId = newWorkspaceId surface.updateWorkspaceId(newWorkspaceId) } - func updateTmuxLayoutReport(_ report: TmuxPaneLayoutReport?) { - guard tmuxLayoutReport != report else { return } - tmuxLayoutReport = report - } - func focus() { surface.setFocus(true) // `unfocus()` force-disables active state to stop stale retries from stealing focus. @@ -233,7 +219,7 @@ final class TerminalPanel: Panel, ObservableObject { return } hostedView.triggerFlash(style: GhosttySurfaceScrollView.flashStyle(for: reason)) - case .surface, .tmuxActivePane: + case .surface: hostedView.triggerFlash(style: GhosttySurfaceScrollView.flashStyle(for: reason)) } } diff --git a/Sources/ProgramaDirectoryTrust.swift b/Sources/ProgramaDirectoryTrust.swift index e20cf059c..b0d86a8ee 100644 --- a/Sources/ProgramaDirectoryTrust.swift +++ b/Sources/ProgramaDirectoryTrust.swift @@ -144,17 +144,6 @@ final class ProgramaDirectoryTrust: @unchecked Sendable { saveLocked() } - /// Remove trust by the trust key directly (as stored/displayed in settings). - func revokeTrustByPath(_ path: String) { - stateLock.lock() - defer { - stateLock.unlock() - postDidChangeNotification() - } - trustedDirectories.removeValue(forKey: path) - saveLocked() - } - /// All currently trusted paths. var allTrustedPaths: [String] { stateLock.lock() diff --git a/Sources/ProgramaSurfaceConfigTemplate.swift b/Sources/ProgramaSurfaceConfigTemplate.swift index 581f99404..bac53b9e8 100644 --- a/Sources/ProgramaSurfaceConfigTemplate.swift +++ b/Sources/ProgramaSurfaceConfigTemplate.swift @@ -1,4 +1,4 @@ -// Extracted from Workspace.swift (nuclear-review #98): ProgramaSurfaceConfigTemplate and the +// ProgramaSurfaceConfigTemplate and the // surface-config-inheritance free functions that operate on it. import Foundation diff --git a/Sources/SessionAutosaveCoordinator.swift b/Sources/SessionAutosaveCoordinator.swift index 54a9a2a67..0dc994575 100644 --- a/Sources/SessionAutosaveCoordinator.swift +++ b/Sources/SessionAutosaveCoordinator.swift @@ -4,7 +4,7 @@ import Bonsplit /// Owns the periodic session-autosave timer, the typing-quiet-period debounce, deferred retry /// scheduling, and fingerprint-based skip logic for background session snapshot persistence. /// -/// Extracted from `AppDelegate` (GitHub issue #187). `AppDelegate` still owns window state and +/// `AppDelegate` owns window state and /// the actual snapshot build/save implementations; this coordinator receives them as injected /// closures rather than a back-reference to `AppDelegate`, keeping the orchestration testable in /// isolation with fakes. diff --git a/Sources/SessionWALStore.swift b/Sources/SessionWALStore.swift index 762e6f9c4..d230b2a87 100644 --- a/Sources/SessionWALStore.swift +++ b/Sources/SessionWALStore.swift @@ -4,13 +4,7 @@ import Bonsplit /// Per-surface durable PTY output WAL + fact file (issue #181, slice 1). /// -/// This file used to be the feat/session-wal-spike byte-counting spike -/// (`SessionOutputTapSpike`) that proved tapping PTY output via Ghostty's -/// PTY tee has no measurable typing-latency cost. -/// It now replaces that counter with a real writer. The filename is kept -/// as-is (not renamed to SessionWAL.swift) because this change was made by -/// an agent without filesystem move/delete tools; the type names below are -/// renamed. A follow-up `git mv` + pbxproj path/name tweak is cosmetic only. +/// Taps PTY output via Ghostty's PTY tee, which has no measurable typing-latency cost. /// /// ## Threading path, tee callback to WAL /// 1. `ghostty_surface_set_pty_tee_cb`'s C callback fires on ghostty's diff --git a/Sources/SettingsModels.swift b/Sources/SettingsModels.swift index 0bf0dd78b..68fe62cbe 100644 --- a/Sources/SettingsModels.swift +++ b/Sources/SettingsModels.swift @@ -167,9 +167,6 @@ enum CommandPaletteSwitcherSearchSettings { static let defaultSearchAllSurfaces = false private static let flag = UserDefaultsFlag(key: searchAllSurfacesKey, defaultValue: defaultSearchAllSurfaces) - static func searchAllSurfacesEnabled(defaults: UserDefaults = .standard) -> Bool { - flag.isEnabled(defaults: defaults) - } } enum ClaudeCodeIntegrationSettings { @@ -274,8 +271,7 @@ enum PreferredEditorSettings { /// The tabs the settings window is split across. /// -/// Settings used to be one scroll of nine stacked sections, which meant finding -/// anything required knowing roughly how far down it lived. Tabs group by the +/// Tabs group settings by the /// question a user is answering, and each tab is further split into its own /// named sections so a tab is never just one undivided pile of rows: General /// covers how the app behaves day to day (App), what it notifies you about diff --git a/Sources/SidebarDragDrop.swift b/Sources/SidebarDragDrop.swift index 20b5876dc..5f47c7974 100644 --- a/Sources/SidebarDragDrop.swift +++ b/Sources/SidebarDragDrop.swift @@ -1,5 +1,5 @@ -// Sidebar drag-and-drop domain, extracted from ContentView.swift (nuclear-review #94.4). -// Pure move: DragOverlayRoutingPolicy, drag payload enums, drop delegates, +// Sidebar drag-and-drop domain. +// Holds DragOverlayRoutingPolicy, drag payload enums, drop delegates, // drop edge/indicator/planner, auto-scroll planner, and drag lifecycle/failsafe // types, consolidated from several non-contiguous locations in ContentView.swift. // diff --git a/Sources/SidebarVisuals.swift b/Sources/SidebarVisuals.swift index e533a4183..ed21f1e36 100644 --- a/Sources/SidebarVisuals.swift +++ b/Sources/SidebarVisuals.swift @@ -1,7 +1,6 @@ -// Sidebar visual chrome, extracted from ContentView.swift (nuclear-review #94.5). -// Pure move: sidebar footer/help-menu/scrim/blur/material/tint/preset enums, -// visual-effect/backdrop views, and the NSColor extension, consolidated from two -// non-contiguous locations in ContentView.swift. +// Sidebar visual chrome. +// Holds sidebar footer/help-menu/scrim/blur/material/tint/preset enums, +// visual-effect/backdrop views, and the NSColor extension. // // Access-level widening: SidebarFooter, SidebarTopScrim, SidebarScrollViewResolver, // SidebarEmptyArea, ClearScrollBackground, DraggableFolderIcon, @@ -1274,10 +1273,6 @@ private struct SidebarVisualEffectBackground: NSViewRepresentable { self.preferLiquidGlass = preferLiquidGlass } - static var liquidGlassAvailable: Bool { - WindowGlassEffect.isAvailable - } - func makeNSView(context: Context) -> NSView { #if compiler(>=6.2) if preferLiquidGlass, #available(macOS 26.0, *) { @@ -1417,17 +1412,6 @@ struct SidebarSurface: View { return true } - private var resolvedTintColor: NSColor? { - // Per-scheme hexes are only ever written by explicit user configuration - // (the preset stamp never sets them) — honor those. Otherwise tint the - // glass with the terminal background so the panel reads as the same - // material family as the terminal, not a desktop-colored slab. - let explicitHex: String? = terminalScheme == .dark ? sidebarTintHexDark : sidebarTintHexLight - if let explicitHex, sidebarTintOpacity > 0, let color = NSColor(hex: explicitHex) { - return color.withAlphaComponent(sidebarTintOpacity) - } - return GhosttyBackgroundTheme.currentColor().withAlphaComponent(0.5) - } } /// Reads the leading inset required to clear traffic lights + left titlebar accessories. diff --git a/Sources/TabManager.swift b/Sources/TabManager.swift index 39a0dc5ed..baddd7081 100644 --- a/Sources/TabManager.swift +++ b/Sources/TabManager.swift @@ -208,10 +208,6 @@ enum WorkspaceTabColorSettings { return palette(defaults: defaults).filter { !builtInNames.contains($0.name) } } - static func defaultColorHex(named name: String) -> String? { - defaultPalette.first(where: { $0.name == name })?.hex - } - static func currentColorHex(named name: String, defaults: UserDefaults = .standard) -> String? { effectivePaletteMap(defaults: defaults)[name] } @@ -225,13 +221,6 @@ enum WorkspaceTabColorSettings { persistPaletteMap(palette, defaults: defaults) } - static func removeColor(named name: String, defaults: UserDefaults = .standard) { - guard let normalizedName = normalizedColorName(name) else { return } - var palette = editablePaletteMap(defaults: defaults) - palette.removeValue(forKey: normalizedName) - persistPaletteMap(palette, defaults: defaults) - } - static func persistPaletteMap(_ rawPalette: [String: String], defaults: UserDefaults = .standard) { let normalizedPalette = normalizedPaletteMap(rawPalette) if normalizedPalette == defaultPaletteMap { @@ -598,10 +587,6 @@ class TabManager: ObservableObject { let panelId: UUID } - /// Thin owned instance of the stateless git/GitHub CLI probing library (GitMetadataProber.swift). - /// Its API is invoked as static calls (`GitMetadataProber.foo(...)`); this instance exists as - /// TabManager's ownership point for that responsibility. - let gitMetadataProber = GitMetadataProber() let focusTransitionCoordinator = FocusTransitionCoordinator() /// The window that owns this TabManager. Set by AppDelegate.registerMainWindow(). @@ -1346,20 +1331,6 @@ class TabManager: ObservableObject { } } - private func scheduleInitialWorkspaceGitMetadataRefresh( - workspaceId: UUID, - panelId: UUID, - directory: String - ) { - scheduleWorkspaceGitMetadataRefresh( - workspaceId: workspaceId, - panelId: panelId, - directory: directory, - delays: Self.initialWorkspaceGitProbeDelays, - reason: "initial" - ) - } - func scheduleWorkspaceGitMetadataRefresh( workspaceId: UUID, panelId: UUID, @@ -2387,12 +2358,6 @@ class TabManager: ObservableObject { func closeTab(_ tab: Workspace) { closeWorkspace(tab) } func closeCurrentTabWithConfirmation() { closeCurrentWorkspaceWithConfirmation() } - func closeCurrentWorkspace() { - guard let selectedId = selectedTabId, - let workspace = workspace(withId: selectedId) else { return } - closeWorkspace(workspace) - } - func closeCurrentPanelWithConfirmation() { #if DEBUG UITestRecorder.incrementInt("closePanelInvocations") @@ -2886,10 +2851,6 @@ class TabManager: ObservableObject { focusedPanelId(for: tabId) } - func rememberFocusedSurface(tabId: UUID, surfaceId: UUID) { - lastFocusedPanelByTab[tabId] = surfaceId - } - func applyWindowBackgroundForSelectedTab() { guard let selectedTabId, let tab = workspace(withId: selectedTabId), @@ -3384,11 +3345,6 @@ class TabManager: ObservableObject { selectedTabId = tabs[index].id } - func selectLastTab() { - guard let lastTab = tabs.last else { return } - selectedTabId = lastTab.id - } - // MARK: - Surface Navigation /// Select the next surface in the currently focused pane of the selected workspace @@ -3471,38 +3427,6 @@ class TabManager: ObservableObject { } } - func navigateForward() { - guard historyIndex < tabHistory.count - 1 else { return } - - // Find the next valid tab in history (skip closed tabs) - let targetIndex = historyIndex + 1 - while targetIndex < tabHistory.count { - let tabId = tabHistory[targetIndex] - if tabs.contains(where: { $0.id == tabId }) { - isNavigatingHistory = true - historyIndex = targetIndex - selectedTabId = tabId - isNavigatingHistory = false - return - } - // Remove closed tab from history - tabHistory.remove(at: targetIndex) - // Don't increment targetIndex since we removed the element - } - } - - var canNavigateBack: Bool { - historyIndex > 0 && tabHistory.prefix(historyIndex).contains { tabId in - tabs.contains { $0.id == tabId } - } - } - - var canNavigateForward: Bool { - historyIndex < tabHistory.count - 1 && tabHistory.suffix(from: historyIndex + 1).contains { tabId in - tabs.contains { $0.id == tabId } - } - } - /// Flash the currently focused panel so the user can visually confirm focus. func triggerFocusFlash() { guard let tab = selectedWorkspace, diff --git a/Sources/TerminalController+Debug.swift b/Sources/TerminalController+Debug.swift index 695712c8d..4711db4f6 100644 --- a/Sources/TerminalController+Debug.swift +++ b/Sources/TerminalController+Debug.swift @@ -1,4 +1,4 @@ -// Extracted from TerminalController.swift (nuclear-review #96): debug.* command handlers plus their private implementation helpers (DEBUG-only and shared). +// debug.* command handlers plus their private implementation helpers (DEBUG-only and shared). import AppKit import Carbon.HIToolbox @preconcurrency import Foundation @@ -414,48 +414,6 @@ extension TerminalController { return .ok(payload) } - /// Dumps the key window's AppKit view tree with frames, visibility, and any - /// opaque layer background — chrome-layering bugs (a stray view painting - /// over content) are otherwise invisible to log-based diagnosis. - nonisolated func v2DebugViewTree() -> V2CallResult { - let lines: [String] = v2MainSync { - guard let window = NSApp.keyWindow ?? NSApp.windows.first(where: { $0.isVisible && $0.contentView != nil }) else { - return [] - } - var out: [String] = [] - out.append( - "WINDOW isOpaque=\(window.isOpaque) bg=\(window.backgroundColor.hexString())@\(String(format: "%.3f", window.backgroundColor.alphaComponent)) " + - "appearance=\(window.effectiveAppearance.name.rawValue)" - ) - @MainActor - func walk(_ view: NSView, depth: Int) { - let frame = view.frame - var line = String(repeating: " ", count: depth) - line += String(describing: type(of: view)).prefix(48) - line += String(format: " (%.0f,%.0f %.0fx%.0f)", frame.origin.x, frame.origin.y, frame.width, frame.height) - if view.isHidden { line += " HIDDEN" } - if let bg = view.layer?.backgroundColor, let color = NSColor(cgColor: bg), color.alphaComponent > 0.01 { - line += " bg=\(color.hexString())@\(String(format: "%.2f", color.alphaComponent))" - } - if view.layer?.cornerRadius ?? 0 > 0 { - line += " r=\(Int(view.layer?.cornerRadius ?? 0))" - } - if let effect = view as? NSVisualEffectView { - line += " material=\(effect.material.rawValue) blend=\(effect.blendingMode.rawValue) " + - "state=\(effect.state.rawValue) alpha=\(String(format: "%.2f", effect.alphaValue)) " + - "emphasized=\(effect.isEmphasized)" - } - out.append(line) - for child in view.subviews { walk(child, depth: depth + 1) } - } - if let root = window.contentView?.superview ?? window.contentView { - walk(root, depth: 0) - } - return out - } - return .ok(["tree": lines]) - } - nonisolated func v2DebugBonsplitUnderflowCount() -> V2CallResult { let resp = bonsplitUnderflowCount() guard resp.hasPrefix("OK ") else { return .err(code: "internal_error", message: resp, data: nil) } @@ -802,117 +760,6 @@ extension TerminalController { } } - private func parseOverlayEventType(_ token: String) -> (isKnown: Bool, eventType: NSEvent.EventType?) { - switch token { - case "leftmousedragged": - return (true, .leftMouseDragged) - case "rightmousedragged": - return (true, .rightMouseDragged) - case "othermousedragged": - return (true, .otherMouseDragged) - case "mousemove", "mousemoved": - return (true, .mouseMoved) - case "mouseentered": - return (true, .mouseEntered) - case "mouseexited": - return (true, .mouseExited) - case "flagschanged": - return (true, .flagsChanged) - case "cursorupdate": - return (true, .cursorUpdate) - case "appkitdefined": - return (true, .appKitDefined) - case "systemdefined": - return (true, .systemDefined) - case "applicationdefined": - return (true, .applicationDefined) - case "periodic": - return (true, .periodic) - case "leftmousedown": - return (true, .leftMouseDown) - case "leftmouseup": - return (true, .leftMouseUp) - case "rightmousedown": - return (true, .rightMouseDown) - case "rightmouseup": - return (true, .rightMouseUp) - case "othermousedown": - return (true, .otherMouseDown) - case "othermouseup": - return (true, .otherMouseUp) - case "scrollwheel": - return (true, .scrollWheel) - case "none": - return (true, nil) - default: - return (false, nil) - } - } - - private func dragPasteboardType(from token: String) -> NSPasteboard.PasteboardType? { - let normalized = token.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() - switch normalized { - case "fileurl", "file-url", "public.file-url": - return .fileURL - case "tabtransfer", "tab-transfer", "com.splittabbar.tabtransfer": - return DragOverlayRoutingPolicy.bonsplitTabTransferType - case "sidebarreorder", "sidebar-reorder", "sidebar_tab_reorder", - "com.darkroom.programa.sidebar-tab-reorder": - return DragOverlayRoutingPolicy.sidebarTabReorderType - default: - // Allow explicit UTI strings for ad-hoc debug probes. - guard token.contains(".") else { return nil } - return NSPasteboard.PasteboardType(token) - } - } - - private func debugDragHitViewDescriptor(_ view: NSView) -> String { - let className = String(describing: type(of: view)) - let pointer = String(describing: Unmanaged.passUnretained(view).toOpaque()) - let types = view.registeredDraggedTypes - let renderedTypes: String - if types.isEmpty { - renderedTypes = "-" - } else { - let raw = types.map(\.rawValue) - renderedTypes = raw.count <= 4 - ? raw.joined(separator: ",") - : raw.prefix(4).joined(separator: ",") + ",+\(raw.count - 4)" - } - return "\(className)@\(pointer){dragTypes=\(renderedTypes)}" - } - - private func unescapeSocketText(_ input: String) -> String { - var out = "" - var escaping = false - for ch in input { - if escaping { - switch ch { - case "n": - out.append("\n") - case "r": - out.append("\r") - case "t": - out.append("\t") - case "\\": - out.append("\\") - default: - out.append("\\") - out.append(ch) - } - escaping = false - } else if ch == "\\" { - escaping = true - } else { - out.append(ch) - } - } - if escaping { - out.append("\\") - } - return out - } - private nonisolated func isTerminalFocused(_ args: String) -> String { let panelArg = args.trimmingCharacters(in: .whitespacesAndNewlines) guard !panelArg.isEmpty else { return "ERROR: Usage: is_terminal_focused " } diff --git a/Sources/TerminalController+Notification.swift b/Sources/TerminalController+Notification.swift index a2a030ac3..a22139924 100644 --- a/Sources/TerminalController+Notification.swift +++ b/Sources/TerminalController+Notification.swift @@ -1,4 +1,4 @@ -// Extracted from TerminalController.swift (nuclear-review #96): notification.* command handlers. +// notification.* command handlers. import AppKit import Carbon.HIToolbox import Foundation diff --git a/Sources/TerminalController+Pane.swift b/Sources/TerminalController+Pane.swift index 57b27b694..6ec54c80a 100644 --- a/Sources/TerminalController+Pane.swift +++ b/Sources/TerminalController+Pane.swift @@ -1,4 +1,4 @@ -// Extracted from TerminalController.swift (nuclear-review #96): pane.* command handlers. +// pane.* command handlers. import AppKit import Carbon.HIToolbox import Foundation diff --git a/Sources/TerminalController+Surface.swift b/Sources/TerminalController+Surface.swift index de73fe411..810c7a0f9 100644 --- a/Sources/TerminalController+Surface.swift +++ b/Sources/TerminalController+Surface.swift @@ -1,4 +1,4 @@ -// Extracted from TerminalController.swift (nuclear-review #96): surface.* command handlers. +// surface.* command handlers. import AppKit import Carbon.HIToolbox import Foundation diff --git a/Sources/TerminalController+SurfaceWait.swift b/Sources/TerminalController+SurfaceWait.swift index 912cfba9b..459b5d0da 100644 --- a/Sources/TerminalController+SurfaceWait.swift +++ b/Sources/TerminalController+SurfaceWait.swift @@ -1,6 +1,6 @@ // surface.wait (#166 task 1): server-owned, event-driven waits on a terminal surface. // -// An agent orchestrating a sibling surface previously had to poll `surface.read_text` in a +// Without it, an agent orchestrating a sibling surface has to poll `surface.read_text` in a // loop and guess when the other side was done -- racy (state can flip between two polls) and // wastes tokens. `surface.wait` blocks the calling socket connection (with a timeout) until // the surface hits a condition and answers in a single request/response round trip. diff --git a/Sources/TerminalController+System.swift b/Sources/TerminalController+System.swift index 99796e359..b71a64762 100644 --- a/Sources/TerminalController+System.swift +++ b/Sources/TerminalController+System.swift @@ -1,4 +1,4 @@ -// Extracted from TerminalController.swift (nuclear-review #96): system.*/settings.*/feedback.*/app.* command handlers. +// system.*/settings.*/feedback.*/app.* command handlers. import AppKit import Carbon.HIToolbox import Foundation diff --git a/Sources/TerminalController+Telemetry.swift b/Sources/TerminalController+Telemetry.swift index 84819f0e7..9bd4ba6b9 100644 --- a/Sources/TerminalController+Telemetry.swift +++ b/Sources/TerminalController+Telemetry.swift @@ -1,4 +1,4 @@ -// Extracted from TerminalController.swift (nuclear-review #96): off-main-parse + main.async-mutate telemetry commands (surface.report_*/ports_kick, workspace.set_status/log/progress/sidebar metadata). +// Off-main-parse + main.async-mutate telemetry commands (surface.report_*/ports_kick, workspace.set_status/log/progress/sidebar metadata). import AppKit import Carbon.HIToolbox import Foundation diff --git a/Sources/TerminalController+Window.swift b/Sources/TerminalController+Window.swift index 971b3546a..c28e8612e 100644 --- a/Sources/TerminalController+Window.swift +++ b/Sources/TerminalController+Window.swift @@ -1,4 +1,4 @@ -// Extracted from TerminalController.swift (nuclear-review #96): window.* command handlers. +// window.* command handlers. import AppKit import Carbon.HIToolbox import Foundation diff --git a/Sources/TerminalController+Workspace.swift b/Sources/TerminalController+Workspace.swift index 0baf44225..26c7ca631 100644 --- a/Sources/TerminalController+Workspace.swift +++ b/Sources/TerminalController+Workspace.swift @@ -1,4 +1,4 @@ -// Extracted from TerminalController.swift (nuclear-review #96): workspace.* command handlers (CRUD, action/tab.action verbs). +// workspace.* command handlers (CRUD, action/tab.action verbs). import AppKit import Carbon.HIToolbox import Foundation diff --git a/Sources/TerminalController.swift b/Sources/TerminalController.swift index 84dce6e7b..bbc53cbe1 100644 --- a/Sources/TerminalController.swift +++ b/Sources/TerminalController.swift @@ -67,9 +67,6 @@ class TerminalController { private nonisolated static let acceptFailureMaxBackoffMs = 5_000 private nonisolated static let acceptFailureMinimumRearmDelayMs = 100 private nonisolated static let acceptFailureRearmThreshold = 50 - private nonisolated static let socketProbePollTimeoutMs: Int32 = 100 - private nonisolated static let socketProbePollAttempts = 3 - private nonisolated static let socketProbePollRetryBackoffUs: useconds_t = 50_000 private nonisolated static let unixSocketPathMaxLength: Int = { var addr = sockaddr_un() // Reserve one byte for the null terminator. @@ -135,17 +132,6 @@ class TerminalController { return delayMs } } - - var debugLabel: String { - switch self { - case .retryImmediately: - return "retry_immediately" - case .resumeAfterDelay: - return "resume_after_delay" - case .rearmAfterDelay: - return "rearm_after_delay" - } - } } private enum SocketBindAttemptResult { @@ -584,25 +570,9 @@ class TerminalController { // `TerminalControllerSocketSecurityTests` via `@testable import` (regression #6618). final class SocketFastPathState: @unchecked Sendable { private let queue = DispatchQueue(label: "com.darkroom.programa.socket-fast-path") - private var lastReportedDirectories: [SocketSurfaceKey: String] = [:] private var lastReportedShellStates: [SocketSurfaceKey: Workspace.PanelShellActivityState] = [:] - private let maxTrackedDirectories = 4096 private let maxTrackedShellStates = 4096 - func shouldPublishDirectory(workspaceId: UUID, panelId: UUID, directory: String) -> Bool { - let key = SocketSurfaceKey(workspaceId: workspaceId, panelId: panelId) - return queue.sync { - if lastReportedDirectories[key] == directory { - return false - } - if lastReportedDirectories.count >= maxTrackedDirectories { - lastReportedDirectories.removeAll(keepingCapacity: true) - } - lastReportedDirectories[key] = directory - return true - } - } - /// Returns `true` when the incoming state differs from the last *applied* state, /// meaning the report is worth dispatching to the main thread. /// @@ -2110,8 +2080,6 @@ class TerminalController { return v2Result(id: id, self.v2DebugLayout()) case "debug.portal.stats": return v2Result(id: id, self.v2DebugPortalStats()) - case "debug.viewtree": - return v2Result(id: id, self.v2DebugViewTree()) case "debug.bonsplit_underflow.count": return v2Result(id: id, self.v2DebugBonsplitUnderflowCount()) case "debug.bonsplit_underflow.reset": @@ -2474,10 +2442,6 @@ class TerminalController { return !(raw is NSNull) } - nonisolated func v2StrictInt(_ params: [String: Any], _ key: String) -> Int? { - v2StrictIntAny(params[key]) - } - private nonisolated func v2StrictIntAny(_ raw: Any?) -> Int? { guard let raw else { return nil } diff --git a/Sources/TerminalCopyMode.swift b/Sources/TerminalCopyMode.swift index 7cd3c4278..66bdd3a78 100644 --- a/Sources/TerminalCopyMode.swift +++ b/Sources/TerminalCopyMode.swift @@ -11,7 +11,7 @@ import Bonsplit import IOSurface import UniformTypeIdentifiers -// MARK: - Terminal Keyboard Copy Mode (split out, Nuclear Review #97; verbatim move) +// MARK: - Terminal Keyboard Copy Mode // Four `terminalKeyTable*`/`terminalKeyboardCopyModeIndicatorText` items below // widened private -> internal (used from GhosttyNSView.swift/GhosttySurfaceScrollView.swift). diff --git a/Sources/TerminalNotificationStore.swift b/Sources/TerminalNotificationStore.swift index 961d97b07..fdeaafce1 100644 --- a/Sources/TerminalNotificationStore.swift +++ b/Sources/TerminalNotificationStore.swift @@ -340,11 +340,9 @@ final class TerminalNotificationStore: ObservableObject { // Judge focus state by the tab manager that owns this tab — the primary // window's manager gives wrong answers for tabs in secondary windows. // - // NOTE (nuclear-review #90): TerminalNotificationStore reaching back into - // AppDelegate.shared for tab-manager lookups is a layering smell — this store should - // arguably be handed the owning TabManager(s) rather than reaching for a global. - // Flagged, not fixed here: untangling it needs a real design pass on how - // TerminalNotificationStore is scoped/injected across windows, not a mechanical move. + // NOTE: reaching back into AppDelegate.shared for tab-manager lookups is a layering + // smell; the store should arguably be handed the owning TabManager(s). Untangling it + // needs a design pass on how the store is scoped/injected across windows. let owningTabManager = AppDelegate.shared?.tabManagerFor(tabId: tabId) ?? AppDelegate.shared?.tabManager let isActiveTab = owningTabManager?.selectedTabId == tabId let focusedSurfaceId = owningTabManager?.focusedSurfaceId(for: tabId) @@ -489,12 +487,6 @@ final class TerminalNotificationStore: ObservableObject { focusedReadIndicatorByTabId.removeValue(forKey: tabId) } - func clearFocusedReadIndicatorIfSurfaceChanged(forTabId tabId: UUID, surfaceId: UUID?) { - guard let existingSurfaceId = focusedReadIndicatorByTabId[tabId] else { return } - guard existingSurfaceId != surfaceId else { return } - focusedReadIndicatorByTabId.removeValue(forKey: tabId) - } - func markAllRead() { var updated = notifications var idsToClear: [String] = [] diff --git a/Sources/TerminalSurface.swift b/Sources/TerminalSurface.swift index e6fe882e6..fafe2163d 100644 --- a/Sources/TerminalSurface.swift +++ b/Sources/TerminalSurface.swift @@ -12,7 +12,7 @@ import IOSurface import UniformTypeIdentifiers import os -// MARK: - Debug Render Instrumentation (split out, Nuclear Review #97; verbatim move) +// MARK: - Debug Render Instrumentation /// Lightweight instrumentation to detect whether Ghostty is actually requesting Metal drawables. /// This helps catch "frozen until refocus" regressions without relying on screenshots (which can @@ -914,19 +914,6 @@ final class TerminalSurface: Identifiable, ObservableObject { return true } - func releasePortalHostIfOwned(hostId: ObjectIdentifier, reason: String) { - guard let current = activePortalHostLease, current.hostId == hostId else { return } - activePortalHostLease = nil -#if DEBUG - dlog( - "terminal.portal.host.release surface=\(id.uuidString.prefix(5)) " + - "reason=\(reason) host=\(hostId) pane=\(current.paneId.uuidString.prefix(5)) " + - "inWin=\(current.inWindow ? 1 : 0) " + - "area=\(String(format: "%.1f", current.area))" - ) -#endif - } - private func recordTeardownRequest(reason: String) { withDebugMetadataLock { if teardownRequestedAt == nil { @@ -1129,10 +1116,6 @@ final class TerminalSurface: Identifiable, ObservableObject { private static let surfaceLogPath = "/tmp/programa-ghostty-surface.log" private static let sizeLogPath = "/tmp/programa-ghostty-size.log" - func debugCurrentPixelSize() -> (width: UInt32, height: UInt32) { - (lastPixelWidth, lastPixelHeight) - } - func debugDesiredFocusState() -> Bool { desiredFocusState } @@ -2365,7 +2348,7 @@ final class TerminalSurface: Identifiable, ObservableObject { /// Force a full size recalculation and surface redraw. func forceRefresh(reason: String = "unspecified") { // PERF (#183): this diagnostic string is consumed only by the DEBUG-only `dlog` below, - // but it used to be built unconditionally -- interpolating the view's bounds and doing a + // and building it unconditionally would interpolate the view's bounds and do a // CAMetalLayer cast on every call, including the one that runs after each text-input // keystroke. Keep the whole thing inside DEBUG so release builds allocate nothing here. #if DEBUG diff --git a/Sources/TerminalWindowPortal.swift b/Sources/TerminalWindowPortal.swift index 41a22d651..e672d7174 100644 --- a/Sources/TerminalWindowPortal.swift +++ b/Sources/TerminalWindowPortal.swift @@ -6,7 +6,7 @@ import Bonsplit #if DEBUG // Widened from private to internal: also called from -// TerminalWindowPortalRegistry.swift (Nuclear Review #97 split). +// TerminalWindowPortalRegistry.swift. func portalDebugToken(_ view: NSView?) -> String { guard let view else { return "nil" } let ptr = Unmanaged.passUnretained(view).toOpaque() diff --git a/Sources/TerminalWindowPortalRegistry.swift b/Sources/TerminalWindowPortalRegistry.swift index f61a7859b..637d42951 100644 --- a/Sources/TerminalWindowPortalRegistry.swift +++ b/Sources/TerminalWindowPortalRegistry.swift @@ -4,7 +4,7 @@ import ObjectiveC import Bonsplit #endif -// MARK: - Terminal Window Portal Registry (split out, Nuclear Review #97; verbatim move) +// MARK: - Terminal Window Portal Registry private var programaWindowTerminalPortalKey: UInt8 = 0 private var programaWindowTerminalPortalCloseObserverKey: UInt8 = 0 diff --git a/Sources/Update/UpdateController.swift b/Sources/Update/UpdateController.swift index 46ac959c2..2481e29f1 100644 --- a/Sources/Update/UpdateController.swift +++ b/Sources/Update/UpdateController.swift @@ -93,11 +93,6 @@ class UpdateController { userDriver.viewModel } - /// True if we're force-installing an update. - var isInstalling: Bool { - installCancellable != nil - } - init() { let defaults = UserDefaults.standard UpdateSettings.apply(to: defaults) diff --git a/Sources/Update/UpdateViewModel.swift b/Sources/Update/UpdateViewModel.swift index a3f24716f..3b1f663b3 100644 --- a/Sources/Update/UpdateViewModel.swift +++ b/Sources/Update/UpdateViewModel.swift @@ -159,28 +159,6 @@ class UpdateViewModel: ObservableObject { } } - var iconColor: Color { - if showsDetectedBackgroundUpdate { - return programaAccentColor() - } - switch effectiveState { - case .idle: - return .secondary - case .permissionRequest: - return .white - case .checking: - return .secondary - case .updateAvailable: - return programaAccentColor() - case .downloading, .extracting, .installing: - return .secondary - case .notFound: - return .secondary - case .error: - return .orange - } - } - var backgroundColor: Color { if showsDetectedBackgroundUpdate { return programaAccentColor() diff --git a/Sources/V2CommandCatalog.swift b/Sources/V2CommandCatalog.swift index 6503869ef..a0a786c63 100644 --- a/Sources/V2CommandCatalog.swift +++ b/Sources/V2CommandCatalog.swift @@ -170,7 +170,6 @@ enum V2CommandCatalog { "debug.terminal.render_stats", "debug.terminals", "debug.type", - "debug.viewtree", "debug.window.screenshot", ] diff --git a/Sources/WindowDragHandleView.swift b/Sources/WindowDragHandleView.swift index 697241f91..4a40d8859 100644 --- a/Sources/WindowDragHandleView.swift +++ b/Sources/WindowDragHandleView.swift @@ -472,48 +472,3 @@ struct WindowDragHandleView: NSViewRepresentable { } } } - -/// Local monitor that guarantees double-clicks in custom titlebar surfaces trigger -/// the standard macOS titlebar action even when the visible strip is hosted by -/// higher-level SwiftUI/AppKit container views. -struct TitlebarDoubleClickMonitorView: NSViewRepresentable { - final class Coordinator { - weak var view: NSView? - var monitor: Any? - - deinit { - if let monitor { - NSEvent.removeMonitor(monitor) - } - } - } - - func makeCoordinator() -> Coordinator { Coordinator() } - - func makeNSView(context: Context) -> NSView { - let view = NSView(frame: .zero) - view.wantsLayer = true - view.layer?.backgroundColor = NSColor.clear.cgColor - - context.coordinator.view = view - - let coordinator = context.coordinator - coordinator.monitor = NSEvent.addLocalMonitorForEvents(matching: [.leftMouseDown]) { [weak coordinator] event in - guard event.clickCount >= 2 else { return event } - guard let coordinator, let view = coordinator.view, let window = view.window else { return event } - guard event.window === window else { return event } - - let point = view.convert(event.locationInWindow, from: nil) - guard view.bounds.contains(point) else { return event } - - let action = performStandardTitlebarDoubleClick(window: window) - return action == nil ? event : nil - } - - return view - } - - func updateNSView(_ nsView: NSView, context: Context) { - context.coordinator.view = nsView - } -} diff --git a/Sources/WindowTerminalHostView.swift b/Sources/WindowTerminalHostView.swift index 102a9b32c..728f08729 100644 --- a/Sources/WindowTerminalHostView.swift +++ b/Sources/WindowTerminalHostView.swift @@ -4,7 +4,7 @@ import ObjectiveC import Bonsplit #endif -// MARK: - Window Terminal Host View (split out, Nuclear Review #97; hitTest() moved byte-for-byte) +// MARK: - Window Terminal Host View final class WindowTerminalHostView: NSView { private struct DividerRegion { diff --git a/Sources/Workspace+Bonsplit.swift b/Sources/Workspace+Bonsplit.swift index 791ef593a..4172ac230 100644 --- a/Sources/Workspace+Bonsplit.swift +++ b/Sources/Workspace+Bonsplit.swift @@ -1,4 +1,4 @@ -// Extracted from Workspace.swift (nuclear-review #98): BonsplitDelegate conformance. +// BonsplitDelegate conformance. import Foundation import SwiftUI diff --git a/Sources/Workspace+FocusGeometry.swift b/Sources/Workspace+FocusGeometry.swift index 9808d3753..9ad41d3ff 100644 --- a/Sources/Workspace+FocusGeometry.swift +++ b/Sources/Workspace+FocusGeometry.swift @@ -1,4 +1,4 @@ -// Extracted from Workspace.swift (nuclear-review #98): focus/geometry reconciliation members +// Focus/geometry reconciliation members // (non-focus-split focus preservation, focusPanel, attention flashes, portal lifecycle, and the // event-driven layout follow-up / terminal-geometry / portal-visibility reconciliation pipeline). @@ -231,27 +231,6 @@ extension Workspace { } - func isCommandPaletteVisibleForWorkspaceWindow() -> Bool { - guard let app = AppDelegate.shared else { - return false - } - - if let manager = app.tabManagerFor(tabId: id), - let windowId = app.windowId(for: manager), - let window = app.mainWindow(for: windowId), - app.isCommandPaletteVisible(for: window) { - return true - } - - if let keyWindow = NSApp.keyWindow, app.isCommandPaletteVisible(for: keyWindow) { - return true - } - if let mainWindow = NSApp.mainWindow, app.isCommandPaletteVisible(for: mainWindow) { - return true - } - return false - } - func moveFocus(direction: NavigationDirection) { // If a pane is zoomed, un-zoom before navigating so the target // pane becomes visible — matches tmux behavior (#1605). diff --git a/Sources/Workspace+Layout.swift b/Sources/Workspace+Layout.swift index 7f6ee9e44..612d8bee4 100644 --- a/Sources/Workspace+Layout.swift +++ b/Sources/Workspace+Layout.swift @@ -1,4 +1,4 @@ -// Extracted from Workspace.swift (nuclear-review #98): programa.json custom layout application (applyCustomLayout and its tree/pane helpers). +// programa.json custom layout application (applyCustomLayout and its tree/pane helpers). @preconcurrency import Foundation import SwiftUI diff --git a/Sources/Workspace+Persistence.swift b/Sources/Workspace+Persistence.swift index 19b8e4b23..288a9c27b 100644 --- a/Sources/Workspace+Persistence.swift +++ b/Sources/Workspace+Persistence.swift @@ -1,4 +1,4 @@ -// Extracted from Workspace.swift (nuclear-review #98): session snapshot/restore (sessionSnapshot, restoreSessionSnapshot, and their layout/panel helpers). +// Session snapshot/restore (sessionSnapshot, restoreSessionSnapshot, and their layout/panel helpers). import Foundation import SwiftUI diff --git a/Sources/Workspace+SidebarTelemetry.swift b/Sources/Workspace+SidebarTelemetry.swift index d86877094..0f258c0fe 100644 --- a/Sources/Workspace+SidebarTelemetry.swift +++ b/Sources/Workspace+SidebarTelemetry.swift @@ -1,4 +1,4 @@ -// Extracted from Workspace.swift (nuclear-review #98): sidebar telemetry mutation/query members +// Sidebar telemetry mutation/query members // (directory, shell-activity, git-branch, pull-request, status, log, and metadata-block state). import Foundation @@ -41,22 +41,6 @@ enum SidebarTelemetryLimits { value.map { utf8ByteCount($0) <= maxBytes } ?? true } - static func truncatedToUTF8Limit(_ value: String, maxBytes: Int) -> String { - guard utf8ByteCount(value) > maxBytes else { return value } - guard maxBytes > 0 else { return "" } - - var result = "" - result.reserveCapacity(maxBytes) - var byteCount = 0 - for character in value { - let characterBytes = String(character).utf8.count - guard characterBytes <= maxBytes - byteCount else { break } - result.append(character) - byteCount += characterBytes - } - return result - } - static func configuredMaxLogEntries() -> Int { let configured = UserDefaults.standard.object(forKey: "sidebarMaxLogEntries") as? Int ?? 50 return max(1, min(500, configured)) @@ -762,14 +746,6 @@ extension Workspace { return true } - func appendSidebarLog(message: String, level: SidebarLogLevel, source: String?) { - let trimmed = message.trimmingCharacters(in: .whitespacesAndNewlines) - guard !trimmed.isEmpty else { return } - _ = appendSidebarLogEntry( - SidebarLogEntry(message: trimmed, level: level, source: source, timestamp: Date()) - ) - } - func restoreSidebarLogEntries(_ entries: [SidebarLogEntry]) { logEntries.removeAll(keepingCapacity: true) for entry in entries { diff --git a/Sources/Workspace+Surfaces.swift b/Sources/Workspace+Surfaces.swift index be704ea7d..45081d528 100644 --- a/Sources/Workspace+Surfaces.swift +++ b/Sources/Workspace+Surfaces.swift @@ -1,4 +1,4 @@ -// Extracted from Workspace.swift (nuclear-review #98): surface creation/adoption/config +// Surface creation/adoption/config // inheritance members (terminal/markdown split + surface creation). import Foundation diff --git a/Sources/Workspace+Theme.swift b/Sources/Workspace+Theme.swift index 1445f8ffe..dd2cfd52b 100644 --- a/Sources/Workspace+Theme.swift +++ b/Sources/Workspace+Theme.swift @@ -1,4 +1,4 @@ -// Extracted from Workspace.swift (nuclear-review #98): theming (split-button tooltips, bonsplit +// Theming (split-button tooltips, bonsplit // chrome/divider appearance, and applyGhosttyChrome). import Foundation diff --git a/Sources/Workspace.swift b/Sources/Workspace.swift index 1a560a3fb..12de08bdb 100644 --- a/Sources/Workspace.swift +++ b/Sources/Workspace.swift @@ -247,10 +247,6 @@ final class Workspace: Identifiable, ObservableObject { }() var focusedSurfaceId: UUID? { focusedPanelId } - var surfaceDirectories: [UUID: String] { - get { panelDirectories } - set { panelDirectories = newValue } - } var processTitle: String @@ -532,8 +528,8 @@ final class Workspace: Identifiable, ObservableObject { var isReconcilingFocusState = false var focusReconcileScheduled = false #if DEBUG - // nuclear-review #98: flipped from `private(set)` to internal so Workspace+FocusGeometry.swift - // (a separate file) can mutate this after scheduleFocusReconcile() moved there. + // Internal (not `private(set)`) so Workspace+FocusGeometry.swift, where + // scheduleFocusReconcile() lives, can mutate this. var debugFocusReconcileScheduledDuringDetachCount: Int = 0 var debugLastDidMoveTabTimestamp: TimeInterval = 0 var debugDidMoveTabEventCount: UInt64 = 0 diff --git a/Sources/WorkspaceContentView.swift b/Sources/WorkspaceContentView.swift index 263dc16ec..c34aa03c4 100644 --- a/Sources/WorkspaceContentView.swift +++ b/Sources/WorkspaceContentView.swift @@ -6,15 +6,10 @@ import Bonsplit enum TmuxOverlayExperimentTarget: String, CaseIterable, Codable, Sendable { case surface case bonsplitPane - case tmuxActivePane var usesWorkspacePaneOverlay: Bool { self == .bonsplitPane } - - var usesTmuxActivePaneOverlay: Bool { - self == .tmuxActivePane - } } struct TmuxOverlayExperimentSettings { @@ -57,49 +52,6 @@ struct TmuxOverlayExperimentSettings { } } -private enum WorkspaceTitlebarInteractionMetrics { - // Keep in sync with Bonsplit's tab bar height so the monitor only covers - // the minimal-mode titlebar strip. - static let minimalModeTopStripHeight: CGFloat = 30 -} - -struct TmuxPaneLayoutPane: Codable, Equatable, Sendable { - let paneId: String - let left: Int - let top: Int - let width: Int - let height: Int - let isActive: Bool -} - -struct TmuxPaneLayoutReport: Codable, Equatable, Sendable { - let panes: [TmuxPaneLayoutPane] - - var activePane: TmuxPaneLayoutPane? { - panes.first(where: \.isActive) ?? panes.first - } -} - -func tmuxActivePaneOverlayRect( - surfaceFrame: CGRect, - cellSize: CGSize, - pane: TmuxPaneLayoutPane -) -> CGRect? { - guard cellSize.width > 0, - cellSize.height > 0, - pane.width > 0, - pane.height > 0 else { - return nil - } - - return CGRect( - x: surfaceFrame.origin.x + (CGFloat(pane.left) * cellSize.width), - y: surfaceFrame.origin.y + (CGFloat(pane.top) * cellSize.height), - width: CGFloat(pane.width) * cellSize.width, - height: CGFloat(pane.height) * cellSize.height - ) -} - private extension PixelRect { var cgRect: CGRect { CGRect(x: x, y: y, width: width, height: height) diff --git a/Sources/WorkspaceSidebarModels.swift b/Sources/WorkspaceSidebarModels.swift index 9fe80629e..2286a181c 100644 --- a/Sources/WorkspaceSidebarModels.swift +++ b/Sources/WorkspaceSidebarModels.swift @@ -1,4 +1,4 @@ -// Extracted from Workspace.swift (nuclear-review #98): sidebar/telemetry value types +// Sidebar/telemetry value types // (status, metadata, log, progress, git-branch, and pull-request models). import Foundation diff --git a/contracts/v2/methods.json b/contracts/v2/methods.json index 0556943fa..417a1570d 100644 --- a/contracts/v2/methods.json +++ b/contracts/v2/methods.json @@ -1294,28 +1294,6 @@ }, "threading": "main" }, - "debug.viewtree": { - "auth_required": true, - "debug_only": true, - "description": "DEBUG-build-only introspection and simulation. (viewtree).", - "errors": [ - "invalid_params", - "not_found", - "internal_error" - ], - "focus_intent": false, - "handler": "v2DebugViewTree", - "params": { - "additionalProperties": false, - "properties": {}, - "required": [], - "type": "object" - }, - "result": { - "type": "object" - }, - "threading": "main" - }, "debug.window.screenshot": { "auth_required": true, "debug_only": true, diff --git a/docs/plans/rust-core-concepts.md b/docs/plans/rust-core-concepts.md index 98571951d..fdfbd13c6 100644 --- a/docs/plans/rust-core-concepts.md +++ b/docs/plans/rust-core-concepts.md @@ -356,7 +356,7 @@ transitions, enabling DECSET 1004 immediately reports current state). skip starves state machines (like the scrollbar dirty/clear split) that live partly inside the throttled call. - **Portal layering contract** (`CLAUDE.md` pitfalls): `SurfaceSearchOverlay` must mount from - `GhosttySurfaceScrollView` (`Sources/GhosttyTerminalView.swift`, the AppKit portal layer), not + `GhosttySurfaceScrollView` (`Sources/GhosttySurfaceScrollView.swift`, the AppKit portal layer), not from SwiftUI panel containers (`Sources/Panels/TerminalPanelView.swift`) — portal-hosted terminal views can sit above SwiftUI during split/workspace churn. Portal registry: `Sources/HostedViewPortalRegistry.swift`, `Sources/TerminalWindowPortal.swift`, @@ -370,7 +370,7 @@ transitions, enabling DECSET 1004 immediately reports current state). events; `TabItemView` (`Sources/ContentView.swift`) relies on `Equatable` + `.equatable()` to skip SwiftUI body re-evaluation during typing — no new `@EnvironmentObject`/`@ObservedObject`/ `@Binding` without updating `==`; `TerminalSurface.forceRefresh()` - (`Sources/GhosttyTerminalView.swift`) runs on every keystroke and must stay allocation/IO-free. + (`Sources/TerminalSurface.swift`) runs on every keystroke and must stay allocation/IO-free. - **Socket command threading policy** (`CLAUDE.md`): telemetry hot-path commands (`surface.report_*`, `surface.ports_kick`, status/progress/log metadata) must not use `DispatchQueue.main.sync`; parse/validate/dedupe off-main, minimal main-thread mutation only. diff --git a/docs/removed/README.md b/docs/removed/README.md index 17373b20f..6017dd160 100644 --- a/docs/removed/README.md +++ b/docs/removed/README.md @@ -15,5 +15,6 @@ Reductive pass of 2026-09-02, base commit 903027ccef. Core kept: the Ghostty ter | AppleScript support | [applescript.md](applescript.md) | 720 | | Inline VS Code (serve-web) | [inline-vscode.md](inline-vscode.md) | 610 | | Custom notification sound files | [custom-notification-sounds.md](custom-notification-sounds.md) | 400 | +| tmux active-pane overlay target | [tmux-active-pane-overlay.md](tmux-active-pane-overlay.md) | 70 | Line counts are git-tracked lines at removal time and include tests and vendored code. diff --git a/docs/removed/tmux-active-pane-overlay.md b/docs/removed/tmux-active-pane-overlay.md new file mode 100644 index 000000000..e50bfffe8 --- /dev/null +++ b/docs/removed/tmux-active-pane-overlay.md @@ -0,0 +1,25 @@ +# tmux active-pane overlay + +Removed 2026-10-08. Last present at commit 959d2d6acf. Restore with `git checkout 959d2d6acf -- Sources/WorkspaceContentView.swift Sources/Panels/TerminalPanel.swift`. + +## What it did + +The overlay experiment had three targets: `surface` (the default, per-surface flash and notification ring), `bonsplitPane` (a workspace-level overlay drawn over the whole Bonsplit pane) and `tmuxActivePane`. The `tmuxActivePane` target drew the flash and ring over the active tmux pane inside a terminal surface, using a layout report of pane rectangles in tmux cell units. Only `defaults write` could select it (`tmuxOverlayExperimentEnabled`, `tmuxOverlayExperimentTarget`); there was no Settings UI. + +## How it was wired + +`TmuxOverlayExperimentTarget.tmuxActivePane` and its `usesTmuxActivePaneOverlay` flag in `Sources/WorkspaceContentView.swift`. The data model was `TmuxPaneLayoutPane` and `TmuxPaneLayoutReport` (with the `activePane` accessor), the helper `tmuxActivePaneOverlayRect(surfaceFrame:cellSize:pane:)` that converted cells to a rect, and `TerminalPanel.tmuxLayoutReport` with `updateTmuxLayoutReport(_:)`. Nothing ever called `updateTmuxLayoutReport`, so the report was always nil and the target behaved exactly like `surface`. + +## Files removed and files edited + +No files were deleted. Edited: `Sources/WorkspaceContentView.swift` (the target case, the flag, the layout types, the rect helper, and the unused `WorkspaceTitlebarInteractionMetrics`) and `Sources/Panels/TerminalPanel.swift` (the published report, its updater, and the switch case in `triggerFlash`). + +The `bonsplitPane` target, `TmuxOverlayExperimentSettings` and the workspace pane overlay controller are still in the tree. Their unit tests set `targetOverrideForTesting = .bonsplitPane`, so removing them is a separate change. + +## What we learned + +The tmux side was never connected: no producer of the layout report existed in the app, the CLI or the socket API. A cell-based rectangle also cannot account for per-pane padding or the Bonsplit tab strip, which is why the workspace-level `bonsplitPane` overlay carries its own chrome-height constants. A future version should take pane geometry from the terminal (tmux control mode events) rather than a report pushed in from outside. + +## Why removed + +It was dead code behind a hidden default-off flag, with no producer feeding it. diff --git a/programaTests/GhosttyConfigTests.swift b/programaTests/GhosttyConfigTests.swift index f48e6b81e..8935bef39 100644 --- a/programaTests/GhosttyConfigTests.swift +++ b/programaTests/GhosttyConfigTests.swift @@ -3080,7 +3080,7 @@ final class ZshShellIntegrationHandoffTests: XCTestCase { let bootstrapSourcePath = repoRoot .appendingPathComponent("Resources/shell-integration/programa-bash-bootstrap.bash") let rawBootstrap = try String(contentsOf: bootstrapSourcePath, encoding: .utf8) - // Mirrors Sources/GhosttyTerminalView.swift's comment/blank-line + // Mirrors Sources/TerminalSurface.swift's comment/blank-line // stripping so the test exercises exactly what ships as // $PROMPT_COMMAND. let leanBootstrap = rawBootstrap diff --git a/programaTests/TerminalAndGhosttyTests.swift b/programaTests/TerminalAndGhosttyTests.swift index a8d7f83d6..e5b644aed 100644 --- a/programaTests/TerminalAndGhosttyTests.swift +++ b/programaTests/TerminalAndGhosttyTests.swift @@ -2127,7 +2127,7 @@ final class GhosttySurfaceOverlayTests: XCTestCase { // Force synchronous native teardown instead of relying on the async // `Task { @MainActor in ghostty_surface_free(...) } ` scheduled from - // TerminalSurface.deinit (Sources/GhosttyTerminalView.swift ~4828). Left to + // TerminalSurface.deinit (Sources/TerminalSurface.swift). Left to // run asynchronously, that teardown's completion time is unbounded and can // bleed into the next test's tightly-timed RunLoop spins/waitUntil polls, // since this test creates a real, window-attached ghostty_surface_t. diff --git a/programaTests/WorkspaceUnitTests.swift b/programaTests/WorkspaceUnitTests.swift index 849f74e5c..df08938a5 100644 --- a/programaTests/WorkspaceUnitTests.swift +++ b/programaTests/WorkspaceUnitTests.swift @@ -3251,7 +3251,7 @@ final class WorkspaceTerminalFocusRecoveryTests: XCTestCase { // newTerminalSplit's initial selection hand-off to rightPanel schedules its own // deferred first-responder/active-state reconciliation for leftPanel (see // scheduleAutomaticFirstResponderApply / resignOwnedFirstResponderIfNeeded in - // GhosttyTerminalView.swift, reason="setActive"). That work is queued on the main + // GhosttySurfaceScrollView.swift, reason="setActive"). That work is queued on the main // queue and normally drains almost immediately, well before this test's manual // makeFirstResponder override below. Under a full serial suite run the main queue // can carry a backlog of unpredictable, varying depth from hundreds of prior diff --git a/tests_v2/test_tmux_compat_matrix.py b/tests_v2/test_tmux_compat_matrix.py index 819bfb39d..b377d2c82 100644 --- a/tests_v2/test_tmux_compat_matrix.py +++ b/tests_v2/test_tmux_compat_matrix.py @@ -297,8 +297,7 @@ def main() -> int: for cmd in (["popup"], ["bind-key", "C-b", "split-window"], ["unbind-key", "C-b"], ["copy-mode"]): proc = _run_cli(cli, cmd, expect_ok=False) - merged = f"{proc.stdout}\n{proc.stderr}".lower() - _must(proc.returncode != 0 and "not supported" in merged, f"Expected not_supported for {cmd}, got: {merged!r}") + _must(proc.returncode != 0, f"Expected {cmd} to be rejected, got exit 0: {proc.stdout!r}") resize_target, resize_flag, resize_axis = _pick_resize_target(c, current_panes) pre_extent = _pane_extent(c, resize_target, resize_axis)