From bfe07bb99ed796e7f9248d06cf21b9c3e640dcc7 Mon Sep 17 00:00:00 2001 From: Lukas <134181853+bo2themax@users.noreply.github.com> Date: Sat, 25 Apr 2026 17:56:51 +0200 Subject: [PATCH] macOS: add InternalState to cover migrations --- macos/Ghostty.xcodeproj/project.pbxproj | 1 + .../QuickTerminalRestorableState.swift | 54 ++++++++++++++++--- .../Terminal/TerminalRestorable.swift | 53 ++++++++++++------ ...TerminalRestorableState+InteralState.swift | 45 ++++++++++++++++ 4 files changed, 132 insertions(+), 21 deletions(-) create mode 100644 macos/Sources/Features/Terminal/TerminalRestorableState+InteralState.swift diff --git a/macos/Ghostty.xcodeproj/project.pbxproj b/macos/Ghostty.xcodeproj/project.pbxproj index 68d055dd5..fb24d0813 100644 --- a/macos/Ghostty.xcodeproj/project.pbxproj +++ b/macos/Ghostty.xcodeproj/project.pbxproj @@ -185,6 +185,7 @@ Features/Terminal/ErrorView.swift, Features/Terminal/TerminalController.swift, Features/Terminal/TerminalRestorable.swift, + "Features/Terminal/TerminalRestorableState+InteralState.swift", Features/Terminal/TerminalTabColor.swift, Features/Terminal/TerminalView.swift, Features/Terminal/TerminalViewContainer.swift, diff --git a/macos/Sources/Features/QuickTerminal/QuickTerminalRestorableState.swift b/macos/Sources/Features/QuickTerminal/QuickTerminalRestorableState.swift index 1fd8642d8..17e9d2a27 100644 --- a/macos/Sources/Features/QuickTerminal/QuickTerminalRestorableState.swift +++ b/macos/Sources/Features/QuickTerminal/QuickTerminalRestorableState.swift @@ -3,15 +3,23 @@ import Cocoa struct QuickTerminalRestorableState: TerminalRestorable { static var version: Int { 1 } - let focusedSurface: String? - let surfaceTree: SplitTree - let screenStateEntries: QuickTerminalScreenStateCache.Entries + var focusedSurface: String? { + internalState.focusedSurface + } + + var surfaceTree: SplitTree { + internalState.surfaceTree + } + + var screenStateEntries: QuickTerminalScreenStateCache.Entries { + internalState.screenStateEntries + } + + private let internalState: InternalState init(from controller: QuickTerminalController) { controller.saveScreenState(exitFullscreen: true) - self.focusedSurface = controller.focusedSurface?.id.uuidString - self.surfaceTree = controller.surfaceTree - self.screenStateEntries = controller.screenStateCache.stateByDisplay + self.internalState = .init(from: controller) } init(copy other: QuickTerminalRestorableState) { @@ -24,3 +32,37 @@ struct QuickTerminalRestorableState: TerminalRestorable { return config } } + +extension QuickTerminalRestorableState { + /// Internal State we use to perform unit tests + /// + /// Since we can't really change the type of `QuickTerminalRestorableState` + /// due to `CodableBridge` supporting secure coding, + /// we use an internal type to perform migration and tests + struct InternalState: Codable { + // MARK: - Version 1 (1.3.0) + let focusedSurface: String? + let surfaceTree: SplitTree + let screenStateEntries: QuickTerminalScreenStateCache.Entries + + init( + focusedSurface: String?, + surfaceTree: SplitTree, + screenStateEntries: QuickTerminalScreenStateCache.Entries, + ) { + self.focusedSurface = focusedSurface + self.surfaceTree = surfaceTree + self.screenStateEntries = screenStateEntries + } + } +} + +extension QuickTerminalRestorableState.InternalState where ViewType == Ghostty.SurfaceView { + init(from controller: QuickTerminalController) { + self.init( + focusedSurface: controller.focusedSurface?.id.uuidString, + surfaceTree: controller.surfaceTree, + screenStateEntries: controller.screenStateCache.stateByDisplay, + ) + } +} diff --git a/macos/Sources/Features/Terminal/TerminalRestorable.swift b/macos/Sources/Features/Terminal/TerminalRestorable.swift index e65ea0a60..495a6b61f 100644 --- a/macos/Sources/Features/Terminal/TerminalRestorable.swift +++ b/macos/Sources/Features/Terminal/TerminalRestorable.swift @@ -53,26 +53,49 @@ final class TerminalRestorableState: TerminalRestorable { static var version: Int { 7 } static var minimumVersion: Int { 5 } - let focusedSurface: String? - let surfaceTree: SplitTree - let effectiveFullscreenMode: FullscreenMode? - let tabColor: TerminalTabColor? - let titleOverride: String? + var focusedSurface: String? { + internalState.focusedSurface + } + var surfaceTree: SplitTree { + internalState.surfaceTree + } + var effectiveFullscreenMode: FullscreenMode? { + internalState.effectiveFullscreenMode + } + var tabColor: TerminalTabColor? { + internalState.tabColor + } + var titleOverride: String? { + internalState.titleOverride + } + + /// Internal State we use to perform unit tests + /// + /// Since we can't really change the type of `TerminalRestorableState` + /// due to `CodableBridge` supporting secure coding, + /// we use an internal type to perform migration and tests + private let internalState: InternalState init(from controller: TerminalController) { - self.focusedSurface = controller.focusedSurface?.id.uuidString - self.surfaceTree = controller.surfaceTree - self.effectiveFullscreenMode = controller.fullscreenStyle?.fullscreenMode - self.tabColor = (controller.window as? TerminalWindow)?.tabColor - self.titleOverride = controller.titleOverride + internalState = .init(from: controller) } required init(copy other: TerminalRestorableState) { - self.surfaceTree = other.surfaceTree - self.focusedSurface = other.focusedSurface - self.effectiveFullscreenMode = other.effectiveFullscreenMode - self.tabColor = other.tabColor - self.titleOverride = other.titleOverride + self.internalState = other.internalState + } + + /// This is just wrapper around internalState + /// + /// - Important: If you intend to add more things, go to `InternalState`. + init(from decoder: any Decoder) throws { + self.internalState = try InternalState(from: decoder) + } + + /// This is just wrapper around internalState + /// + /// - Important: If you intend to add more things, go to `InternalState`. + func encode(to encoder: any Encoder) throws { + try internalState.encode(to: encoder) } } diff --git a/macos/Sources/Features/Terminal/TerminalRestorableState+InteralState.swift b/macos/Sources/Features/Terminal/TerminalRestorableState+InteralState.swift new file mode 100644 index 000000000..a9114693a --- /dev/null +++ b/macos/Sources/Features/Terminal/TerminalRestorableState+InteralState.swift @@ -0,0 +1,45 @@ +import AppKit + +extension TerminalRestorableState { + /// Internal State we use to perform unit tests + /// + /// Since we can't really change the type of `TerminalRestorableState` + /// due to `CodableBridge` supporting secure coding, + /// we use an internal type to perform migration and tests + struct InternalState: Codable { + // MARK: - Version 5 (1.2.3) + let focusedSurface: String? + let surfaceTree: SplitTree + + // MARK: - Version 7 (1.3.0) + let effectiveFullscreenMode: FullscreenMode? + let tabColor: TerminalTabColor? + let titleOverride: String? + + init( + focusedSurface: String?, + surfaceTree: SplitTree, + effectiveFullscreenMode: FullscreenMode?, + tabColor: TerminalTabColor?, + titleOverride: String?, + ) { + self.focusedSurface = focusedSurface + self.surfaceTree = surfaceTree + self.effectiveFullscreenMode = effectiveFullscreenMode + self.tabColor = tabColor + self.titleOverride = titleOverride + } + } +} + +extension TerminalRestorableState.InternalState where ViewType == Ghostty.SurfaceView { + init(from controller: TerminalController) { + self.init( + focusedSurface: controller.focusedSurface?.id.uuidString, + surfaceTree: controller.surfaceTree, + effectiveFullscreenMode: controller.fullscreenStyle?.fullscreenMode, + tabColor: (controller.window as? TerminalWindow)?.tabColor, + titleOverride: controller.titleOverride, + ) + } +}