From 5accee5d02be0cda5b5fcce726f9148db07e83fb Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Thu, 5 Mar 2026 16:24:06 +0530 Subject: [PATCH] Update onboarding --- DynamicIsland/ContentView.swift | 9 ++- DynamicIsland/Localizable.xcstrings | 79 ++----------------- .../Onboarding/ProfileSelectionView.swift | 27 +++++++ .../components/Settings/SettingsView.swift | 29 +++++++ DynamicIsland/models/Constants.swift | 4 +- .../models/DynamicIslandViewModel.swift | 11 ++- 6 files changed, 78 insertions(+), 81 deletions(-) diff --git a/DynamicIsland/ContentView.swift b/DynamicIsland/ContentView.swift index 2573d18..5555fe7 100644 --- a/DynamicIsland/ContentView.swift +++ b/DynamicIsland/ContentView.swift @@ -396,9 +396,10 @@ struct ContentView: View { } } .onAppear(perform: { - runAfter(1) { - withAnimation(vm.animation) { - if coordinator.firstLaunch { + if coordinator.firstLaunch { + // Single open during first launch; closeHello() handles the timed close. + runAfter(1) { + withAnimation(vm.animation) { openNotch() } } @@ -1635,7 +1636,7 @@ struct ContentView: View { } private func shouldPreventAutoClose() -> Bool { - hasAnyActivePopovers() || vm.isAutoCloseSuppressed || SharingStateManager.shared.preventNotchClose + coordinator.firstLaunch || hasAnyActivePopovers() || vm.isAutoCloseSuppressed || SharingStateManager.shared.preventNotchClose } // Helper to prevent rapid haptic feedback diff --git a/DynamicIsland/Localizable.xcstrings b/DynamicIsland/Localizable.xcstrings index 0fd79b0..37382a8 100644 --- a/DynamicIsland/Localizable.xcstrings +++ b/DynamicIsland/Localizable.xcstrings @@ -1607,10 +1607,6 @@ } } }, - "±10 Seconds" : { - "comment" : "Display name for the \"±10 Seconds\" music skip behavior option.", - "isCommentAutoGenerated" : true - }, "0" : { "comment" : "A label displayed in a circular gauge to indicate the minimum battery level.", "isCommentAutoGenerated" : true, @@ -1927,10 +1923,6 @@ } } }, - "Accent" : { - "comment" : "Display name for the \"Accent\" caps lock indicator tint mode.", - "isCommentAutoGenerated" : true - }, "Accent color" : { "localizations" : { "ar" : { @@ -2276,10 +2268,6 @@ } } }, - "Adaptive gradient" : { - "comment" : "Name of the timer icon color mode option that uses an adaptive gradient.", - "isCommentAutoGenerated" : true - }, "Add" : { "localizations" : { "ar" : { @@ -3875,10 +3863,6 @@ } } }, - "Apple Music" : { - "comment" : "Localized name for the \"Now Playing\" media controller type.", - "isCommentAutoGenerated" : true - }, "Applies the notch-style parallax effect to the lock screen media widget album art." : { "localizations" : { "pt-BR" : { @@ -4293,10 +4277,6 @@ } } }, - "Bar" : { - "comment" : "Localized name for the 'Bar' timer progress style.", - "isCommentAutoGenerated" : true - }, "Battery" : { "localizations" : { "ar" : { @@ -8567,10 +8547,6 @@ } } }, - "Digital" : { - "comment" : "The display name for the \"Digital\" reminder presentation style.", - "isCommentAutoGenerated" : true - }, "Dimensions" : { "localizations" : { "pt-BR" : { @@ -8862,6 +8838,9 @@ } } } + }, + "Display Style" : { + }, "Displays a HUD notification when Bluetooth audio devices (headphones, AirPods, speakers) connect, showing device name and battery level." : { "comment" : "A footer for the \"Bluetooth Audio Devices\" section in the HUD settings.", @@ -11966,10 +11945,6 @@ } } }, - "Green" : { - "comment" : "Name of the \"Green\" Caps Lock Indicator Tint Mode.", - "isCommentAutoGenerated" : true - }, "Height:" : { "extractionState" : "stale", "localizations" : { @@ -14092,6 +14067,9 @@ } } } + }, + "Main screen style" : { + }, "Manage indicator preferences from Settings → Privacy. No manual microphone toggle is required." : { "comment" : "A description of how to manage the microphone privacy indicator preferences.", @@ -15647,6 +15625,7 @@ } }, "Minutes" : { + "extractionState" : "stale", "localizations" : { "fr" : { "stringUnit" : { @@ -17863,10 +17842,6 @@ } } }, - "Now Playing" : { - "comment" : "Localized name for each media controller type.", - "isCommentAutoGenerated" : true - }, "Number of lines kept in the scrollback buffer. Higher values use more memory." : { }, @@ -18242,10 +18217,6 @@ } } }, - "Panel" : { - "comment" : "Display name for the \"Panel\" clipboard display mode.", - "isCommentAutoGenerated" : true - }, "Panel mode shows clipboard in a floating window near the notch." : { "comment" : "A description of the \"Panel\" clipboard display mode.", "isCommentAutoGenerated" : true, @@ -19860,10 +19831,6 @@ } } }, - "Ring" : { - "comment" : "Localized name for the \"Ring\" timer progress style.", - "isCommentAutoGenerated" : true - }, "Rotation:" : { "extractionState" : "stale", "localizations" : { @@ -20690,10 +20657,6 @@ } } }, - "Separate Tab" : { - "comment" : "Name of the clipboard display mode option that opens the clipboard in a separate tab.", - "isCommentAutoGenerated" : true - }, "Separate Tab mode integrates Copied Items and Notes into a single view. If both are enabled, Notes appear on the right and Clipboard on the left." : { "comment" : "A description of the \"Separate Tab\" clipboard display mode.", "isCommentAutoGenerated" : true, @@ -21755,14 +21718,6 @@ } } }, - "Shows screen assistant as a dropdown attached to the AI button" : { - "comment" : "Description of the \"Popover\" display mode for the screen assistant.", - "isCommentAutoGenerated" : true - }, - "Shows screen assistant in a floating panel near the notch" : { - "comment" : "Description of the \"panel\" display mode for the screen assistant.", - "isCommentAutoGenerated" : true - }, "Shows the model's reasoning process before the final answer" : { "localizations" : { "fr" : { @@ -21825,10 +21780,6 @@ } } }, - "Skip forward or backward by ten seconds" : { - "comment" : "Description of the \"Skip forward or backward by ten seconds\" music skip behavior.", - "isCommentAutoGenerated" : true - }, "Slider color" : { "localizations" : { "ar" : { @@ -22397,10 +22348,6 @@ } } }, - "Spotify" : { - "comment" : "Add more media controller types here.", - "isCommentAutoGenerated" : true - }, "Square" : { "localizations" : { "ar" : { @@ -22513,10 +22460,6 @@ }, "Standard notch media controls are hidden. Re-enable the toggle above to restore them." : { - }, - "Standard previous/next track controls" : { - "comment" : "Description of a music skip behavior option that uses standard previous/next track controls.", - "isCommentAutoGenerated" : true }, "Start" : { "localizations" : { @@ -23878,10 +23821,6 @@ }, "shouldTranslate" : false }, - "Track Skip" : { - "comment" : "Name of the music skip behavior option that skips to the next track.", - "isCommentAutoGenerated" : true - }, "Transform" : { "extractionState" : "stale", "localizations" : { @@ -25803,10 +25742,6 @@ "comment" : "A footer note explaining the app's funding.", "isCommentAutoGenerated" : true }, - "Youtube Music" : { - "comment" : "Add more media player types here.", - "isCommentAutoGenerated" : true - }, "YouTube Music requires this third-party app to be installed: " : { "localizations" : { "ar" : { diff --git a/DynamicIsland/components/Onboarding/ProfileSelectionView.swift b/DynamicIsland/components/Onboarding/ProfileSelectionView.swift index 92b6594..d2a072c 100644 --- a/DynamicIsland/components/Onboarding/ProfileSelectionView.swift +++ b/DynamicIsland/components/Onboarding/ProfileSelectionView.swift @@ -209,6 +209,7 @@ func applyProfileSettings(_ profiles: Set) { if isDeveloper { Defaults[.enableColorPickerFeature] = true Defaults[.enableStatsFeature] = true + Defaults[.enableTerminalFeature] = true Defaults[.enableTimerFeature] = true Defaults[.enableScreenAssistant] = true Defaults[.showMirror] = false @@ -262,9 +263,35 @@ func applyProfileSettings(_ profiles: Set) { Defaults[.menubarIcon] = true Defaults[.enableHaptics] = true + // Lyrics disabled by default for all profiles + Defaults[.enableLyrics] = false + + // Weather widget defaults to inline style + Defaults[.lockScreenWeatherWidgetStyle] = .inline + + // Auto-detect notch: Dynamic Island for non-notch Macs, standard notch otherwise + if mainScreenHasNotch() { + Defaults[.externalDisplayStyle] = .notch + } else { + Defaults[.externalDisplayStyle] = .dynamicIsland + } + + // Lock screen glass: custom liquid glass v11 on macOS 26+ + if #available(macOS 26.0, *) { + Defaults[.lockScreenGlassStyle] = .liquid + Defaults[.lockScreenGlassCustomizationMode] = .customLiquid + Defaults[.lockScreenMusicLiquidGlassVariant] = .v11 + } + print("✅ Applied profile settings for: \(profiles.joined(separator: ", "))") } +/// Returns `true` when the main screen has a physical notch (safe area insets > 0). +private func mainScreenHasNotch() -> Bool { + guard let screen = NSScreen.main else { return false } + return screen.safeAreaInsets.top > 0 +} + #Preview { ProfileSelectionView(onContinue: { profiles in print("Selected profiles: \(profiles)") diff --git a/DynamicIsland/components/Settings/SettingsView.swift b/DynamicIsland/components/Settings/SettingsView.swift index 9bd3df3..6c92f66 100644 --- a/DynamicIsland/components/Settings/SettingsView.swift +++ b/DynamicIsland/components/Settings/SettingsView.swift @@ -778,6 +778,7 @@ struct SettingsView: View { SettingsSearchEntry(tab: .shelf, title: "Quick Share Service", keywords: ["shelf", "share", "airdrop"], highlightID: SettingsTab.shelf.highlightID(for: "Quick Share Service")), // Appearance + SettingsSearchEntry(tab: .appearance, title: "Main screen style", keywords: ["dynamic island", "pill", "non-notch", "display style", "notch style"], highlightID: SettingsTab.appearance.highlightID(for: "Main screen style")), SettingsSearchEntry(tab: .appearance, title: "Settings icon in notch", keywords: ["settings button", "toolbar"], highlightID: SettingsTab.appearance.highlightID(for: "Settings icon in notch")), SettingsSearchEntry(tab: .appearance, title: "Enable window shadow", keywords: ["shadow", "appearance"], highlightID: SettingsTab.appearance.highlightID(for: "Enable window shadow")), SettingsSearchEntry(tab: .appearance, title: "Corner radius scaling", keywords: ["corner radius", "shape"], highlightID: SettingsTab.appearance.highlightID(for: "Corner radius scaling")), @@ -3457,6 +3458,7 @@ struct Appearance: View { @Default(.lockScreenTimerWidgetUsesBlur) private var timerGlassModeIsGlass @Default(.enableLockScreenMediaWidget) private var enableLockScreenMediaWidget @Default(.enableLockScreenTimerWidget) private var enableLockScreenTimerWidget + @Default(.externalDisplayStyle) private var externalDisplayStyle @State private var selectedListVisualizer: CustomVisualizer? = nil @State private var isIconImporterPresented = false @@ -3468,6 +3470,12 @@ struct Appearance: View { @State private var url: String = "" @State private var speed: CGFloat = 1.0 + /// Whether the main screen has a physical notch. + private var mainScreenHasPhysicalNotch: Bool { + guard let screen = NSScreen.main else { return false } + return screen.safeAreaInsets.top > 0 + } + private var notchWidthRange: ClosedRange { let minW = Double(currentRecommendedMinimumNotchWidth()) let maxW = min(900, Double(maxAllowedNotchWidth())) @@ -3528,6 +3536,27 @@ struct Appearance: View { Text("General") } + // Show display style picker only on non-notch Macs (main screen has no physical notch) + if !mainScreenHasPhysicalNotch { + Section { + Picker("Main screen style", selection: $externalDisplayStyle) { + ForEach(ExternalDisplayStyle.allCases) { style in + Text(style.localizedName) + .tag(style) + } + } + .onChange(of: externalDisplayStyle) { + NotificationCenter.default.post(name: Notification.Name.notchHeightChanged, object: nil) + } + .settingsHighlight(id: highlightID("Main screen style")) + Text(externalDisplayStyle.description) + .font(.caption) + .foregroundStyle(.secondary) + } header: { + Text("Display Style") + } + } + notchWidthControls() Section { diff --git a/DynamicIsland/models/Constants.swift b/DynamicIsland/models/Constants.swift index c718ea5..bce1462 100644 --- a/DynamicIsland/models/Constants.swift +++ b/DynamicIsland/models/Constants.swift @@ -786,7 +786,7 @@ extension Defaults.Keys { static let lockScreenWeatherRefreshInterval = Key("lockScreenWeatherRefreshInterval", default: 30 * 60) static let lockScreenWeatherShowsLocation = Key("lockScreenWeatherShowsLocation", default: true) static let lockScreenWeatherShowsSunrise = Key("lockScreenWeatherShowsSunrise", default: true) - static let lockScreenWeatherWidgetStyle = Key("lockScreenWeatherWidgetStyle", default: .circular) + static let lockScreenWeatherWidgetStyle = Key("lockScreenWeatherWidgetStyle", default: .inline) static let lockScreenWeatherTemperatureUnit = Key("lockScreenWeatherTemperatureUnit", default: .celsius) static let lockScreenWeatherShowsAQI = Key("lockScreenWeatherShowsAQI", default: true) static let lockScreenWeatherAQIScale = Key("lockScreenWeatherAQIScale", default: .us) @@ -1078,7 +1078,7 @@ extension Defaults.Keys { static let enableMinimalisticUI = Key("enableMinimalisticUI", default: false) // MARK: Lyrics Feature - static let enableLyrics = Key("enableLyrics", default: true) + static let enableLyrics = Key("enableLyrics", default: false) // MARK: Notes Feature static let enableNotes = Key("enableNotes", default: false) diff --git a/DynamicIsland/models/DynamicIslandViewModel.swift b/DynamicIsland/models/DynamicIslandViewModel.swift index c98c713..aa101ef 100644 --- a/DynamicIsland/models/DynamicIslandViewModel.swift +++ b/DynamicIsland/models/DynamicIslandViewModel.swift @@ -395,11 +395,16 @@ class DynamicIslandViewModel: NSObject, ObservableObject { } } + private var helloCloseScheduled = false + func closeHello() { + guard !helloCloseScheduled else { return } + helloCloseScheduled = true DispatchQueue.main.asyncAfter(deadline: .now() + 3.5) { [weak self] in - self?.coordinator.firstLaunch = false - withAnimation(self?.animationLibrary.animation) { - self?.close() + guard let self else { return } + self.coordinator.firstLaunch = false + withAnimation(self.animationLibrary.animation) { + self.close() } } }