From f451ea8e4603a63058a9f019623a8d18b103b98f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 24 Feb 2026 10:20:25 -0800 Subject: [PATCH] macos: move icon codable/equatable to extension --- .../ColorizedGhosttyIcon.swift | 95 ++++++++++++------- 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/macos/Sources/Features/Custom App Icon/ColorizedGhosttyIcon.swift b/macos/Sources/Features/Custom App Icon/ColorizedGhosttyIcon.swift index 62f58a063..99d684369 100644 --- a/macos/Sources/Features/Custom App Icon/ColorizedGhosttyIcon.swift +++ b/macos/Sources/Features/Custom App Icon/ColorizedGhosttyIcon.swift @@ -1,33 +1,6 @@ import Cocoa -struct ColorizedGhosttyIcon: Codable, Equatable { - init(screenColors: [NSColor], ghostColor: NSColor, frame: Ghostty.MacOSIconFrame) { - self.screenColors = screenColors - self.ghostColor = ghostColor - self.frame = frame - } - - init(from decoder: any Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let screenColorHexes = try container.decode([String].self, forKey: .screenColors) - let screenColors = screenColorHexes.compactMap(NSColor.init(hex:)) - let ghostColorHex = try container.decode(String.self, forKey: .ghostColor) - guard let ghostColor = NSColor(hex: ghostColorHex) else { - throw NSError(domain: "Custom Icon Error", code: 1, userInfo: [ - NSLocalizedDescriptionKey: "Failed to decode ghost color from \(ghostColorHex)" - ]) - } - let frame = try container.decode(Ghostty.MacOSIconFrame.self, forKey: .frame) - self.init(screenColors: screenColors, ghostColor: ghostColor, frame: frame) - } - - func encode(to encoder: any Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(screenColors.compactMap(\.hexString), forKey: .screenColors) - try container.encode(ghostColor.hexString, forKey: .ghostColor) - try container.encode(frame, forKey: .frame) - } - +struct ColorizedGhosttyIcon { /// The colors that make up the gradient of the screen. let screenColors: [NSColor] @@ -37,12 +10,6 @@ struct ColorizedGhosttyIcon: Codable, Equatable { /// The frame type to use let frame: Ghostty.MacOSIconFrame - private enum CodingKeys: String, CodingKey { - case screenColors - case ghostColor - case frame - } - /// Make a custom colorized ghostty icon. func makeImage(in bundle: Bundle) -> NSImage? { // All of our layers (not in order) @@ -86,3 +53,63 @@ struct ColorizedGhosttyIcon: Codable, Equatable { ]) } } + +// MARK: Codable + +extension ColorizedGhosttyIcon: Codable { + private enum CodingKeys: String, CodingKey { + case version + case screenColors + case ghostColor + case frame + + static let currentVersion: Int = 1 + } + + init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + // If no version exists then this is the legacy v0 format. + let version = try container.decodeIfPresent(Int.self, forKey: .version) ?? 0 + guard version == 0 || version == CodingKeys.currentVersion else { + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Unsupported ColorizedGhosttyIcon version: \(version)" + ) + ) + } + + let screenColorHexes = try container.decode([String].self, forKey: .screenColors) + let screenColors = screenColorHexes.compactMap(NSColor.init(hex:)) + let ghostColorHex = try container.decode(String.self, forKey: .ghostColor) + guard let ghostColor = NSColor(hex: ghostColorHex) else { + throw DecodingError.dataCorruptedError( + forKey: .ghostColor, + in: container, + debugDescription: "Failed to decode ghost color from \(ghostColorHex)" + ) + } + let frame = try container.decode(Ghostty.MacOSIconFrame.self, forKey: .frame) + self.init(screenColors: screenColors, ghostColor: ghostColor, frame: frame) + } + + func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(CodingKeys.currentVersion, forKey: .version) + try container.encode(screenColors.compactMap(\.hexString), forKey: .screenColors) + try container.encode(ghostColor.hexString, forKey: .ghostColor) + try container.encode(frame, forKey: .frame) + } + +} + +// MARK: Equatable + +extension ColorizedGhosttyIcon: Equatable { + static func == (lhs: Self, rhs: Self) -> Bool { + lhs.frame == rhs.frame && + lhs.screenColors.compactMap(\.hexString) == rhs.screenColors.compactMap(\.hexString) && + lhs.ghostColor.hexString == rhs.ghostColor.hexString + } +}