From fdeb56c0324ed8bf0a93bf8e3b0000c6a2ae8be5 Mon Sep 17 00:00:00 2001 From: arzafran Date: Mon, 12 Oct 2026 13:31:26 -0300 Subject: [PATCH] refactor: remove unused imports, stale comments and stray prints Drop unused import lines across Sources, CLI, tests and vendored bonsplit. Reword comments that narrate old behaviour into present tense, keeping the mechanism where it guards a future edit. Error-path print calls in ghostty init and the socket listener become NSLog so they still reach the system log in Release builds. Regenerate V2CommandCatalog.swift for the generator comment change and delete one dead local in TabBarView. --- .github/workflows/ci.yml | 16 +++---- CLI/CLI+AgentEventAdapters.swift | 13 +++--- CLI/CLI+Hooks.swift | 2 +- CLI/programa.swift | 20 +++------ Sources/AgentActivityState.swift | 6 +-- ...pDelegate+SessionSnapshotPersistence.swift | 6 +-- Sources/AppDelegate+UITestCmdClick.swift | 1 - .../AppDelegate+UITestStressWorkspaces.swift | 1 - Sources/AppDelegate.swift | 18 ++++---- Sources/BackgroundLogWriter.swift | 2 +- Sources/ClosedTerminalUndoStore.swift | 5 +-- Sources/CommandPaletteController.swift | 13 +++--- Sources/CommandPaletteSearchEngine.swift | 2 +- Sources/ContentView+CommandPalette.swift | 5 --- Sources/ContentView+SidebarResizer.swift | 4 +- Sources/ContentView.swift | 1 - Sources/DebugWindows.swift | 1 - Sources/GhosttyApp.swift | 43 +++++++------------ Sources/GhosttyNSView.swift | 11 ++--- Sources/GhosttySurfaceScrollView.swift | 11 ----- Sources/GhosttyTerminalScrollBoost.swift | 4 +- Sources/GhosttyTerminalSupport.swift | 7 --- .../GhosttyTerminalView+Accessibility.swift | 6 --- Sources/GhosttyTerminalView+DragDrop.swift | 8 ---- Sources/GhosttyTerminalView+IME.swift | 7 --- Sources/GhosttyTerminalView+Keyboard.swift | 8 ---- Sources/GhosttyTerminalView+Mouse.swift | 9 +--- Sources/GhosttyTerminalView+RenderStats.swift | 7 --- .../GhosttyTerminalView+SwiftUIWrapper.swift | 7 --- Sources/GhosttyTitleUpdateDispatcher.swift | 11 ++--- Sources/GitMetadataProber.swift | 2 - Sources/PortScanner.swift | 4 +- Sources/ProgramaConfigExecutor.swift | 20 ++++----- Sources/ReviewDiffProber.swift | 5 +-- Sources/SessionAutosaveCoordinator.swift | 6 +-- Sources/SessionEscrow.swift | 24 +++++------ Sources/SessionPersistence.swift | 5 +-- Sources/SessionWALStore.swift | 3 +- Sources/SettingsView.swift | 3 +- Sources/SidebarDragDrop.swift | 6 +-- Sources/SidebarVisuals.swift | 10 ++--- Sources/TabItemView.swift | 2 +- Sources/TabManager+UITestHarness.swift | 3 -- Sources/TabManager.swift | 12 +++--- Sources/TerminalController+Telemetry.swift | 7 ++- Sources/TerminalController.swift | 11 ++--- Sources/TerminalCopyMode.swift | 7 +-- Sources/TerminalDirectoryOpener.swift | 3 -- Sources/TerminalSurface.swift | 6 +-- Sources/TerminalThemeStore.swift | 7 ++- Sources/TerminalWindowPortal.swift | 2 - Sources/TypingProfiler.swift | 3 -- Sources/V2CommandCatalog.swift | 2 +- Sources/WindowSwizzles.swift | 7 --- Sources/Workspace+Bonsplit.swift | 11 ++--- Sources/Workspace+FocusGeometry.swift | 2 - Sources/Workspace+Layout.swift | 3 -- Sources/Workspace+Persistence.swift | 7 +-- Sources/Workspace+SidebarTelemetry.swift | 3 -- Sources/Workspace+Surfaces.swift | 3 -- Sources/Workspace+Theme.swift | 3 -- Sources/Workspace.swift | 5 +-- Sources/WorkspaceShortcutMapper.swift | 3 -- Sources/WorkspaceSidebarModels.swift | 3 -- core/crates/programad/src/fdpass.rs | 30 ++++++------- .../AppDelegateShortcutRoutingTests.swift | 4 +- programaTests/GhosttyConfigTests.swift | 2 +- .../GhosttyScrollPreciseBoostTests.swift | 3 +- .../InactivePaneFirstClickFocusTests.swift | 10 ++--- programaTests/ProgramaConfigTests.swift | 4 +- .../ProgramaDirectoryTrustTests.swift | 6 +-- ...erminalControllerSocketSecurityTests.swift | 10 ++--- .../WorkspaceCloseConfirmationTests.swift | 3 +- scripts/gen-v2-contract.py | 2 +- .../Bonsplit/Internal/Views/TabBarView.swift | 18 ++++---- .../Bonsplit/Public/DebugEventLog.swift | 2 +- 76 files changed, 181 insertions(+), 371 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16f9eb2f0..076e3169c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -332,8 +332,8 @@ jobs: # Build the Debug app + UI test bundle once and share it with every job that # only needs to *run* it (socket-integration-tests, tests-build-and-lag, - # ui-regressions). Those three jobs used to each pay their own ~7 min - # "Build app" step for an identical build; this collapses that to one. + # ui-regressions). Building once here saves each of those three jobs + # its own ~7 min "Build app" step for an identical build. # unit-tests keeps building its own artifacts: it uses the programa-unit # scheme/test host, a different target from the programa app scheme built # here, so its output isn't the same product. @@ -1175,13 +1175,11 @@ jobs: DISPLAY_DONE="/tmp/programa-ui-test-display-${TOKEN}.done" HELPER_LOG="/tmp/programa-ui-test-display-${TOKEN}-helper.log" - # This churn test used to spin up its own second CGVirtualDisplay - # alongside the persistent one from the previous step. A headless CI - # session only reliably holds one live CGVirtualDisplay at a time — - # the second instantiation was reproducibly failing with "Failed to - # create CGVirtualDisplay" on both retry attempts (a masked bug: an - # earlier version of this step didn't fail the job when that - # happened, so it silently never ran the regression test at all). + # This churn test must not spin up a second CGVirtualDisplay alongside + # the persistent one from the previous step. A headless CI session + # only reliably holds one live CGVirtualDisplay at a time; a second + # instantiation fails with "Failed to create CGVirtualDisplay" on + # every retry. # Reuse the persistent display's ID instead of creating a second one. PERSISTENT_ID_PATH="/tmp/programa-vdisplay-persistent.id" if [ ! -f "$PERSISTENT_ID_PATH" ]; then diff --git a/CLI/CLI+AgentEventAdapters.swift b/CLI/CLI+AgentEventAdapters.swift index 4305fec3a..a29e843b7 100644 --- a/CLI/CLI+AgentEventAdapters.swift +++ b/CLI/CLI+AgentEventAdapters.swift @@ -37,9 +37,9 @@ extension ProgramaCLI { } /// Reports a surface's agent activity state, then a matching `agent.event`, in that - /// order, with the same workspace/surface ids -- combines the two adjacent calls that - /// used to sit back-to-back at each hook call site in CLI+Hooks.swift. Same two socket - /// sends, same order, same arguments as before; purely a call-site collapse. + /// order, with the same workspace/surface ids -- combines the two adjacent calls a hook + /// call site in CLI+Hooks.swift would otherwise make back-to-back: two socket + /// sends, in that order, with the same arguments. func reportAgentStateAndEvent( client: SocketClient, provider: String, @@ -60,9 +60,9 @@ extension ProgramaCLI { } /// Sends `agent.needs_input` (docs/plans/agent-state-unification.md T2): one atomic - /// socket call replacing the `notification.create_for_target` + + /// socket call covering the `notification.create_for_target` + /// `workspace.set_status` "Needs input" + `surface.report_agent_state` trio a hook - /// used to send independently for the same "needs input" moment. + /// would otherwise send independently for the same "needs input" moment. func reportAgentNeedsInput( client: SocketClient, provider: String, @@ -107,8 +107,7 @@ extension ProgramaCLI { } /// Maps a classified notification subtitle ("Permission" / "Waiting") to the matching - /// `agent.event` type. Mirrors the `switch summary.subtitle` blocks that used to sit - /// inline at the Claude Code and Codex notification-hook call sites in CLI+Hooks.swift. + /// `agent.event` type. Shared by the Claude Code and Codex notification-hook call sites in CLI+Hooks.swift. func reportAgentEventForClassifiedNotificationSubtitle( client: SocketClient, provider: String, diff --git a/CLI/CLI+Hooks.swift b/CLI/CLI+Hooks.swift index 5533b8b13..b18291729 100644 --- a/CLI/CLI+Hooks.swift +++ b/CLI/CLI+Hooks.swift @@ -730,7 +730,7 @@ extension ProgramaCLI { let mappedSession = ctx.parsed.sessionId.flatMap { ctx.store.lookup(sessionId: $0) } // Deliberately best-effort, for the same reason as the surfaceId resolution - // further down. A throw here used to abort the hook before any of the three + // further down. A throw here would abort the hook before any of the three // independent clears below, while Claude Code continued with a stale red // "blocked" badge that nothing would clear. // diff --git a/CLI/programa.swift b/CLI/programa.swift index 5f175b369..133fa9583 100644 --- a/CLI/programa.swift +++ b/CLI/programa.swift @@ -357,7 +357,7 @@ enum CLIProcessRunner { /// Execution context passed to a `CommandDescriptor`'s `execute` closure. /// -/// Bundles everything a command body previously read from `run()`'s locals +/// Bundles everything a command body reads from `run()`'s locals /// (commandArgs, client, jsonOutput, idFormat, windowId, and the literal /// command name, needed by handlers shared across several names such as /// the tmux-compat group). @@ -431,7 +431,7 @@ struct CLIArgumentGrammar { /// Single source of truth for a CLI command's name(s), its one-line entry in /// the grouped `Commands:` help block, and how it executes. /// -/// This collapses what used to be independently maintained in three places: +/// This one table covers what would otherwise be three separate places: /// the kebab-case dispatch switch, the free-text `usage()` help string, and /// (implicitly) the "is this a known command" check used for unknown-command /// handling. A command added to the table automatically gets dispatch, @@ -4402,7 +4402,7 @@ struct ProgramaCLI { return trimmed.isEmpty ? nil : trimmed } - // MARK: - Race command (fan one prompt across N agents in isolated worktrees, v1: spawn only) + // MARK: - Race command (fan one prompt across N agents in isolated worktrees, spawn only) /// The only agents `race` supports: the three that report state via lifecycle hooks /// (Claude Code, OpenCode, Codex). Racing needs a trustworthy idle signal, which @@ -4411,14 +4411,7 @@ struct ProgramaCLI { /// elsewhere in the app for status badges. private static let raceSupportedAgents: [String] = ["claude", "opencode", "codex"] - /// Builds the shell command line that starts an agent with `prompt` as its initial task. - /// Reuses the same bare binary names Programa already invokes elsewhere for these agents - /// (`initialTerminalInput: "claude\n"` for the "New Claude Code Workspace" shortcut in - /// AppDelegate.swift, and the `process_names` in Resources/AgentDetection/*.json), just - /// with the prompt appended as a shell-quoted positional argument so the agent starts - /// working immediately instead of sitting at its own empty prompt. No prior art exists in - /// this codebase for "agent + initial prompt argument" specifically, so this is a new, - /// minimal one-place mapping rather than an existing construction being reused verbatim. + /// Builds the shell command line that starts an agent with `prompt` as a shell-quoted positional argument. private func raceAgentLaunchCommand(agent: String, prompt: String) -> String { "\(agent) \(shellSingleQuote(prompt))" } @@ -4477,9 +4470,8 @@ struct ProgramaCLI { // Fail immediately, before creating anything, if the cwd isn't inside a git repo. let repo = try resolveWorktreeRepoRoot(explicit: nil) - // Racing twice in a row used to collide on every index, because the branches were - // always `/1.../N`. Start after whatever `/` branches - // already exist so a second run just continues the numbering. + // Start after whatever `/` branches already exist so repeated runs + // continue the numbering instead of colliding. let taken = existingRaceIndexes(repo: repo, prefix: prefix) let firstIndex = (taken.max() ?? 0) + 1 let indexes = Array(firstIndex..<(firstIndex + n)) diff --git a/Sources/AgentActivityState.swift b/Sources/AgentActivityState.swift index f137a984d..3fe9a4594 100644 --- a/Sources/AgentActivityState.swift +++ b/Sources/AgentActivityState.swift @@ -121,9 +121,9 @@ extension AgentPresence { } } -/// The single derived glyph/label/tint for a workspace's aggregate agent state, replacing the -/// three independent renderings that used to read `panelAgentStates`, `statusEntries["claude_code"]`, -/// and the notification subtitle separately. `TabItemView` is the only consumer. +/// The single derived glyph/label/tint for a workspace's aggregate agent state, derived once from +/// `panelAgentStates`, `statusEntries["claude_code"]`, and the notification subtitle +/// so no consumer renders them independently. `TabItemView` is the only consumer. struct SidebarAgentIndicator: Equatable { enum Tint: Equatable { case blocked diff --git a/Sources/AppDelegate+SessionSnapshotPersistence.swift b/Sources/AppDelegate+SessionSnapshotPersistence.swift index d2230a113..74e8d31d2 100644 --- a/Sources/AppDelegate+SessionSnapshotPersistence.swift +++ b/Sources/AppDelegate+SessionSnapshotPersistence.swift @@ -2,9 +2,9 @@ import AppKit import Bonsplit import Foundation -/// Session snapshot build/save/persist machinery, extracted from `AppDelegate.swift` to stay -/// under its structural line budget (`scripts/check-structural-budgets.py`). Behavior-neutral -/// split: everything here still runs on `AppDelegate`, just from a sibling file. +/// Session snapshot build/save/persist machinery, kept out of `AppDelegate.swift` to stay +/// under its structural line budget (`scripts/check-structural-budgets.py`). +/// Everything here runs on `AppDelegate`, from a sibling file. extension AppDelegate { /// A synchronous save returns the disk-write result; an asynchronous save returns queue /// acceptance and reports the completed disk write through its main-queue completion. diff --git a/Sources/AppDelegate+UITestCmdClick.swift b/Sources/AppDelegate+UITestCmdClick.swift index c51528746..942c0b699 100644 --- a/Sources/AppDelegate+UITestCmdClick.swift +++ b/Sources/AppDelegate+UITestCmdClick.swift @@ -4,7 +4,6 @@ import SwiftUI import Bonsplit import CoreServices import UserNotifications -import Combine import ObjectiveC.runtime import Darwin diff --git a/Sources/AppDelegate+UITestStressWorkspaces.swift b/Sources/AppDelegate+UITestStressWorkspaces.swift index eea5fdaec..a6efaf053 100644 --- a/Sources/AppDelegate+UITestStressWorkspaces.swift +++ b/Sources/AppDelegate+UITestStressWorkspaces.swift @@ -4,7 +4,6 @@ import SwiftUI import Bonsplit import CoreServices import UserNotifications -import Combine import ObjectiveC.runtime import Darwin diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index a700a49ea..5dd94b641 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -7,7 +7,6 @@ import UserNotifications import Combine import ObjectiveC.runtime import Darwin -import Security private enum ProgramaThemeNotifications { static let reloadConfig = Notification.Name("com.darkroom.programa.themes.reload-config") @@ -2264,15 +2263,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser /// Runs everything that must be re-asserted after the Mac wakes from sleep. /// See `SessionEscrow.swift`'s `serve` death-detection loop for the - /// confirmed root cause this responds to: the escrow holder used to - /// measure heartbeat staleness with wall-clock time, so any sleep longer - /// than `SessionEscrowPolicy.heartbeatStaleAfter` (6s) made it declare - /// every escrowed PTY dead the instant the app woke back up and start - /// reading from them concurrently with the still-alive app, corrupting - /// terminal output until the app was force-quit. That's fixed at the - /// source (mach-uptime-based staleness, which pauses across sleep), but - /// this handler still proactively refreshes the escrow connection and - /// renderer state on every wake as defense in depth and to leave a + /// failure this guards against: if the escrow holder measured heartbeat + /// staleness with wall-clock time, any sleep longer than + /// `SessionEscrowPolicy.heartbeatStaleAfter` (6s) would make it declare + /// every escrowed PTY dead the instant the app woke and start reading + /// from them concurrently with the still-alive app, corrupting terminal + /// output until force-quit. Staleness is mach-uptime based (pauses across + /// sleep), and this handler additionally refreshes the escrow connection + /// and renderer state on every wake as defense in depth and to leave a /// record in the always-on diagnostics log if it ever happens again. private func handleSystemDidWake() { dilog("wake.lifecycle", "didWake") diff --git a/Sources/BackgroundLogWriter.swift b/Sources/BackgroundLogWriter.swift index 326404d64..e7c71a274 100644 --- a/Sources/BackgroundLogWriter.swift +++ b/Sources/BackgroundLogWriter.swift @@ -1,6 +1,6 @@ import Foundation -// MARK: - BackgroundLogWriter (ported from upstream cmux cb2129a5a1) +// MARK: - BackgroundLogWriter // // Avoids a per-call FileManager.fileExists + FileHandle(forWritingTo:) // open -> seekToEnd -> write -> close pattern, which would run synchronously under a lock diff --git a/Sources/ClosedTerminalUndoStore.swift b/Sources/ClosedTerminalUndoStore.swift index d69478674..5cc18ecf0 100644 --- a/Sources/ClosedTerminalUndoStore.swift +++ b/Sources/ClosedTerminalUndoStore.swift @@ -1,7 +1,6 @@ import Foundation -/// Generic, closure-based undo staging for terminal panel closes (issue #140, ported from -/// upstream cmux's task-undo-close-grace-period). Closing a terminal stages it here instead of +/// Generic, closure-based undo staging for terminal panel closes (issue #140). Closing a terminal stages it here instead of /// tearing it down immediately; if nothing restores it within the grace period, `finalize` runs /// the real teardown. /// @@ -37,7 +36,7 @@ final class ClosedTerminalUndoStore { /// Stages a close for undo. `restore` reattaches the closed panel; `finalize` performs the /// real teardown once the grace period elapses without a restore. Returns an opaque id that - /// can be used to cancel staging early (not currently needed by any caller). + /// can be used to cancel staging early. @discardableResult func stage(restore: @escaping () -> Void, finalize: @escaping () -> Void) -> UUID { let id = UUID() diff --git a/Sources/CommandPaletteController.swift b/Sources/CommandPaletteController.swift index 28d554893..335842eb0 100644 --- a/Sources/CommandPaletteController.swift +++ b/Sources/CommandPaletteController.swift @@ -1,7 +1,7 @@ // 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, +// CommandPaletteController owns every piece of palette state that is +// exclusively used by the command palette (query, mode, // search corpus/results, rename/workspace-description drafts, focus-restore // targets, usage history, etc.). ContentView holds a single // `@StateObject private var commandPaletteController` and exposes each @@ -15,13 +15,12 @@ // SwiftUI View-only property wrapper and cannot be hosted on an // ObservableObject. // -// Access-level widening: CommandPaletteMode, CommandPaletteRestoreFocusTarget, +// CommandPaletteMode, CommandPaletteRestoreFocusTarget, // CommandPaletteTextSelectionBehavior, and CommandPaletteMultilineTextEditorRepresentable -// were `private` nested types inside ContentView; widened to internal (dropped -// the `private` modifier) so this controller (a different file) can reference -// them. No other behavior change. CommandPaletteCommand, CommandPaletteSearchResult, +// are internal nested types of ContentView so this controller (a different file) can +// reference them. CommandPaletteCommand, CommandPaletteSearchResult, // CommandPaletteListScope, CommandPalettePendingActivation, and -// CommandPaletteUsageEntry were already internal nested types; referenced here +// CommandPaletteUsageEntry are also internal nested types; referenced here // via `ContentView.` qualification since nested-type name lookup requires it // from outside the enclosing type's lexical scope. diff --git a/Sources/CommandPaletteSearchEngine.swift b/Sources/CommandPaletteSearchEngine.swift index 1d77a9892..9c57a0ba7 100644 --- a/Sources/CommandPaletteSearchEngine.swift +++ b/Sources/CommandPaletteSearchEngine.swift @@ -172,7 +172,7 @@ enum CommandPaletteFuzzyMatcher { // the two interpretations point at candidates of very different lengths/title // positions: `distancePenalty`/`trailingPenalty` (both scale with the matched // word's position and the candidate's overall length) can swing by hundreds of - // points, which used to swamp a mere 10-point base gap and let a shorter, + // points, which would swamp a mere 10-point base gap and let a shorter, // less-relevant command's stray-extra-character interpretation outrank a // longer command's genuine one-character-omitted match on its own keyword. return 260 diff --git a/Sources/ContentView+CommandPalette.swift b/Sources/ContentView+CommandPalette.swift index e50ad0f7b..0f44b8e11 100644 --- a/Sources/ContentView+CommandPalette.swift +++ b/Sources/ContentView+CommandPalette.swift @@ -9,13 +9,8 @@ // remain on the `ContentView` struct in ContentView.swift, since SwiftUI's // `@State` cannot be split across files without a larger controller-based // rewrite (tracked as a follow-up). -// -// A handful of members these functions call into were widened from `private` -// to `internal` (no other behavior change) so this extension can see them; -// see the CV1 PR description for the exact list. import AppKit -import Combine import SwiftUI extension ContentView { diff --git a/Sources/ContentView+SidebarResizer.swift b/Sources/ContentView+SidebarResizer.swift index 6c7ba82c7..1ae99ee6e 100644 --- a/Sources/ContentView+SidebarResizer.swift +++ b/Sources/ContentView+SidebarResizer.swift @@ -2,8 +2,8 @@ // The backing @State stays on `ContentView` (SwiftUI requires stored properties on the primary // declaration); those properties, plus `SidebarResizerHandle`, `updateSidebarResizerBandState`, // `installSidebarResizerPointerMonitorIfNeeded`, `removeSidebarResizerPointerMonitor`, and -// `sidebarResizerOverlay`, were widened from `private` to internal so this extension and -// ContentView.swift's view body can both see them. See the PR description for the exact list. +// `sidebarResizerOverlay`, are internal so this extension and +// ContentView.swift's view body can both see them. import AppKit import SwiftUI diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 2836b1658..63d961537 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -4,7 +4,6 @@ import Combine import ImageIO import SwiftUI import ObjectiveC -import UniformTypeIdentifiers struct ContentView: View { @ObservedObject var updateViewModel: UpdateViewModel diff --git a/Sources/DebugWindows.swift b/Sources/DebugWindows.swift index 0ca01d769..10513dba1 100644 --- a/Sources/DebugWindows.swift +++ b/Sources/DebugWindows.swift @@ -2,7 +2,6 @@ import AppKit import SwiftUI import Darwin import Bonsplit -import UniformTypeIdentifiers private func localizedDebugLabel(_ value: String) -> String { String(localized: String.LocalizationValue(value)) diff --git a/Sources/GhosttyApp.swift b/Sources/GhosttyApp.swift index a235231a9..a16f832c6 100644 --- a/Sources/GhosttyApp.swift +++ b/Sources/GhosttyApp.swift @@ -1,18 +1,12 @@ import Foundation import SwiftUI import AppKit -import Metal import QuartzCore -import Combine import CoreText import Darwin -import Carbon.HIToolbox import Bonsplit -import IOSurface -import UniformTypeIdentifiers // MARK: - GhosttyApp -// `GhosttySurfaceCallbackContext` widened private -> internal (also constructed from TerminalSurface.swift). private func programaRuntimeReadClipboardCallback( _ userdata: UnsafeMutableRawPointer?, @@ -70,9 +64,6 @@ private enum GhosttyClipboardRequestIdentityRegistry { } } -// Widened from private to internal: also constructed directly from -// 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 // reference -- weak or strong -- to a Swift UI/runtime object such as `GhosttyNSView` or @@ -116,16 +107,16 @@ final class GhosttySurfaceCallbackContext { /// `read_clipboard_cb`/`confirm_read_clipboard_cb` resolve this off the ghostty IO thread /// (see the "off-main" comment on `runtimeReadClipboardCallback` below) -- so a /// main-actor-only registry is NOT sound here. This uses a lock instead. Unlike the -/// original fix (#262), the registry now OWNS the data: `register`/`updateTabId` store the +/// original fix (#262), the registry OWNS the data: `register`/`updateTabId` store the /// snapshot directly, keyed by pointer, so `resolve(from:)` is a pure dictionary lookup -- /// it never calls `Unmanaged.fromOpaque(_:).takeUnretainedValue()` and never touches the -/// `GhosttySurfaceCallbackContext` instance at all. This removes two defects the pointer-set -/// version had: (1) the DEBUG stale-pointer `dlog` used to fire while `lock` was held, and -/// dlog does synchronous real-time file I/O -- on the frequent stale-teardown path under -/// surface churn that serialized every ghostty callback on file I/O; now the log is emitted -/// after the lock is released. (2) `resolve` used to read `context.tabId`, which took the +/// `GhosttySurfaceCallbackContext` instance at all. This avoids two defects a pointer-set +/// design has: (1) a DEBUG stale-pointer `dlog` fired while `lock` is held would do +/// synchronous real-time file I/O on the frequent stale-teardown path under surface +/// churn, serializing every ghostty callback on file I/O; the log is emitted +/// after the lock is released. (2) reading `context.tabId` in `resolve` would take the /// context's own lock while still holding the registry lock (a nested-lock pattern) and -/// required dereferencing a pointer that is exactly what we're trying to make un-dereferenceable. +/// require dereferencing a pointer that is exactly what we're trying to make un-dereferenceable. enum GhosttySurfaceUserdataRegistry { private static let lock = NSLock() private static var liveContexts: [UnsafeMutableRawPointer: GhosttySurfaceCallbackSnapshot] = [:] @@ -451,8 +442,8 @@ class GhosttyApp { private let backgroundLogURL = GhosttyApp.resolveBackgroundLogURL() private let backgroundLogStartUptime = ProcessInfo.processInfo.systemUptime // Guards only the cheap `backgroundLogSequence` increment below; the actual file - // write is offloaded to `backgroundLogWriter`'s serial queue (ported from upstream - // cmux cb2129a5a1) so callers never block on FileHandle open/seek/write/close. + // write is offloaded to `backgroundLogWriter`'s serial queue + // so callers never block on FileHandle open/seek/write/close. private let backgroundLogLock = NSLock() private var backgroundLogSequence: UInt64 = 0 // Non-lazy: `handleAction`/`logBackground` run on Ghostty callback threads, and @@ -520,13 +511,13 @@ class GhosttyApp { // Initialize Ghostty library first let result = ghostty_init(UInt(CommandLine.argc), CommandLine.unsafeArgv) if result != GHOSTTY_SUCCESS { - print("Failed to initialize ghostty: \(result)") + NSLog("Failed to initialize ghostty: \(result)") return } // Load config guard let primaryConfig = ghostty_config_new() else { - print("Failed to create ghostty config") + NSLog("Failed to create ghostty config") return } @@ -687,7 +678,7 @@ class GhosttyApp { ghostty_config_free(primaryConfig) guard let fallbackConfig = ghostty_config_new() else { - print("Failed to create ghostty fallback config") + NSLog("Failed to create ghostty fallback config") return } @@ -706,7 +697,7 @@ class GhosttyApp { Self.initLog("ghostty_app_new(fallback) failed") Self.dumpConfigDiagnostics(fallbackConfig, label: "fallback") #endif - print("Failed to create ghostty app") + NSLog("Failed to create ghostty app") ghostty_config_free(fallbackConfig) return } @@ -842,8 +833,6 @@ class GhosttyApp { /// default based on the system's preferred languages without overriding /// user-managed fallback chains or configured fonts that already cover /// the affected CJK ranges. - /// - /// See: https://github.com/manaflow-ai/cmux/pull/1017 private func loadCJKFontFallbackIfNeeded(_ config: ghostty_config_t) { guard let mappings = Self.autoInjectedCJKFontMappings() else { return } @@ -2077,7 +2066,7 @@ class GhosttyApp { .flatMap { String(cString: $0) } ?? "" if let tabId = callbackTabId, let surfaceId = callbackSurfaceId { - // Coalesced (ported from upstream cmux c30733e5e6): shells/agent CLIs + // Coalesced: shells/agent CLIs // that rewrite the title on every render would otherwise flood the // main actor with one NotificationCenter post per keystroke. The // dispatcher guarantees the last title set within the window is @@ -2408,8 +2397,8 @@ class GhosttyApp { #if DEBUG /// Test-only seam exposing the private `callbackContext(from:)` resolution (which /// wraps `GhosttySurfaceUserdataRegistry.resolve(from:)`) so tests can verify a stale - /// userdata pointer resolves to `nil` instead of dereferencing freed memory. Widened - /// from `private` to an internal, DEBUG-only static func rather than exposing the + /// userdata pointer resolves to `nil` instead of dereferencing freed memory. This is an + /// internal, DEBUG-only static func rather than exposing the /// registry or the callback context type directly, matching the DEBUG-only test /// scaffolding pattern used elsewhere (e.g. `TerminalSurface.replaceSurfaceWithFreedPointerForTesting`). static func debugCallbackContextResolves(from userdata: UnsafeMutableRawPointer?) -> Bool { diff --git a/Sources/GhosttyNSView.swift b/Sources/GhosttyNSView.swift index 20823798c..2d5066607 100644 --- a/Sources/GhosttyNSView.swift +++ b/Sources/GhosttyNSView.swift @@ -3,10 +3,6 @@ import SwiftUI import AppKit import Metal import QuartzCore -import Combine -import CoreText -import Darwin -import Carbon.HIToolbox import Bonsplit import IOSurface import UniformTypeIdentifiers @@ -163,10 +159,9 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { var trackingArea: NSTrackingArea? private var windowObserver: NSObjectProtocol? private var screenParametersObserver: NSObjectProtocol? - // Teardown-race guard (ported from #258's GhosttySurfaceScrollView fix; this was - // the residual crasher on the reland gate — the `notification.object as? NSWindow` - // downcast in windowDidChangeScreen implicitly retaining a freed notification - // object). NotificationCenter's removeObserver does NOT cancel a block already + // Teardown-race guard (same pattern as GhosttySurfaceScrollView; it protects the + // `notification.object as? NSWindow` downcast in windowDidChangeScreen from + // implicitly retaining a freed notification object). NotificationCenter's removeObserver does NOT cancel a block already // handed off to the main queue — during rapid window create/close, a block // enqueued against the *previous* window/registration batch can still run after // viewDidMoveToWindow has already torn down and re-registered observers for a diff --git a/Sources/GhosttySurfaceScrollView.swift b/Sources/GhosttySurfaceScrollView.swift index 2adbccd53..328857763 100644 --- a/Sources/GhosttySurfaceScrollView.swift +++ b/Sources/GhosttySurfaceScrollView.swift @@ -3,13 +3,9 @@ import SwiftUI import AppKit import Metal import QuartzCore -import Combine -import CoreText import Darwin -import Carbon.HIToolbox import Bonsplit import IOSurface -import UniformTypeIdentifiers // MARK: - Scroll View Wrapper // Layering contract: SurfaceSearchOverlay stays mounted from this file — see @@ -145,9 +141,6 @@ final class GhosttySurfaceScrollView: NSView { private let backgroundView: NSView private let scrollView: GhosttyScrollView 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). let surfaceView: GhosttyNSView private let inactiveOverlayView: GhosttyFlashOverlayView private let dropZoneOverlayView: GhosttyFlashOverlayView @@ -250,8 +243,6 @@ final class GhosttySurfaceScrollView: NSView { flashCounts[surfaceId, default: 0] += 1 } - // Widened from private to internal: called from the debug-only RenderStats - // 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. @@ -277,8 +268,6 @@ final class GhosttySurfaceScrollView: NSView { return String(describing: contents) } - // Widened from private to internal: called from the debug-only RenderStats - // 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 { diff --git a/Sources/GhosttyTerminalScrollBoost.swift b/Sources/GhosttyTerminalScrollBoost.swift index efbc65db8..768904458 100644 --- a/Sources/GhosttyTerminalScrollBoost.swift +++ b/Sources/GhosttyTerminalScrollBoost.swift @@ -1,7 +1,7 @@ import AppKit -/// Classifies a terminal scroll event for the historical precise-delta 2x -/// boost (ported from upstream cmux da4e4bc460). +/// Classifies a terminal scroll event for the precise-delta 2x +/// boost. /// /// Trackpads and Magic Mouse drive a continuous gesture phase; high-res plain /// wheels (e.g. Logitech free-spin) report precise deltas but leave both diff --git a/Sources/GhosttyTerminalSupport.swift b/Sources/GhosttyTerminalSupport.swift index 072bee0b1..f3ee9c6fe 100644 --- a/Sources/GhosttyTerminalSupport.swift +++ b/Sources/GhosttyTerminalSupport.swift @@ -1,12 +1,7 @@ import Foundation import SwiftUI import AppKit -import Metal -import QuartzCore -import Combine -import CoreText import Darwin -import Carbon.HIToolbox import Bonsplit import IOSurface import UniformTypeIdentifiers @@ -50,8 +45,6 @@ func programaShouldUseClearWindowBackground(for opacity: Double) -> Bool { programaShouldUseTransparentBackgroundWindow() || opacity < 0.999 } -// Widened from private to internal: used by both GhosttyApp.swift and -// 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. diff --git a/Sources/GhosttyTerminalView+Accessibility.swift b/Sources/GhosttyTerminalView+Accessibility.swift index 7894c90fa..4d48e420f 100644 --- a/Sources/GhosttyTerminalView+Accessibility.swift +++ b/Sources/GhosttyTerminalView+Accessibility.swift @@ -1,15 +1,9 @@ import Foundation import SwiftUI import AppKit -import Metal import QuartzCore -import Combine -import CoreText -import Darwin -import Carbon.HIToolbox import Bonsplit import IOSurface -import UniformTypeIdentifiers // MARK: - GhosttyNSView + Accessibility // diff --git a/Sources/GhosttyTerminalView+DragDrop.swift b/Sources/GhosttyTerminalView+DragDrop.swift index d16c178e6..bea90cca3 100644 --- a/Sources/GhosttyTerminalView+DragDrop.swift +++ b/Sources/GhosttyTerminalView+DragDrop.swift @@ -1,15 +1,7 @@ import Foundation import SwiftUI import AppKit -import Metal -import QuartzCore -import Combine -import CoreText -import Darwin -import Carbon.HIToolbox import Bonsplit -import IOSurface -import UniformTypeIdentifiers // MARK: - GhosttyNSView + Drag & Drop // diff --git a/Sources/GhosttyTerminalView+IME.swift b/Sources/GhosttyTerminalView+IME.swift index 087c8f0ac..8686c9c53 100644 --- a/Sources/GhosttyTerminalView+IME.swift +++ b/Sources/GhosttyTerminalView+IME.swift @@ -1,15 +1,8 @@ import Foundation import SwiftUI import AppKit -import Metal -import QuartzCore -import Combine -import CoreText -import Darwin import Carbon.HIToolbox import Bonsplit -import IOSurface -import UniformTypeIdentifiers // MARK: - GhosttyNSView + IME (NSTextInputClient) // diff --git a/Sources/GhosttyTerminalView+Keyboard.swift b/Sources/GhosttyTerminalView+Keyboard.swift index 17d23479a..e8242148f 100644 --- a/Sources/GhosttyTerminalView+Keyboard.swift +++ b/Sources/GhosttyTerminalView+Keyboard.swift @@ -1,15 +1,7 @@ import Foundation import SwiftUI import AppKit -import Metal -import QuartzCore -import Combine -import CoreText -import Darwin -import Carbon.HIToolbox import Bonsplit -import IOSurface -import UniformTypeIdentifiers // MARK: - GhosttyNSView + Keyboard // diff --git a/Sources/GhosttyTerminalView+Mouse.swift b/Sources/GhosttyTerminalView+Mouse.swift index c9187fdce..b9b4ff042 100644 --- a/Sources/GhosttyTerminalView+Mouse.swift +++ b/Sources/GhosttyTerminalView+Mouse.swift @@ -1,15 +1,9 @@ import Foundation import SwiftUI import AppKit -import Metal import QuartzCore -import Combine -import CoreText import Darwin -import Carbon.HIToolbox import Bonsplit -import IOSurface -import UniformTypeIdentifiers // MARK: - GhosttyNSView + Mouse // @@ -921,8 +915,7 @@ extension GhosttyNSView { var y = event.scrollingDeltaY let precision = event.hasPreciseScrollingDeltas // Boost only gesture-driven precise deltas; high-res mouse wheels - // report precise deltas with no phase and must not be doubled - // (ported from upstream cmux da4e4bc460). + // report precise deltas with no phase and must not be doubled. if GhosttyTerminalScrollBoost(event: event).shouldDoublePreciseScrollDelta { x *= 2 y *= 2 diff --git a/Sources/GhosttyTerminalView+RenderStats.swift b/Sources/GhosttyTerminalView+RenderStats.swift index f8cc4c92a..3ff71cf97 100644 --- a/Sources/GhosttyTerminalView+RenderStats.swift +++ b/Sources/GhosttyTerminalView+RenderStats.swift @@ -1,15 +1,8 @@ import Foundation import SwiftUI import AppKit -import Metal -import QuartzCore -import Combine -import CoreText -import Darwin -import Carbon.HIToolbox import Bonsplit import IOSurface -import UniformTypeIdentifiers // MARK: - GhosttySurfaceScrollView + RenderStats // diff --git a/Sources/GhosttyTerminalView+SwiftUIWrapper.swift b/Sources/GhosttyTerminalView+SwiftUIWrapper.swift index 6b6ecc960..d24d949a3 100644 --- a/Sources/GhosttyTerminalView+SwiftUIWrapper.swift +++ b/Sources/GhosttyTerminalView+SwiftUIWrapper.swift @@ -1,15 +1,8 @@ import Foundation import SwiftUI import AppKit -import Metal import QuartzCore -import Combine -import CoreText -import Darwin -import Carbon.HIToolbox import Bonsplit -import IOSurface -import UniformTypeIdentifiers // MARK: - GhosttyTerminalView (SwiftUI Wrapper) // diff --git a/Sources/GhosttyTitleUpdateDispatcher.swift b/Sources/GhosttyTitleUpdateDispatcher.swift index 227254bf4..ee2a065b7 100644 --- a/Sources/GhosttyTitleUpdateDispatcher.swift +++ b/Sources/GhosttyTitleUpdateDispatcher.swift @@ -1,10 +1,10 @@ import Foundation -// MARK: - GhosttyTitleUpdateDispatcher (ported from upstream cmux c30733e5e6) +// MARK: - GhosttyTitleUpdateDispatcher // -// Every GHOSTTY_ACTION_SET_TITLE callback used to post `.ghosttyDidSetTitle` via an -// unconditional `DispatchQueue.main.async`. Shells and agent CLIs that rewrite the -// title on every render (e.g. progress spinners) flood the main actor with +// Posting `.ghosttyDidSetTitle` through an unconditional `DispatchQueue.main.async` on +// every GHOSTTY_ACTION_SET_TITLE callback would let shells and agent CLIs that rewrite +// the title on every render (e.g. progress spinners) flood the main actor with // NotificationCenter posts during typing, adding up to real input latency. // // This dispatcher coalesces title updates per surface: at most one @@ -12,9 +12,6 @@ import Foundation // that arrive while a flush is already scheduled overwrite the pending title rather // than queuing a second post, but the *last* title set within the window is always // the one delivered -- no title is ever silently dropped. -// -// Only the coalescing machinery is ported here. Upstream's notification-hook / -// desktop-notification ingress is cmux agent-chat machinery programa doesn't have. final class GhosttyTitleUpdateDispatcher { /// Identifies the surface a title update belongs to. Coalescing is scoped per key /// so a burst of title churn on one surface never delays or drops updates for diff --git a/Sources/GitMetadataProber.swift b/Sources/GitMetadataProber.swift index 63639af0f..9e5eae621 100644 --- a/Sources/GitMetadataProber.swift +++ b/Sources/GitMetadataProber.swift @@ -615,7 +615,6 @@ struct GitMetadataProber { pullRequestLookupThrottle.removeAll() } - // Widened from `private` to `internal`: called from TabManager.swift. nonisolated static func initialWorkspaceGitMetadataSnapshot( for directory: String ) -> InitialWorkspaceGitMetadataSnapshot { @@ -1122,7 +1121,6 @@ struct GitMetadataProber { return String(trimmed.prefix(180)) } - // Widened from `private` to `internal`: called from TabManager.swift. nonisolated static func normalizedBranchName(_ branch: String?) -> String? { let trimmed = branch?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" return trimmed.isEmpty ? nil : trimmed diff --git a/Sources/PortScanner.swift b/Sources/PortScanner.swift index 585fa401f..6c8f8c864 100644 --- a/Sources/PortScanner.swift +++ b/Sources/PortScanner.swift @@ -2,7 +2,7 @@ import AppKit import Foundation import os -/// Batched port scanner that replaces per-shell `ps + lsof` scanning. +/// Batched port scanner: one coalesced `ps + lsof` pass instead of per-shell scanning. /// /// Each shell sends a lightweight `report_tty` + `ports_kick` over the socket. /// PortScanner coalesces kicks across all panels, then runs a single @@ -692,7 +692,7 @@ final class PortScanner: @unchecked Sendable { } /// Every `ps`/`lsof` run is bounded: a hung `lsof` (for example on a stale network mount) - /// used to block this serial queue, and with it all port scanning, for the rest of the + /// would block this serial queue, and with it all port scanning, for the rest of the /// session. Returns nil when the tool timed out or could not run; callers skip the cycle. private static let subprocessTimeout: TimeInterval = 5 private static let subprocessStdoutLimit = 16 * 1024 * 1024 diff --git a/Sources/ProgramaConfigExecutor.swift b/Sources/ProgramaConfigExecutor.swift index 8731c052c..be573c957 100644 --- a/Sources/ProgramaConfigExecutor.swift +++ b/Sources/ProgramaConfigExecutor.swift @@ -143,7 +143,7 @@ struct ProgramaConfigExecutor { /// /// | directory | `confirm:` | prompt? | /// |-----------|------------|---------| - /// | untrusted | absent | **yes** -- this is the case that used to run silently | + /// | untrusted | absent | **yes** -- this is the case that must never run silently | /// | untrusted | true | yes | /// | trusted | absent | no | /// | trusted | true | yes -- author's "ask me anyway" marker | @@ -162,17 +162,15 @@ struct ProgramaConfigExecutor { /// The single gate in front of everything a `programa.json` can run or send. Returns false /// when the user declined, in which case the caller must not proceed. /// - /// Trust is the app's decision, not the config file's. It previously was not: the gate ran - /// only `if command.confirm == true`, and `confirm` is a field in the very file we are - /// deciding whether to trust, so any config could opt out of being checked by simply - /// omitting it. `workspace`-type commands were never gated at all, despite being able to - /// carry an arbitrary `command` and `env` per surface. Between them, cloning a repo and - /// picking a plausible-looking entry out of the command palette ran arbitrary shell with no - /// prompt. Recipes are the same shape of risk with a different payload: a cloned repo could + /// Trust is the app's decision, not the config file's. Gating on + /// `command.confirm == true` alone would let any config opt out of being checked by omitting + /// it, since `confirm` is a field in the very file being judged. `workspace`-type commands + /// can carry an arbitrary `command` and `env` per surface, so they are gated too. Otherwise, + /// cloning a repo and picking a plausible-looking entry out of the command palette would run + /// arbitrary shell with no prompt. Recipes are the same shape of risk with a different payload: a cloned repo could /// ship a recipe whose prompt text is chosen by an attacker and fired at the user's coding /// agent, so they go through this exact same gate rather than a parallel path. /// - /// Now: /// - untrusted directory: always confirm, whatever the file says, for every entry type; /// - trusted directory: run, unless the author set `confirm: true`, which stays meaningful /// as a "this one is destructive, ask me anyway" marker (commands only -- recipes have no @@ -307,8 +305,8 @@ struct ProgramaConfigExecutor { /// unfiltered (only Programa's own `PROGRAMA_*` identity keys are protected, see /// `protectedStartupEnvironmentKeys` in TerminalSurface.swift), so `ZDOTDIR` / /// `BASH_ENV` / `ENV` set here execute a repo-controlled shell startup file the - /// moment the surface spawns. Filtering surfaces down to "has a command" -- as this - /// function used to -- let such a surface contribute nothing to the dialog, so the + /// moment the surface spawns. Filtering surfaces down to "has a command" -- as a + /// command-only filter would -- lets such a surface contribute nothing to the dialog, so the /// user would approve a workspace the dialog claimed ran nothing. Every surface that /// sets `command`, `cwd`, or `env` must produce a visible line. private static func surfaceDescriptions(in node: ProgramaLayoutNode) -> [String] { diff --git a/Sources/ReviewDiffProber.swift b/Sources/ReviewDiffProber.swift index fd615989f..3fcfa83bc 100644 --- a/Sources/ReviewDiffProber.swift +++ b/Sources/ReviewDiffProber.swift @@ -75,7 +75,7 @@ struct ReviewDiffProber { private nonisolated static func uncommittedSnapshot(repoRoot: String) -> ReviewDiffSnapshot { // `HEAD` doesn't resolve on an unborn branch (a repo with commits never made yet). Diff // against the empty tree in that case so staged files before the first commit still show - // up as added, instead of `git diff HEAD` failing and (previously) reading as clean. + // up as added, instead of `git diff HEAD` failing and reading as clean. let diffRangeArgs = headDiffRangeArgs(repoRoot: repoRoot) switch diffAndOverrides(repoRoot: repoRoot, diffRangeArgs: diffRangeArgs) { case .failure(let error): @@ -158,8 +158,7 @@ struct ReviewDiffProber { } /// `git diff --numstat` reports `-\t-\t` for binary files -- a cheap single extra - /// invocation used to build a binary-file set before the unified-diff parse, per - /// docs/plans/diff-review-panel.md §3 point 4. + /// invocation builds a binary-file set before the unified-diff parse. private nonisolated static func binaryFilePaths(repoRoot: String, diffRangeArgs: [String]) -> Result, ReviewDiffError> { switch runCommandResult( directory: repoRoot, diff --git a/Sources/SessionAutosaveCoordinator.swift b/Sources/SessionAutosaveCoordinator.swift index 0dc994575..54787da03 100644 --- a/Sources/SessionAutosaveCoordinator.swift +++ b/Sources/SessionAutosaveCoordinator.swift @@ -255,9 +255,9 @@ final class SessionAutosaveCoordinator { } } - // Widened from `fileprivate` to `internal`: NSWindow.programa_sendEvent(_:) (in - // WindowSwizzles.swift) calls AppDelegate.shared?.sessionAutosave.recordTypingActivity() - // from a different file. Refs #95, #187. + // NSWindow.programa_sendEvent(_:) (in WindowSwizzles.swift) calls + // AppDelegate.shared?.sessionAutosave.recordTypingActivity() from a different file. + // Refs #95, #187. // // HOT PATH: called on every keyDown via NSWindow.programa_sendEvent. Keep this a single // property write -- no allocation, logging, formatting, or locks. diff --git a/Sources/SessionEscrow.swift b/Sources/SessionEscrow.swift index d81354a69..1397c8d05 100644 --- a/Sources/SessionEscrow.swift +++ b/Sources/SessionEscrow.swift @@ -131,10 +131,10 @@ import Bonsplit /// `HeldSession` carries a `stopRequested` flag (mutated only under /// `SessionEscrowHolder.registryLock`) and a `DispatchSemaphore` the drain /// thread signals exactly once, right after it stops. The drain loop itself -/// no longer blocks in a bare `read()`: it `poll()`s the fd together with +/// never blocks in a bare `read()`: it `poll()`s the fd together with /// the session's self-pipe (`HeldSession.requestStop()` writes one byte to /// it), so a stop is noticed immediately even while the child is producing -/// no output at all, and only calls the actual (now effectively non-blocking, since +/// no output at all, and only calls the actual (effectively non-blocking, since /// `poll` already confirmed readability) `read()` once data is pending -- /// this is the "bounded by one `drainReadBufferSize` read" a retrieval /// waits out. A retrieval request sets `stopRequested`, blocks on the @@ -479,15 +479,12 @@ enum EscrowWireFormat { /// `session_escrow_shim.h`, wired into this target via /// `programa-Bridging-Header.h`). /// -/// The fd-passing send/receive used to be hand-rolled here in Swift, -/// manually computing the `cmsghdr` control-message layout since Darwin's -/// `` `CMSG_*` macros are C preprocessor macros and aren't -/// importable into Swift. That version's `sendmsg` failed with `EINVAL` in -/// a real repro despite the byte-offset arithmetic matching the macros on -/// paper -- marshalling `msghdr`/`cmsghdr` by hand across several nested -/// `withUnsafe...` closures is exactly the kind of detail that's easy to -/// get subtly wrong in a way that's hard to diagnose without a debugger on -/// the actual kernel call. `session_escrow_shim.c` does the same job in +/// Darwin's `` `CMSG_*` macros are C preprocessor macros and aren't +/// importable into Swift, so marshalling `msghdr`/`cmsghdr` by hand across several +/// nested `withUnsafe...` closures is easy to get subtly wrong (a hand-rolled +/// `sendmsg` failed with `EINVAL` despite byte offsets matching the macros on +/// paper) and hard to diagnose without a debugger on the actual kernel call. +/// `session_escrow_shim.c` does the same job in /// ~30 lines of plain C using the real `CMSG_*` macros, which is strictly /// more reliable for this one piece; everything else (framing, session /// bookkeeping, heartbeats, retry/backoff) stays in Swift. @@ -2512,13 +2509,12 @@ enum SessionEscrowHolder { /// exactly the same file (same path, same rotation cap) the app's own /// `SessionWALStore` was writing before it died. Runs on a dedicated /// thread -- this process has no rendering/latency constraints, unlike - /// the app -- but no longer blocks in a bare `read()`: it `poll()`s + /// the app -- and never blocks in a bare `read()`: it `poll()`s /// first (bounded, `SessionEscrowPolicy.drainPollIntervalMilliseconds`) /// so `session.stopRequested` (set by a retrieval, see /// `handleRetrieveRequest`) is noticed promptly even while the child is /// producing no output. See the file-level "Drain/retrieve - /// coordination" doc comment for the full contract. Unlike the - /// pre-slice-2 version, this method does NOT unconditionally close + /// coordination" doc comment for the full contract. This method does NOT unconditionally close /// `session.fd` on every exit path -- only on a genuine end-of-stream /// (the child truly exited). Stopping for a retrieval must leave the fd /// open and untouched so it can be handed back; closing it here would diff --git a/Sources/SessionPersistence.swift b/Sources/SessionPersistence.swift index fceb5bcee..e34c70261 100644 --- a/Sources/SessionPersistence.swift +++ b/Sources/SessionPersistence.swift @@ -1197,9 +1197,8 @@ enum TerminalReplayModeReset { /// transcript for a session whose child did not survive to be reattached) /// for injection into a freshly spawned surface's revive-seed path /// (`TerminalSurface.pendingReviveSeed` / `seedRevivedScrollbackIfPending`), -/// instead of the old temp-file + `PROGRAMA_RESTORE_SCROLLBACK_FILE` + -/// shell-rc-`cat` mechanism this replaces (formerly -/// `SessionScrollbackReplayStore`). A pure text transform now -- no temp +/// rather than a temp-file + `PROGRAMA_RESTORE_SCROLLBACK_FILE` + +/// shell-rc-`cat` mechanism. A pure text transform -- no temp /// files, no environment variables, no shell integration involved -- because /// the seed path bypasses the pty entirely (`ghostty_surface_process_output`) /// exactly like the escrow-revive path already does. See diff --git a/Sources/SessionWALStore.swift b/Sources/SessionWALStore.swift index d230b2a87..19edaf0e1 100644 --- a/Sources/SessionWALStore.swift +++ b/Sources/SessionWALStore.swift @@ -812,8 +812,7 @@ enum SessionWALCore { /// Extracted out of the singleton/file-system-coupled store method so it /// is directly unit-testable: the raw-byte suffix cut below has the exact /// same torn-escape/torn-UTF-8 hazard `readFrameAndDelta` already guards - /// against at its own cut sites (~554) -- this call site was previously - /// missing that guard, leaking orphaned CSI/UTF-8 fragments as literal + /// against at its own cut sites (~554), so orphaned CSI/UTF-8 fragments never leak as literal /// replayed text. static func decodeFallbackScrollbackText( rotated: Data?, diff --git a/Sources/SettingsView.swift b/Sources/SettingsView.swift index 7dccab75e..0e5936a0a 100644 --- a/Sources/SettingsView.swift +++ b/Sources/SettingsView.swift @@ -234,7 +234,6 @@ struct SettingsView: View { #if DEBUG dlog("notification.ui enableTapped state=\(state)") #endif - NSLog("notification.ui enableTapped state=%@", state) switch notificationStore.authorizationState { case .unknown, .notDetermined: notificationStore.requestAuthorizationFromSettings() @@ -346,7 +345,7 @@ struct SettingsView: View { .safeAreaInset(edge: .top, spacing: 0) { ZStack(alignment: .top) { // A gradient that fades to clear never fully hides what scrolls behind - // it, so rows used to read straight through the tab strip. A solid + // it, so rows read straight through the tab strip. A solid // header material plus a hairline on scroll is what AppKit does. AboutVisualEffectBackground(material: .headerView, blendingMode: .withinWindow) diff --git a/Sources/SidebarDragDrop.swift b/Sources/SidebarDragDrop.swift index 5f47c7974..9ee6bfb49 100644 --- a/Sources/SidebarDragDrop.swift +++ b/Sources/SidebarDragDrop.swift @@ -3,10 +3,8 @@ // drop edge/indicator/planner, auto-scroll planner, and drag lifecycle/failsafe // types, consolidated from several non-contiguous locations in ContentView.swift. // -// Access-level widening: SidebarDragFailsafeMonitor and SidebarExternalDropOverlay -// were file-private to the old ContentView.swift; widened to internal (dropped -// the `private` modifier) because VerticalTabsSidebar and ContentView, which stay -// in ContentView.swift, still construct/reference them. No other behavior change. +// SidebarDragFailsafeMonitor and SidebarExternalDropOverlay are internal because +// VerticalTabsSidebar and ContentView (in ContentView.swift) construct/reference them. import AppKit import Bonsplit diff --git a/Sources/SidebarVisuals.swift b/Sources/SidebarVisuals.swift index ed21f1e36..fa0780f5b 100644 --- a/Sources/SidebarVisuals.swift +++ b/Sources/SidebarVisuals.swift @@ -2,12 +2,10 @@ // 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, -// TitlebarLeadingInsetReader, and SidebarBackdrop were -// file-private to the old ContentView.swift; widened to internal (dropped the -// `private` modifier) because VerticalTabsSidebar and ContentView, which stay in -// ContentView.swift, still construct/reference them. No other behavior change. +// SidebarFooter, SidebarTopScrim, SidebarScrollViewResolver, SidebarEmptyArea, +// ClearScrollBackground, DraggableFolderIcon, TitlebarLeadingInsetReader, and +// SidebarBackdrop are internal because VerticalTabsSidebar and ContentView +// (in ContentView.swift) construct/reference them. import AppKit import Bonsplit diff --git a/Sources/TabItemView.swift b/Sources/TabItemView.swift index 0b9b6bf07..28357a8c7 100644 --- a/Sources/TabItemView.swift +++ b/Sources/TabItemView.swift @@ -313,7 +313,7 @@ struct TabItemView: View, Equatable { return SidebarTitle.strippingLeadingStatusGlyph(tab.title) } - /// Status entry keys/values that used to duplicate agent state in the metadata rows. + /// Status entry keys/values that duplicate agent state in the metadata rows. /// Filtered out at render time so older CLI builds that still send them don't double up /// with the badge above. private static let agentStateStatusKeys: Set = ["claude_code", "codex", "opencode"] diff --git a/Sources/TabManager+UITestHarness.swift b/Sources/TabManager+UITestHarness.swift index 37bff17d0..1daf126f1 100644 --- a/Sources/TabManager+UITestHarness.swift +++ b/Sources/TabManager+UITestHarness.swift @@ -305,7 +305,6 @@ extension TabManager { return (attached, hasSurface, firstResponder) } - // Widened from `private` to `internal`: called from TabManager.init() in TabManager.swift. func setupUITestFocusShortcutsIfNeeded() { guard !didSetupUITestFocusShortcuts else { return } didSetupUITestFocusShortcuts = true @@ -333,7 +332,6 @@ extension TabManager { ) } - // Widened from `private` to `internal`: called from TabManager.init() in TabManager.swift. func setupSplitCloseRightUITestIfNeeded() { guard !didSetupSplitCloseRightUITest else { return } didSetupSplitCloseRightUITest = true @@ -845,7 +843,6 @@ extension TabManager { return object } - // Widened from `private` to `internal`: called from TabManager.init() in TabManager.swift. func setupChildExitKeyboardUITestIfNeeded() { guard !didSetupChildExitKeyboardUITest else { return } didSetupChildExitKeyboardUITest = true diff --git a/Sources/TabManager.swift b/Sources/TabManager.swift index 27d74a571..2272529ab 100644 --- a/Sources/TabManager.swift +++ b/Sources/TabManager.swift @@ -765,8 +765,6 @@ class TabManager: ObservableObject { #endif #if DEBUG - // Widened from `private` to `internal` (default access) so TabManager+UITestHarness.swift, - // which owns the setup functions that mutate this state, can see it across files. var didSetupSplitCloseRightUITest = false var didSetupUITestFocusShortcuts = false var didSetupChildExitKeyboardUITest = false @@ -1011,7 +1009,9 @@ class TabManager: ObservableObject { if panel.searchState == nil { panel.searchState = TerminalSurface.SearchState() } - NSLog("Find: startSearch workspace=%@ panel=%@", panel.workspaceId.uuidString, panel.id.uuidString) +#if DEBUG + dlog("Find: startSearch workspace=\(panel.workspaceId.uuidString) panel=\(panel.id.uuidString)") +#endif NotificationCenter.default.post(name: .ghosttySearchFocus, object: panel.surface) _ = panel.performBindingAction("start_search") return @@ -1029,7 +1029,9 @@ class TabManager: ObservableObject { if panel.searchState == nil { panel.searchState = TerminalSurface.SearchState() } - NSLog("Find: searchSelection workspace=%@ panel=%@", panel.workspaceId.uuidString, panel.id.uuidString) +#if DEBUG + dlog("Find: searchSelection workspace=\(panel.workspaceId.uuidString) panel=\(panel.id.uuidString)") +#endif NotificationCenter.default.post(name: .ghosttySearchFocus, object: panel.surface) _ = panel.performBindingAction("search_selection") } @@ -2811,7 +2813,7 @@ class TabManager: ObservableObject { // MARK: - Panel/Surface ID Access - /// Returns the focused panel ID for a tab (replaces focusedSurfaceId) + /// Returns the focused panel ID for a tab. func focusedPanelId(for tabId: UUID) -> UUID? { workspace(withId: tabId)?.focusedPanelId } diff --git a/Sources/TerminalController+Telemetry.swift b/Sources/TerminalController+Telemetry.swift index 4eaccf91f..233d8db7a 100644 --- a/Sources/TerminalController+Telemetry.swift +++ b/Sources/TerminalController+Telemetry.swift @@ -440,10 +440,9 @@ extension TerminalController { } /// Atomically reports a surface as blocked on user input and posts the matching - /// notification (docs/plans/agent-state-unification.md T2): replaces the three - /// independent socket calls (`notification.create_for_target` + - /// `workspace.set_status` "Needs input" + `surface.report_agent_state`) a hook used to - /// make for the same "needs input" moment, any one of which could fail alone. Parses + /// notification in one call, instead of three independent socket calls + /// (`notification.create_for_target` + `workspace.set_status` "Needs input" + + /// `surface.report_agent_state`), any one of which could fail alone. Parses /// and validates off-main; hops to main once via `v2ScheduleSurfaceTelemetryMutation` /// (notification creation must run on main -- it drives AppKit) and performs the state /// write, notification, and supervision update in that single hop. diff --git a/Sources/TerminalController.swift b/Sources/TerminalController.swift index 8576e9fd8..7c1f109b0 100644 --- a/Sources/TerminalController.swift +++ b/Sources/TerminalController.swift @@ -973,7 +973,7 @@ class TerminalController { // Create socket let newServerSocket = socket(AF_UNIX, SOCK_STREAM, 0) guard newServerSocket >= 0 else { - print("TerminalController: Failed to create socket") + NSLog("TerminalController: Failed to create socket") return } @@ -1002,7 +1002,7 @@ class TerminalController { close(newServerSocket) return case .failure: - print("TerminalController: Failed to bind socket") + NSLog("TerminalController: Failed to bind socket") close(newServerSocket) return } @@ -1011,7 +1011,7 @@ class TerminalController { // Listen guard listen(newServerSocket, Self.socketListenBacklog) >= 0 else { - print("TerminalController: Failed to listen on socket") + NSLog("TerminalController: Failed to listen on socket") close(newServerSocket) return } @@ -1031,7 +1031,6 @@ class TerminalController { } listenerActivated = true let listenerSocket = newServerSocket - print("TerminalController: Listening on \(activeSocketPath)") dilog("socket.listener", "start path=\(activeSocketPath) accessMode=\(accessMode)") NotificationCenter.default.post( name: .socketListenerDidStart, @@ -1247,9 +1246,7 @@ class TerminalController { (socketPath, mode_t(accessMode.socketFilePermissions)) } if chmod(currentSocketPath, permissions) != 0 { - print( - "TerminalController: Failed to set socket permissions to \(String(permissions, radix: 8)) for \(currentSocketPath)" - ) + NSLog("TerminalController: Failed to set socket permissions to %@ for %@", String(permissions, radix: 8), currentSocketPath) } } diff --git a/Sources/TerminalCopyMode.swift b/Sources/TerminalCopyMode.swift index 66bdd3a78..9b0c1b236 100644 --- a/Sources/TerminalCopyMode.swift +++ b/Sources/TerminalCopyMode.swift @@ -1,19 +1,14 @@ import Foundation import SwiftUI import AppKit -import Metal -import QuartzCore -import Combine import CoreText -import Darwin import Carbon.HIToolbox import Bonsplit import IOSurface -import UniformTypeIdentifiers // MARK: - Terminal Keyboard Copy Mode // Four `terminalKeyTable*`/`terminalKeyboardCopyModeIndicatorText` items below -// widened private -> internal (used from GhosttyNSView.swift/GhosttySurfaceScrollView.swift). +// are internal: used from GhosttyNSView.swift/GhosttySurfaceScrollView.swift. enum TerminalKeyboardCopyModeSelectionMove: String, Equatable { case left diff --git a/Sources/TerminalDirectoryOpener.swift b/Sources/TerminalDirectoryOpener.swift index 7d401c339..adaaaa98a 100644 --- a/Sources/TerminalDirectoryOpener.swift +++ b/Sources/TerminalDirectoryOpener.swift @@ -2,10 +2,7 @@ import AppKit import SwiftUI import Bonsplit import CoreServices -import UserNotifications -import Combine import ObjectiveC.runtime -import Darwin enum FinderServicePathResolver { private static func canonicalDirectoryPath(_ path: String) -> String { diff --git a/Sources/TerminalSurface.swift b/Sources/TerminalSurface.swift index 82aa6fde7..0775fa269 100644 --- a/Sources/TerminalSurface.swift +++ b/Sources/TerminalSurface.swift @@ -1,7 +1,6 @@ import Foundation import SwiftUI import AppKit -import Metal import QuartzCore import Combine import CoreText @@ -9,7 +8,6 @@ import Darwin import Carbon.HIToolbox import Bonsplit import IOSurface -import UniformTypeIdentifiers import os // MARK: - Debug Render Instrumentation @@ -1256,7 +1254,7 @@ final class TerminalSurface: Identifiable, ObservableObject { #endif guard let app = GhosttyApp.shared.app else { - print("Ghostty app not initialized") + NSLog("Ghostty app not initialized") #if DEBUG Self.surfaceLog("createSurface FAILED surface=\(id.uuidString): ghostty app not initialized") #endif @@ -1588,7 +1586,7 @@ final class TerminalSurface: Identifiable, ObservableObject { } GhosttySurfaceUserdataRegistry.release(surfaceCallbackContext) surfaceCallbackContext = nil - print("Failed to create ghostty surface") + NSLog("Failed to create ghostty surface") #if DEBUG Self.surfaceLog("createSurface FAILED surface=\(id.uuidString): ghostty_surface_new returned nil") if let cfg = GhosttyApp.shared.config { diff --git a/Sources/TerminalThemeStore.swift b/Sources/TerminalThemeStore.swift index 6e8813fa6..47769703e 100644 --- a/Sources/TerminalThemeStore.swift +++ b/Sources/TerminalThemeStore.swift @@ -166,8 +166,7 @@ struct TerminalThemeStore { return Self.directiveValue(for: "theme", in: body) } - /// Reads only the managed block (replaces `managedRawThemeValue()`'s role for backups, now - /// generalized to all five directives). + /// Reads only the managed block, for backups of all five directives. func managedRawAppearance() -> TerminalAppearanceOverrides { guard let body = managedBlockBody() else { return TerminalAppearanceOverrides() } @@ -372,8 +371,8 @@ struct TerminalThemeStore { } } - /// "Last matching line wins" scan for a single `key = value` directive, generalized from - /// the theme-only scanner this store used to have. + /// "Last matching line wins" scan for a single `key = value` directive, shared by every + /// managed directive. private static func directiveValue(for key: String, in contents: String) -> String? { var lastValue: String? diff --git a/Sources/TerminalWindowPortal.swift b/Sources/TerminalWindowPortal.swift index e672d7174..1b22bcc88 100644 --- a/Sources/TerminalWindowPortal.swift +++ b/Sources/TerminalWindowPortal.swift @@ -5,8 +5,6 @@ import Bonsplit #endif #if DEBUG -// Widened from private to internal: also called from -// TerminalWindowPortalRegistry.swift. func portalDebugToken(_ view: NSView?) -> String { guard let view else { return "nil" } let ptr = Unmanaged.passUnretained(view).toOpaque() diff --git a/Sources/TypingProfiler.swift b/Sources/TypingProfiler.swift index b0af44043..42afa1b32 100644 --- a/Sources/TypingProfiler.swift +++ b/Sources/TypingProfiler.swift @@ -2,10 +2,7 @@ import AppKit import SwiftUI import Bonsplit import CoreServices -import UserNotifications -import Combine import ObjectiveC.runtime -import Darwin #if DEBUG enum ProgramaTypingTiming { diff --git a/Sources/V2CommandCatalog.swift b/Sources/V2CommandCatalog.swift index a0a786c63..b28981f11 100644 --- a/Sources/V2CommandCatalog.swift +++ b/Sources/V2CommandCatalog.swift @@ -174,7 +174,7 @@ enum V2CommandCatalog { ] /// Full method list for the current build configuration, mirroring the - /// conditional `#if DEBUG` append that used to live inline in + /// conditional `#if DEBUG` append in /// `TerminalController.v2Capabilities()`. static var methods: [String] { #if DEBUG diff --git a/Sources/WindowSwizzles.swift b/Sources/WindowSwizzles.swift index 5e37ebe89..b71e52eb3 100644 --- a/Sources/WindowSwizzles.swift +++ b/Sources/WindowSwizzles.swift @@ -2,10 +2,7 @@ import AppKit import SwiftUI import Bonsplit import CoreServices -import UserNotifications -import Combine import ObjectiveC.runtime -import Darwin private var programaFirstResponderGuardHitViewContext: NSView? private var programaWindowFirstResponderBypassDepth = 0 @@ -24,8 +21,6 @@ func programaIsWindowFirstResponderBypassActive() -> Bool { } #if DEBUG -// Widened from `private extension` to `extension`: AppDelegate.installWindowResponderSwizzles() -// (in AppDelegate.swift) references these @objc methods via #selector(...) for method swizzling. Refs #95. // DEBUG-only: the swizzle exists solely for typing-timing instrumentation. extension NSApplication { @objc func programa_applicationSendEvent(_ event: NSEvent) { @@ -56,8 +51,6 @@ extension NSApplication { } #endif -// Widened from `private extension` to `extension`: AppDelegate.installWindowResponderSwizzles() -// (in AppDelegate.swift) references these @objc methods via #selector(...) for method swizzling. Refs #95. extension NSWindow { @objc func programa_makeFirstResponder(_ responder: NSResponder?) -> Bool { if programaIsWindowFirstResponderBypassActive() { diff --git a/Sources/Workspace+Bonsplit.swift b/Sources/Workspace+Bonsplit.swift index d56d5707b..9b815c81d 100644 --- a/Sources/Workspace+Bonsplit.swift +++ b/Sources/Workspace+Bonsplit.swift @@ -4,10 +4,7 @@ import Foundation import SwiftUI import AppKit import Bonsplit -import Combine -import CryptoKit import Darwin -import Network import CoreText /// One predicate for the workspace split cap: the pane-chrome split buttons @@ -522,7 +519,7 @@ extension Workspace: @preconcurrency BonsplitDelegate { } #if DEBUG - NSLog("[Workspace] didCloseTab panelId=\(panelId) remainingPanels=\(panels.count - 1) remainingPanes=\(controller.allPaneIds.count)") + dlog("[Workspace] didCloseTab panelId=\(panelId) remainingPanels=\(panels.count - 1) remainingPanes=\(controller.allPaneIds.count)") #endif let panel = panels[panelId] @@ -688,9 +685,9 @@ extension Workspace: @preconcurrency BonsplitDelegate { } /// Canonical per-panel metadata teardown shared by every close path. - /// The single-surface and pane-close paths previously hand-copied this - /// list and drifted (pane close leaked inheritance font points and never - /// cleared notifications). Aggregate recomputes (recomputeListeningPorts) + /// Every close path calls this one list so none can drift (a hand-copied + /// pane-close list leaked inheritance font points and never cleared + /// notifications). Aggregate recomputes (recomputeListeningPorts) /// stay at the call sites. private func removeSurfaceMetadata(panelId: UUID, isDetaching: Bool = false) { panelDirectories.removeValue(forKey: panelId) diff --git a/Sources/Workspace+FocusGeometry.swift b/Sources/Workspace+FocusGeometry.swift index 9ad41d3ff..feb370b6c 100644 --- a/Sources/Workspace+FocusGeometry.swift +++ b/Sources/Workspace+FocusGeometry.swift @@ -7,9 +7,7 @@ import SwiftUI import AppKit import Bonsplit import Combine -import CryptoKit import Darwin -import Network import CoreText extension Workspace { diff --git a/Sources/Workspace+Layout.swift b/Sources/Workspace+Layout.swift index 612d8bee4..b0afd12cc 100644 --- a/Sources/Workspace+Layout.swift +++ b/Sources/Workspace+Layout.swift @@ -4,10 +4,7 @@ import SwiftUI import AppKit import Bonsplit -import Combine -import CryptoKit import Darwin -import Network import CoreText @MainActor diff --git a/Sources/Workspace+Persistence.swift b/Sources/Workspace+Persistence.swift index 288a9c27b..f55da6098 100644 --- a/Sources/Workspace+Persistence.swift +++ b/Sources/Workspace+Persistence.swift @@ -4,10 +4,7 @@ import Foundation import SwiftUI import AppKit import Bonsplit -import Combine -import CryptoKit import Darwin -import Network import CoreText extension Workspace { @@ -129,13 +126,13 @@ extension Workspace { // before releasing any surface gated under this restore pass -- see // `TerminalSurface.isSessionRestoreSettling`'s doc comment. // - // Widened to two turns (was one): the primary-window restore path + // The gate waits two turns: the primary-window restore path // runs this restore before `window.setFrame(restoredFrame, display: // true)` (`AppDelegate.swift` restore-then-setFrame ordering), and the // terminal portal's own geometry sync is itself deferred past the // current layout turn (`TerminalWindowPortalRegistry // .scheduleExternalGeometrySynchronize`, - // `GhosttyTerminalView+SwiftUIWrapper.swift`). One turn released the + // `GhosttyTerminalView+SwiftUIWrapper.swift`). A single turn would release the // gate before the surface ever saw the final window width, which is // what let width-dependent WAL replay scatter text across the wrong // columns on restore. diff --git a/Sources/Workspace+SidebarTelemetry.swift b/Sources/Workspace+SidebarTelemetry.swift index 0f258c0fe..9eee358c2 100644 --- a/Sources/Workspace+SidebarTelemetry.swift +++ b/Sources/Workspace+SidebarTelemetry.swift @@ -5,10 +5,7 @@ import Foundation import SwiftUI import AppKit import Bonsplit -import Combine -import CryptoKit import Darwin -import Network import CoreText enum SidebarTelemetryLimits { diff --git a/Sources/Workspace+Surfaces.swift b/Sources/Workspace+Surfaces.swift index 831d226d0..c64576e01 100644 --- a/Sources/Workspace+Surfaces.swift +++ b/Sources/Workspace+Surfaces.swift @@ -5,10 +5,7 @@ import Foundation import SwiftUI import AppKit import Bonsplit -import Combine -import CryptoKit import Darwin -import Network import CoreText extension Workspace { diff --git a/Sources/Workspace+Theme.swift b/Sources/Workspace+Theme.swift index dd2cfd52b..d878924a3 100644 --- a/Sources/Workspace+Theme.swift +++ b/Sources/Workspace+Theme.swift @@ -5,10 +5,7 @@ import Foundation import SwiftUI import AppKit import Bonsplit -import Combine -import CryptoKit import Darwin -import Network import CoreText extension Workspace { diff --git a/Sources/Workspace.swift b/Sources/Workspace.swift index 1dedfcb9e..971f58457 100644 --- a/Sources/Workspace.swift +++ b/Sources/Workspace.swift @@ -3,9 +3,7 @@ import SwiftUI import AppKit import Bonsplit import Combine -import CryptoKit import Darwin -import Network import CoreText /// Workspace represents a sidebar tab. @@ -273,8 +271,7 @@ final class Workspace: Identifiable, ObservableObject { // known for this panel, there is no attached shell whose work // could be lost — never prompt. The ghostty needs-confirm // fallback can spuriously return true in the pre-attach window - // (ported from upstream cmux 2e03978ae1, adapted to our - // shell-integration telemetry signals). + // (keyed off our shell-integration telemetry signals). guard hasKnownTTY else { return false } return fallbackNeedsConfirmClose } diff --git a/Sources/WorkspaceShortcutMapper.swift b/Sources/WorkspaceShortcutMapper.swift index 534c26553..5810bd8f2 100644 --- a/Sources/WorkspaceShortcutMapper.swift +++ b/Sources/WorkspaceShortcutMapper.swift @@ -2,10 +2,7 @@ import AppKit import SwiftUI import Bonsplit import CoreServices -import UserNotifications -import Combine import ObjectiveC.runtime -import Darwin enum WorkspaceShortcutMapper { /// Maps numbered workspace shortcuts to a zero-based workspace index. diff --git a/Sources/WorkspaceSidebarModels.swift b/Sources/WorkspaceSidebarModels.swift index 2286a181c..b864f3982 100644 --- a/Sources/WorkspaceSidebarModels.swift +++ b/Sources/WorkspaceSidebarModels.swift @@ -5,10 +5,7 @@ import Foundation import SwiftUI import AppKit import Bonsplit -import Combine -import CryptoKit import Darwin -import Network import CoreText struct SidebarWorkspaceHierarchyEntry: Equatable { diff --git a/core/crates/programad/src/fdpass.rs b/core/crates/programad/src/fdpass.rs index 5bed8e845..a68caaaf6 100644 --- a/core/crates/programad/src/fdpass.rs +++ b/core/crates/programad/src/fdpass.rs @@ -470,18 +470,16 @@ mod tests { // Isolated from the default `cargo test` run and re-run alone (see the // shared-core CI step: `cargo test -p programad --locked --lib -- - // --ignored --test-threads=1`). `RecvWithFds` (above `recv_with_fds`) - // fixed the production bug this test's EPIPE check used to be exposed - // to: `MsgStream::fill_more` now closes a rejected batch while - // `recv_with_fds`'s `SPAWN_FD_LOCK` guard is still held, instead of - // after it, so a concurrent fork can no longer inherit a not-yet-closed - // duplicate from *this* rejection path. Kept isolated anyway as - // defense in depth: the EPIPE assertion below is still, structurally, - // sensitive to *any* process anywhere holding the pipe's read end, not - // just this one -- see `too_many_descriptors_are_closed_before_the_reject_returns` - // below for a regression test that instead counts this process's own - // fd table, which is robust against other processes by construction - // and is the one that would have caught the fixed bug directly. + // --ignored --test-threads=1`). `MsgStream::fill_more` closes a + // rejected batch while `recv_with_fds`'s `SPAWN_FD_LOCK` guard (see + // `RecvWithFds`) is still held, so a concurrent fork cannot inherit a + // not-yet-closed duplicate from *this* rejection path. Kept isolated + // anyway as defense in depth: the EPIPE assertion below is + // structurally sensitive to *any* process anywhere holding the pipe's + // read end, not just this one -- see + // `too_many_descriptors_are_closed_before_the_reject_returns` below, + // which instead counts this process's own fd table and is robust + // against other processes by construction. #[tokio::test] #[ignore = "must run alone: see the shared-core CI step for why"] async fn max_sendmsg_descriptors_close_without_leak() { @@ -556,10 +554,10 @@ mod tests { .unwrap_or(0) } - /// Regression test for the production bug fixed alongside this test: - /// `recv_with_fds` used to drop its `SPAWN_FD_LOCK` guard on its own - /// return, before `MsgStream::fill_more` (the caller) actually closed - /// a rejected batch -- see `RecvWithFds`'s doc comment. Unlike + /// Guards that `recv_with_fds` keeps its `SPAWN_FD_LOCK` guard alive + /// until `MsgStream::fill_more` (the caller) has closed a rejected + /// batch, rather than dropping it on return -- see `RecvWithFds`'s doc + /// comment. Unlike /// `max_sendmsg_descriptors_close_without_leak` above, this doesn't /// infer leak-freedom from a side effect (a write returning EPIPE) /// that's sensitive to what *any* process holds open; it counts this diff --git a/programaTests/AppDelegateShortcutRoutingTests.swift b/programaTests/AppDelegateShortcutRoutingTests.swift index 228288ad0..e8187b2f2 100644 --- a/programaTests/AppDelegateShortcutRoutingTests.swift +++ b/programaTests/AppDelegateShortcutRoutingTests.swift @@ -5323,7 +5323,7 @@ final class AppDelegateShortcutRoutingTests: XCTestCase { /// Regression test for the ghostty IO-thread callback-context teardown race. /// /// Root cause: `GhosttySurfaceCallbackContext` (the per-surface userdata handed to - /// ghostty) used to hold `weak var surfaceView: GhosttyNSView?` and `weak var + /// ghostty) must not hold `weak var surfaceView: GhosttyNSView?` and `weak var /// terminalSurface: TerminalSurface?`. Ghostty's IO thread invokes callbacks that /// read those weak refs SYNCHRONOUSLY, before any main-thread hop -- /// `runtimeReadClipboardCallback` read `callbackContext.runtimeSurface` directly, and @@ -5338,7 +5338,7 @@ final class AppDelegateShortcutRoutingTests: XCTestCase { /// (`PROGRAMA_FORCE_OCCLUDED=1`, honored by /// `GhosttySurfaceScrollView.applyEffectiveOcclusion()`) with a live shell in each /// window -- exactly the kind of concurrent IO-thread callback traffic vs. main-thread - /// teardown that used to race. This same rapid create/close shape is also the one the + /// teardown that could race. This same rapid create/close shape is also the one the /// `windowObserverGeneration` guard in `GhosttySurfaceScrollView.viewDidMoveToWindow` /// targets (a second, distinct teardown-race mechanism: an /// `NSNotificationCenter.addObserver(queue: .main)` block already handed off to the diff --git a/programaTests/GhosttyConfigTests.swift b/programaTests/GhosttyConfigTests.swift index 29031d255..fafa571cb 100644 --- a/programaTests/GhosttyConfigTests.swift +++ b/programaTests/GhosttyConfigTests.swift @@ -2969,7 +2969,7 @@ final class ZshShellIntegrationHandoffTests: XCTestCase { func testBashBootstrapPreservesStarshipPromptCommandAcrossPrompts() throws { // Regression test: Programa's local macOS bash bootstrap is injected - // as PROMPT_COMMAND and used to begin by unconditionally clobbering + // as PROMPT_COMMAND and must not begin by unconditionally clobbering // PROMPT_COMMAND. A user's startup files run // `eval "$(starship init bash)"` before the first prompt, which // appends starship_precmd to PROMPT_COMMAND; the bootstrap then diff --git a/programaTests/GhosttyScrollPreciseBoostTests.swift b/programaTests/GhosttyScrollPreciseBoostTests.swift index 5d9fec864..54a9ba7b2 100644 --- a/programaTests/GhosttyScrollPreciseBoostTests.swift +++ b/programaTests/GhosttyScrollPreciseBoostTests.swift @@ -1,8 +1,7 @@ import XCTest @testable import Programa_DEV -/// Regression tests for the precise-scroll 2x boost gating (ported from -/// upstream cmux da4e4bc460): only gesture-driven precise deltas (trackpad, +/// Regression tests for the precise-scroll 2x boost gating: only gesture-driven precise deltas (trackpad, /// Magic Mouse) are boosted; high-res mouse wheels report precise deltas /// with empty phases and must scroll unboosted. final class GhosttyScrollPreciseBoostTests: XCTestCase { diff --git a/programaTests/InactivePaneFirstClickFocusTests.swift b/programaTests/InactivePaneFirstClickFocusTests.swift index ae30d13e0..cec50687e 100644 --- a/programaTests/InactivePaneFirstClickFocusTests.swift +++ b/programaTests/InactivePaneFirstClickFocusTests.swift @@ -9,12 +9,12 @@ import WebKit #endif /// Clicking into an inactive Programa window activates the window without also -/// focusing the pane under the pointer. That used to be a preference -/// (`paneFirstClickFocus.enabled`); it is now the only behaviour, so these -/// tests pin it rather than exercising both sides of a toggle. +/// focusing the pane under the pointer. This is the only behaviour +/// (there is no preference), so these tests pin it rather than exercising both +/// sides of a toggle. /// -/// The three view classes are covered separately because each one used to -/// override `acceptsFirstMouse` on its own, and a future override on any of +/// The three view classes are covered separately because each one +/// overrides `acceptsFirstMouse` on its own, and a future override on any of /// them would silently reintroduce click-through. @MainActor final class InactivePaneFirstClickFocusTests: XCTestCase { diff --git a/programaTests/ProgramaConfigTests.swift b/programaTests/ProgramaConfigTests.swift index 8cb8c75e0..7a14475b9 100644 --- a/programaTests/ProgramaConfigTests.swift +++ b/programaTests/ProgramaConfigTests.swift @@ -611,7 +611,7 @@ final class ProgramaConfigDecodingTests: XCTestCase { // MARK: - JSONC (comments + trailing commas) config parsing // -// Regression coverage for a real user pain point: a project's programa.json (ported from +// Regression coverage for a real user pain point: a project's programa.json (like // ~/.config/programa/programa.json) failed to parse with "Unexpected character '/' at line 5 col 4" // because plain JSONDecoder rejects `//`/`/* */` comments and trailing commas. These tests // exercise the same JSONCParser.preprocess -> JSONDecoder pipeline ProgramaConfigStore uses @@ -970,7 +970,7 @@ final class ProgramaLayoutEncodingTests: XCTestCase { /// ran only when the file itself set `confirm: true`, so any config could opt out of being /// checked by leaving the key off, and `workspace`-type commands were never gated at all. /// -/// These pin the corrected truth table. The first case is the one that used to execute +/// These pin the corrected truth table. The first case is the one that must never execute /// arbitrary shell with no prompt at all. @MainActor final class ProgramaConfigTrustGateTests: XCTestCase { diff --git a/programaTests/ProgramaDirectoryTrustTests.swift b/programaTests/ProgramaDirectoryTrustTests.swift index b555f8524..58040e367 100644 --- a/programaTests/ProgramaDirectoryTrustTests.swift +++ b/programaTests/ProgramaDirectoryTrustTests.swift @@ -6,9 +6,9 @@ import XCTest @testable import Programa #endif -/// Issue #188: trust used to be a flat list of directory paths with no way to tell whether the -/// `programa.json` inside had changed since approval, so a later edit to an already-trusted -/// config ran with no prompt at all. These pin the fix: trust is pinned to a digest of the +/// Issue #188: a flat list of trusted directory paths cannot tell whether the +/// `programa.json` inside changed since approval, so a later edit to an already-trusted +/// config would run with no prompt at all. These pin the fix: trust is pinned to a digest of the /// config's *executable content* (JSONC comments/formatting stripped, keys canonicalized), so a /// changed command is detected while a comment edit or reformat is not. /// diff --git a/programaTests/TerminalControllerSocketSecurityTests.swift b/programaTests/TerminalControllerSocketSecurityTests.swift index 55558da8b..ea7a1337e 100644 --- a/programaTests/TerminalControllerSocketSecurityTests.swift +++ b/programaTests/TerminalControllerSocketSecurityTests.swift @@ -984,7 +984,7 @@ final class TerminalControllerSocketSecurityTests: XCTestCase { XCTAssertEqual(otherWorkspace.metadataBlocks["other-metadata"]?.markdown, "other-details") } - /// Regression for #6618: `shouldPublishShellActivity` used to record the state + /// Regression for #6618: `shouldPublishShellActivity` must not record the state /// it was queried with (write-on-read). When a report arrived before the panel /// existed, that premature write suppressed every later identical report, so the /// panel's shell state stayed `.unknown` forever. The read and the write are now @@ -1816,9 +1816,9 @@ final class TerminalControllerSocketSecurityTests: XCTestCase { XCTAssertEqual(created, [direct.id.uuidString, alias.id.uuidString], "Both creation APIs must publish exactly once") } - /// Regression for #82: `surface.report_tty`/`surface.ports_kick` used to block the socket - /// thread with `DispatchQueue.main.sync` so the response could echo the surface resolved on - /// main (e.g. falling back to the focused surface when `surface_id` is omitted). They now + /// Regression for #82: `surface.report_tty`/`surface.ports_kick` must not block the socket + /// thread with `DispatchQueue.main.sync` to echo the surface resolved on + /// main (e.g. falling back to the focused surface when `surface_id` is omitted). They /// follow the same off-main-parse + main.async-mutate telemetry shape as /// `workspace.set_status`/`workspace.report_meta_block`: the JSON-RPC response acknowledges /// immediately (echoing the request, not a value resolved on main) and the surface-resolving @@ -1903,7 +1903,7 @@ final class TerminalControllerSocketSecurityTests: XCTestCase { to: socketPath ) - // The unknown surface_id can no longer be validated synchronously (that check now + // The unknown surface_id cannot be validated synchronously (that check // happens inside the fire-and-forget main.async mutation), so the request is acked // immediately and the mutation silently no-ops once it resolves. XCTAssertEqual(reportTTYResponse["ok"] as? Bool, true, "Unexpected JSON-RPC response: \(reportTTYResponse)") diff --git a/programaTests/WorkspaceCloseConfirmationTests.swift b/programaTests/WorkspaceCloseConfirmationTests.swift index 68d29cf1a..5ee80db8e 100644 --- a/programaTests/WorkspaceCloseConfirmationTests.swift +++ b/programaTests/WorkspaceCloseConfirmationTests.swift @@ -2,8 +2,7 @@ import XCTest @testable import Programa_DEV /// Regression tests for the close-confirmation-before-tty-attach race -/// (ported from upstream cmux 2e03978ae1, adapted to our shell-integration -/// telemetry signals). +/// (keyed off our shell-integration telemetry signals). /// /// Right after a terminal is created, no shell-activity state has been /// reported and no TTY is known for the panel. In that window there is no diff --git a/scripts/gen-v2-contract.py b/scripts/gen-v2-contract.py index 7eaeca9f0..12d3efedc 100644 --- a/scripts/gen-v2-contract.py +++ b/scripts/gen-v2-contract.py @@ -82,7 +82,7 @@ def swift_array(name, items, doc): body += swift_array("debugMethods", debug, "Methods only available in DEBUG builds, appended after `baseMethods`.") body += "\n\n" body += " /// Full method list for the current build configuration, mirroring the\n" - body += " /// conditional `#if DEBUG` append that used to live inline in\n" + body += " /// conditional `#if DEBUG` append in\n" body += " /// `TerminalController.v2Capabilities()`.\n" body += " static var methods: [String] {\n" body += " #if DEBUG\n" diff --git a/vendor/bonsplit/Sources/Bonsplit/Internal/Views/TabBarView.swift b/vendor/bonsplit/Sources/Bonsplit/Internal/Views/TabBarView.swift index 02e6add7a..d129bc75c 100644 --- a/vendor/bonsplit/Sources/Bonsplit/Internal/Views/TabBarView.swift +++ b/vendor/bonsplit/Sources/Bonsplit/Internal/Views/TabBarView.swift @@ -149,13 +149,13 @@ struct TabBarView: View { // glass island so the empty drag target does not expand the island. dropZoneAfterTabs - // Reserved gutter behind the split-action icon cluster. This used to - // be plain `.padding(.trailing, 114)` — dead space with no NSView, so - // AppKit's hit-test walk fell into the ScrollView's own hosting/clip - // view here instead of reaching the tab bar's window-drag background, - // silently swallowing clicks (#window-drag-areas). A real (still - // fully transparent) drag-capture view makes window drag work in this - // strip the same way it already does in the 30pt zone before it. + // Reserved gutter behind the split-action icon cluster. Keep this a + // real view, not plain `.padding(.trailing, 114)`: padding is dead + // space with no NSView, so AppKit's hit-test walk falls into the + // ScrollView's own hosting/clip view instead of reaching the tab bar's + // window-drag background and silently swallows clicks + // (#window-drag-areas). A fully transparent drag-capture view makes + // window drag work in this strip as it does in the 30pt zone before it. if showSplitButtons { splitButtonsGutterDragZone } @@ -490,8 +490,7 @@ struct TabBarView: View { // MARK: - Split Buttons Gutter Drag Zone /// Fixed-width gutter reserved behind the split-action icon cluster (terminal/ - /// split buttons), matching the 114pt previously consumed by trailing padding. Kept as a - /// real drag-capturing view so window drag works there. + /// split buttons), 114pt wide. Kept as a real drag-capturing view so window drag works there. @ViewBuilder private var splitButtonsGutterDragZone: some View { TabBarDragZoneView() @@ -609,7 +608,6 @@ struct TabBarView: View { let fadeWidth: CGFloat = 24 let shouldShowButtons = showSplitButtons && (presentationMode != "minimal" || isHoveringTabBar) let buttonClearWidth: CGFloat = shouldShowButtons ? 90 : 0 - let buttonFadeWidth: CGFloat = shouldShowButtons ? fadeWidth : 0 HStack(spacing: 0) { // Left scroll fade diff --git a/vendor/bonsplit/Sources/Bonsplit/Public/DebugEventLog.swift b/vendor/bonsplit/Sources/Bonsplit/Public/DebugEventLog.swift index ea35a8261..0e4014f5a 100644 --- a/vendor/bonsplit/Sources/Bonsplit/Public/DebugEventLog.swift +++ b/vendor/bonsplit/Sources/Bonsplit/Public/DebugEventLog.swift @@ -64,7 +64,7 @@ public final class DebugEventLog: @unchecked Sendable { if let handle = FileHandle(forWritingAtPath: Self.logPath) { // Throwing APIs instead of the deprecated seekToEndOfFile/write/closeFile // trio, which raise ObjC exceptions (uncatchable from Swift) if the file - // disappears out from under us. Ported from upstream bonsplit cffd9a6. + // disappears out from under us. _ = try? handle.seekToEnd() try? handle.write(contentsOf: data) try? handle.close()