diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e32ff8..a7b4191 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.0.2] - 2024-12-06 +### Changed +- Added a softer shade of orange and red when light mode is enabled to improve visibility and readability. +- If launching the app using a URL scheme, the app will now exit **only** when `supportcompanion://` is used. This allows for the app to be started using a URL scheme and remain open when using `supportcompanion://home` or similar. Example: + +Will exit when the window is closed: +```bash +open supportcompanion:// +``` + +Will remain open when the window is closed: +```bash +open supportcompanion://home +``` + +### Added +- Option to hide Categories and Dividers on the Desktop Info view. This allows for a cleaner and more focused view of the information displayed. Example configuration: +```xml +DesktopInfoHideItems + + Category + Divider + +``` + ## [2.0.1] - 2024-12-05 ### Changed - Added a preinstall script that will uninstall version 1.X if found. diff --git a/SupportCompanion/AppDelegate.swift b/SupportCompanion/AppDelegate.swift index 281aaf5..91627e1 100644 --- a/SupportCompanion/AppDelegate.swift +++ b/SupportCompanion/AppDelegate.swift @@ -18,6 +18,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { let appStateManager = AppStateManager.shared var mainWindow: NSWindow? static var urlLaunch = false + static var shouldExit = false private var notificationDelegate: NotificationDelegate? private var cancellables: Set = [] @AppStorage("isDarkMode") private var isDarkMode: Bool = false @@ -25,6 +26,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { func application(_ application: NSApplication, open urls: [URL]) { guard let url = urls.first else { return } + switch url.host?.lowercased() { + case nil: + AppDelegate.shouldExit = true + default: + AppDelegate.shouldExit = false + } AppDelegate.urlLaunch = true showWindow() NotificationCenter.default.post(name: .handleIncomingURL, object: url) diff --git a/SupportCompanion/Components/CardData.swift b/SupportCompanion/Components/CardData.swift index 788564e..15e3140 100644 --- a/SupportCompanion/Components/CardData.swift +++ b/SupportCompanion/Components/CardData.swift @@ -9,6 +9,7 @@ import Foundation import SwiftUI struct CardData: View { + @Environment(\.colorScheme) var colorScheme let info: [(key: String, display: String, value: InfoValue)] let customContent: (String, InfoValue) -> AnyView @@ -56,7 +57,7 @@ struct CardData: View { case Constants.Battery.Keys.temperature: temperatureContent(value: value) case Constants.PlatformSSO.Keys.registrationCompleted: - pssoRegistrationContent(value: value, color: colorForValue(key: key, value: value)) + pssoRegistrationContent(value: value) default: defaultText(value: value, key: key) } @@ -65,19 +66,31 @@ struct CardData: View { /// Displays health-specific content with color coding private func healthContent(value: InfoValue) -> some View { - Text(value.displayValue) - .foregroundColor(colorForValue(key: "Health", value: value)) - .font(.system(size: 14)) - + Text("%") - .font(.system(size: 14)) + let color = colorForValue(key: Constants.Battery.Keys.health, value: value) + let isGreen = color == .green + + return Group { + Text(value.displayValue) + .foregroundColor(color) + .font(.system(size: 14)) + .shadow(color: isGreen ? .black.opacity(0.4) : .clear, radius: 1, x: 0, y: 1) + Text("%") + .font(.system(size: 14)) + } } private func temperatureContent(value: InfoValue) -> some View { - Text(value.displayValue) - .foregroundColor(colorForValue(key: Constants.Battery.Keys.temperature, value: value)) - .font(.system(size: 14)) - + Text("°C") - .font(.system(size: 14)) + let color = colorForValue(key: Constants.Battery.Keys.temperature, value: value) + let isGreen = color == .green + + return Group { + Text(value.displayValue) + .foregroundColor(color) + .font(.system(size: 14)) + .shadow(color: isGreen ? .black.opacity(0.4) : .clear, radius: 1, x: 0, y: 1) + Text("°C") + .font(.system(size: 14)) + } } /// Displays generic content with a suffix (e.g., "days") @@ -89,10 +102,14 @@ struct CardData: View { .font(.system(size: 14)) } - private func pssoRegistrationContent(value: InfoValue, color: Color = .primary) -> some View { - Text(value.displayValue) + private func pssoRegistrationContent(value: InfoValue) -> some View { + let color = colorForValue(key: Constants.PlatformSSO.Keys.registrationCompleted, value: value) + let isGreen = color == .green + + return Text(value.displayValue) .foregroundColor(colorForValue(key: Constants.PlatformSSO.Keys.registrationCompleted, value: value)) .font(.system(size: 14)) + .shadow(color: isGreen ? .black.opacity(0.4) : .clear, radius: 1, x: 0, y: 1) } /// Displays FileVault-specific content with icons @@ -101,9 +118,10 @@ struct CardData: View { if value.displayValue == "Enabled" { Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) + .shadow(color: .black.opacity(0.4), radius: 1, x: 0, y: 1) } else { Image(systemName: "xmark.circle.fill") - .foregroundColor(.red) + .foregroundColor((colorScheme == .light ? .redLight : .red)) } Text(value.displayValue) .font(.system(size: 14)) @@ -122,30 +140,34 @@ struct CardData: View { switch key { case "Health": if let intValue = value.rawValue as? Int { - return intValue <= 30 ? .red : (intValue < 80 ? .orange : .green) + return intValue <= 30 + ? (colorScheme == .light ? .redLight : .red) + : (intValue < 80 + ? (colorScheme == .light ? .orangeLight : .orange) + : .green) } case "LastRestart": if let intValue = value.rawValue as? Int { - return intValue > 7 ? .red : .green + return intValue > 7 ? (colorScheme == .light ? .redLight : .red) : .green } case "FileVault": if let boolValue = value.rawValue as? Bool { - return !boolValue ? .red : .green + return !boolValue ? (colorScheme == .light ? .redLight : .red) : .green } case Constants.PlatformSSO.Keys.registrationCompleted: if let boolValue = value.rawValue as? Bool { - return !boolValue ? .red : .green + return !boolValue ? (colorScheme == .light ? .redLight : .red) : .green } case Constants.KerberosSSO.Keys.expiryDays: if let intValue = value.rawValue as? Int { - return intValue <= 30 ? .orange : (intValue < 2 ? .red : .green) + return intValue <= 30 ? (colorScheme == .light ? .orangeLight : .orange) : (intValue < 2 ? (colorScheme == .light ? .redLight : .red) : .green) } case Constants.Battery.Keys.temperature: if let doubleValue = value.rawValue as? Double { - return doubleValue > 80 ? .red : (doubleValue >= 60 ? .orange : .green) + return doubleValue > 80 ? (colorScheme == .light ? .redLight : .red) : (doubleValue >= 60 ? (colorScheme == .light ? .orange : .orange) : .green) } else if let intValue = value.rawValue as? Int { let temperature = Double(intValue) - return temperature > 80 ? .red : (temperature >= 60 ? .orange : .green) + return temperature > 80 ? (colorScheme == .light ? .redLight : .red) : (temperature >= 60 ? (colorScheme == .light ? .orangeLight : .orange) : .green) } else { return .primary } @@ -154,4 +176,13 @@ struct CardData: View { } return .primary } + + struct ConditionalShadowModifier: ViewModifier { + let isGreen: Bool + + func body(content: Content) -> some View { + content + .shadow(color: isGreen ? .black.opacity(0.4) : .clear, radius: 2, x: 0, y: 1) + } + } } diff --git a/SupportCompanion/ContentView.swift b/SupportCompanion/ContentView.swift index e20358b..aff9013 100644 --- a/SupportCompanion/ContentView.swift +++ b/SupportCompanion/ContentView.swift @@ -145,7 +145,7 @@ struct ContentView: View { case "knowledgebase": selectedItem = items.first(where: { $0.id == Constants.Navigation.knowledgeBase }) default: - Logger.shared.logDebug("Unhandled URL: \(url)") + selectedItem = items.first(where: { $0.id == Constants.Navigation.home }) } } diff --git a/SupportCompanion/Extensions/Extensions.swift b/SupportCompanion/Extensions/Extensions.swift index 3f989d8..2012c16 100644 --- a/SupportCompanion/Extensions/Extensions.swift +++ b/SupportCompanion/Extensions/Extensions.swift @@ -71,7 +71,7 @@ extension ToastConfig { extension AppDelegate: NSWindowDelegate { func windowWillClose(_ notification: Notification) { - if AppDelegate.urlLaunch { + if AppDelegate.shouldExit { NSApplication.shared.terminate(nil) } Logger.shared.logDebug("Main window is closing.") @@ -87,4 +87,12 @@ extension AppDelegate: NSWindowDelegate { extension Notification.Name { static let handleIncomingURL = Notification.Name("handleIncomingURL") -} \ No newline at end of file +} + +extension Color { + // Orange shades + static let orangeLight = Color(hue: 0.1, saturation: 0.9, brightness: 0.75) // Softer orange for light mode + + // Red shades + static let redLight = Color(hue: 0.02, saturation: 0.8, brightness: 0.7) // Softer red for light mode +} diff --git a/SupportCompanion/Info.plist b/SupportCompanion/Info.plist index d792057..d6817b8 100644 --- a/SupportCompanion/Info.plist +++ b/SupportCompanion/Info.plist @@ -19,9 +19,9 @@ LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) CFBundleShortVersionString - 2.0.1 + 2.0.2 CFBundleVersion - 2.0.1 + 2.0.2 CFBundleURLTypes diff --git a/SupportCompanion/Models/Storage.swift b/SupportCompanion/Models/Storage.swift index 3d6d7e7..a95524c 100644 --- a/SupportCompanion/Models/Storage.swift +++ b/SupportCompanion/Models/Storage.swift @@ -14,16 +14,6 @@ struct StorageInfo: Identifiable { let fileVault: Bool let usage: Double - var percentageColor: Color { - if usage > 80 { - return Color(NSColor.red) - } else if usage > 60 { - return Color(NSColor.orange) - } else { - return Color(NSColor.green) - } - } - func toKeyValuePairs() -> [(key: String, display: String, value: InfoValue)] { return [ ( diff --git a/SupportCompanion/ViewModels/StorageInfoManager.swift b/SupportCompanion/ViewModels/StorageInfoManager.swift index 31a6e5a..a0a48b3 100644 --- a/SupportCompanion/ViewModels/StorageInfoManager.swift +++ b/SupportCompanion/ViewModels/StorageInfoManager.swift @@ -10,6 +10,8 @@ import Combine import SwiftUI class StorageInfoManager: ObservableObject { + @Environment(\.colorScheme) var colorScheme + static let shared = StorageInfoManager( storageInfo: StorageInfo( id: UUID(), @@ -38,6 +40,16 @@ class StorageInfoManager: ObservableObject { self.updateStorageInfo() } + func getPercentageColor(percentage: Double) -> Color { + if percentage < 50 { + return .green + } else if percentage < 80 { + return colorScheme == .light ? .orangeLight : .orange + } else { + return colorScheme == .light ? .redLight : .red + } + } + func updateStorageInfo(usagePercentage: Double = getStorageUsagePercentage()) { DispatchQueue.main.async { self.storageInfo = StorageInfo( diff --git a/SupportCompanion/Views/Cards/DeviceInformationCard.swift b/SupportCompanion/Views/Cards/DeviceInformationCard.swift index da484b6..10893d0 100644 --- a/SupportCompanion/Views/Cards/DeviceInformationCard.swift +++ b/SupportCompanion/Views/Cards/DeviceInformationCard.swift @@ -11,9 +11,12 @@ import SwiftUI struct DeviceInformationCard: View { @ObservedObject var viewModel: CardGridViewModel @EnvironmentObject var appState: AppStateManager + @Environment(\.colorScheme) var colorScheme var body: some View { if viewModel.isCardVisible("DeviceInformation") { + let groupedData = groupedDeviceInfoArray() // Precomputed grouped data + CustomCard( title: "\(Constants.CardTitle.deviceInfo)", titleImageName: "laptopcomputer", @@ -22,26 +25,13 @@ struct DeviceInformationCard: View { buttonHelpText: Constants.ToolTips.deviceInfoCopy, content: { VStack(alignment: .leading, spacing: 10) { - ForEach(Array(groupedDeviceInfoArray().enumerated()), id: \.1.0) { index, group in - SectionHeader(title: group.0) // Category (e.g., Hardware Specifications) - VStack(alignment: .leading, spacing: 5) { - ForEach(group.1, id: \.key) { item in - if item.key == Constants.DeviceInfo.Keys.lastRestart { - LastRestartRow( - label: item.display, - value: item.value.rawValue as? Int ?? 0 - ) - } else { - DeviceInfoRow(label: item.display, value: item.value.displayValue) - } - } - } - - // Add a divider only if it's not the last group - if index < groupedDeviceInfoArray().count - 1 { - Divider() - .background(Color.white.opacity(0.2)) - } + ForEach(Array(groupedData.enumerated()), id: \.1.0) { index, group in + DeviceInfoSection( + index: index, + group: group, + totalGroups: groupedData.count, + colorScheme: colorScheme // Pass colorScheme explicitly + ) } } .padding(.horizontal) @@ -73,10 +63,42 @@ struct DeviceInformationCard: View { } } +struct DeviceInfoSection: View { + let index: Int + let group: (String, [(key: String, display: String, value: InfoValue)]) + let totalGroups: Int + let colorScheme: ColorScheme + + var body: some View { + VStack(alignment: .leading, spacing: 5) { + SectionHeader(title: group.0) // Category (e.g., Hardware Specifications) + + VStack(alignment: .leading, spacing: 5) { + ForEach(group.1, id: \.key) { item in + if item.key == Constants.DeviceInfo.Keys.lastRestart { + LastRestartRow( + label: item.display, + value: item.value.rawValue as? Int ?? 0, + colorScheme: colorScheme // Pass colorScheme explicitly + ) + } else { + DeviceInfoRow(label: item.display, value: item.value.displayValue) + } + } + } + + // Add a divider only if it's not the last group + if index < totalGroups - 1 { + Divider() + .background(Color.white.opacity(0.2)) + } + } + } +} + struct SectionHeader: View { let title: String - // Computed property for the image name private var image: String { switch title { case Constants.DeviceInfo.Categories.hardwareSpecs: @@ -103,18 +125,17 @@ struct SectionHeader: View { struct DeviceInfoRow: View { let label: String let value: String? + var body: some View { HStack { Text(label) .font(.system(size: 14)) .bold() - //.frame(width: 150, alignment: .leading) // Set fixed width for labels + .frame(width: 150, alignment: .leading) // Set fixed width for labels Spacer() Text(value ?? "N/A") .font(.system(size: 14)) - //.multilineTextAlignment(.trailing) // Ensure values align properly - } } } @@ -122,8 +143,12 @@ struct DeviceInfoRow: View { struct LastRestartRow: View { let label: String let value: Int // Days since last restart + let colorScheme: ColorScheme var body: some View { + let color = colorForLastRestart(value: value) + let isGreen = color == .green + HStack { Text(label) .font(.system(size: 14)) @@ -133,24 +158,25 @@ struct LastRestartRow: View { Spacer() HStack(spacing: 5) { Text("\(value) \(Constants.General.days)") - .foregroundColor(colorForLastRestart(value: value)) + .foregroundColor(color) + .shadow(color: isGreen ? .black.opacity(0.4) : .clear, radius: 1, x: 0, y: 1) Image(systemName: "clock.fill") - .foregroundColor(colorForLastRestart(value: value)) + .foregroundColor(color) + .shadow(color: isGreen ? .black.opacity(0.4) : .clear, radius: 1, x: 0, y: 1) } .font(.system(size: 14)) .help(Constants.ToolTips.deviceLastRebooted) } } - /// Determine the color based on the number of days since the last restart private func colorForLastRestart(value: Int) -> Color { switch value { case 0...2: return .green case 3...7: - return .orange + return colorScheme == .light ? .orangeLight : .orange default: - return .red + return colorScheme == .light ? .redLight : .red } } } diff --git a/SupportCompanion/Views/Cards/KSSOCard.swift b/SupportCompanion/Views/Cards/KSSOCard.swift index 82af5ec..666aedd 100644 --- a/SupportCompanion/Views/Cards/KSSOCard.swift +++ b/SupportCompanion/Views/Cards/KSSOCard.swift @@ -14,7 +14,7 @@ struct KSSOCard: View { var body: some View { VStack(alignment: .leading){ if "" != appState.ssoInfoManager.kerberosSSO.username { - CustomCard(title: "\(Constants.CardTitle.kerberosSSO)", titleImageName: "lock.fill", content: { + CustomCard(title: "\(Constants.CardTitle.kerberosSSO)", titleImageName: "lock.fill", useMultiColor: false, content: { VStack(alignment: .leading) { CardData(info: appState.ssoInfoManager.kerberosSSO.toKeyValuePairs()) } diff --git a/SupportCompanion/Views/Cards/PSSOCard.swift b/SupportCompanion/Views/Cards/PSSOCard.swift index b7e48ed..6222f5c 100644 --- a/SupportCompanion/Views/Cards/PSSOCard.swift +++ b/SupportCompanion/Views/Cards/PSSOCard.swift @@ -15,7 +15,7 @@ struct PSSOCard: View { var body: some View { VStack(alignment: .leading){ if "" != appState.ssoInfoManager.platformSSO.loginType { - CustomCard(title: "\(Constants.CardTitle.platformSSO)", titleImageName: "lock.fill", content: { + CustomCard(title: "\(Constants.CardTitle.platformSSO)", titleImageName: "lock.fill", useMultiColor: false, content: { VStack(alignment: .leading) { CardData(info: appState.ssoInfoManager.platformSSO.toKeyValuePairs()) } diff --git a/SupportCompanion/Views/Cards/StorageDeviceManagementStack.swift b/SupportCompanion/Views/Cards/StorageDeviceManagementStack.swift index de2a2dd..0010ca3 100644 --- a/SupportCompanion/Views/Cards/StorageDeviceManagementStack.swift +++ b/SupportCompanion/Views/Cards/StorageDeviceManagementStack.swift @@ -11,7 +11,7 @@ import SwiftUI struct StorageDeviceManagementStack: View { @ObservedObject var viewModel: CardGridViewModel @EnvironmentObject var appState: AppStateManager - + @Environment(\.colorScheme) var colorScheme var body: some View { if !viewModel.isCardVisible(Constants.Cards.storageCardName) && !viewModel.isCardVisible(Constants.Cards.deviceManagementCardName) { @@ -38,7 +38,9 @@ struct StorageDeviceManagementStack: View { Text("\(String(format: "%.1f", appState.storageInfoManager.storageInfo.usage))% Used") .font(.system(size: 14))} ) - .tint(appState.storageInfoManager.storageInfo.percentageColor) + .tint(appState.storageInfoManager.storageInfo.usage < 50 ? Color.green + : appState.storageInfoManager.storageInfo.usage < 80 ? (colorScheme == .light ? .orangeLight : .orange) + : (colorScheme == .light ? .redLight : .red)) .padding(.top) ) } diff --git a/SupportCompanion/Views/TransparentView.swift b/SupportCompanion/Views/TransparentView.swift index 9dfac91..abf0cba 100644 --- a/SupportCompanion/Views/TransparentView.swift +++ b/SupportCompanion/Views/TransparentView.swift @@ -25,16 +25,22 @@ struct TransparentView: View { VStack(alignment: .leading) { // Title for the Info View - Text(Constants.CardTitle.deviceInfo) - .font(.title2) - .bold() - .foregroundColor(.white) - .padding(.bottom, 10) - .shadow(radius: 2) + if !appState.preferences.desktopInfoHideItems.contains("Category") { + Text(Constants.CardTitle.deviceInfo) + .font(.title2) + .bold() + .foregroundColor(.white) + .padding(.bottom, 10) + .shadow(radius: 2) + } // Grouped Device Information ForEach(Array(groupedDeviceInfoArray().enumerated()), id: \.1.0) { index, group in - SectionHeaderTransparent(title: group.0) // Section title (e.g., "Hardware Specifications") + SectionHeaderTransparent( + title: group.0, + addHeader: shouldShowGategory(), + fontSize: CGFloat(appState.preferences.desktopInfoFontSize) + ) // Section title (e.g., "Hardware Specifications") VStack(alignment: .leading) { ForEach(group.1.filter { !appState.preferences.desktopInfoHideItems.contains($0.key) }, id: \.key) { item in deviceInfoRow(for: item) @@ -44,7 +50,7 @@ struct TransparentView: View { // Add a divider only if it's not the last group if index < groupedDeviceInfoArray().count - 1 { - Divider() + shouldShowDivider() .background(Color.white.opacity(0.2)) .shadow(radius: 2) .padding(.vertical, 5) @@ -53,22 +59,30 @@ struct TransparentView: View { if appState.preferences.desktopInfoLevel > 3 && !appState.preferences.desktopInfoHideItems.contains("Storage"){ // Storage Section - Divider() + shouldShowDivider() .background(Color.white.opacity(0.2)) .shadow(radius: 2) .padding(.vertical, 5) - SectionHeaderTransparent(title: Constants.CardTitle.storage) + SectionHeaderTransparent( + title: Constants.CardTitle.storage, + addHeader: shouldShowGategory(), + fontSize: CGFloat(appState.preferences.desktopInfoFontSize) + ) storageInfoSection() } if appState.preferences.desktopInfoLevel > 4 && !appState.preferences.desktopInfoHideItems.contains("Support"){ - Divider() + shouldShowDivider() .background(Color.white.opacity(0.2)) .shadow(radius: 2) .padding(.vertical, 5) - SectionHeaderTransparent(title: Constants.Support.Titles.support) + SectionHeaderTransparent( + title: Constants.Support.Titles.support, + addHeader: shouldShowGategory(), + fontSize: CGFloat(appState.preferences.desktopInfoFontSize) + ) supportInfoSection() } } @@ -86,6 +100,16 @@ struct TransparentView: View { } .frame(height: contentHeight) } + + private func shouldShowDivider() -> some View { + !appState.preferences.desktopInfoHideItems.contains("Divider") + ? AnyView(Divider()) + : AnyView(EmptyView()) + } + + private func shouldShowGategory() -> Bool { + !appState.preferences.desktopInfoHideItems.contains("Category") + } private func localizedHideCheck(_ standardKey: String) -> Bool { let hideItems = appState.preferences.desktopInfoHideItems @@ -231,6 +255,8 @@ struct TransparentView: View { struct SectionHeaderTransparent: View { let title: String + let addHeader: Bool + let fontSize: CGFloat // Computed property for the image name private var image: String { @@ -251,13 +277,17 @@ struct SectionHeaderTransparent: View { } var body: some View { - HStack(spacing: 8) { - Image(systemName: image) - Text(title) - .font(.headline) + if addHeader { + HStack(spacing: 8) { + Image(systemName: image) + Text(title) + .font(.system(size: fontSize)) + } + .shadow(radius: 2) + .padding(.vertical, 5) + } else { + EmptyView() } - .shadow(radius: 2) - .padding(.vertical, 5) } } diff --git a/build.zsh b/build.zsh index 6c23324..f4a96a7 100755 --- a/build.zsh +++ b/build.zsh @@ -250,15 +250,15 @@ cp "$LA_PATH/payload/$LA_NAME" "$PKGBUILDDIR/payload/Library/LaunchAgents/$LA_NA check_exit_code "$?" "Error copying launch agent" -create_pkg $BUNDLE_IDENTIFIER $AUTOMATED_SC_BUILD $PKGBUILDDIR "${BUILDSDIR}/SupportCompanion_suite-${AUTOMATED_SC_BUILD}.pkg" "/" +create_pkg $BUNDLE_IDENTIFIER $AUTOMATED_SC_BUILD $PKGBUILDDIR "${BUILDSDIR}/SupportCompanion_Suite-${AUTOMATED_SC_BUILD}.pkg" "/" -generate_dist_file $BUNDLE_IDENTIFIER $AUTOMATED_SC_BUILD $DIST_FILE "_suite" +generate_dist_file $BUNDLE_IDENTIFIER $AUTOMATED_SC_BUILD $DIST_FILE "_Suite" run_product_build_sign $BUILDSDIR "${BUILDSDIR}/${APP_NAME}_Suite-${AUTOMATED_SC_BUILD}" notarize_and_staple "${BUILDSDIR}/${APP_NAME}_Suite-${AUTOMATED_SC_BUILD}.pkg" -cp "${BUILDSDIR}/${APP_NAME}_suite-${AUTOMATED_SC_BUILD}.pkg" "${RELEASEDIR}/${APP_NAME}_suite-${AUTOMATED_SC_BUILD}.pkg" +cp "${BUILDSDIR}/${APP_NAME}_Suite-${AUTOMATED_SC_BUILD}.pkg" "${RELEASEDIR}/${APP_NAME}_Suite-${AUTOMATED_SC_BUILD}.pkg" echo "Build complete: ${RELEASEDIR}/${APP_NAME}_Suite-${AUTOMATED_SC_BUILD}.pkg"