Update onboarding

This commit is contained in:
Hariharan Mudaliar 2026-03-05 16:24:06 +05:30
commit 5accee5d02
No known key found for this signature in database
GPG key ID: 025583EB0BE5640E
6 changed files with 78 additions and 81 deletions

View file

@ -396,9 +396,10 @@ struct ContentView: View {
} }
} }
.onAppear(perform: { .onAppear(perform: {
runAfter(1) { if coordinator.firstLaunch {
withAnimation(vm.animation) { // Single open during first launch; closeHello() handles the timed close.
if coordinator.firstLaunch { runAfter(1) {
withAnimation(vm.animation) {
openNotch() openNotch()
} }
} }
@ -1635,7 +1636,7 @@ struct ContentView: View {
} }
private func shouldPreventAutoClose() -> Bool { private func shouldPreventAutoClose() -> Bool {
hasAnyActivePopovers() || vm.isAutoCloseSuppressed || SharingStateManager.shared.preventNotchClose coordinator.firstLaunch || hasAnyActivePopovers() || vm.isAutoCloseSuppressed || SharingStateManager.shared.preventNotchClose
} }
// Helper to prevent rapid haptic feedback // Helper to prevent rapid haptic feedback

View file

@ -1607,10 +1607,6 @@
} }
} }
}, },
"±10 Seconds" : {
"comment" : "Display name for the \"±10 Seconds\" music skip behavior option.",
"isCommentAutoGenerated" : true
},
"0" : { "0" : {
"comment" : "A label displayed in a circular gauge to indicate the minimum battery level.", "comment" : "A label displayed in a circular gauge to indicate the minimum battery level.",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
@ -1927,10 +1923,6 @@
} }
} }
}, },
"Accent" : {
"comment" : "Display name for the \"Accent\" caps lock indicator tint mode.",
"isCommentAutoGenerated" : true
},
"Accent color" : { "Accent color" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -2276,10 +2268,6 @@
} }
} }
}, },
"Adaptive gradient" : {
"comment" : "Name of the timer icon color mode option that uses an adaptive gradient.",
"isCommentAutoGenerated" : true
},
"Add" : { "Add" : {
"localizations" : { "localizations" : {
"ar" : { "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." : { "Applies the notch-style parallax effect to the lock screen media widget album art." : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -4293,10 +4277,6 @@
} }
} }
}, },
"Bar" : {
"comment" : "Localized name for the 'Bar' timer progress style.",
"isCommentAutoGenerated" : true
},
"Battery" : { "Battery" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -8567,10 +8547,6 @@
} }
} }
}, },
"Digital" : {
"comment" : "The display name for the \"Digital\" reminder presentation style.",
"isCommentAutoGenerated" : true
},
"Dimensions" : { "Dimensions" : {
"localizations" : { "localizations" : {
"pt-BR" : { "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." : { "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.", "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:" : { "Height:" : {
"extractionState" : "stale", "extractionState" : "stale",
"localizations" : { "localizations" : {
@ -14092,6 +14067,9 @@
} }
} }
} }
},
"Main screen style" : {
}, },
"Manage indicator preferences from Settings → Privacy. No manual microphone toggle is required." : { "Manage indicator preferences from Settings → Privacy. No manual microphone toggle is required." : {
"comment" : "A description of how to manage the microphone privacy indicator preferences.", "comment" : "A description of how to manage the microphone privacy indicator preferences.",
@ -15647,6 +15625,7 @@
} }
}, },
"Minutes" : { "Minutes" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"fr" : { "fr" : {
"stringUnit" : { "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." : { "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." : { "Panel mode shows clipboard in a floating window near the notch." : {
"comment" : "A description of the \"Panel\" clipboard display mode.", "comment" : "A description of the \"Panel\" clipboard display mode.",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
@ -19860,10 +19831,6 @@
} }
} }
}, },
"Ring" : {
"comment" : "Localized name for the \"Ring\" timer progress style.",
"isCommentAutoGenerated" : true
},
"Rotation:" : { "Rotation:" : {
"extractionState" : "stale", "extractionState" : "stale",
"localizations" : { "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." : { "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.", "comment" : "A description of the \"Separate Tab\" clipboard display mode.",
"isCommentAutoGenerated" : true, "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" : { "Shows the model's reasoning process before the final answer" : {
"localizations" : { "localizations" : {
"fr" : { "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" : { "Slider color" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -22397,10 +22348,6 @@
} }
} }
}, },
"Spotify" : {
"comment" : "Add more media controller types here.",
"isCommentAutoGenerated" : true
},
"Square" : { "Square" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -22513,10 +22460,6 @@
}, },
"Standard notch media controls are hidden. Re-enable the toggle above to restore them." : { "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" : { "Start" : {
"localizations" : { "localizations" : {
@ -23878,10 +23821,6 @@
}, },
"shouldTranslate" : false "shouldTranslate" : false
}, },
"Track Skip" : {
"comment" : "Name of the music skip behavior option that skips to the next track.",
"isCommentAutoGenerated" : true
},
"Transform" : { "Transform" : {
"extractionState" : "stale", "extractionState" : "stale",
"localizations" : { "localizations" : {
@ -25803,10 +25742,6 @@
"comment" : "A footer note explaining the app's funding.", "comment" : "A footer note explaining the app's funding.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
}, },
"Youtube Music" : {
"comment" : "Add more media player types here.",
"isCommentAutoGenerated" : true
},
"YouTube Music requires this third-party app to be installed: " : { "YouTube Music requires this third-party app to be installed: " : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {

View file

@ -209,6 +209,7 @@ func applyProfileSettings(_ profiles: Set<String>) {
if isDeveloper { if isDeveloper {
Defaults[.enableColorPickerFeature] = true Defaults[.enableColorPickerFeature] = true
Defaults[.enableStatsFeature] = true Defaults[.enableStatsFeature] = true
Defaults[.enableTerminalFeature] = true
Defaults[.enableTimerFeature] = true Defaults[.enableTimerFeature] = true
Defaults[.enableScreenAssistant] = true Defaults[.enableScreenAssistant] = true
Defaults[.showMirror] = false Defaults[.showMirror] = false
@ -262,9 +263,35 @@ func applyProfileSettings(_ profiles: Set<String>) {
Defaults[.menubarIcon] = true Defaults[.menubarIcon] = true
Defaults[.enableHaptics] = 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: ", "))") 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 { #Preview {
ProfileSelectionView(onContinue: { profiles in ProfileSelectionView(onContinue: { profiles in
print("Selected profiles: \(profiles)") print("Selected profiles: \(profiles)")

View file

@ -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")), SettingsSearchEntry(tab: .shelf, title: "Quick Share Service", keywords: ["shelf", "share", "airdrop"], highlightID: SettingsTab.shelf.highlightID(for: "Quick Share Service")),
// Appearance // 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: "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: "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")), 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(.lockScreenTimerWidgetUsesBlur) private var timerGlassModeIsGlass
@Default(.enableLockScreenMediaWidget) private var enableLockScreenMediaWidget @Default(.enableLockScreenMediaWidget) private var enableLockScreenMediaWidget
@Default(.enableLockScreenTimerWidget) private var enableLockScreenTimerWidget @Default(.enableLockScreenTimerWidget) private var enableLockScreenTimerWidget
@Default(.externalDisplayStyle) private var externalDisplayStyle
@State private var selectedListVisualizer: CustomVisualizer? = nil @State private var selectedListVisualizer: CustomVisualizer? = nil
@State private var isIconImporterPresented = false @State private var isIconImporterPresented = false
@ -3468,6 +3470,12 @@ struct Appearance: View {
@State private var url: String = "" @State private var url: String = ""
@State private var speed: CGFloat = 1.0 @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<Double> { private var notchWidthRange: ClosedRange<Double> {
let minW = Double(currentRecommendedMinimumNotchWidth()) let minW = Double(currentRecommendedMinimumNotchWidth())
let maxW = min(900, Double(maxAllowedNotchWidth())) let maxW = min(900, Double(maxAllowedNotchWidth()))
@ -3528,6 +3536,27 @@ struct Appearance: View {
Text("General") 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() notchWidthControls()
Section { Section {

View file

@ -786,7 +786,7 @@ extension Defaults.Keys {
static let lockScreenWeatherRefreshInterval = Key<TimeInterval>("lockScreenWeatherRefreshInterval", default: 30 * 60) static let lockScreenWeatherRefreshInterval = Key<TimeInterval>("lockScreenWeatherRefreshInterval", default: 30 * 60)
static let lockScreenWeatherShowsLocation = Key<Bool>("lockScreenWeatherShowsLocation", default: true) static let lockScreenWeatherShowsLocation = Key<Bool>("lockScreenWeatherShowsLocation", default: true)
static let lockScreenWeatherShowsSunrise = Key<Bool>("lockScreenWeatherShowsSunrise", default: true) static let lockScreenWeatherShowsSunrise = Key<Bool>("lockScreenWeatherShowsSunrise", default: true)
static let lockScreenWeatherWidgetStyle = Key<LockScreenWeatherWidgetStyle>("lockScreenWeatherWidgetStyle", default: .circular) static let lockScreenWeatherWidgetStyle = Key<LockScreenWeatherWidgetStyle>("lockScreenWeatherWidgetStyle", default: .inline)
static let lockScreenWeatherTemperatureUnit = Key<LockScreenWeatherTemperatureUnit>("lockScreenWeatherTemperatureUnit", default: .celsius) static let lockScreenWeatherTemperatureUnit = Key<LockScreenWeatherTemperatureUnit>("lockScreenWeatherTemperatureUnit", default: .celsius)
static let lockScreenWeatherShowsAQI = Key<Bool>("lockScreenWeatherShowsAQI", default: true) static let lockScreenWeatherShowsAQI = Key<Bool>("lockScreenWeatherShowsAQI", default: true)
static let lockScreenWeatherAQIScale = Key<LockScreenWeatherAirQualityScale>("lockScreenWeatherAQIScale", default: .us) static let lockScreenWeatherAQIScale = Key<LockScreenWeatherAirQualityScale>("lockScreenWeatherAQIScale", default: .us)
@ -1078,7 +1078,7 @@ extension Defaults.Keys {
static let enableMinimalisticUI = Key<Bool>("enableMinimalisticUI", default: false) static let enableMinimalisticUI = Key<Bool>("enableMinimalisticUI", default: false)
// MARK: Lyrics Feature // MARK: Lyrics Feature
static let enableLyrics = Key<Bool>("enableLyrics", default: true) static let enableLyrics = Key<Bool>("enableLyrics", default: false)
// MARK: Notes Feature // MARK: Notes Feature
static let enableNotes = Key<Bool>("enableNotes", default: false) static let enableNotes = Key<Bool>("enableNotes", default: false)

View file

@ -395,11 +395,16 @@ class DynamicIslandViewModel: NSObject, ObservableObject {
} }
} }
private var helloCloseScheduled = false
func closeHello() { func closeHello() {
guard !helloCloseScheduled else { return }
helloCloseScheduled = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3.5) { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + 3.5) { [weak self] in
self?.coordinator.firstLaunch = false guard let self else { return }
withAnimation(self?.animationLibrary.animation) { self.coordinator.firstLaunch = false
self?.close() withAnimation(self.animationLibrary.animation) {
self.close()
} }
} }
} }