From b9616dcb90d7bba8eb9abeb5013483fc41310077 Mon Sep 17 00:00:00 2001 From: Emir <85408428+overflow-sudo@users.noreply.github.com> Date: Sun, 3 Nov 2024 09:22:34 +0300 Subject: [PATCH 01/74] Placeholder provided by media player If the artwork data cannot be found, it will be replaced with a placeholder provided by media player --- boringNotch/managers/MusicManager.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 04be40d..6c7bdbf 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -172,7 +172,13 @@ class MusicManager: ObservableObject { let artist = information["kMRMediaRemoteNowPlayingInfoArtist"] as? String ?? "" let album = information["kMRMediaRemoteNowPlayingInfoAlbum"] as? String ?? "" let duration = information["kMRMediaRemoteNowPlayingInfoDuration"] as? TimeInterval ?? lastMusicItem?.duration ?? 0 - let artworkData = information["kMRMediaRemoteNowPlayingInfoArtworkData"] as? Data + + + guard let artworkData = information["kMRMediaRemoteNowPlayingInfoArtworkData"] as? Data else { + let placeholder = AppIconAsNSImage(for: bundleIdentifier)?.pngRepresentation + return (title, artist, album, duration, placeholder) + } + return (title, artist, album, duration, artworkData) } @@ -190,6 +196,8 @@ class MusicManager: ObservableObject { self.songTitle = newInfo.title self.album = newInfo.album self.songDuration = newInfo.duration + print(newInfo.duration) + } private func updateArtwork(_ artworkData: Data?) { From 07c540c40a7c730a6e806362b8cae860dc9c26b9 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Tue, 5 Nov 2024 02:00:36 -0500 Subject: [PATCH 02/74] rewrite logic for detirmining when to change album art and when to use app icon as a placeholder --- boringNotch/managers/MusicManager.swift | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 6c7bdbf..792ccb8 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -172,13 +172,7 @@ class MusicManager: ObservableObject { let artist = information["kMRMediaRemoteNowPlayingInfoArtist"] as? String ?? "" let album = information["kMRMediaRemoteNowPlayingInfoAlbum"] as? String ?? "" let duration = information["kMRMediaRemoteNowPlayingInfoDuration"] as? TimeInterval ?? lastMusicItem?.duration ?? 0 - - - guard let artworkData = information["kMRMediaRemoteNowPlayingInfoArtworkData"] as? Data else { - let placeholder = AppIconAsNSImage(for: bundleIdentifier)?.pngRepresentation - return (title, artist, album, duration, placeholder) - } - + let artworkData = information["kMRMediaRemoteNowPlayingInfoArtworkData"] as? Data return (title, artist, album, duration, artworkData) } @@ -196,12 +190,10 @@ class MusicManager: ObservableObject { self.songTitle = newInfo.title self.album = newInfo.album self.songDuration = newInfo.duration - print(newInfo.duration) - } private func updateArtwork(_ artworkData: Data?) { - if let artworkData = artworkData, + if let artworkData = artworkData ?? AppIconAsNSImage(for: bundleIdentifier)?.tiffRepresentation, let artworkImage = NSImage(data: artworkData) { self.usingAppIconForArtwork = false self.updateAlbumArt(newAlbumArt: artworkImage) From ee9a9379138b50f8c5aab6a06f0e1b689089494a Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:27:38 -0500 Subject: [PATCH 03/74] Hide mini app icon when app icon is being used for artwork --- boringNotch/components/Notch/NotchHomeView.swift | 2 +- boringNotch/managers/MusicManager.swift | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 7f74a14..c228692 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -61,7 +61,7 @@ struct NotchHomeView: View { .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) if vm.notchState == .open && !musicManager.usingAppIconForArtwork { - AppIcon(for: musicManager.bundleIdentifier ?? "com.apple.Music") + AppIcon(for: musicManager.bundleIdentifier) .resizable() .aspectRatio(contentMode: .fill) .frame(width: 30, height: 30) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 792ccb8..f2609ad 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -162,7 +162,7 @@ class MusicManager: ObservableObject { // MARK: - Helper Methods private func updateBundleIdentifier(_ bundle: String?) { if let bundle = bundle { - self.bundleIdentifier = bundle + self.bundleIdentifier = bundle == "com.apple.WebKit.GPU" ? "com.apple.Safari" : bundle } } @@ -193,16 +193,24 @@ class MusicManager: ObservableObject { } private func updateArtwork(_ artworkData: Data?) { - if let artworkData = artworkData ?? AppIconAsNSImage(for: bundleIdentifier)?.tiffRepresentation, + if let artworkData = artworkData, let artworkImage = NSImage(data: artworkData) { self.usingAppIconForArtwork = false self.updateAlbumArt(newAlbumArt: artworkImage) - } else if let appIconImage = AppIconAsNSImage(for: bundleIdentifier ?? nowPlaying.appBundleIdentifier ?? "") { + } else if let appIconImage = AppIconAsNSImage(for: bundleIdentifier) { self.usingAppIconForArtwork = true self.updateAlbumArt(newAlbumArt: appIconImage) } } + private func updatePlaybackState(_ state: Int?) { + if let state = state { + self.musicIsPaused(state: state == 1, setIdle: true) + } else if self.isPlaying { + self.musicIsPaused(state: false, setIdle: true) + } + } + func musicIsPaused(state: Bool, bypass: Bool = false, setIdle: Bool = false) { if musicToggledManually && !bypass { return } From f8feb8ae2ba654f38b17e99663828bf372236ba2 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 6 Nov 2024 19:23:07 -0500 Subject: [PATCH 04/74] Use the isPlaying function rather than guessing based on playback rate is this is sometimes 0 even though something is playing --- boringNotch/managers/MusicManager.swift | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index f2609ad..49b8679 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -186,6 +186,9 @@ class MusicManager: ObservableObject { MRMediaRemoteGetNowPlayingApplicationIsPlaying(DispatchQueue.main) { [weak self] isPlaying in self?.musicIsPaused(state: isPlaying, setIdle: true) } + + if !self.isPlaying { return } + self.artistName = newInfo.artist self.songTitle = newInfo.title self.album = newInfo.album @@ -203,14 +206,6 @@ class MusicManager: ObservableObject { } } - private func updatePlaybackState(_ state: Int?) { - if let state = state { - self.musicIsPaused(state: state == 1, setIdle: true) - } else if self.isPlaying { - self.musicIsPaused(state: false, setIdle: true) - } - } - func musicIsPaused(state: Bool, bypass: Bool = false, setIdle: Bool = false) { if musicToggledManually && !bypass { return } From db082e7fcf315b72fabceb7c79ef495a80d98ccf Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 6 Nov 2024 19:47:21 -0500 Subject: [PATCH 05/74] adjust states inside thread for new playing detection --- boringNotch/managers/MusicManager.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 49b8679..7cd75b4 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -185,14 +185,14 @@ class MusicManager: ObservableObject { self.lastMusicItem = (title: newInfo.title, artist: newInfo.artist, album: newInfo.album, duration: newInfo.duration, artworkData: lastMusicItem?.artworkData) MRMediaRemoteGetNowPlayingApplicationIsPlaying(DispatchQueue.main) { [weak self] isPlaying in self?.musicIsPaused(state: isPlaying, setIdle: true) + + guard isPlaying else { return } + + self?.artistName = newInfo.artist + self?.songTitle = newInfo.title + self?.album = newInfo.album + self?.songDuration = newInfo.duration } - - if !self.isPlaying { return } - - self.artistName = newInfo.artist - self.songTitle = newInfo.title - self.album = newInfo.album - self.songDuration = newInfo.duration } private func updateArtwork(_ artworkData: Data?) { From 67184f6cda5ba883c44e662383481183725104cd Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 6 Nov 2024 20:43:10 -0500 Subject: [PATCH 06/74] Update bundle identifier logic for more accurate icon --- boringNotch/components/Notch/NotchHomeView.swift | 2 +- boringNotch/managers/MusicManager.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index c228692..7f74a14 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -61,7 +61,7 @@ struct NotchHomeView: View { .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) if vm.notchState == .open && !musicManager.usingAppIconForArtwork { - AppIcon(for: musicManager.bundleIdentifier) + AppIcon(for: musicManager.bundleIdentifier ?? "com.apple.Music") .resizable() .aspectRatio(contentMode: .fill) .frame(width: 30, height: 30) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 7cd75b4..7dbbf6a 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -162,7 +162,7 @@ class MusicManager: ObservableObject { // MARK: - Helper Methods private func updateBundleIdentifier(_ bundle: String?) { if let bundle = bundle { - self.bundleIdentifier = bundle == "com.apple.WebKit.GPU" ? "com.apple.Safari" : bundle + self.bundleIdentifier = bundle } } @@ -200,7 +200,7 @@ class MusicManager: ObservableObject { let artworkImage = NSImage(data: artworkData) { self.usingAppIconForArtwork = false self.updateAlbumArt(newAlbumArt: artworkImage) - } else if let appIconImage = AppIconAsNSImage(for: bundleIdentifier) { + } else if let appIconImage = AppIconAsNSImage(for: bundleIdentifier ?? nowPlaying.appBundleIdentifier ?? "") { self.usingAppIconForArtwork = true self.updateAlbumArt(newAlbumArt: appIconImage) } From b56f9d6e532c4cff6fcb4bdd70617e7d9375fcf0 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:10:39 -0500 Subject: [PATCH 07/74] allow timeline to update while paused, for the case when the back skip button is pressed or the source modifies the elapsedTime after being paused --- boringNotch/components/Notch/NotchHomeView.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 7f74a14..1154337 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -175,7 +175,8 @@ struct NotchHomeView: View { } private func updateSliderValue() { - guard !dragging, musicManager.timestampDate > lastDragged else { return } + guard !dragging, musicManager.timestampDate > lastDragged, + musicManager.timestampDate > musicManager.lastUpdated else { return } let currentTime = Date() let timeDifference = currentTime.timeIntervalSince(musicManager.timestampDate) // Calculate the real-time elapsed time From cb02d4454ba7dabcf088c7af19a65dacba4546ab Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:37:37 -0500 Subject: [PATCH 08/74] More fixes --- boringNotch/components/Notch/NotchHomeView.swift | 3 +-- boringNotch/managers/MusicManager.swift | 11 ++++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 1154337..7f74a14 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -175,8 +175,7 @@ struct NotchHomeView: View { } private func updateSliderValue() { - guard !dragging, musicManager.timestampDate > lastDragged, - musicManager.timestampDate > musicManager.lastUpdated else { return } + guard !dragging, musicManager.timestampDate > lastDragged else { return } let currentTime = Date() let timeDifference = currentTime.timeIntervalSince(musicManager.timestampDate) // Calculate the real-time elapsed time diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 7dbbf6a..04be40d 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -185,14 +185,11 @@ class MusicManager: ObservableObject { self.lastMusicItem = (title: newInfo.title, artist: newInfo.artist, album: newInfo.album, duration: newInfo.duration, artworkData: lastMusicItem?.artworkData) MRMediaRemoteGetNowPlayingApplicationIsPlaying(DispatchQueue.main) { [weak self] isPlaying in self?.musicIsPaused(state: isPlaying, setIdle: true) - - guard isPlaying else { return } - - self?.artistName = newInfo.artist - self?.songTitle = newInfo.title - self?.album = newInfo.album - self?.songDuration = newInfo.duration } + self.artistName = newInfo.artist + self.songTitle = newInfo.title + self.album = newInfo.album + self.songDuration = newInfo.duration } private func updateArtwork(_ artworkData: Data?) { From d7b64c679cfdc90ad85d357e2f174ee24b9cba35 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Sun, 17 Nov 2024 22:43:52 -0500 Subject: [PATCH 09/74] modify screen lock monitoring --- boringNotch/boringNotchApp.swift | 12 ++++- boringNotch/managers/NotchSpaceManager.swift | 48 +------------------- 2 files changed, 12 insertions(+), 48 deletions(-) diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index 7b23336..34e580e 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -18,7 +18,6 @@ struct DynamicNotchApp: App { @Default(.menubarIcon) var showMenuBarIcon @Environment(\.openWindow) var openWindow let updaterController: SPUStandardUpdaterController - let notchSpace = NotchSpaceManager.shared.notchSpace init() { updaterController = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil) @@ -97,6 +96,14 @@ class AppDelegate: NSObject, NSApplicationDelegate { NotificationCenter.default.removeObserver(self) } + @objc func onScreenLocked(_: Notification) { + print("Screen locked") + } + + @objc func onScreenUnlocked(_: Notification) { + print("Screen unlocked") + } + func applicationDidFinishLaunching(_ notification: Notification) { @@ -117,6 +124,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { self?.adjustWindowPosition() } + DistributedNotificationCenter.default().addObserver(self, selector: #selector(onScreenLocked(_:)), name: NSNotification.Name(rawValue: "com.apple.screenIsLocked"), object: nil) + DistributedNotificationCenter.default().addObserver(self, selector: #selector(onScreenUnlocked(_:)), name: NSNotification.Name(rawValue: "com.apple.screenIsUnlocked"), object: nil) + KeyboardShortcuts.onKeyDown(for: .toggleSneakPeek) { [weak self] in guard let self = self else { return } diff --git a/boringNotch/managers/NotchSpaceManager.swift b/boringNotch/managers/NotchSpaceManager.swift index 7e8ca48..5b90f84 100644 --- a/boringNotch/managers/NotchSpaceManager.swift +++ b/boringNotch/managers/NotchSpaceManager.swift @@ -11,54 +11,8 @@ class NotchSpaceManager { let notchSpace: CGSSpace private var eventTap: CFMachPort? private var runLoopSource: CFRunLoopSource? - + private init() { notchSpace = CGSSpace(level: 2147483647) // Max level - startMonitoring() - } - - deinit { - stopMonitoring() - } - - private func startMonitoring() { - DistributedNotificationCenter.default().addObserver(self, selector: #selector(screenLockStateChanged), name: NSNotification.Name("com.apple.screenIsLocked"), object: nil) - DistributedNotificationCenter.default().addObserver(self, selector: #selector(screenLockStateChanged), name: NSNotification.Name("com.apple.screenIsUnlocked"), object: nil) - - - // Set up the event tap to detect a lack of events (indicative of lock, but not definitive) - let eventMask = (1 << CGEventType.keyDown.rawValue) | (1 << CGEventType.mouseMoved.rawValue) - guard let eventTap = CGEvent.tapCreate(tap: .cgSessionEventTap, place: .headInsertEventTap, options: .listenOnly, eventsOfInterest: CGEventMask(eventMask), callback: { (proxy, type, event, refcon) -> Unmanaged? in - - return Unmanaged.passRetained(event) - }, userInfo: nil) else { - print("Failed to create event tap") - return - } - - self.eventTap = eventTap - - let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0) - self.runLoopSource = runLoopSource - CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, CFRunLoopMode.commonModes) - } - - private func stopMonitoring() { - DistributedNotificationCenter.default().removeObserver(self) - - if let eventTap = eventTap { - CFMachPortInvalidate(eventTap) - } - if let runLoopSource = runLoopSource { - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource, CFRunLoopMode.commonModes) - } - - eventTap = nil - runLoopSource = nil - } - - @objc private func screenLockStateChanged(_ notification: Notification) { - let isLocked = notification.name == NSNotification.Name("com.apple.screenIsLocked") - print("Screen is \(isLocked ? "locked" : "unlocked")") } } From a2db151739d540b3e1b4ea65f32af56361b306a6 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Sun, 17 Nov 2024 22:45:07 -0500 Subject: [PATCH 10/74] temporary fix for repositioning problems --- boringNotch/boringNotchApp.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index 34e580e..6958dd3 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -199,9 +199,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { @objc func screenConfigurationDidChange() { let currentScreens = NSScreen.screens - guard currentScreens.count != previousScreens?.count else { return } + //guard currentScreens.count != previousScreens?.count || previousScreens?.first(where: {$0.localizedName == vm.selectedScreen})?.backingScaleFactor == currentScreens.first(where: {$0.localizedName == vm.selectedScreen})?.backingScaleFactor else { return } previousScreens = currentScreens - adjustWindowPosition(changeAlpha: true) + adjustWindowPosition() } @objc func adjustWindowPosition(changeAlpha: Bool = false) { From 1cb0152e1e8959156c3dceea8c11a37dc93fc2ec Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Sun, 17 Nov 2024 23:02:17 -0500 Subject: [PATCH 11/74] Changed selectedScreen to preferredScreen and added a seperate selectedDisplay so that boringNotch remembers the last manually selected screen and tries to use it if it is available --- boringNotch/boringNotchApp.swift | 2 +- boringNotch/components/Settings/SettingsView.swift | 2 +- boringNotch/models/BoringViewModel.swift | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index 6958dd3..ebdb223 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -205,7 +205,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } @objc func adjustWindowPosition(changeAlpha: Bool = false) { - if !NSScreen.screens.contains(where: {$0.localizedName == vm.selectedScreen}) { + if !NSScreen.screens.contains(where: {$0.localizedName == vm.preferredScreen}) { vm.selectedScreen = NSScreen.main?.localizedName ?? "Unknown" } diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index 0d55720..29dd860 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -141,7 +141,7 @@ struct GeneralSettings: View { Section { Defaults.Toggle("Menubar icon", key: .menubarIcon) LaunchAtLogin.Toggle("Launch at login") - Picker("Show on a specific display", selection: $vm.selectedScreen) { + Picker("Show on a specific display", selection: $vm.preferredScreen) { ForEach(screens, id: \.self) { screen in Text(screen) } diff --git a/boringNotch/models/BoringViewModel.swift b/boringNotch/models/BoringViewModel.swift index 4b9884c..3f46216 100644 --- a/boringNotch/models/BoringViewModel.swift +++ b/boringNotch/models/BoringViewModel.swift @@ -144,12 +144,15 @@ class BoringViewModel: NSObject, ObservableObject { toggleHudReplacement() } } - - @AppStorage("selected_screen_name") var selectedScreen = NSScreen.main?.localizedName ?? "Unknown" { + + @AppStorage("preferred_screen_name") var preferredScreen = NSScreen.main?.localizedName ?? "Unknown" { didSet { + selectedScreen = preferredScreen NotificationCenter.default.post(name: Notification.Name.selectedScreenChanged, object: nil) } } + + @Published var selectedScreen: String = NSScreen.main?.localizedName ?? "Unknown" @AppStorage("currentMicStatus") var currentMicStatus: Bool = true var notifier: TheBoringWorkerNotifier = .init() @@ -175,6 +178,7 @@ class BoringViewModel: NSObject, ObservableObject { } .assign(to: \.anyDropZoneTargeting, on: self) .store(in: &cancellables) + self.selectedScreen = preferredScreen } func open() { From 77ed09c340ee27f994e8bcef8b621ab5c5c1b062 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Sun, 17 Nov 2024 23:09:57 -0500 Subject: [PATCH 12/74] Add a Default Key and settings for show on all displays --- boringNotch/components/Settings/SettingsView.swift | 3 +++ boringNotch/models/Constants.swift | 1 + 2 files changed, 4 insertions(+) diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index 29dd860..721b9eb 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -103,6 +103,7 @@ struct GeneralSettings: View { //@State var nonNotchHeightMode: NonNotchHeightMode = .matchRealNotchSize @Default(.nonNotchHeight) var nonNotchHeight @Default(.nonNotchHeightMode) var nonNotchHeightMode + @Default(.showOnAllDisplays) var showOnAllDisplays var body: some View { Form { @@ -141,11 +142,13 @@ struct GeneralSettings: View { Section { Defaults.Toggle("Menubar icon", key: .menubarIcon) LaunchAtLogin.Toggle("Launch at login") + Defaults.Toggle("Show on all displays", key: .showOnAllDisplays) Picker("Show on a specific display", selection: $vm.preferredScreen) { ForEach(screens, id: \.self) { screen in Text(screen) } } + .disabled(showOnAllDisplays) .onChange(of: NSScreen.screens) { old, new in screens = new.compactMap({$0.localizedName}) } diff --git a/boringNotch/models/Constants.swift b/boringNotch/models/Constants.swift index 4693e0f..2930ca4 100644 --- a/boringNotch/models/Constants.swift +++ b/boringNotch/models/Constants.swift @@ -18,6 +18,7 @@ struct CustomVisualizer: Codable, Hashable, Equatable, Defaults.Serializable { extension Defaults.Keys { // MARK: General static let menubarIcon = Key("menubarIcon", default: true) + static let showOnAllDisplays = Key("showOnAllDisplays", default: false) static let releaseName = Key("releaseName", default: "Glowing Panda đŸŒ (Snooty)") // MARK: Behavior From d6b88255af5d75b21a73cb733d1a2e6ce488d134 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:53:39 -0500 Subject: [PATCH 13/74] Add all displays as a beta feature --- boringNotch/boringNotchApp.swift | 161 ++++++++++++------ .../components/Settings/SettingsView.swift | 12 +- 2 files changed, 122 insertions(+), 51 deletions(-) diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index ebdb223..1c3e6ac 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -78,6 +78,7 @@ struct DynamicNotchApp: App { class AppDelegate: NSObject, NSApplicationDelegate { var statusItem: NSStatusItem? + var windows: [NSScreen: NSWindow] = [:] var window: NSWindow! var sizing: Sizes = .init() let vm: BoringViewModel = .init() @@ -123,54 +124,87 @@ class AppDelegate: NSObject, NSApplicationDelegate { NotificationCenter.default.addObserver(forName: Notification.Name.notchHeightChanged, object: nil, queue: nil) { [weak self] _ in self?.adjustWindowPosition() } - + + NotificationCenter.default.addObserver(forName: Notification.Name.showOnAllDisplaysChanged, object: nil, queue: nil) { [weak self] _ in + if(!Defaults[.showOnAllDisplays]) { + self?.window = BoringNotchWindow( + contentRect: NSRect(x: 0, y: 0, width: self!.sizing.size.opened.width! + 20, height: self!.sizing.size.opened.height! + 30), + styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], + backing: .buffered, + defer: false + ) + + if let windowValues = self?.windows.values { + for window in windowValues { + window.close() + } + } + + self?.window.contentView = NSHostingView(rootView: ContentView(batteryModel: .init(vm: self!.vm)).environmentObject(self!.vm).environmentObject(MusicManager(vm: self!.vm)!)) + + self?.adjustWindowPosition(changeAlpha: true) + + self?.window.orderFrontRegardless() + + NotchSpaceManager.shared.notchSpace.windows.insert(self!.window) + } else { + self?.window.close() + self?.windows = [:] + self?.adjustWindowPosition() + } + } + DistributedNotificationCenter.default().addObserver(self, selector: #selector(onScreenLocked(_:)), name: NSNotification.Name(rawValue: "com.apple.screenIsLocked"), object: nil) DistributedNotificationCenter.default().addObserver(self, selector: #selector(onScreenUnlocked(_:)), name: NSNotification.Name(rawValue: "com.apple.screenIsUnlocked"), object: nil) - + KeyboardShortcuts.onKeyDown(for: .toggleSneakPeek) { [weak self] in guard let self = self else { return } - + self.vm.toggleSneakPeek( status: !self.vm.sneakPeek.show, type: .music, duration: 3.0 ) } - + KeyboardShortcuts.onKeyDown(for: .toggleNotchOpen) { [weak self] in guard let self = self else { return } switch self.vm.notchState { - case .closed: - self.vm.open() - self.closeNotchWorkItem?.cancel() - - let workItem = DispatchWorkItem { - self.vm.close() - } - self.closeNotchWorkItem = workItem - - DispatchQueue.main.asyncAfter(deadline: .now() + 3.0, execute: workItem) - case .open: - self.closeNotchWorkItem?.cancel() - self.closeNotchWorkItem = nil + case .closed: + self.vm.open() + self.closeNotchWorkItem?.cancel() + + let workItem = DispatchWorkItem { self.vm.close() + } + self.closeNotchWorkItem = workItem + + DispatchQueue.main.asyncAfter(deadline: .now() + 3.0, execute: workItem) + case .open: + self.closeNotchWorkItem?.cancel() + self.closeNotchWorkItem = nil + self.vm.close() } } - window = BoringNotchWindow( - contentRect: NSRect(x: 0, y: 0, width: sizing.size.opened.width! + 20, height: sizing.size.opened.height! + 30), - styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], - backing: .buffered, - defer: false - ) - - window.contentView = NSHostingView(rootView: ContentView(batteryModel: .init(vm: self.vm)).environmentObject(vm).environmentObject(MusicManager(vm: vm)!)) - - adjustWindowPosition(changeAlpha: true) - - window.orderFrontRegardless() - - NotchSpaceManager.shared.notchSpace.windows.insert(window) + if !Defaults[.showOnAllDisplays] { + window = BoringNotchWindow( + contentRect: NSRect(x: 0, y: 0, width: sizing.size.opened.width! + 20, height: sizing.size.opened.height! + 30), + styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], + backing: .buffered, + defer: false + ) + + window.contentView = NSHostingView(rootView: ContentView(batteryModel: .init(vm: self.vm)).environmentObject(vm).environmentObject(MusicManager(vm: vm)!)) + + adjustWindowPosition(changeAlpha: true) + + window.orderFrontRegardless() + + NotchSpaceManager.shared.notchSpace.windows.insert(window) + } else { + adjustWindowPosition() + } if vm.firstLaunch { DispatchQueue.main.async { @@ -205,26 +239,54 @@ class AppDelegate: NSObject, NSApplicationDelegate { } @objc func adjustWindowPosition(changeAlpha: Bool = false) { - if !NSScreen.screens.contains(where: {$0.localizedName == vm.preferredScreen}) { - vm.selectedScreen = NSScreen.main?.localizedName ?? "Unknown" - } - - let selectedScreen = NSScreen.screens.first(where: {$0.localizedName == vm.selectedScreen}) - closedNotchSize = setNotchSize(screen: selectedScreen?.localizedName) - - if let screenFrame = selectedScreen { - window.alphaValue = changeAlpha ? 0 : 1 - window.makeKeyAndOrderFront(nil) + if Defaults[.showOnAllDisplays] { + for screen in NSScreen.screens { + if windows[screen] == nil { + let window = BoringNotchWindow( + contentRect: NSRect(x: 0, y: 0, width: sizing.size.opened.width! + 20, height: sizing.size.opened.height! + 30), + styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], + backing: .buffered, + defer: false + ) + window.contentView = NSHostingView( + rootView: ContentView(batteryModel: .init(vm: self.vm)) + .environmentObject(vm) + .environmentObject(MusicManager(vm: vm)!) + ) + windows[screen] = window + window.orderFrontRegardless() + NotchSpaceManager.shared.notchSpace.windows.insert(window) + } + if let window = windows[screen] { + window.alphaValue = changeAlpha ? 0 : 1 + DispatchQueue.main.async { + window.setFrameOrigin(screen.frame.origin.applying(CGAffineTransform(translationX: (screen.frame.width / 2) - window.frame.width / 2, y: screen.frame.height - window.frame.height))) + window.alphaValue = 1 + } + } + } + } else { + if !NSScreen.screens.contains(where: {$0.localizedName == vm.preferredScreen}) { + vm.selectedScreen = NSScreen.main?.localizedName ?? "Unknown" + } - DispatchQueue.main.async {[weak self] in - self!.window.setFrameOrigin(screenFrame.frame.origin.applying(CGAffineTransform(translationX: (screenFrame.frame.width / 2) - self!.window.frame.width / 2, y: screenFrame.frame.height - self!.window.frame.height))) - self!.window.alphaValue = 1 + let selectedScreen = NSScreen.screens.first(where: {$0.localizedName == vm.selectedScreen}) + closedNotchSize = setNotchSize(screen: selectedScreen?.localizedName) + + if let screenFrame = selectedScreen { + window.alphaValue = changeAlpha ? 0 : 1 + window.makeKeyAndOrderFront(nil) + + DispatchQueue.main.async {[weak self] in + self!.window.setFrameOrigin(screenFrame.frame.origin.applying(CGAffineTransform(translationX: (screenFrame.frame.width / 2) - self!.window.frame.width / 2, y: screenFrame.frame.height - self!.window.frame.height))) + self!.window.alphaValue = 1 + } + } + // TODO: optimize animation here, especially for custom height slider + // TODO: don't animate if the window is changing screens + if vm.notchState == .closed { + vm.close() } - } - //TODO: optimize animation here, especially for custom height slider - //TODO: don't animate if the window is changing screens - if vm.notchState == .closed { - vm.close() } } @@ -248,4 +310,5 @@ class AppDelegate: NSObject, NSApplicationDelegate { extension Notification.Name { static let selectedScreenChanged = Notification.Name("SelectedScreenChanged") static let notchHeightChanged = Notification.Name("NotchHeightChanged") + static let showOnAllDisplaysChanged = Notification.Name("showOnAllDisplaysChanged") } diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index 721b9eb..9240d19 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -138,11 +138,19 @@ struct GeneralSettings: View { } header: { Text("Accent color") } - + Section { Defaults.Toggle("Menubar icon", key: .menubarIcon) LaunchAtLogin.Toggle("Launch at login") - Defaults.Toggle("Show on all displays", key: .showOnAllDisplays) + Defaults.Toggle(key: .showOnAllDisplays) { + HStack { + Text("Show on all displays") + customBadge(text: "Beta") + } + } + .onChange(of: showOnAllDisplays) { + NotificationCenter.default.post(name: Notification.Name.showOnAllDisplaysChanged, object: nil) + } Picker("Show on a specific display", selection: $vm.preferredScreen) { ForEach(screens, id: \.self) { screen in Text(screen) From fb0454fd29e09ff964046522e5b7aa62f85e90dc Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Sun, 17 Nov 2024 18:55:19 -0500 Subject: [PATCH 14/74] Dynamically change settings views based on preferences and fix deprecated onChange calls --- .../components/Settings/SettingsView.swift | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index 9240d19..c05e3da 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -75,8 +75,8 @@ struct SettingsView: View { } .environmentObject(extensionManager) .formStyle(.grouped) - .onChange(of: scenePhase) { _, phase in - if phase == .active { + .onChange(of: scenePhase) { + if scenePhase == .active { NSApp.setActivationPolicy(.regular) NSApp.activate(ignoringOtherApps: true) } @@ -103,8 +103,9 @@ struct GeneralSettings: View { //@State var nonNotchHeightMode: NonNotchHeightMode = .matchRealNotchSize @Default(.nonNotchHeight) var nonNotchHeight @Default(.nonNotchHeightMode) var nonNotchHeightMode - @Default(.showOnAllDisplays) var showOnAllDisplays - + @Default(.enableGestures) var enableGestures + @Default(.openNotchOnHover) var openNotchOnHover + var body: some View { Form { Section { @@ -156,9 +157,8 @@ struct GeneralSettings: View { Text(screen) } } - .disabled(showOnAllDisplays) - .onChange(of: NSScreen.screens) { old, new in - screens = new.compactMap({$0.localizedName}) + .onChange(of: NSScreen.screens) { + screens = NSScreen.screens.compactMap({$0.localizedName}) } } header: { Text("System features") @@ -174,9 +174,7 @@ struct GeneralSettings: View { .tag(NonNotchHeightMode.custom) } .onChange(of: nonNotchHeightMode) { - _, - new in - switch new { + switch nonNotchHeightMode { case .matchMenuBar: nonNotchHeight = 24 case .matchRealNotchSize: @@ -190,7 +188,7 @@ struct GeneralSettings: View { Slider(value: $nonNotchHeight, in: 10...40, step: 1) { Text("Custom notch size - \(nonNotchHeight, specifier: "%.0f")") } - .onChange(of: nonNotchHeight) { _, new in + .onChange(of: nonNotchHeight) { NotificationCenter.default.post(name: Notification.Name.notchHeightChanged, object: nil) } } @@ -210,14 +208,19 @@ struct GeneralSettings: View { .controlSize(.extraLarge) } .navigationTitle("General") + .onChange(of: openNotchOnHover) { + if !openNotchOnHover { + enableGestures = true + } + } } @ViewBuilder func gestureControls() -> some View { Section { Defaults.Toggle("Enable gestures", key: .enableGestures) - .disabled(!Defaults[.openNotchOnHover]) - if Defaults[.enableGestures] { + .disabled(!openNotchOnHover) + if enableGestures { Toggle("Media change with horizontal gestures", isOn: .constant(false)) .disabled(true) Defaults.Toggle("Close gesture", key: .closeGestureEnabled) @@ -249,15 +252,18 @@ struct GeneralSettings: View { Defaults.Toggle("Enable haptics", key: .enableHaptics) Defaults.Toggle("Open notch on hover", key: .openNotchOnHover) Toggle("Remember last tab", isOn: $vm.openLastTabByDefault) - if Defaults[.openNotchOnHover] { + if openNotchOnHover { Slider(value: $minimumHoverDuration, in: 0...1, step: 0.1) { HStack { Text("Minimum hover duration") Spacer() - Text("\(Defaults[.minimumHoverDuration], specifier: "%.1f")s") + Text("\(minimumHoverDuration, specifier: "%.1f")s") .foregroundStyle(.secondary) } } + .onChange(of: minimumHoverDuration) { + NotificationCenter.default.post(name: Notification.Name.notchHeightChanged, object: nil) + } } } header: { Text("Notch behavior") @@ -378,8 +384,8 @@ struct HUD: View { Text("Inline") .tag(true) } - .onChange(of: Defaults[.inlineHUD]) { _, newValue in - if newValue { + .onChange(of: Defaults[.inlineHUD]) { + if Defaults[.inlineHUD] { withAnimation { Defaults[.systemEventIndicatorShadow] = false Defaults[.enableGradient] = false From f08001963b0cf55c57e7296a7642ab432aef878b Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:28:32 -0500 Subject: [PATCH 15/74] Better sneak peek logic for song change and ignoring empty entries --- boringNotch/managers/MusicManager.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 04be40d..9f9998d 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -182,6 +182,10 @@ class MusicManager: ObservableObject { updateArtwork(newInfo.artworkData) self.lastMusicItem?.artworkData = newInfo.artworkData } + // Only update sneak peek if the music info has changed + if((newInfo.title, newInfo.artist, newInfo.album) != (lastMusicItem?.title, lastMusicItem?.artist, lastMusicItem?.album) && (newInfo.title, newInfo.artist) != ("", "")) { + updateSneakPeek() + } self.lastMusicItem = (title: newInfo.title, artist: newInfo.artist, album: newInfo.album, duration: newInfo.duration, artworkData: lastMusicItem?.artworkData) MRMediaRemoteGetNowPlayingApplicationIsPlaying(DispatchQueue.main) { [weak self] isPlaying in self?.musicIsPaused(state: isPlaying, setIdle: true) @@ -219,7 +223,7 @@ class MusicManager: ObservableObject { updateFullscreenMediaDetection() // Only update sneak peek if the state has actually changed - if previousState != state { + if previousState != state && (songTitle, artistName) != ("", "") { updateSneakPeek() } From da41032b7f59bcc708c4554ba33f016f46587104 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:41:39 -0500 Subject: [PATCH 16/74] update timeline slider logic for better behaviour when paused --- boringNotch/Localizable.xcstrings | 5 ++++- boringNotch/components/Notch/NotchHomeView.swift | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/boringNotch/Localizable.xcstrings b/boringNotch/Localizable.xcstrings index 51c2ad9..be51572 100644 --- a/boringNotch/Localizable.xcstrings +++ b/boringNotch/Localizable.xcstrings @@ -774,6 +774,9 @@ }, "TheBoringNotch" : { + }, + "Toggle Notch Open:" : { + }, "Toggle Sneak Peek:" : { @@ -830,4 +833,4 @@ } }, "version" : "1.0" -} \ No newline at end of file +} diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 7f74a14..40042a4 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -177,7 +177,7 @@ struct NotchHomeView: View { private func updateSliderValue() { guard !dragging, musicManager.timestampDate > lastDragged else { return } let currentTime = Date() - let timeDifference = currentTime.timeIntervalSince(musicManager.timestampDate) + let timeDifference = (musicManager.isPlaying ? currentTime.timeIntervalSince(musicManager.timestampDate) : musicManager.lastUpdated.timeIntervalSince(musicManager.timestampDate)) // Calculate the real-time elapsed time let currentElapsedTime = musicManager.elapsedTime + (timeDifference * musicManager.playbackRate) sliderValue = min(currentElapsedTime, musicManager.songDuration) From b1417f01fd922959b65a11e1932cc41eb439f008 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 20 Nov 2024 20:52:30 -0500 Subject: [PATCH 17/74] Refactor the code by transferring certain elements from BoringViewModel to BoringView Coordinator. This will help isolate the singleton-required code from the ViewModel that is now specific to each window. --- BoringViewCoordinator.swift | 167 ++++++++++++++++++ boringNotch.xcodeproj/project.pbxproj | 4 + boringNotch/models/BoringViewModel.swift | 209 +++-------------------- 3 files changed, 197 insertions(+), 183 deletions(-) create mode 100644 BoringViewCoordinator.swift diff --git a/BoringViewCoordinator.swift b/BoringViewCoordinator.swift new file mode 100644 index 0000000..0d3962e --- /dev/null +++ b/BoringViewCoordinator.swift @@ -0,0 +1,167 @@ +// +// BoringViewCoordinator.swift +// boringNotch +// +// Created by Alexander on 2024-11-20. +// + +import Combine +import SwiftUI +import TheBoringWorkerNotifier +import Defaults + +enum SneakContentType { + case brightness + case volume + case backlight + case music + case mic + case battery + case download +} + +struct sneakPeek { + var show: Bool = false + var type: SneakContentType = .music + var value: CGFloat = 0 + var icon: String = "" +} + +struct SharedSneakPeek: Codable { + var show: Bool + var type: String + var value: String + var icon: String +} + +class BoringViewCoordinator: ObservableObject { + static let shared = BoringViewCoordinator() + var notifier: TheBoringWorkerNotifier = .init() + + @Published var currentView: NotchViews = .home + private var sneakPeekDispatch: DispatchWorkItem? + + @AppStorage("firstLaunch") var firstLaunch: Bool = true + @AppStorage("showWhatsNew") var showWhatsNew: Bool = true + @AppStorage("musicLiveActivity") var showMusicLiveActivityOnClosed: Bool = true + @AppStorage("currentMicStatus") var currentMicStatus: Bool = true + + @AppStorage("alwaysShowTabs") var alwaysShowTabs: Bool = true { + didSet { + if !alwaysShowTabs { + openLastTabByDefault = false + if TrayDrop.shared.isEmpty || !Defaults[.openShelfByDefault] { + currentView = .home + } + } + } + } + + @AppStorage("openLastTabByDefault") var openLastTabByDefault: Bool = false { + didSet { + if openLastTabByDefault { + alwaysShowTabs = true + } + } + } + + @AppStorage("hudReplacement") var hudReplacement: Bool = true { + didSet { + notifier.postNotification(name: notifier.toggleHudReplacementNotification.name, userInfo: nil) + } + } + + @AppStorage("preferred_screen_name") var preferredScreen = NSScreen.main?.localizedName ?? "Unknown" { + didSet { + selectedScreen = preferredScreen + NotificationCenter.default.post(name: Notification.Name.selectedScreenChanged, object: nil) + } + } + + @Published var selectedScreen: String = NSScreen.main?.localizedName ?? "Unknown" + + @Published var optionKeyPressed: Bool = true + + private init() { + self.selectedScreen = preferredScreen + notifier = TheBoringWorkerNotifier() + } + + func setupWorkersNotificationObservers() { + notifier.setupObserver(notification: notifier.micStatusNotification, handler: initialMicStatus) + notifier.setupObserver(notification: notifier.sneakPeakNotification, handler: sneakPeekEvent) + } + + @objc func sneakPeekEvent(_ notification: Notification) { + let decoder = JSONDecoder() + if let decodedData = try? decoder.decode(SharedSneakPeek.self, from: notification.userInfo?.first?.value as! Data) { + let contentType = decodedData.type == "brightness" ? SneakContentType.brightness : decodedData.type == "volume" ? SneakContentType.volume : decodedData.type == "backlight" ? SneakContentType.backlight : decodedData.type == "mic" ? SneakContentType.mic : SneakContentType.brightness + + let value = CGFloat((NumberFormatter().number(from: decodedData.value) ?? 0.0).floatValue) + let icon = decodedData.icon + + print(decodedData) + + toggleSneakPeek(status: decodedData.show, type: contentType, value: value, icon: icon) + + } else { + print("Failed to decode JSON data") + } + } + + func toggleSneakPeek(status: Bool, type: SneakContentType, duration: TimeInterval = 1.5, value: CGFloat = 0, icon: String = "") { + self.sneakPeekDuration = duration + if type != .music { + //close() + if !hudReplacement { + return + } + } + DispatchQueue.main.async { + withAnimation(.smooth) { + self.sneakPeek.show = status + self.sneakPeek.type = type + self.sneakPeek.value = value + self.sneakPeek.icon = icon + } + } + + if type == .mic { + currentMicStatus = value == 1 + } + } + + private var sneakPeekDuration: TimeInterval = 1.5 + @Published var sneakPeek: sneakPeek = .init() { + didSet { + if sneakPeek.show { + sneakPeekDispatch?.cancel() + + sneakPeekDispatch = DispatchWorkItem { [weak self] in + guard let self = self else { return } + withAnimation { + self.toggleSneakPeek(status: false, type: SneakContentType.music) + self.sneakPeekDuration = 1.5 + } + } + DispatchQueue.main + .asyncAfter( + deadline: .now() + self.sneakPeekDuration, + execute: sneakPeekDispatch! + ) + } + } + } + + @objc func initialMicStatus(_ notification: Notification) { + currentMicStatus = notification.userInfo?.first?.value as! Bool + } + + func toggleMic() { + notifier.postNotification(name: notifier.toggleMicNotification.name, userInfo: nil) + } + + func showEmpty() { + currentView = .home + } +} diff --git a/boringNotch.xcodeproj/project.pbxproj b/boringNotch.xcodeproj/project.pbxproj index afc44ef..0c57967 100644 --- a/boringNotch.xcodeproj/project.pbxproj +++ b/boringNotch.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 112FB7352CCF16F70015238C /* NotchSpaceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 112FB7342CCF16F70015238C /* NotchSpaceManager.swift */; }; + 11CC44A22CEE614100C7244B /* BoringViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11CC44A12CEE614100C7244B /* BoringViewCoordinator.swift */; }; 14288DDC2C6E015000B9F80C /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14288DD62C6E015000B9F80C /* AudioPlayer.swift */; }; 14288DE82C6E01C800B9F80C /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14288DE72C6E01C800B9F80C /* ProgressIndicator.swift */; }; 14288DFE2C6E9A4700B9F80C /* BoringSystemTiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14288DFD2C6E9A4700B9F80C /* BoringSystemTiles.swift */; }; @@ -113,6 +114,7 @@ /* Begin PBXFileReference section */ 112FB7342CCF16F70015238C /* NotchSpaceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchSpaceManager.swift; sourceTree = ""; }; + 11CC44A12CEE614100C7244B /* BoringViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoringViewCoordinator.swift; sourceTree = ""; }; 14288DD62C6E015000B9F80C /* AudioPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = ""; }; 14288DD72C6E015000B9F80C /* boringNotch-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "boringNotch-Bridging-Header.h"; sourceTree = ""; }; 14288DE72C6E01C800B9F80C /* ProgressIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.swift; sourceTree = ""; }; @@ -326,6 +328,7 @@ 14CEF4092C5CAED200855D72 = { isa = PBXGroup; children = ( + 11CC44A12CEE614100C7244B /* BoringViewCoordinator.swift */, 14CEF4142C5CAED300855D72 /* boringNotch */, 14CEF4132C5CAED300855D72 /* Products */, 14D031EC2C689DB70096E6A1 /* Frameworks */, @@ -647,6 +650,7 @@ files = ( 14C08BB92C8DE4B1000F8AA0 /* BoringCalendar.swift in Sources */, 147163982C5D35B70068B555 /* MusicVisualizer.swift in Sources */, + 11CC44A22CEE614100C7244B /* BoringViewCoordinator.swift in Sources */, B186543C2C6F49AE000B926A /* ShortcutConstants.swift in Sources */, B1D365CE2C6A979C0047BDBC /* LiveActivityModifier.swift in Sources */, 14D570C02C5EA5870011E668 /* AnimatedFace.swift in Sources */, diff --git a/boringNotch/models/BoringViewModel.swift b/boringNotch/models/BoringViewModel.swift index 3f46216..8d18e3e 100644 --- a/boringNotch/models/BoringViewModel.swift +++ b/boringNotch/models/BoringViewModel.swift @@ -10,39 +10,6 @@ import SwiftUI import TheBoringWorkerNotifier import Defaults -private let availableDirectories = FileManager - .default - .urls(for: .documentDirectory, in: .userDomainMask) -let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! -let bundleIdentifier = Bundle.main.bundleIdentifier! -let appVersion = "\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "") (\(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? ""))" - -let temporaryDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! - -enum SneakContentType { - case brightness - case volume - case backlight - case music - case mic - case battery - case download -} - -struct sneakPeek { - var show: Bool = false - var type: SneakContentType = .music - var value: CGFloat = 0 - var icon: String = "" -} - -struct SharedSneakPeek: Codable { - var show: Bool - var type: String - var value: String - var icon: String -} - enum BrowserType { case chromium case safari @@ -56,73 +23,28 @@ struct ExpandedItem { } class BoringViewModel: NSObject, ObservableObject { - var cancellables: Set = [] - + @ObservedObject var coordinator = BoringViewCoordinator.shared + let animationLibrary: BoringAnimations = .init() let animation: Animation? @Published var contentType: ContentType = .normal @Published private(set) var notchState: NotchState = .closed - @Published var currentView: NotchViews = .home - @Published var headerTitle: String = "Glowing đŸŒ" - @Published var emptyStateText: String = "Play some jams, ladies, and watch me shine! New features coming soon! đŸŽ¶ 🚀" - @Published var sizes: Sizes = .init() - @Published var musicPlayerSizes: MusicPlayerElementSizes = .init() - @AppStorage("firstLaunch") var firstLaunch: Bool = true - @AppStorage("showWhatsNew") var showWhatsNew: Bool = true - @Published var whatsNewOnClose: (() -> Void)? - @Published var notchMetastability: Bool = true // True if notch not open - private var sneakPeekDispatch: DispatchWorkItem? + private var expandingViewDispatch: DispatchWorkItem? - @Published var showCHPanel: Bool = false - @Published var optionKeyPressed: Bool = true - @AppStorage("musicLiveActivity") var showMusicLiveActivityOnClosed: Bool = true - @Published var spacing: CGFloat = 16 @Published var dragDetectorTargeting: Bool = false @Published var dropZoneTargeting: Bool = false @Published var dropEvent: Bool = false @Published var anyDropZoneTargeting: Bool = false + var cancellables: Set = [] - @AppStorage("alwaysShowTabs") var alwaysShowTabs: Bool = true { - didSet { - if !alwaysShowTabs { - openLastTabByDefault = false - if TrayDrop.shared.isEmpty || !Defaults[.openShelfByDefault] { - currentView = .home - } - } - } - } + var screen: String? - @AppStorage("openLastTabByDefault") var openLastTabByDefault: Bool = false { - didSet { - if openLastTabByDefault { - alwaysShowTabs = true - } - } - } - private var sneakPeekDuration: TimeInterval = 1.5 - @Published var sneakPeek: sneakPeek = .init() { - didSet { - if sneakPeek.show { - sneakPeekDispatch?.cancel() + @Published var notchSize: CGSize = getClosedNotchSize() + @Published var closedNotchSize: CGSize = getClosedNotchSize() - sneakPeekDispatch = DispatchWorkItem { [weak self] in - guard let self = self else { return } - withAnimation { - self.toggleSneakPeek(status: false, type: SneakContentType.music) - self.sneakPeekDuration = 1.5 - } - } - DispatchQueue.main - .asyncAfter( - deadline: .now() + self.sneakPeekDuration, - execute: sneakPeekDispatch! - ) - } - } - } + var notifier: TheBoringWorkerNotifier = .init() @Published var expandingView: ExpandedItem = .init() { didSet { @@ -138,25 +60,7 @@ class BoringViewModel: NSObject, ObservableObject { } } } - - @AppStorage("hudReplacement") var hudReplacement: Bool = true { - didSet { - toggleHudReplacement() - } - } - @AppStorage("preferred_screen_name") var preferredScreen = NSScreen.main?.localizedName ?? "Unknown" { - didSet { - selectedScreen = preferredScreen - NotificationCenter.default.post(name: Notification.Name.selectedScreenChanged, object: nil) - } - } - - @Published var selectedScreen: String = NSScreen.main?.localizedName ?? "Unknown" - - @AppStorage("currentMicStatus") var currentMicStatus: Bool = true - var notifier: TheBoringWorkerNotifier = .init() - deinit { destroy() } @@ -166,11 +70,15 @@ class BoringViewModel: NSObject, ObservableObject { cancellables.removeAll() } - override - init() { + init(screen: String? = nil) { animation = animationLibrary.animation - notifier = TheBoringWorkerNotifier() + super.init() + + notifier = coordinator.notifier + self.screen = screen + self.notchSize = getClosedNotchSize(screen: screen) + self.closedNotchSize = notchSize Publishers.CombineLatest($dropZoneTargeting, $dragDetectorTargeting) .map { value1, value2 in @@ -178,82 +86,21 @@ class BoringViewModel: NSObject, ObservableObject { } .assign(to: \.anyDropZoneTargeting, on: self) .store(in: &cancellables) - self.selectedScreen = preferredScreen } func open() { withAnimation(.bouncy) { - self.notchSize = .init(width: Sizes().size.opened.width!, height: Sizes().size.opened.height!) - self.notchMetastability = true + self.notchSize = CGSize(width: openNotchSize.width, height: openNotchSize.height) self.notchState = .open } } - func setupWorkersNotificationObservers() { - notifier.setupObserver(notification: notifier.sneakPeakNotification, handler: sneakPeekEvent) - - notifier.setupObserver(notification: notifier.micStatusNotification, handler: initialMicStatus) - } - - @objc func initialMicStatus(_ notification: Notification) { - currentMicStatus = notification.userInfo?.first?.value as! Bool - } - - @objc func sneakPeekEvent(_ notification: Notification) { - let decoder = JSONDecoder() - if let decodedData = try? decoder.decode(SharedSneakPeek.self, from: notification.userInfo?.first?.value as! Data) { - let contentType = decodedData.type == "brightness" ? SneakContentType.brightness : decodedData.type == "volume" ? SneakContentType.volume : decodedData.type == "backlight" ? SneakContentType.backlight : decodedData.type == "mic" ? SneakContentType.mic : SneakContentType.brightness - - let value = CGFloat((NumberFormatter().number(from: decodedData.value) ?? 0.0).floatValue) - let icon = decodedData.icon - - print(decodedData) - - toggleSneakPeek(status: decodedData.show, type: contentType, value: value, icon: icon) - - } else { - print("Failed to decode JSON data") - } - } - - @Published var notchSize: CGSize = .init(width: Sizes().size.closed.width!, height: Sizes().size.closed.height!) - func toggleMusicLiveActivity(status: Bool) { withAnimation(.smooth) { // self.showMusicLiveActivityOnClosed = status } } - func toggleMic() { - notifier.postNotification(name: notifier.toggleMicNotification.name, userInfo: nil) - } - - func toggleSneakPeek(status: Bool, type: SneakContentType, duration: TimeInterval = 1.5, value: CGFloat = 0, icon: String = "") { - self.sneakPeekDuration = duration - if type != .music { - close() - if !hudReplacement { - return - } - } - DispatchQueue.main.async { - withAnimation(.smooth) { - self.sneakPeek.show = status - self.sneakPeek.type = type - self.sneakPeek.value = value - self.sneakPeek.icon = icon - } - } - - if type == .mic { - currentMicStatus = value == 1 - } - } - - func toggleHudReplacement() { - notifier.postNotification(name: notifier.toggleHudReplacementNotification.name, userInfo: nil) - } - func toggleExpandingView(status: Bool, type: SneakContentType, value: CGFloat = 0, browser: BrowserType = .chromium) { if expandingView.show { withAnimation(.smooth) { @@ -272,20 +119,20 @@ class BoringViewModel: NSObject, ObservableObject { func close() { withAnimation(.smooth) { - self.notchSize = .init(width: Sizes().size.closed.width!, height: Sizes().size.closed.height!) + self.notchSize = getClosedNotchSize(screen: screen) + closedNotchSize = notchSize self.notchState = .closed - self.notchMetastability = false } // Set the current view to shelf if it contains files and the user enables openShelfByDefault // Otherwise, if the user has not enabled openLastShelfByDefault, set the view to home if !TrayDrop.shared.isEmpty && Defaults[.openShelfByDefault] { - currentView = .shelf - } else if !openLastTabByDefault { - currentView = .home + coordinator.currentView = .shelf + } else if !coordinator.openLastTabByDefault { + coordinator.currentView = .home } } - + func openClipboard() { notifier.postNotification(name: notifier.showClipboardNotification.name, userInfo: nil) } @@ -294,15 +141,11 @@ class BoringViewModel: NSObject, ObservableObject { openClipboard() } - func showEmpty() { - currentView = .home - } - func closeHello() { - DispatchQueue.main.asyncAfter(deadline: .now() + 3.2) { - self.firstLaunch = false - withAnimation(self.animationLibrary.animation) { - self.close() + DispatchQueue.main.asyncAfter(deadline: .now() + 3.2) { [weak self] in + self?.coordinator.firstLaunch = false + withAnimation(self?.animationLibrary.animation) { + self?.close() } } } From dd0189a4ea0e34b838e874d03977d63c178b9fc9 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 20 Nov 2024 20:55:12 -0500 Subject: [PATCH 18/74] Use new BoringView Coordinator, refactor matters.swift and sizes, and add new show on all displays beta option --- boringNotch/ContentView.swift | 57 ++++++++--------- boringNotch/Localizable.xcstrings | 5 +- boringNotch/boringNotchApp.swift | 62 ++++++++++++------- .../components/BoringSystemTiles.swift | 7 ++- .../Live activities/InlineHUD.swift | 8 +-- .../LiveActivityModifier.swift | 2 +- .../components/Notch/BoringHeader.swift | 7 ++- .../components/Notch/NotchContentView.swift | 23 +++---- .../components/Notch/NotchHomeView.swift | 7 ++- .../components/Notch/NotchShelfView.swift | 8 +-- .../components/Settings/SettingsView.swift | 17 ++--- .../components/Shelf/DropItemView.swift | 7 ++- .../components/Tabs/TabSelectionView.swift | 14 ++--- .../components/Webcam/WebcamView.swift | 4 +- boringNotch/extensions/Button+Bouncing.swift | 2 +- boringNotch/managers/MusicManager.swift | 3 +- .../models/BatteryStatusViewModel.swift | 3 +- boringNotch/models/Constants.swift | 10 +++ boringNotch/sizing/matters.swift | 53 ++++------------ 19 files changed, 154 insertions(+), 145 deletions(-) diff --git a/boringNotch/ContentView.swift b/boringNotch/ContentView.swift index 7aa6ec9..3546959 100644 --- a/boringNotch/ContentView.swift +++ b/boringNotch/ContentView.swift @@ -19,6 +19,7 @@ struct ContentView: View { @EnvironmentObject var musicManager: MusicManager @StateObject var webcamManager: WebcamManager = .init() + @ObservedObject var coordinator = BoringViewCoordinator.shared @State private var hoverStartTime: Date? @State private var hoverTimer: Timer? @@ -35,14 +36,14 @@ struct ContentView: View { var body: some View { ZStack(alignment: .top) { NotchLayout() - .padding(.horizontal, vm.notchState == .open ? Defaults[.cornerRadiusScaling] ? (vm.sizes.cornerRadius.opened.inset!) : (vm.sizes.cornerRadius.closed.inset! - 5) : 12) + .padding(.horizontal, vm.notchState == .open ? Defaults[.cornerRadiusScaling] ? (cornerRadiusInsets.opened) : (cornerRadiusInsets.closed - 5) : 12) .padding([.horizontal, .bottom], vm.notchState == .open ? 12 : 0) - .frame(maxWidth: (((musicManager.isPlaying || !musicManager.isPlayerIdle) && vm.notchState == .closed && vm.showMusicLiveActivityOnClosed) || (vm.expandingView.show && (vm.expandingView.type == .battery)) || Defaults[.inlineHUD]) ? nil : vm.notchSize.width + ((hoverAnimation || (vm.notchState == .closed)) ? 20 : 0) + gestureProgress, maxHeight: ((vm.sneakPeek.show && vm.sneakPeek.type != .music) || (vm.sneakPeek.show && vm.sneakPeek.type == .music && vm.notchState == .closed)) ? nil : vm.notchSize.height + (hoverAnimation ? 8 : 0) + gestureProgress / 3, alignment: .top) + .frame(maxWidth: (((musicManager.isPlaying || !musicManager.isPlayerIdle) && vm.notchState == .closed && coordinator.showMusicLiveActivityOnClosed) || (vm.expandingView.show && (vm.expandingView.type == .battery)) || Defaults[.inlineHUD]) ? nil : vm.notchSize.width + ((hoverAnimation || (vm.notchState == .closed)) ? 20 : 0) + gestureProgress, maxHeight: ((coordinator.sneakPeek.show && coordinator.sneakPeek.type != .music) || (coordinator.sneakPeek.show && coordinator.sneakPeek.type == .music && vm.notchState == .closed)) ? nil : vm.notchSize.height + (hoverAnimation ? 8 : 0) + gestureProgress / 3, alignment: .top) .background(.black) .mask { - NotchShape(cornerRadius: ((vm.notchState == .open) && Defaults[.cornerRadiusScaling]) ? vm.sizes.cornerRadius.opened.inset : vm.sizes.cornerRadius.closed.inset) + NotchShape(cornerRadius: ((vm.notchState == .open) && Defaults[.cornerRadiusScaling]) ? cornerRadiusInsets.opened : cornerRadiusInsets.closed) } - .frame(width: vm.notchState == .closed ? (((musicManager.isPlaying || !musicManager.isPlayerIdle) && vm.showMusicLiveActivityOnClosed) || (vm.expandingView.show && (vm.expandingView.type == .battery)) || (Defaults[.inlineHUD] && vm.sneakPeek.show && vm.sneakPeek.type != .music)) ? nil : Sizes().size.closed.width! + (hoverAnimation ? 20 : 0) + gestureProgress : nil, height: vm.notchState == .closed ? Sizes().size.closed.height! + (hoverAnimation ? 8 : 0) + gestureProgress / 3 : nil, alignment: .top) + .frame(width: vm.notchState == .closed ? (((musicManager.isPlaying || !musicManager.isPlayerIdle) && coordinator.showMusicLiveActivityOnClosed) || (vm.expandingView.show && (vm.expandingView.type == .battery)) || (Defaults[.inlineHUD] && coordinator.sneakPeek.show && coordinator.sneakPeek.type != .music)) ? nil : vm.closedNotchSize.width + (hoverAnimation ? 20 : 0) + gestureProgress : nil, height: vm.notchState == .closed ? vm.closedNotchSize.height + (hoverAnimation ? 8 : 0) + gestureProgress / 3 : nil, alignment: .top) .conditionalModifier(Defaults[.openNotchOnHover]) { view in view .onHover { hovering in @@ -55,7 +56,7 @@ struct ContentView: View { haptics.toggle() } - if vm.sneakPeek.show { + if coordinator.sneakPeek.show { return } @@ -147,7 +148,7 @@ struct ContentView: View { .onAppear(perform: { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { withAnimation(vm.animation) { - if vm.firstLaunch { + if coordinator.firstLaunch { doOpen() } } @@ -171,7 +172,7 @@ struct ContentView: View { .keyboardShortcut("E", modifiers: .command) } } - .frame(maxWidth: Sizes().size.opened.width! + 40, maxHeight: Sizes().size.opened.height! + 20, alignment: .top) + .frame(maxWidth: openNotchSize.width + 40, maxHeight: openNotchSize.height + 20, alignment: .top) .shadow(color: ((vm.notchState == .open || hoverAnimation) && Defaults[.enableShadow]) ? .black.opacity(0.6) : .clear, radius: Defaults[.cornerRadiusScaling] ? 10 : 5) .background(dragDetector) .environmentObject(vm) @@ -184,7 +185,7 @@ struct ContentView: View { func NotchLayout() -> some View { VStack(alignment: .leading) { VStack(alignment: .leading) { - if vm.firstLaunch { + if coordinator.firstLaunch { Spacer() HelloAnimation().frame(width: 200, height: 80).onAppear(perform: { vm.closeHello() @@ -201,7 +202,7 @@ struct ContentView: View { Rectangle() .fill(.black) - .frame(width: vm.sizes.size.closed.width! + 5) + .frame(width: vm.closedNotchSize.width + 5) HStack { BoringBatteryView( @@ -212,27 +213,27 @@ struct ContentView: View { } .frame(width: 76, alignment: .trailing) } - .frame(height: Sizes().size.closed.height! + (hoverAnimation ? 8 : 0), alignment: .center) - } else if vm.sneakPeek.show && Defaults[.inlineHUD] && (vm.sneakPeek.type != .music) && (vm.sneakPeek.type != .battery) { - InlineHUD(type: $vm.sneakPeek.type, value: $vm.sneakPeek.value, icon: $vm.sneakPeek.icon, hoverAnimation: $hoverAnimation, gestureProgress: $gestureProgress) + .frame(height: vm.closedNotchSize.height + (hoverAnimation ? 8 : 0), alignment: .center) + } else if coordinator.sneakPeek.show && Defaults[.inlineHUD] && (coordinator.sneakPeek.type != .music) && (coordinator.sneakPeek.type != .battery) { + InlineHUD(type: $coordinator.sneakPeek.type, value: $coordinator.sneakPeek.value, icon: $coordinator.sneakPeek.icon, hoverAnimation: $hoverAnimation, gestureProgress: $gestureProgress) .transition(.opacity) - } else if !vm.expandingView.show && vm.notchState == .closed && (musicManager.isPlaying || !musicManager.isPlayerIdle) && vm.showMusicLiveActivityOnClosed { + } else if !vm.expandingView.show && vm.notchState == .closed && (musicManager.isPlaying || !musicManager.isPlayerIdle) && coordinator.showMusicLiveActivityOnClosed { MusicLiveActivity() } else { BoringHeader() - .frame(height: max(24, Sizes().size.closed.height!)) + .frame(height: max(24, vm.closedNotchSize.height)) .blur(radius: abs(gestureProgress) > 0.3 ? min(abs(gestureProgress), 8) : 0) } - if vm.sneakPeek.show && !Defaults[.inlineHUD] { - if (vm.sneakPeek.type != .music) && (vm.sneakPeek.type != .battery) { - SystemEventIndicatorModifier(eventType: $vm.sneakPeek.type, value: $vm.sneakPeek.value, icon: $vm.sneakPeek.icon, sendEventBack: { _ in + if coordinator.sneakPeek.show && !Defaults[.inlineHUD] { + if (coordinator.sneakPeek.type != .music) && (coordinator.sneakPeek.type != .battery) { + SystemEventIndicatorModifier(eventType: $coordinator.sneakPeek.type, value: $coordinator.sneakPeek.value, icon: $coordinator.sneakPeek.icon, sendEventBack: { _ in // }) .padding(.bottom, 10) .padding(.leading, 4) .padding(.trailing, 8) - } else if vm.sneakPeek.type != .battery { + } else if coordinator.sneakPeek.type != .battery { if vm.notchState == .closed { HStack(alignment: .center) { Image(systemName: "music.note") @@ -247,7 +248,7 @@ struct ContentView: View { } } } - .conditionalModifier((vm.sneakPeek.show && (vm.sneakPeek.type == .music) && vm.notchState == .closed) || (vm.sneakPeek.show && (vm.sneakPeek.type != .music) && (musicManager.isPlaying || !musicManager.isPlayerIdle))) { view in + .conditionalModifier((coordinator.sneakPeek.show && (coordinator.sneakPeek.type == .music) && vm.notchState == .closed) || (coordinator.sneakPeek.show && (coordinator.sneakPeek.type != .music) && (musicManager.isPlaying || !musicManager.isPlayerIdle))) { view in view .fixedSize() } @@ -255,7 +256,7 @@ struct ContentView: View { ZStack { if vm.notchState == .open { - switch vm.currentView { + switch coordinator.currentView { case .home: NotchHomeView(albumArtNamespace: albumArtNamespace) case .shelf: @@ -282,15 +283,15 @@ struct ContentView: View { .aspectRatio(contentMode: .fill) ) .clipped() - .clipShape(RoundedRectangle(cornerRadius: vm.musicPlayerSizes.image.cornerRadius.closed.inset!)) + .clipShape(RoundedRectangle(cornerRadius: MusicPlayerImageSizes.cornerRadiusInset.closed)) .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) - .frame(width: max(0, Sizes().size.closed.height! - 12), height: max(0, Sizes().size.closed.height! - 12)) + .frame(width: max(0, vm.closedNotchSize.height - 12), height: max(0, vm.closedNotchSize.height - 12)) } - .frame(width: max(0, Sizes().size.closed.height! - (hoverAnimation ? 0 : 12) + gestureProgress / 2), height: max(0, Sizes().size.closed.height! - (hoverAnimation ? 0 : 12))) + .frame(width: max(0, vm.closedNotchSize.height - (hoverAnimation ? 0 : 12) + gestureProgress / 2), height: max(0, vm.closedNotchSize.height - (hoverAnimation ? 0 : 12))) Rectangle() .fill(.black) - .frame(width: Sizes().size.closed.width! - 20) + .frame(width: vm.closedNotchSize.width - 20) HStack { if useMusicVisualizer { @@ -307,10 +308,10 @@ struct ContentView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) } } - .frame(width: max(0, Sizes().size.closed.height! - (hoverAnimation ? 0 : 12) + gestureProgress / 2), - height: max(0,Sizes().size.closed.height! - (hoverAnimation ? 0 : 12)), alignment: .center) + .frame(width: max(0, vm.closedNotchSize.height - (hoverAnimation ? 0 : 12) + gestureProgress / 2), + height: max(0,vm.closedNotchSize.height - (hoverAnimation ? 0 : 12)), alignment: .center) } - .frame(height: Sizes().size.closed.height! + (hoverAnimation ? 8 : 0), alignment: .center) + .frame(height: vm.closedNotchSize.height + (hoverAnimation ? 8 : 0), alignment: .center) } @ViewBuilder @@ -322,7 +323,7 @@ struct ContentView: View { .onDrop(of: [.data], isTargeted: $vm.dragDetectorTargeting) { _ in true } .onChange(of: vm.anyDropZoneTargeting) { _, isTargeted in if isTargeted, vm.notchState == .closed { - vm.currentView = .shelf + coordinator.currentView = .shelf doOpen() } else if !isTargeted { print("DROP EVENT", vm.dropEvent) diff --git a/boringNotch/Localizable.xcstrings b/boringNotch/Localizable.xcstrings index be51572..61f5ef5 100644 --- a/boringNotch/Localizable.xcstrings +++ b/boringNotch/Localizable.xcstrings @@ -733,6 +733,9 @@ }, "Show on a specific display" : { + }, + "Show on all displays" : { + }, "Slider color" : { @@ -833,4 +836,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index 1c3e6ac..836cb9b 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -59,7 +59,6 @@ struct DynamicNotchApp: App { Settings { SettingsView(updaterController: updaterController) - .environmentObject(appDelegate.vm) } Window("Onboarding", id: "onboarding") { @@ -79,9 +78,10 @@ struct DynamicNotchApp: App { class AppDelegate: NSObject, NSApplicationDelegate { var statusItem: NSStatusItem? var windows: [NSScreen: NSWindow] = [:] + var viewModels: [NSScreen: BoringViewModel] = [:] var window: NSWindow! - var sizing: Sizes = .init() let vm: BoringViewModel = .init() + @ObservedObject var coordinator = BoringViewCoordinator.shared var whatsNewWindow: NSWindow? var timer: Timer? let calenderManager = CalendarManager() @@ -107,8 +107,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) { - - vm.setupWorkersNotificationObservers(); + coordinator.setupWorkersNotificationObservers(); NotificationCenter.default.addObserver( self, @@ -128,7 +127,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { NotificationCenter.default.addObserver(forName: Notification.Name.showOnAllDisplaysChanged, object: nil, queue: nil) { [weak self] _ in if(!Defaults[.showOnAllDisplays]) { self?.window = BoringNotchWindow( - contentRect: NSRect(x: 0, y: 0, width: self!.sizing.size.opened.width! + 20, height: self!.sizing.size.opened.height! + 30), + contentRect: NSRect(x: 0, y: 0, width: openNotchSize.width + 20, height: openNotchSize.height + 30), styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], backing: .buffered, defer: false @@ -160,8 +159,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { KeyboardShortcuts.onKeyDown(for: .toggleSneakPeek) { [weak self] in guard let self = self else { return } - self.vm.toggleSneakPeek( - status: !self.vm.sneakPeek.show, + self.coordinator.toggleSneakPeek( + status: !self.coordinator.sneakPeek.show, type: .music, duration: 3.0 ) @@ -169,13 +168,25 @@ class AppDelegate: NSObject, NSApplicationDelegate { KeyboardShortcuts.onKeyDown(for: .toggleNotchOpen) { [weak self] in guard let self = self else { return } - switch self.vm.notchState { + + let mouseLocation = NSEvent.mouseLocation + + var viewModel = self.vm; + + if(Defaults[.showOnAllDisplays]) { + for screen in NSScreen.screens { + if screen.frame.contains(mouseLocation) { + viewModel = viewModels[screen] ?? viewModel + } + } + } + switch viewModel.notchState { case .closed: - self.vm.open() + viewModel.open() self.closeNotchWorkItem?.cancel() let workItem = DispatchWorkItem { - self.vm.close() + viewModel.close() } self.closeNotchWorkItem = workItem @@ -183,13 +194,13 @@ class AppDelegate: NSObject, NSApplicationDelegate { case .open: self.closeNotchWorkItem?.cancel() self.closeNotchWorkItem = nil - self.vm.close() + viewModel.close() } } if !Defaults[.showOnAllDisplays] { window = BoringNotchWindow( - contentRect: NSRect(x: 0, y: 0, width: sizing.size.opened.width! + 20, height: sizing.size.opened.height! + 30), + contentRect: NSRect(x: 0, y: 0, width: openNotchSize.width + 20, height: openNotchSize.height + 30), styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], backing: .buffered, defer: false @@ -206,7 +217,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { adjustWindowPosition() } - if vm.firstLaunch { + if coordinator.firstLaunch { DispatchQueue.main.async { self.openWindow(id: "onboarding") } @@ -242,18 +253,20 @@ class AppDelegate: NSObject, NSApplicationDelegate { if Defaults[.showOnAllDisplays] { for screen in NSScreen.screens { if windows[screen] == nil { + let viewModel: BoringViewModel = .init(screen: screen.localizedName) let window = BoringNotchWindow( - contentRect: NSRect(x: 0, y: 0, width: sizing.size.opened.width! + 20, height: sizing.size.opened.height! + 30), + contentRect: NSRect(x: 0, y: 0, width: openNotchSize.width + 20, height: openNotchSize.height + 30), styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], backing: .buffered, defer: false ) window.contentView = NSHostingView( - rootView: ContentView(batteryModel: .init(vm: self.vm)) - .environmentObject(vm) - .environmentObject(MusicManager(vm: vm)!) + rootView: ContentView(batteryModel: .init(vm: viewModel)) + .environmentObject(viewModel) + .environmentObject(MusicManager(vm: viewModel)!) ) windows[screen] = window + viewModels[screen] = viewModel window.orderFrontRegardless() NotchSpaceManager.shared.notchSpace.windows.insert(window) } @@ -264,14 +277,19 @@ class AppDelegate: NSObject, NSApplicationDelegate { window.alphaValue = 1 } } + if let viewModel = viewModels[screen] { + if viewModel.notchState == .closed { + viewModel.close() + } + } } } else { - if !NSScreen.screens.contains(where: {$0.localizedName == vm.preferredScreen}) { - vm.selectedScreen = NSScreen.main?.localizedName ?? "Unknown" + if !NSScreen.screens.contains(where: {$0.localizedName == coordinator.preferredScreen}) { + coordinator.selectedScreen = NSScreen.main?.localizedName ?? "Unknown" } - let selectedScreen = NSScreen.screens.first(where: {$0.localizedName == vm.selectedScreen}) - closedNotchSize = setNotchSize(screen: selectedScreen?.localizedName) + let selectedScreen = NSScreen.screens.first(where: {$0.localizedName == coordinator.selectedScreen}) + vm.notchSize = getClosedNotchSize(screen: selectedScreen?.localizedName) if let screenFrame = selectedScreen { window.alphaValue = changeAlpha ? 0 : 1 @@ -282,8 +300,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { self!.window.alphaValue = 1 } } - // TODO: optimize animation here, especially for custom height slider - // TODO: don't animate if the window is changing screens if vm.notchState == .closed { vm.close() } diff --git a/boringNotch/components/BoringSystemTiles.swift b/boringNotch/components/BoringSystemTiles.swift index 873ef66..f9d859b 100644 --- a/boringNotch/components/BoringSystemTiles.swift +++ b/boringNotch/components/BoringSystemTiles.swift @@ -69,6 +69,7 @@ func logout() { struct BoringSystemTiles: View { @EnvironmentObject var vm: BoringViewModel + @ObservedObject var coordinator = BoringViewCoordinator.shared struct ItemButton { var icon: String @@ -87,10 +88,10 @@ struct BoringSystemTiles: View { // }, label: "💡 Keyboard Backlight") } GridRow { - SystemItemButton(icon: vm.currentMicStatus ? "mic" : "mic.slash", onTap: { - vm.toggleMic() + SystemItemButton(icon: coordinator.currentMicStatus ? "mic" : "mic.slash", onTap: { + coordinator.toggleMic() vm.close() - }, label: "Toggle Microphone", showEmojis: Defaults[.showEmojis], emoji: vm.currentMicStatus ? "😀" : "đŸ€«") + }, label: "Toggle Microphone", showEmojis: Defaults[.showEmojis], emoji: coordinator.currentMicStatus ? "😀" : "đŸ€«") // SystemItemButton(icon: "lock", onTap: { // logout() // }, label: "🔒 Lock My Device") diff --git a/boringNotch/components/Live activities/InlineHUD.swift b/boringNotch/components/Live activities/InlineHUD.swift index edbe911..19f9200 100644 --- a/boringNotch/components/Live activities/InlineHUD.swift +++ b/boringNotch/components/Live activities/InlineHUD.swift @@ -60,11 +60,11 @@ struct InlineHUD: View { .allowsTightening(true) .contentTransition(.numericText()) } - .frame(width: 100 - (hoverAnimation ? 0 : 12) + gestureProgress / 2, height: vm.sizes.size.closed.height! - (hoverAnimation ? 0 : 12), alignment: .leading) + .frame(width: 100 - (hoverAnimation ? 0 : 12) + gestureProgress / 2, height: vm.notchSize.height - (hoverAnimation ? 0 : 12), alignment: .leading) Rectangle() .fill(.black) - .frame(width: vm.sizes.size.closed.width! - 20) + .frame(width: vm.closedNotchSize.width - 20) HStack { if (type == .mic) { @@ -91,9 +91,9 @@ struct InlineHUD: View { } } .padding(.trailing, 4) - .frame(width: 100 - (hoverAnimation ? 0 : 12) + gestureProgress / 2, height: vm.sizes.size.closed.height! - (hoverAnimation ? 0 : 12), alignment: .center) + .frame(width: 100 - (hoverAnimation ? 0 : 12) + gestureProgress / 2, height: vm.closedNotchSize.height - (hoverAnimation ? 0 : 12), alignment: .center) } - .frame(height: Sizes().size.closed.height! + (hoverAnimation ? 8 : 0), alignment: .center) + .frame(height: vm.closedNotchSize.height + (hoverAnimation ? 8 : 0), alignment: .center) } func SpeakerSymbol(_ value: CGFloat) -> String { diff --git a/boringNotch/components/Live activities/LiveActivityModifier.swift b/boringNotch/components/Live activities/LiveActivityModifier.swift index 8df1c1a..e10fc0c 100644 --- a/boringNotch/components/Live activities/LiveActivityModifier.swift +++ b/boringNotch/components/Live activities/LiveActivityModifier.swift @@ -24,7 +24,7 @@ struct LiveActivityModifier: ViewModifier { HStack { leftContent() Spacer() - .frame(minWidth: Sizes().size.closed.width) + //.frame(minWidth: vm.closedNotchSize.width) rightContent() } .padding() diff --git a/boringNotch/components/Notch/BoringHeader.swift b/boringNotch/components/Notch/BoringHeader.swift index 7989fac..325bb87 100644 --- a/boringNotch/components/Notch/BoringHeader.swift +++ b/boringNotch/components/Notch/BoringHeader.swift @@ -11,11 +11,12 @@ import Defaults struct BoringHeader: View { @EnvironmentObject var vm: BoringViewModel @EnvironmentObject var batteryModel: BatteryStatusViewModel + @ObservedObject var coordinator = BoringViewCoordinator.shared @StateObject var tvm = TrayDrop.shared var body: some View { HStack(spacing: 0) { HStack { - if (!tvm.isEmpty || vm.alwaysShowTabs) && Defaults[.boringShelf] { + if (!tvm.isEmpty || coordinator.alwaysShowTabs) && Defaults[.boringShelf] { TabSelectionView() } else if vm.notchState == .open { EmptyView() @@ -30,8 +31,8 @@ struct BoringHeader: View { if vm.notchState == .open { Rectangle() .fill(NSScreen.screens - .first(where: {$0.localizedName == vm.selectedScreen})?.safeAreaInsets.top ?? 0 > 0 ? .black : .clear) - .frame(width: Sizes().size.closed.width! - 5) + .first(where: {$0.localizedName == coordinator.selectedScreen})?.safeAreaInsets.top ?? 0 > 0 ? .black : .clear) + .frame(width: vm.closedNotchSize.width - 5) .mask { NotchShape() } diff --git a/boringNotch/components/Notch/NotchContentView.swift b/boringNotch/components/Notch/NotchContentView.swift index 94efaf6..b4370ed 100644 --- a/boringNotch/components/Notch/NotchContentView.swift +++ b/boringNotch/components/Notch/NotchContentView.swift @@ -13,14 +13,15 @@ struct NotchContentView: View { @EnvironmentObject var musicManager: MusicManager @EnvironmentObject var batteryModel: BatteryStatusViewModel @ObservedObject var webcamManager: WebcamManager + @ObservedObject var coordinator = BoringViewCoordinator.shared var body: some View { - VStack(alignment: vm.firstLaunch ? .center : .leading, spacing: 0) { + VStack(alignment: coordinator.firstLaunch ? .center : .leading, spacing: 0) { if vm.notchState == .open { BoringHeader() .animation(.spring(response: 0.7, dampingFraction: 0.8, blendDuration: 0.8), value: vm.notchState) .padding(.top, 10) - if vm.firstLaunch { + if coordinator.firstLaunch { Spacer() HelloAnimation().frame(width: 180, height: 60).onAppear(perform: { vm.closeHello() @@ -29,7 +30,7 @@ struct NotchContentView: View { } } - if !vm.firstLaunch { + if !coordinator.firstLaunch { HStack(spacing: 14) { if vm.notchState == .closed && vm.expandingView.show { if(vm.expandingView.type == .battery){ @@ -45,17 +46,17 @@ struct NotchContentView: View { } } if !vm.expandingView.show { - if(vm.notchState == .closed || vm.currentView == .home){ + if(vm.notchState == .closed || coordinator.currentView == .home){ HStack (spacing: 6){ ZStack { Image(nsImage: musicManager.albumArt) .resizable() .aspectRatio(contentMode: .fill) .frame( - width: vm.notchState == .open ? vm.musicPlayerSizes.image.size.opened.width : vm.musicPlayerSizes.image.size.closed.width, - height: vm.notchState == .open ? vm.musicPlayerSizes.image.size.opened.height : vm.musicPlayerSizes.image.size.closed.height + width: vm.notchState == .open ? MusicPlayerImageSizes.size.opened.width : MusicPlayerImageSizes.size.closed.width, + height: vm.notchState == .open ? MusicPlayerImageSizes.size.opened.height : MusicPlayerImageSizes.size.closed.height ) - .cornerRadius(vm.notchState == .open ? vm.musicPlayerSizes.image.cornerRadius.opened.inset! : vm.musicPlayerSizes.image.cornerRadius.closed.inset!) + .cornerRadius(vm.notchState == .open ? MusicPlayerImageSizes.cornerRadiusInset.opened : MusicPlayerImageSizes.cornerRadiusInset.closed) .scaledToFit() .padding(.leading, vm.notchState == .open ? 0 : 3) if vm.notchState == .open { @@ -72,7 +73,7 @@ struct NotchContentView: View { } } if vm.notchState == .open { - switch vm.currentView { + switch coordinator.currentView { case .home: EmptyView() @@ -123,13 +124,13 @@ struct NotchContentView: View { func calculateFrameWidthforNotchContent() -> CGFloat? { // Calculate intermediate values let chargingInfoWidth: CGFloat = vm.expandingView.show ? ((vm.expandingView.type == .download ? downloadSneakSize.width : batterySneakSize.width) + 10) : 0 - let musicPlayingWidth: CGFloat = (!vm.firstLaunch && !vm.expandingView.show && (musicManager.isPlaying || (musicManager.isPlayerIdle ? Defaults[.showNotHumanFace] : true))) ? 60 : -15 + let musicPlayingWidth: CGFloat = (!coordinator.firstLaunch && !vm.expandingView.show && (musicManager.isPlaying || (musicManager.isPlayerIdle ? Defaults[.showNotHumanFace] : true))) ? 60 : -15 - let closedWidth: CGFloat = vm.sizes.size.closed.width! - 5 + let closedWidth: CGFloat = vm.closedNotchSize.width - 5 let dynamicWidth: CGFloat = chargingInfoWidth + musicPlayingWidth + closedWidth // Return the appropriate width based on the notch state - return vm.notchState == .open ? vm.musicPlayerSizes.player.size.opened.width! + 30 : dynamicWidth + (vm.sneakPeek.show ? -12 : 0) + return vm.notchState == .open ? playerWidth + 30 : dynamicWidth + (coordinator.sneakPeek.show ? -12 : 0) } diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 40042a4..f25c6e7 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -14,6 +14,7 @@ struct NotchHomeView: View { @EnvironmentObject var musicManager: MusicManager @EnvironmentObject var batteryModel: BatteryStatusViewModel @EnvironmentObject var webcamManager: WebcamManager + @ObservedObject var coordinator = BoringViewCoordinator.shared @State private var sliderValue: Double = 0 @State private var dragging: Bool = false @@ -22,7 +23,7 @@ struct NotchHomeView: View { let albumArtNamespace: Namespace.ID var body: some View { - if !vm.firstLaunch { + if !coordinator.firstLaunch { HStack(alignment: .top, spacing: 30) { HStack { ZStack(alignment: .bottomTrailing) { @@ -35,7 +36,7 @@ struct NotchHomeView: View { .aspectRatio(contentMode: .fill) ) .clipped() - .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? vm.musicPlayerSizes.image.cornerRadius.opened.inset! : vm.musicPlayerSizes.image.cornerRadius.closed.inset!)) + .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? MusicPlayerImageSizes.cornerRadiusInset.opened : MusicPlayerImageSizes.cornerRadiusInset.closed)) .scaleEffect(x: 1.3, y: 2.8) .rotationEffect(.degrees(92)) .blur(radius: 35) @@ -57,7 +58,7 @@ struct NotchHomeView: View { .aspectRatio(contentMode: .fill) ) .clipped() - .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? vm.musicPlayerSizes.image.cornerRadius.opened.inset! : vm.musicPlayerSizes.image.cornerRadius.closed.inset!)) + .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? MusicPlayerImageSizes.cornerRadiusInset.opened : MusicPlayerImageSizes.cornerRadiusInset.closed)) .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) if vm.notchState == .open && !musicManager.usingAppIconForArtwork { diff --git a/boringNotch/components/Notch/NotchShelfView.swift b/boringNotch/components/Notch/NotchShelfView.swift index 27a0378..6bc8a36 100644 --- a/boringNotch/components/Notch/NotchShelfView.swift +++ b/boringNotch/components/Notch/NotchShelfView.swift @@ -20,7 +20,7 @@ struct NotchShelfView: View { } var panel: some View { - RoundedRectangle(cornerRadius: Sizes().cornerRadius.opened.inset! - 8) + RoundedRectangle(cornerRadius: 16) .strokeBorder(style: StrokeStyle(lineWidth: 4, dash: [10])) .foregroundStyle(.white.opacity(0.1)) .overlay { @@ -48,14 +48,14 @@ struct NotchShelfView: View { } } else { ScrollView(.horizontal) { - HStack(spacing: vm.spacing) { + HStack(spacing: spacing) { ForEach(tvm.items) { item in DropItemView(item: item) } } - .padding(vm.spacing) + .padding(spacing) } - .padding(-vm.spacing) + .padding(-spacing) .scrollIndicators(.never) } } diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index c05e3da..ddde67d 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -15,7 +15,6 @@ import AVFoundation import LottieUI struct SettingsView: View { - @EnvironmentObject var vm: BoringViewModel @Environment(\.scenePhase) private var scenePhase @StateObject var extensionManager = BoringExtensionManager() let updaterController: SPUStandardUpdaterController @@ -95,6 +94,7 @@ struct GeneralSettings: View { @State private var screens: [String] = NSScreen.screens.compactMap({$0.localizedName}) let accentColors: [Color] = [.blue, .purple, .pink, .red, .orange, .yellow, .green, .gray] @EnvironmentObject var vm: BoringViewModel + @ObservedObject var coordinator = BoringViewCoordinator.shared @Default(.accentColor) var accentColor @Default(.mirrorShape) var mirrorShape @Default(.showEmojis) var showEmojis @@ -152,7 +152,7 @@ struct GeneralSettings: View { .onChange(of: showOnAllDisplays) { NotificationCenter.default.post(name: Notification.Name.showOnAllDisplaysChanged, object: nil) } - Picker("Show on a specific display", selection: $vm.preferredScreen) { + Picker("Show on a specific display", selection: $coordinator.preferredScreen) { ForEach(screens, id: \.self) { screen in Text(screen) } @@ -251,7 +251,7 @@ struct GeneralSettings: View { Section { Defaults.Toggle("Enable haptics", key: .enableHaptics) Defaults.Toggle("Open notch on hover", key: .openNotchOnHover) - Toggle("Remember last tab", isOn: $vm.openLastTabByDefault) + Toggle("Remember last tab", isOn: $coordinator.openLastTabByDefault) if openNotchOnHover { Slider(value: $minimumHoverDuration, in: 0...1, step: 0.1) { HStack { @@ -370,10 +370,11 @@ struct HUD: View { @EnvironmentObject var vm: BoringViewModel @Default(.inlineHUD) var inlineHUD @Default(.enableGradient) var enableGradient + @ObservedObject var coordinator = BoringViewCoordinator.shared var body: some View { Form { Section { - Toggle("Enable HUD replacement", isOn: $vm.hudReplacement) + Toggle("Enable HUD replacement", isOn: $coordinator.hudReplacement) } header: { Text("General") } @@ -412,14 +413,14 @@ struct HUD: View { } struct Media: View { - @EnvironmentObject var vm: BoringViewModel @Default(.waitInterval) var waitInterval + @ObservedObject var coordinator = BoringViewCoordinator.shared var body: some View { Form { Section { Toggle( "Enable music live activity", - isOn: $vm.showMusicLiveActivityOnClosed.animation() + isOn: $coordinator.showMusicLiveActivityOnClosed.animation() ) Defaults.Toggle("Enable sneak peek", key: .enableSneakPeek) HStack { @@ -679,7 +680,7 @@ struct Extensions: View { } struct Appearance: View { - @EnvironmentObject var vm: BoringViewModel + @ObservedObject var coordinator = BoringViewCoordinator.shared @Default(.mirrorShape) var mirrorShape @Default(.sliderColor) var sliderColor @Default(.useMusicVisualizer) var useMusicVisualizer @@ -696,7 +697,7 @@ struct Appearance: View { var body: some View { Form { Section { - Toggle("Always show tabs", isOn: $vm.alwaysShowTabs) + Toggle("Always show tabs", isOn: $coordinator.alwaysShowTabs) Defaults.Toggle("Settings icon in notch", key: .settingsIconInNotch) Defaults.Toggle("Enable window shadow", key: .enableShadow) Defaults.Toggle("Corner radius scaling", key: .cornerRadiusScaling) diff --git a/boringNotch/components/Shelf/DropItemView.swift b/boringNotch/components/Shelf/DropItemView.swift index 7b3b0e5..f4110e0 100644 --- a/boringNotch/components/Shelf/DropItemView.swift +++ b/boringNotch/components/Shelf/DropItemView.swift @@ -12,6 +12,7 @@ import UniformTypeIdentifiers struct DropItemView: View { let item: TrayDrop.DropItem @EnvironmentObject var vm: BoringViewModel + @ObservedObject var coordinator = BoringViewCoordinator.shared @StateObject var tvm = TrayDrop.shared @State var hover = false @@ -43,11 +44,11 @@ struct DropItemView: View { Circle() .fill(.white) .overlay(Image(systemName: "xmark").foregroundStyle(.black).font(.system(size: 7)).fontWeight(.semibold)) - .frame(width: vm.spacing, height: vm.spacing) + .frame(width: spacing, height: spacing) .opacity(hover ? 1 : 0) // TODO: Use option key pressed to show delete - .scaleEffect(vm.optionKeyPressed ? 1 : 0.5) + .scaleEffect(coordinator.optionKeyPressed ? 1 : 0.5) .transition(.blurReplace.combined(with: .scale)) - .offset(x: vm.spacing / 2, y: -vm.spacing / 2) + .offset(x: spacing / 2, y: -spacing / 2) .onTapGesture { tvm.delete(item.id) } .shadow(color: .black, radius: 3) } diff --git a/boringNotch/components/Tabs/TabSelectionView.swift b/boringNotch/components/Tabs/TabSelectionView.swift index c6eb5da..a71272f 100644 --- a/boringNotch/components/Tabs/TabSelectionView.swift +++ b/boringNotch/components/Tabs/TabSelectionView.swift @@ -20,26 +20,26 @@ let tabs = [ ] struct TabSelectionView: View { - @EnvironmentObject var vm: BoringViewModel + @ObservedObject var coordinator = BoringViewCoordinator.shared @Namespace var animation var body: some View { HStack(spacing: 0) { ForEach(tabs) { tab in - TabButton(label: tab.label, icon: tab.icon, selected: vm.currentView == tab.view) { + TabButton(label: tab.label, icon: tab.icon, selected: coordinator.currentView == tab.view) { withAnimation(.smooth) { - vm.currentView = tab.view + coordinator.currentView = tab.view } } .frame(height: 26) - .foregroundStyle(tab.view == vm.currentView ? .white : .gray) + .foregroundStyle(tab.view == coordinator.currentView ? .white : .gray) .background { - if tab.view == vm.currentView { + if tab.view == coordinator.currentView { Capsule() - .fill(vm.currentView == tab.view ? Color(nsColor: .secondarySystemFill) : Color.clear) + .fill(coordinator.currentView == tab.view ? Color(nsColor: .secondarySystemFill) : Color.clear) .matchedGeometryEffect(id: "capsule", in: animation) } else { Capsule() - .fill(vm.currentView == tab.view ? Color(nsColor: .secondarySystemFill) : Color.clear) + .fill(coordinator.currentView == tab.view ? Color(nsColor: .secondarySystemFill) : Color.clear) .matchedGeometryEffect(id: "capsule", in: animation) .hidden() } diff --git a/boringNotch/components/Webcam/WebcamView.swift b/boringNotch/components/Webcam/WebcamView.swift index 42992cd..53dca73 100644 --- a/boringNotch/components/Webcam/WebcamView.swift +++ b/boringNotch/components/Webcam/WebcamView.swift @@ -19,14 +19,14 @@ struct CameraPreviewView: View { if let previewLayer = webcamManager.previewLayer { CameraPreviewLayerView(previewLayer: previewLayer) .scaleEffect(x: -1, y: 1) - .clipShape(RoundedRectangle(cornerRadius: Defaults[.mirrorShape] == .rectangle ? !Defaults[.cornerRadiusScaling] ? vm.musicPlayerSizes.image.cornerRadius.closed.inset! : vm.musicPlayerSizes.image.cornerRadius.opened.inset! : 100)) + .clipShape(RoundedRectangle(cornerRadius: Defaults[.mirrorShape] == .rectangle ? !Defaults[.cornerRadiusScaling] ? MusicPlayerImageSizes.cornerRadiusInset.closed : MusicPlayerImageSizes.cornerRadiusInset.opened : 100)) .frame(width: geometry.size.width, height: geometry.size.width) .opacity(webcamManager.isSessionRunning ? 1 : 0) } if !webcamManager.isSessionRunning { ZStack { - RoundedRectangle(cornerRadius: Defaults[.mirrorShape] == .rectangle ? !Defaults[.cornerRadiusScaling] ? vm.musicPlayerSizes.image.cornerRadius.closed.inset! : 12 : 100) + RoundedRectangle(cornerRadius: Defaults[.mirrorShape] == .rectangle ? !Defaults[.cornerRadiusScaling] ? MusicPlayerImageSizes.cornerRadiusInset.closed : 12 : 100) .fill(Color(red: 20/255, green: 20/255, blue: 20/255)) .strokeBorder(.white.opacity(0.04), lineWidth: 1) .frame(width: geometry.size.width, height: geometry.size.width) diff --git a/boringNotch/extensions/Button+Bouncing.swift b/boringNotch/extensions/Button+Bouncing.swift index 2780b57..6b4d918 100644 --- a/boringNotch/extensions/Button+Bouncing.swift +++ b/boringNotch/extensions/Button+Bouncing.swift @@ -15,7 +15,7 @@ struct BouncingButtonStyle: ButtonStyle { configuration.label .padding(12) .background( - RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? Sizes().cornerRadius.opened.inset! - 14 : vm.musicPlayerSizes.image.cornerRadius.closed.inset!) + RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? 10 : MusicPlayerImageSizes.cornerRadiusInset.closed) .fill(Color(red: 20/255, green: 20/255, blue: 20/255)) .strokeBorder(.white.opacity(0.04), lineWidth: 1) ) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 9f9998d..57899fd 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -40,6 +40,7 @@ class MusicManager: ObservableObject { @Published var timestampDate: Date = Date() @Published var playbackRate: Double = 0 @ObservedObject var detector: FullscreenMediaDetector + @ObservedObject var coordinator = BoringViewCoordinator.shared @Published var usingAppIconForArtwork: Bool = false var nowPlaying: NowPlaying @@ -241,7 +242,7 @@ class MusicManager: ObservableObject { private func updateSneakPeek() { if self.isPlaying && Defaults[.enableSneakPeek] && !self.detector.currentAppInFullScreen { - self.vm.toggleSneakPeek(status: true, type: SneakContentType.music) + coordinator.toggleSneakPeek(status: true, type: SneakContentType.music) } } diff --git a/boringNotch/models/BatteryStatusViewModel.swift b/boringNotch/models/BatteryStatusViewModel.swift index 8d92ec9..e90896a 100644 --- a/boringNotch/models/BatteryStatusViewModel.swift +++ b/boringNotch/models/BatteryStatusViewModel.swift @@ -13,6 +13,7 @@ import Defaults class BatteryStatusViewModel: ObservableObject { private var vm: BoringViewModel + @ObservedObject var coordinator = BoringViewCoordinator.shared @Published var batteryPercentage: Float = 0.0 @Published var isPluggedIn: Bool = false @Published var showChargingInfo: Bool = false @@ -46,7 +47,7 @@ class BatteryStatusViewModel: ObservableObject { } if (isCharging && !self.isPluggedIn) { - DispatchQueue.main.asyncAfter(deadline: .now() + (vm.firstLaunch ? 6 : 0)) { + DispatchQueue.main.asyncAfter(deadline: .now() + (coordinator.firstLaunch ? 6 : 0)) { self.vm.toggleExpandingView(status: true, type: .battery) self.showChargingInfo = true self.isPluggedIn = true diff --git a/boringNotch/models/Constants.swift b/boringNotch/models/Constants.swift index 2930ca4..619ed34 100644 --- a/boringNotch/models/Constants.swift +++ b/boringNotch/models/Constants.swift @@ -8,6 +8,16 @@ import SwiftUI import Defaults +private let availableDirectories = FileManager + .default + .urls(for: .documentDirectory, in: .userDomainMask) +let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! +let bundleIdentifier = Bundle.main.bundleIdentifier! +let appVersion = "\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "") (\(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? ""))" + +let temporaryDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! +let spacing: CGFloat = 16 + struct CustomVisualizer: Codable, Hashable, Equatable, Defaults.Serializable { let UUID: UUID var name: String diff --git a/boringNotch/sizing/matters.swift b/boringNotch/sizing/matters.swift index 2bce9eb..c33341a 100644 --- a/boringNotch/sizing/matters.swift +++ b/boringNotch/sizing/matters.swift @@ -9,12 +9,20 @@ import SwiftUI import Foundation import Defaults -var closedNotchSize: CGSize = setNotchSize() +let playerWidth: CGFloat = 440 -var downloadSneakSize: CGSize = .init(width: 65, height: 1) -var batterySneakSize: CGSize = .init(width: 160, height: 1) +let downloadSneakSize: CGSize = .init(width: 65, height: 1) +let batterySneakSize: CGSize = .init(width: 160, height: 1) -func setNotchSize(screen: String? = nil) -> CGSize { +let openNotchSize: CGSize = .init(width: 580, height: 175) +let cornerRadiusInsets: (opened: CGFloat, closed: CGFloat) = (opened: 24, closed: 10) + +struct MusicPlayerImageSizes { + static let cornerRadiusInset: (opened: CGFloat, closed: CGFloat) = (opened: 13.0, closed: 4.0) + static let size = (opened: CGSize(width: 90, height: 90), closed: CGSize(width: 20, height: 20)) +} + +func getClosedNotchSize(screen: String? = nil) -> CGSize { // Default notch size, to avoid using optionals var notchHeight: CGFloat = Defaults[.nonNotchHeight] var notchWidth: CGFloat = 185 @@ -49,40 +57,3 @@ func setNotchSize(screen: String? = nil) -> CGSize { return .init(width: notchWidth, height: notchHeight) } - -struct Area { - var width: CGFloat? - var height: CGFloat? - var inset: CGFloat? -} - -struct StatesSizes { - var opened: Area - var closed: Area -} - -struct Sizes { - var cornerRadius: StatesSizes = StatesSizes(opened: Area(inset: 24), closed: Area(inset: 10)) - var size: StatesSizes = StatesSizes( - opened: Area(width: 580, height: 175), - closed: Area(width: closedNotchSize.width, height: closedNotchSize.height) - ) -} - -struct MusicPlayerElementSizes { - - var baseSize: Sizes = Sizes() - - var image: Sizes = Sizes( - cornerRadius: StatesSizes( - opened: Area(inset: 13), closed: Area(inset: 4)), - size: StatesSizes( - opened: Area(width: 90, height: 90), closed: Area(width: 20, height: 20) - ) - ) - var player: Sizes = Sizes( - size: StatesSizes( - opened: Area(width: 440), closed: Area(width: closedNotchSize.width) - ) - ) -} From a64982d97c0fe95eeef0342e7bb6004360499f40 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 20 Nov 2024 20:55:58 -0500 Subject: [PATCH 19/74] Move BoringViewCoordinator into folder --- boringNotch.xcodeproj/project.pbxproj | 2 +- .../BoringViewCoordinator.swift | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename BoringViewCoordinator.swift => boringNotch/BoringViewCoordinator.swift (100%) diff --git a/boringNotch.xcodeproj/project.pbxproj b/boringNotch.xcodeproj/project.pbxproj index 0c57967..ce0a1f6 100644 --- a/boringNotch.xcodeproj/project.pbxproj +++ b/boringNotch.xcodeproj/project.pbxproj @@ -328,7 +328,6 @@ 14CEF4092C5CAED200855D72 = { isa = PBXGroup; children = ( - 11CC44A12CEE614100C7244B /* BoringViewCoordinator.swift */, 14CEF4142C5CAED300855D72 /* boringNotch */, 14CEF4132C5CAED300855D72 /* Products */, 14D031EC2C689DB70096E6A1 /* Frameworks */, @@ -367,6 +366,7 @@ 14CEF41E2C5CAED400855D72 /* boringNotch.entitlements */, 14CEF41B2C5CAED400855D72 /* Preview Content */, 112FB72F2CCF12CC0015238C /* private */, + 11CC44A12CEE614100C7244B /* BoringViewCoordinator.swift */, ); path = boringNotch; sourceTree = ""; diff --git a/BoringViewCoordinator.swift b/boringNotch/BoringViewCoordinator.swift similarity index 100% rename from BoringViewCoordinator.swift rename to boringNotch/BoringViewCoordinator.swift From f15cbb0075bb671d5373e1c18757b4a203af58d9 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Thu, 21 Nov 2024 02:19:12 -0500 Subject: [PATCH 20/74] Add the option to customize the size of the notch window on notched screens (seperate control from non-notched displays) --- boringNotch/Localizable.xcstrings | 11 +++-- .../components/Settings/SettingsView.swift | 44 ++++++++++++++++--- boringNotch/enums/generic.swift | 2 +- boringNotch/models/Constants.swift | 11 +++-- boringNotch/sizing/matters.swift | 8 +++- 5 files changed, 61 insertions(+), 15 deletions(-) diff --git a/boringNotch/Localizable.xcstrings b/boringNotch/Localizable.xcstrings index 61f5ef5..b715c63 100644 --- a/boringNotch/Localizable.xcstrings +++ b/boringNotch/Localizable.xcstrings @@ -547,13 +547,16 @@ "No extension installed" : { }, - "Non-notch displays" : { + "Non-notch display height" : { }, - "Non-notch screen height" : { + "Notch behavior" : { }, - "Notch behavior" : { + "Notch display height" : { + + }, + "Notch Height" : { }, "OK" : { @@ -836,4 +839,4 @@ } }, "version" : "1.0" -} \ No newline at end of file +} diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index ddde67d..a9bb129 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -103,6 +103,9 @@ struct GeneralSettings: View { //@State var nonNotchHeightMode: NonNotchHeightMode = .matchRealNotchSize @Default(.nonNotchHeight) var nonNotchHeight @Default(.nonNotchHeightMode) var nonNotchHeightMode + @Default(.notchHeight) var notchHeight + @Default(.notchHeightMode) var notchHeightMode + @Default(.showOnAllDisplays) var showOnAllDisplays @Default(.enableGestures) var enableGestures @Default(.openNotchOnHover) var openNotchOnHover @@ -165,13 +168,44 @@ struct GeneralSettings: View { } Section { - Picker("Non-notch screen height", selection: $nonNotchHeightMode) { + Picker(selection: $notchHeightMode, label: + HStack { + Text("Notch display height") + customBadge(text: "Beta") + }) { + Text("Match real notch size") + .tag(WindowHeightMode.matchRealNotchSize) + Text("Match menubar height") + .tag(WindowHeightMode.matchMenuBar) + Text("Custom height") + .tag(WindowHeightMode.custom) + } + .onChange(of: notchHeightMode) { + switch notchHeightMode { + case .matchRealNotchSize: + notchHeight = 38 + case .matchMenuBar: + notchHeight = 44 + case .custom: + nonNotchHeight = 38 + } + NotificationCenter.default.post(name: Notification.Name.notchHeightChanged, object: nil) + } + if notchHeightMode == .custom { + Slider(value: $nonNotchHeight, in: 15...45, step: 1) { + Text("Custom notch size - \(nonNotchHeight, specifier: "%.0f")") + } + .onChange(of: nonNotchHeight) { + NotificationCenter.default.post(name: Notification.Name.notchHeightChanged, object: nil) + } + } + Picker("Non-notch display height", selection: $nonNotchHeightMode) { Text("Match menubar height") - .tag(NonNotchHeightMode.matchMenuBar) + .tag(WindowHeightMode.matchMenuBar) Text("Match real notch size") - .tag(NonNotchHeightMode.matchRealNotchSize) + .tag(WindowHeightMode.matchRealNotchSize) Text("Custom height") - .tag(NonNotchHeightMode.custom) + .tag(WindowHeightMode.custom) } .onChange(of: nonNotchHeightMode) { switch nonNotchHeightMode { @@ -193,7 +227,7 @@ struct GeneralSettings: View { } } } header: { - Text("Non-notch displays") + Text("Notch Height") } NotchBehaviour() diff --git a/boringNotch/enums/generic.swift b/boringNotch/enums/generic.swift index d595db1..e70b459 100644 --- a/boringNotch/enums/generic.swift +++ b/boringNotch/enums/generic.swift @@ -56,7 +56,7 @@ enum MirrorShapeEnum: String, Defaults.Serializable { case circle = "Circular" } -enum NonNotchHeightMode: String, Defaults.Serializable { +enum WindowHeightMode: String, Defaults.Serializable { case matchMenuBar = "Match menubar height" case matchRealNotchSize = "Match real notch height" case custom = "Custom height" diff --git a/boringNotch/models/Constants.swift b/boringNotch/models/Constants.swift index 619ed34..77b7e2a 100644 --- a/boringNotch/models/Constants.swift +++ b/boringNotch/models/Constants.swift @@ -35,11 +35,16 @@ extension Defaults.Keys { static let minimumHoverDuration = Key("minimumHoverDuration", default: 0.3) static let enableHaptics = Key("enableHaptics", default: true) static let openNotchOnHover = Key("openNotchOnHover", default: true) - static let nonNotchHeightMode = Key( + static let notchHeightMode = Key( + "notchHeightMode", + default: WindowHeightMode.matchRealNotchSize + ) + static let nonNotchHeightMode = Key( "nonNotchHeightMode", - default: NonNotchHeightMode.matchRealNotchSize + default: WindowHeightMode.matchRealNotchSize ) - static let nonNotchHeight = Key("nonNotchHeight", default: 32) + static let nonNotchHeight = Key("notchHeight", default: 32) + static let notchHeight = Key("notchHeight", default: 32) //static let openLastTabByDefault = Key("openLastTabByDefault", default: false) // MARK: Appearance diff --git a/boringNotch/sizing/matters.swift b/boringNotch/sizing/matters.swift index c33341a..3e9989a 100644 --- a/boringNotch/sizing/matters.swift +++ b/boringNotch/sizing/matters.swift @@ -49,8 +49,12 @@ func getClosedNotchSize(screen: String? = nil) -> CGSize { // Check if the Mac has a notch if screen.safeAreaInsets.top > 0 { - print("has notch") - notchHeight = screen.safeAreaInsets.top + notchHeight = Defaults[.notchHeight] + if Defaults[.notchHeightMode] == .matchRealNotchSize { + notchHeight = screen.safeAreaInsets.top + } else if Defaults[.notchHeightMode] == .matchMenuBar { + notchHeight = screen.frame.maxY - screen.visibleFrame.maxY + } } print("height", notchHeight) } From f993014ddd9684dae1bbf884b7b05a25e7aacf50 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:25:03 -0500 Subject: [PATCH 21/74] settings resize changes --- boringNotch/components/Settings/SettingsView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index a9bb129..ec62304 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -70,7 +70,6 @@ struct SettingsView: View { .tint(Defaults[.accentColor]) } detail: { GeneralSettings() - .navigationSplitViewColumnWidth(500) } .environmentObject(extensionManager) .formStyle(.grouped) @@ -86,6 +85,7 @@ struct SettingsView: View { } .introspect(.window, on: .macOS(.v14, .v15)) { window in window.toolbarStyle = .unified + window.styleMask.update(with: .resizable) } } } From 6c1580b28bd1398e2765fa37dde4979ae356a523 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:30:49 -0500 Subject: [PATCH 22/74] add opactity and blur to NotchHomeView --- boringNotch/components/Notch/NotchHomeView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index f25c6e7..05a57cf 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -164,6 +164,7 @@ struct NotchHomeView: View { // Stop the timer when the view disappears timer?.cancel() } + .transition(.opacity.combined(with: .blurReplace)) } } From d0614555c590364efef2aa219121980ad8a921e9 Mon Sep 17 00:00:00 2001 From: Harsh Vardhan Goswami Date: Wed, 18 Dec 2024 20:13:21 +0530 Subject: [PATCH 23/74] =?UTF-8?q?=F0=9F=A6=A6=20Dr.=20OtterAI=20Code=20Rev?= =?UTF-8?q?iew,=20PhD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/code-review.yml | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/code-review.yml diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml new file mode 100644 index 0000000..965bdb9 --- /dev/null +++ b/.github/workflows/code-review.yml @@ -0,0 +1,40 @@ +name: 🩩 Dr. OtterAI Code Review, PhD + +on: + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - '**.md' + - 'LICENSE' + - '.gitignore' + +jobs: + code-review: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + actions: write + issues: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required to get full history for better context + + - name: 🩩 Dr. OtterAI Code Review, PhD + uses: fofsinx/OtterAI@1.1.1 + + with: + openai_api_key: ${{ secrets.OPENAI_API_KEY }} + openai_base_url: ${{ secrets.OPENAI_BASE_URL }} + github_token: ${{ secrets.GITHUB_TOKEN }} + model: 'gpt-4o-mini' + extra_prompt: | + Please focus on: + - Swift best practices + - Security implications of the changes + - Performance optimizations + - Code maintainability and documentation + - Potential edge cases and error handling + - Use emojis in your comments and be very specific. + - Add code examples in markdown format. \ No newline at end of file From a242d6a4d98d5abfa430e2b0750add4891c8da39 Mon Sep 17 00:00:00 2001 From: Harsh Vardhan Goswami Date: Wed, 18 Dec 2024 20:15:58 +0530 Subject: [PATCH 24/74] Fix syntax error --- .github/workflows/code-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 965bdb9..3b68dd1 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -21,7 +21,7 @@ jobs: with: fetch-depth: 0 # Required to get full history for better context - - name: 🩩 Dr. OtterAI Code Review, PhD + - name: 🩩 Dr. OtterAI Code Review, PhD uses: fofsinx/OtterAI@1.1.1 with: From 18a6cb4a541a555b01d22988a488ef97c2d43491 Mon Sep 17 00:00:00 2001 From: Harsh Vardhan Goswami Date: Wed, 18 Dec 2024 20:28:09 +0530 Subject: [PATCH 25/74] Issue here remove for now --- .github/workflows/code-review.yml | 40 ------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 .github/workflows/code-review.yml diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml deleted file mode 100644 index 3b68dd1..0000000 --- a/.github/workflows/code-review.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: 🩩 Dr. OtterAI Code Review, PhD - -on: - pull_request: - types: [opened, synchronize, reopened] - paths-ignore: - - '**.md' - - 'LICENSE' - - '.gitignore' - -jobs: - code-review: - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - actions: write - issues: write - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Required to get full history for better context - - - name: 🩩 Dr. OtterAI Code Review, PhD - uses: fofsinx/OtterAI@1.1.1 - - with: - openai_api_key: ${{ secrets.OPENAI_API_KEY }} - openai_base_url: ${{ secrets.OPENAI_BASE_URL }} - github_token: ${{ secrets.GITHUB_TOKEN }} - model: 'gpt-4o-mini' - extra_prompt: | - Please focus on: - - Swift best practices - - Security implications of the changes - - Performance optimizations - - Code maintainability and documentation - - Potential edge cases and error handling - - Use emojis in your comments and be very specific. - - Add code examples in markdown format. \ No newline at end of file From fe5cb1fd3bbb267d23a1d3d7879060e35665959f Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Fri, 22 Nov 2024 12:10:55 +0530 Subject: [PATCH 26/74] Added Blur Effect to Text when expanding Notch --- .../components/Notch/NotchHomeView.swift | 82 ++++++++++--------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 05a57cf..6fb5568 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -74,48 +74,54 @@ struct NotchHomeView: View { .buttonStyle(PlainButtonStyle()) } - VStack(alignment: .leading) { - GeometryReader { geo in - VStack(alignment: .leading, spacing: 4){ - MarqueeText($musicManager.songTitle, font: .headline, nsFont: .headline, textColor: .white, frameWidth: geo.size.width) - MarqueeText( - $musicManager.artistName, - font: .headline, - nsFont: .headline, - textColor: Defaults[.playerColorTinting] ? Color(nsColor: musicManager.avgColor) - .ensureMinimumBrightness(factor: 0.6) : .gray, - frameWidth: geo.size.width - ) - .fontWeight(.medium) - - MusicSliderView(sliderValue: $sliderValue, - duration: $musicManager.songDuration, - lastDragged: $lastDragged, - color: musicManager.avgColor, - dragging: $dragging) { newValue in - musicManager.seekTrack(to: newValue) + Group { + if vm.notchState == .open { + VStack(alignment: .leading) { + GeometryReader { geo in + VStack(alignment: .leading, spacing: 4){ + MarqueeText($musicManager.songTitle, font: .headline, nsFont: .headline, textColor: .white, frameWidth: geo.size.width) + MarqueeText( + $musicManager.artistName, + font: .headline, + nsFont: .headline, + textColor: Defaults[.playerColorTinting] ? Color(nsColor: musicManager.avgColor) + .ensureMinimumBrightness(factor: 0.6) : .gray, + frameWidth: geo.size.width + ) + .fontWeight(.medium) + + MusicSliderView(sliderValue: $sliderValue, + duration: $musicManager.songDuration, + lastDragged: $lastDragged, + color: musicManager.avgColor, + dragging: $dragging) { newValue in + musicManager.seekTrack(to: newValue) + } + .padding(.top, 5) + .frame(height: 36) + } } - .padding(.top, 5) - .frame(height: 36) - } - } - .padding(.top, 10) - .padding(.leading, 5) - HStack(spacing: 8) { - HoverButton(icon: "backward.fill") { - musicManager.previousTrack() - } - HoverButton(icon: musicManager.isPlaying ? "pause.fill" : "play.fill") { - musicManager.togglePlayPause() - } - HoverButton(icon: "forward.fill") { - musicManager.nextTrack() + .padding(.top, 10) + .padding(.leading, 5) + HStack(spacing: 8) { + HoverButton(icon: "backward.fill") { + musicManager.previousTrack() + } + HoverButton(icon: musicManager.isPlaying ? "pause.fill" : "play.fill") { + print("tapped") + musicManager.togglePlayPause() + } + HoverButton(icon: "forward.fill") { + musicManager.nextTrack() + } + } + .frame(maxWidth: .infinity, alignment: .center) } + .buttonStyle(PlainButtonStyle()) + .frame(minWidth: 170) } - .frame(maxWidth: .infinity, alignment: .center) } - .buttonStyle(PlainButtonStyle()) - .frame(minWidth: 170) + .transition(.blurReplace) } if Defaults[.showCalendar] { From c8b942c246297af67923cf5fdce19f47d3cf2d28 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Fri, 22 Nov 2024 12:13:34 +0530 Subject: [PATCH 27/74] Added Matched Geometry Effect to Spectrum --- boringNotch/ContentView.swift | 1 + boringNotch/components/Notch/NotchHomeView.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/boringNotch/ContentView.swift b/boringNotch/ContentView.swift index 3546959..596e467 100644 --- a/boringNotch/ContentView.swift +++ b/boringNotch/ContentView.swift @@ -303,6 +303,7 @@ struct ContentView: View { ) .frame(width: 16, height: 12) } + .matchedGeometryEffect(id: "spectrum", in: albumArtNamespace) } else { LottieAnimationView() .frame(maxWidth: .infinity, maxHeight: .infinity) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 6fb5568..a423906 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -155,6 +155,7 @@ struct NotchHomeView: View { .frame(width: 16, height: 12) } .frame(width: 50, alignment: .center) + .matchedGeometryEffect(id: "spectrum", in: albumArtNamespace) } // BoringSystemTiles() // .transition(.blurReplace.animation(.spring(.bouncy(duration: 0.3)).delay(0.1))) From 4a570ed8269257afedec91f0f3fd200efc8417f4 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Fri, 22 Nov 2024 12:44:50 +0530 Subject: [PATCH 28/74] Added Blur Effect to Notch Opening All elements inside the notch are now animated with an elegant blur effect that tries to imitate Apple's implementation as much as possible. --- .../components/Notch/NotchHomeView.swift | 242 +++++++++--------- 1 file changed, 122 insertions(+), 120 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index a423906..3c4a75f 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -23,33 +23,12 @@ struct NotchHomeView: View { let albumArtNamespace: Namespace.ID var body: some View { - if !coordinator.firstLaunch { - HStack(alignment: .top, spacing: 30) { - HStack { - ZStack(alignment: .bottomTrailing) { - if Defaults[.lightingEffect] { - Color.clear - .aspectRatio(1, contentMode: .fit) - .background( - Image(nsImage: musicManager.albumArt) - .resizable() - .aspectRatio(contentMode: .fill) - ) - .clipped() - .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? MusicPlayerImageSizes.cornerRadiusInset.opened : MusicPlayerImageSizes.cornerRadiusInset.closed)) - .scaleEffect(x: 1.3, y: 2.8) - .rotationEffect(.degrees(92)) - .blur(radius: 35) - .opacity(min(0.6, 1 - max(musicManager.albumArt.getBrightness(), 0.3))) - .onAppear { - print(musicManager.albumArt.getBrightness()) - } - } - - Button { - musicManager.openMusicApp() - } label: { - ZStack(alignment: .bottomTrailing) { + Group { + if !vm.firstLaunch { + HStack(alignment: .top, spacing: 30) { + HStack { + ZStack(alignment: .bottomTrailing) { + if Defaults[.lightingEffect] { Color.clear .aspectRatio(1, contentMode: .fit) .background( @@ -58,121 +37,144 @@ struct NotchHomeView: View { .aspectRatio(contentMode: .fill) ) .clipped() - .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? MusicPlayerImageSizes.cornerRadiusInset.opened : MusicPlayerImageSizes.cornerRadiusInset.closed)) - .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) - - if vm.notchState == .open && !musicManager.usingAppIconForArtwork { - AppIcon(for: musicManager.bundleIdentifier ?? "com.apple.Music") - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: 30, height: 30) - .offset(x: 10, y: 10) - .transition(.scale.combined(with: .opacity).animation(.bouncy.delay(0.3))) - } + .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? vm.musicPlayerSizes.image.cornerRadius.opened.inset! : vm.musicPlayerSizes.image.cornerRadius.closed.inset!)) + .scaleEffect(x: 1.3, y: 2.8) + .rotationEffect(.degrees(92)) + .blur(radius: 35) + .opacity(min(0.6, 1 - max(musicManager.albumArt.getBrightness(), 0.3))) + .onAppear { + print(musicManager.albumArt.getBrightness()) + } } - } - .buttonStyle(PlainButtonStyle()) - } - - Group { - if vm.notchState == .open { - VStack(alignment: .leading) { - GeometryReader { geo in - VStack(alignment: .leading, spacing: 4){ - MarqueeText($musicManager.songTitle, font: .headline, nsFont: .headline, textColor: .white, frameWidth: geo.size.width) - MarqueeText( - $musicManager.artistName, - font: .headline, - nsFont: .headline, - textColor: Defaults[.playerColorTinting] ? Color(nsColor: musicManager.avgColor) - .ensureMinimumBrightness(factor: 0.6) : .gray, - frameWidth: geo.size.width + + Button { + musicManager.openMusicApp() + } label: { + ZStack(alignment: .bottomTrailing) { + Color.clear + .aspectRatio(1, contentMode: .fit) + .background( + Image(nsImage: musicManager.albumArt) + .resizable() + .aspectRatio(contentMode: .fill) ) - .fontWeight(.medium) - - MusicSliderView(sliderValue: $sliderValue, - duration: $musicManager.songDuration, - lastDragged: $lastDragged, - color: musicManager.avgColor, - dragging: $dragging) { newValue in - musicManager.seekTrack(to: newValue) - } - .padding(.top, 5) - .frame(height: 36) + .clipped() + .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? vm.musicPlayerSizes.image.cornerRadius.opened.inset! : vm.musicPlayerSizes.image.cornerRadius.closed.inset!)) + .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) + + if vm.notchState == .open && !musicManager.usingAppIconForArtwork { + AppIcon(for: musicManager.bundleIdentifier ?? "com.apple.Music") + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 30, height: 30) + .offset(x: 10, y: 10) + .transition(.scale.combined(with: .opacity).animation(.bouncy.delay(0.3))) } } - .padding(.top, 10) - .padding(.leading, 5) - HStack(spacing: 8) { - HoverButton(icon: "backward.fill") { - musicManager.previousTrack() - } - HoverButton(icon: musicManager.isPlaying ? "pause.fill" : "play.fill") { - print("tapped") - musicManager.togglePlayPause() + } + .buttonStyle(PlainButtonStyle()) + } + + Group { + if vm.notchState == .open { + VStack(alignment: .leading) { + GeometryReader { geo in + VStack(alignment: .leading, spacing: 4){ + MarqueeText($musicManager.songTitle, font: .headline, nsFont: .headline, textColor: .white, frameWidth: geo.size.width) + MarqueeText( + $musicManager.artistName, + font: .headline, + nsFont: .headline, + textColor: Defaults[.playerColorTinting] ? Color(nsColor: musicManager.avgColor) + .ensureMinimumBrightness(factor: 0.6) : .gray, + frameWidth: geo.size.width + ) + .fontWeight(.medium) + + MusicSliderView(sliderValue: $sliderValue, + duration: $musicManager.songDuration, + lastDragged: $lastDragged, + color: musicManager.avgColor, + dragging: $dragging) { newValue in + musicManager.seekTrack(to: newValue) + } + .padding(.top, 5) + .frame(height: 36) + } } - HoverButton(icon: "forward.fill") { - musicManager.nextTrack() + .padding(.top, 10) + .padding(.leading, 5) + HStack(spacing: 8) { + HoverButton(icon: "backward.fill") { + musicManager.previousTrack() + } + HoverButton(icon: musicManager.isPlaying ? "pause.fill" : "play.fill") { + print("tapped") + musicManager.togglePlayPause() + } + HoverButton(icon: "forward.fill") { + musicManager.nextTrack() + } } + .frame(maxWidth: .infinity, alignment: .center) } - .frame(maxWidth: .infinity, alignment: .center) + .buttonStyle(PlainButtonStyle()) + .frame(minWidth: 170) } - .buttonStyle(PlainButtonStyle()) - .frame(minWidth: 170) } } - .transition(.blurReplace) - } - - if Defaults[.showCalendar] { - CalendarView() - .onContinuousHover { phase in - if Defaults[.closeGestureEnabled] { - switch phase { + + if Defaults[.showCalendar] { + CalendarView() + .onContinuousHover { phase in + if Defaults[.closeGestureEnabled] { + switch phase { case .active: Defaults[.closeGestureEnabled] = false case .ended: Defaults[.closeGestureEnabled] = false + } } } - } + } + + if Defaults[.showMirror] && webcamManager.cameraAvailable && webcamManager.authorizationStatus != .denied { + CameraPreviewView(webcamManager: webcamManager) + .scaledToFit() + .opacity(vm.notchState == .closed ? 0 : 1) + .blur(radius: vm.notchState == .closed ? 20 : 0) + } + + if (!Defaults[.showMirror] || !webcamManager.cameraAvailable || webcamManager.authorizationStatus == .denied) && !Defaults[.showCalendar] { + Rectangle() + .fill(Defaults[.coloredSpectrogram] ? Color(nsColor: musicManager.avgColor).gradient : Color.gray.gradient) + .mask { + AudioSpectrumView( + isPlaying: $musicManager.isPlaying + ) + .frame(width: 16, height: 12) + } + .frame(width: 50, alignment: .center) + .matchedGeometryEffect(id: "spectrum", in: albumArtNamespace) + } + // BoringSystemTiles() + // .transition(.blurReplace.animation(.spring(.bouncy(duration: 0.3)).delay(0.1))) + // .opacity(vm.notchState == .closed ? 0 : 1) + // .blur(radius: vm.notchState == .closed ? 20 : 0) } - - if Defaults[.showMirror] && webcamManager.cameraAvailable && webcamManager.authorizationStatus != .denied { - CameraPreviewView(webcamManager: webcamManager) - .scaledToFit() - .opacity(vm.notchState == .closed ? 0 : 1) - .blur(radius: vm.notchState == .closed ? 20 : 0) + .onAppear { + // Initialize the slider value and start the timer + sliderValue = musicManager.elapsedTime + startTimer() } - - if (!Defaults[.showMirror] || !webcamManager.cameraAvailable || webcamManager.authorizationStatus == .denied) && !Defaults[.showCalendar] { - Rectangle() - .fill(Defaults[.coloredSpectrogram] ? Color(nsColor: musicManager.avgColor).gradient : Color.gray.gradient) - .mask { - AudioSpectrumView( - isPlaying: $musicManager.isPlaying - ) - .frame(width: 16, height: 12) - } - .frame(width: 50, alignment: .center) - .matchedGeometryEffect(id: "spectrum", in: albumArtNamespace) + .onDisappear { + // Stop the timer when the view disappears + timer?.cancel() } - // BoringSystemTiles() - // .transition(.blurReplace.animation(.spring(.bouncy(duration: 0.3)).delay(0.1))) - // .opacity(vm.notchState == .closed ? 0 : 1) - // .blur(radius: vm.notchState == .closed ? 20 : 0) - } - .onAppear { - // Initialize the slider value and start the timer - sliderValue = musicManager.elapsedTime - startTimer() - } - .onDisappear { - // Stop the timer when the view disappears - timer?.cancel() } .transition(.opacity.combined(with: .blurReplace)) } + .blur(radius: vm.notchState == .closed ? 15 : 0) } private func startTimer() { From d6d7a49ba999d61db48667ec589e6d39aa6ab59e Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Fri, 22 Nov 2024 12:45:40 +0530 Subject: [PATCH 29/74] Notch Open Animation Updated to Mimic iOS Behavior --- boringNotch/models/BoringViewModel.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/boringNotch/models/BoringViewModel.swift b/boringNotch/models/BoringViewModel.swift index 8d18e3e..19e06fe 100644 --- a/boringNotch/models/BoringViewModel.swift +++ b/boringNotch/models/BoringViewModel.swift @@ -89,8 +89,9 @@ class BoringViewModel: NSObject, ObservableObject { } func open() { - withAnimation(.bouncy) { - self.notchSize = CGSize(width: openNotchSize.width, height: openNotchSize.height) + withAnimation(.smooth.speed(2)) { + self.notchSize = .init(width: Sizes().size.opened.width!, height: Sizes().size.opened.height!) + self.notchMetastability = true self.notchState = .open } } From f9f9465388df008c83d7959195849e009c352dd7 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Fri, 22 Nov 2024 12:46:08 +0530 Subject: [PATCH 30/74] Improved Animations --- boringNotch/components/Notch/NotchContentView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/boringNotch/components/Notch/NotchContentView.swift b/boringNotch/components/Notch/NotchContentView.swift index b4370ed..5cf5552 100644 --- a/boringNotch/components/Notch/NotchContentView.swift +++ b/boringNotch/components/Notch/NotchContentView.swift @@ -19,7 +19,7 @@ struct NotchContentView: View { VStack(alignment: coordinator.firstLaunch ? .center : .leading, spacing: 0) { if vm.notchState == .open { BoringHeader() - .animation(.spring(response: 0.7, dampingFraction: 0.8, blendDuration: 0.8), value: vm.notchState) + .animation(.spring(response: 1, dampingFraction: 1, blendDuration: 0.8), value: vm.notchState) .padding(.top, 10) if coordinator.firstLaunch { Spacer() @@ -118,7 +118,7 @@ struct NotchContentView: View { } } .frame(width: calculateFrameWidthforNotchContent()) - .transition(.blurReplace.animation(.spring(.bouncy(duration: 0.5)))) + .transition(.blurReplace.animation(.interactiveSpring(dampingFraction: 1))) } func calculateFrameWidthforNotchContent() -> CGFloat? { From 47b0414cf5d6a4050ceede5a88ad46958ae5b5e1 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Fri, 22 Nov 2024 12:50:39 +0530 Subject: [PATCH 31/74] Reduced Blur Spread to Prevent Interference with Physical Notch --- boringNotch/components/Notch/NotchHomeView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 3c4a75f..d6b8233 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -38,7 +38,7 @@ struct NotchHomeView: View { ) .clipped() .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? vm.musicPlayerSizes.image.cornerRadius.opened.inset! : vm.musicPlayerSizes.image.cornerRadius.closed.inset!)) - .scaleEffect(x: 1.3, y: 2.8) + .scaleEffect(x: 1.4, y: 1.4) .rotationEffect(.degrees(92)) .blur(radius: 35) .opacity(min(0.6, 1 - max(musicManager.albumArt.getBrightness(), 0.3))) From 51c3b4120fd13cdf873d4d9184fecec4557d2e60 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Fri, 22 Nov 2024 15:10:28 +0530 Subject: [PATCH 32/74] Fixed Uneven Corner Radii, Added Continous Radius The notch outline now matches the physical notch perfectly. Previously, there was a slight mismatch. --- boringNotch/components/Notch/NotchShape.swift | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/boringNotch/components/Notch/NotchShape.swift b/boringNotch/components/Notch/NotchShape.swift index c9878e1..f93187a 100644 --- a/boringNotch/components/Notch/NotchShape.swift +++ b/boringNotch/components/Notch/NotchShape.swift @@ -34,31 +34,41 @@ struct NotchShape: Shape { var path = Path() // Start from the top left corner path.move(to: CGPoint(x: rect.minX, y: rect.minY)) + // Top left inner curve path.addQuadCurve( - to: CGPoint(x: rect.minX + topCornerRadius, y: topCornerRadius), - control: CGPoint(x: rect.minX + topCornerRadius, y: rect.minY) // Control point for inner curve + to: CGPoint(x: rect.minX + topCornerRadius, y: rect.minY + topCornerRadius), + control: CGPoint(x: rect.minX + topCornerRadius, y: rect.minY) ) + // Left vertical line path.addLine(to: CGPoint(x: rect.minX + topCornerRadius, y: rect.maxY - bottomCornerRadius)) + // Bottom left corner path.addQuadCurve( to: CGPoint(x: rect.minX + topCornerRadius + bottomCornerRadius, y: rect.maxY), control: CGPoint(x: rect.minX + topCornerRadius, y: rect.maxY) ) + + // Bottom horizontal line path.addLine(to: CGPoint(x: rect.maxX - topCornerRadius - bottomCornerRadius, y: rect.maxY)) + // Bottom right corner path.addQuadCurve( to: CGPoint(x: rect.maxX - topCornerRadius, y: rect.maxY - bottomCornerRadius), control: CGPoint(x: rect.maxX - topCornerRadius, y: rect.maxY) ) - path.addLine(to: CGPoint(x: rect.maxX - topCornerRadius, y: rect.minY + bottomCornerRadius)) - // Closing the path to top right inner curve + // Right vertical line + path.addLine(to: CGPoint(x: rect.maxX - topCornerRadius, y: rect.minY + topCornerRadius)) + + // Top right inner curve path.addQuadCurve( to: CGPoint(x: rect.maxX, y: rect.minY), - control: CGPoint(x: rect.maxX - topCornerRadius, y: rect.minY) // Control point for inner curve + control: CGPoint(x: rect.maxX - topCornerRadius, y: rect.minY) ) + + // Closing the path path.addLine(to: CGPoint(x: rect.minX, y: rect.minY)) return path } From c70e3448c862c3ef885afadb94fd3d85d368d5d2 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Fri, 22 Nov 2024 15:11:06 +0530 Subject: [PATCH 33/74] Reduced Blur Delay --- boringNotch/components/Notch/BoringHeader.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/boringNotch/components/Notch/BoringHeader.swift b/boringNotch/components/Notch/BoringHeader.swift index 325bb87..e65b3fe 100644 --- a/boringNotch/components/Notch/BoringHeader.swift +++ b/boringNotch/components/Notch/BoringHeader.swift @@ -25,7 +25,7 @@ struct BoringHeader: View { .frame(maxWidth: .infinity, alignment: .leading) .opacity(vm.notchState == .closed ? 0 : 1) .blur(radius: vm.notchState == .closed ? 20 : 0) - .animation(.smooth.delay(0.2), value: vm.notchState) + .animation(.smooth.delay(0.1), value: vm.notchState) .zIndex(2) if vm.notchState == .open { @@ -67,7 +67,7 @@ struct BoringHeader: View { .frame(maxWidth: .infinity, alignment: .trailing) .opacity(vm.notchState == .closed ? 0 : 1) .blur(radius: vm.notchState == .closed ? 20 : 0) - .animation(.smooth.delay(0.2), value: vm.notchState) + .animation(.smooth.delay(0.1), value: vm.notchState) .zIndex(2) } .foregroundColor(.gray) From 929fd04b15bfba6ee90c938598c2e143fac99840 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Fri, 22 Nov 2024 15:28:35 +0530 Subject: [PATCH 34/74] Added Safe Area for Hover Zone To ensure the notch does not close if the cursor is within 30px of the notch from the bottom. --- boringNotch/ContentView.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/boringNotch/ContentView.swift b/boringNotch/ContentView.swift index 596e467..5779424 100644 --- a/boringNotch/ContentView.swift +++ b/boringNotch/ContentView.swift @@ -43,7 +43,8 @@ struct ContentView: View { .mask { NotchShape(cornerRadius: ((vm.notchState == .open) && Defaults[.cornerRadiusScaling]) ? cornerRadiusInsets.opened : cornerRadiusInsets.closed) } - .frame(width: vm.notchState == .closed ? (((musicManager.isPlaying || !musicManager.isPlayerIdle) && coordinator.showMusicLiveActivityOnClosed) || (vm.expandingView.show && (vm.expandingView.type == .battery)) || (Defaults[.inlineHUD] && coordinator.sneakPeek.show && coordinator.sneakPeek.type != .music)) ? nil : vm.closedNotchSize.width + (hoverAnimation ? 20 : 0) + gestureProgress : nil, height: vm.notchState == .closed ? vm.closedNotchSize.height + (hoverAnimation ? 8 : 0) + gestureProgress / 3 : nil, alignment: .top) + .frame(width: vm.notchState == .closed ? (((musicManager.isPlaying || !musicManager.isPlayerIdle) && vm.showMusicLiveActivityOnClosed) || (vm.expandingView.show && (vm.expandingView.type == .battery)) || (Defaults[.inlineHUD] && vm.sneakPeek.show && vm.sneakPeek.type != .music)) ? nil : Sizes().size.closed.width! + (hoverAnimation ? 20 : 0) + gestureProgress : nil, height: vm.notchState == .closed ? Sizes().size.closed.height! + (hoverAnimation ? 8 : 0) + gestureProgress / 3 : nil, alignment: .top) + .padding(.bottom, vm.notchState == .open ? 30 : 0) // Safe area to ensure the notch does not close if the cursor is within 30px of the notch from the bottom. .conditionalModifier(Defaults[.openNotchOnHover]) { view in view .onHover { hovering in From cfdf360bc31cf935e5cbfab9e5545a7d7f5eb6c6 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Sat, 23 Nov 2024 19:04:22 +0530 Subject: [PATCH 35/74] Removed Excess Horizontal Padding, Corner Radius now matches. --- boringNotch/ContentView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boringNotch/ContentView.swift b/boringNotch/ContentView.swift index 5779424..1d634ba 100644 --- a/boringNotch/ContentView.swift +++ b/boringNotch/ContentView.swift @@ -36,7 +36,7 @@ struct ContentView: View { var body: some View { ZStack(alignment: .top) { NotchLayout() - .padding(.horizontal, vm.notchState == .open ? Defaults[.cornerRadiusScaling] ? (cornerRadiusInsets.opened) : (cornerRadiusInsets.closed - 5) : 12) + .padding(.horizontal, vm.notchState == .open ? Defaults[.cornerRadiusScaling] ? (vm.sizes.cornerRadius.opened.inset! - 5) : (vm.sizes.cornerRadius.closed.inset! - 5) : 12) .padding([.horizontal, .bottom], vm.notchState == .open ? 12 : 0) .frame(maxWidth: (((musicManager.isPlaying || !musicManager.isPlayerIdle) && vm.notchState == .closed && coordinator.showMusicLiveActivityOnClosed) || (vm.expandingView.show && (vm.expandingView.type == .battery)) || Defaults[.inlineHUD]) ? nil : vm.notchSize.width + ((hoverAnimation || (vm.notchState == .closed)) ? 20 : 0) + gestureProgress, maxHeight: ((coordinator.sneakPeek.show && coordinator.sneakPeek.type != .music) || (coordinator.sneakPeek.show && coordinator.sneakPeek.type == .music && vm.notchState == .closed)) ? nil : vm.notchSize.height + (hoverAnimation ? 8 : 0) + gestureProgress / 3, alignment: .top) .background(.black) From a318851367ffdb25574a5291822a9a50e9b519d3 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Sat, 23 Nov 2024 19:07:44 +0530 Subject: [PATCH 36/74] Artwork now shrinks when paused --- boringNotch/ContentView.swift | 3 ++- boringNotch/components/Notch/NotchHomeView.swift | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/boringNotch/ContentView.swift b/boringNotch/ContentView.swift index 1d634ba..9fa56ca 100644 --- a/boringNotch/ContentView.swift +++ b/boringNotch/ContentView.swift @@ -284,7 +284,8 @@ struct ContentView: View { .aspectRatio(contentMode: .fill) ) .clipped() - .clipShape(RoundedRectangle(cornerRadius: MusicPlayerImageSizes.cornerRadiusInset.closed)) + .clipShape(RoundedRectangle(cornerRadius: vm.musicPlayerSizes.image.cornerRadius.closed.inset!)) + .opacity(musicManager.isPlaying ? 1 : 0.4) .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) .frame(width: max(0, vm.closedNotchSize.height - 12), height: max(0, vm.closedNotchSize.height - 12)) } diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index d6b8233..105c4ac 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -60,6 +60,7 @@ struct NotchHomeView: View { ) .clipped() .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? vm.musicPlayerSizes.image.cornerRadius.opened.inset! : vm.musicPlayerSizes.image.cornerRadius.closed.inset!)) + .opacity(musicManager.isPlaying ? 1 : 0.4) .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) if vm.notchState == .open && !musicManager.usingAppIconForArtwork { @@ -71,6 +72,7 @@ struct NotchHomeView: View { .transition(.scale.combined(with: .opacity).animation(.bouncy.delay(0.3))) } } + .scaleEffect(musicManager.isPlaying ? 1 : 0.85) } .buttonStyle(PlainButtonStyle()) } From 769517d6f96c7266d07a9ee559a25dbc12b01c9a Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Sat, 23 Nov 2024 19:21:51 +0530 Subject: [PATCH 37/74] Improved Slider Responsiveness --- .../components/Notch/NotchHomeView.swift | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 105c4ac..4f92aeb 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -255,12 +255,11 @@ struct CustomSlider: View { @Binding var lastDragged: Date var onValueChange: ((Double) -> Void)? var thumbSize: CGFloat = 12 - @State private var hovered: Bool = false var body: some View { GeometryReader { geometry in let width = geometry.size.width - let height = geometry.size.height + let height = CGFloat(dragging ? 9 : 5) let rangeSpan = range.upperBound - range.lowerBound let filledTrackWidth = min(rangeSpan == .zero ? 0 : ((value - range.lowerBound) / rangeSpan) * width, width) @@ -277,6 +276,8 @@ struct CustomSlider: View { .frame(width: filledTrackWidth, height: height) } .cornerRadius(height / 2) + .frame(height: 10) + .contentShape(Rectangle()) .highPriorityGesture( DragGesture(minimumDistance: 0) .onChanged { gesture in @@ -292,20 +293,8 @@ struct CustomSlider: View { lastDragged = Date() } ) - .onContinuousHover { phase in - switch phase { - case .active: - withAnimation { - hovered = true - } - case .ended: - withAnimation { - hovered = false - } - } - } + .animation(.bouncy.speed(1.4), value: dragging) } - .frame(height: dragging || hovered ? 8 : 5) } } From ab00919b1623fafa8134ac319d6f42af66acaae5 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Sat, 23 Nov 2024 19:25:18 +0530 Subject: [PATCH 38/74] Reduced Padding between Song Name and Artist Name --- .../components/Notch/NotchHomeView.swift | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 4f92aeb..614baad 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -82,16 +82,18 @@ struct NotchHomeView: View { VStack(alignment: .leading) { GeometryReader { geo in VStack(alignment: .leading, spacing: 4){ - MarqueeText($musicManager.songTitle, font: .headline, nsFont: .headline, textColor: .white, frameWidth: geo.size.width) - MarqueeText( - $musicManager.artistName, - font: .headline, - nsFont: .headline, - textColor: Defaults[.playerColorTinting] ? Color(nsColor: musicManager.avgColor) - .ensureMinimumBrightness(factor: 0.6) : .gray, - frameWidth: geo.size.width - ) - .fontWeight(.medium) + VStack(alignment: .leading, spacing: 0) { + MarqueeText($musicManager.songTitle, font: .headline, nsFont: .headline, textColor: .white, frameWidth: geo.size.width) + MarqueeText( + $musicManager.artistName, + font: .headline, + nsFont: .headline, + textColor: Defaults[.playerColorTinting] ? Color(nsColor: musicManager.avgColor) + .ensureMinimumBrightness(factor: 0.6) : .gray, + frameWidth: geo.size.width + ) + .fontWeight(.medium) + } MusicSliderView(sliderValue: $sliderValue, duration: $musicManager.songDuration, From 7e26cbfd98655e214dc8f2154d2bbf77aa85d08a Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Sat, 23 Nov 2024 19:34:51 +0530 Subject: [PATCH 39/74] Increased Play Button Size --- boringNotch/components/HoverButton.swift | 11 +++++++---- boringNotch/components/Notch/NotchHomeView.swift | 6 +++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/boringNotch/components/HoverButton.swift b/boringNotch/components/HoverButton.swift index ed4f7e6..7b68448 100644 --- a/boringNotch/components/HoverButton.swift +++ b/boringNotch/components/HoverButton.swift @@ -10,26 +10,29 @@ import SwiftUI struct HoverButton: View { var icon: String var iconColor: Color = .white; + var scale: Image.Scale = .medium var action: () -> Void var contentTransition: ContentTransition = .symbolEffect; @State private var isHovering = false var body: some View { + let size = CGFloat(scale == .large ? 40 : 30) + Button(action: action) { Rectangle() .fill(.clear) .contentShape(Rectangle()) - .frame(width: 30, height: 30) + .frame(width: size, height: size) .overlay { Capsule() - .fill(isHovering ? Color.gray.opacity(0.3) : .clear) - .frame(width: 30, height: 30) + .fill(isHovering ? Color.gray.opacity(0.2) : .clear) + .frame(width: size, height: size) .overlay { Image(systemName: icon) .foregroundColor(iconColor) .contentTransition(contentTransition) - .imageScale(.medium) + .font(scale == .large ? .largeTitle : .body) } } } diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 614baad..4f64af8 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -109,14 +109,14 @@ struct NotchHomeView: View { .padding(.top, 10) .padding(.leading, 5) HStack(spacing: 8) { - HoverButton(icon: "backward.fill") { + HoverButton(icon: "backward.fill", scale: .medium) { musicManager.previousTrack() } - HoverButton(icon: musicManager.isPlaying ? "pause.fill" : "play.fill") { + HoverButton(icon: musicManager.isPlaying ? "pause.fill" : "play.fill", scale: .large) { print("tapped") musicManager.togglePlayPause() } - HoverButton(icon: "forward.fill") { + HoverButton(icon: "forward.fill", scale: .medium) { musicManager.nextTrack() } } From 88460e2438b3eee9df08a646860d9116c158a6a8 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Sat, 23 Nov 2024 21:35:25 +0530 Subject: [PATCH 40/74] Added Move Transition and Enhanced Blur Effect --- .../components/Live activities/MarqueeTextView.swift | 2 +- boringNotch/components/Notch/NotchHomeView.swift | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/boringNotch/components/Live activities/MarqueeTextView.swift b/boringNotch/components/Live activities/MarqueeTextView.swift index 82b64a7..06b0963 100644 --- a/boringNotch/components/Live activities/MarqueeTextView.swift +++ b/boringNotch/components/Live activities/MarqueeTextView.swift @@ -101,7 +101,7 @@ struct MarqueeText: View { } } .frame(width: frameWidth, alignment: .leading) - .clipped() +// .clipped() } .frame(height: textSize.height * 1.3) } diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 4f64af8..76766a0 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -24,7 +24,7 @@ struct NotchHomeView: View { var body: some View { Group { - if !vm.firstLaunch { + if !vm.firstLaunch && vm.notchState == .open { HStack(alignment: .top, spacing: 30) { HStack { ZStack(alignment: .bottomTrailing) { @@ -178,7 +178,9 @@ struct NotchHomeView: View { } .transition(.opacity.combined(with: .blurReplace)) } - .blur(radius: vm.notchState == .closed ? 15 : 0) + .animation(.smooth.speed(1.5), value: vm.notchState) + .transition(.move(edge: .top).combined(with: .blurReplace)) + .blur(radius: vm.notchState == .closed ? 30 : 0) } private func startTimer() { From 4169cf7f5c5978231f4ae935321fea163c3bf4fb Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Sat, 23 Nov 2024 21:46:25 +0530 Subject: [PATCH 41/74] Revert "Added Move Transition and Enhanced Blur Effect" This reverts commit 4176e1b319312aaca02c2c9f8455af74480616f4. --- .../components/Live activities/MarqueeTextView.swift | 2 +- boringNotch/components/Notch/NotchHomeView.swift | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/boringNotch/components/Live activities/MarqueeTextView.swift b/boringNotch/components/Live activities/MarqueeTextView.swift index 06b0963..82b64a7 100644 --- a/boringNotch/components/Live activities/MarqueeTextView.swift +++ b/boringNotch/components/Live activities/MarqueeTextView.swift @@ -101,7 +101,7 @@ struct MarqueeText: View { } } .frame(width: frameWidth, alignment: .leading) -// .clipped() + .clipped() } .frame(height: textSize.height * 1.3) } diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 76766a0..4f64af8 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -24,7 +24,7 @@ struct NotchHomeView: View { var body: some View { Group { - if !vm.firstLaunch && vm.notchState == .open { + if !vm.firstLaunch { HStack(alignment: .top, spacing: 30) { HStack { ZStack(alignment: .bottomTrailing) { @@ -178,9 +178,7 @@ struct NotchHomeView: View { } .transition(.opacity.combined(with: .blurReplace)) } - .animation(.smooth.speed(1.5), value: vm.notchState) - .transition(.move(edge: .top).combined(with: .blurReplace)) - .blur(radius: vm.notchState == .closed ? 30 : 0) + .blur(radius: vm.notchState == .closed ? 15 : 0) } private func startTimer() { From 1a37432d922da1d2868f94c167d44dcdf94d9ac7 Mon Sep 17 00:00:00 2001 From: Emir <85408428+overflow-sudo@users.noreply.github.com> Date: Sun, 3 Nov 2024 09:22:34 +0300 Subject: [PATCH 42/74] Placeholder provided by media player If the artwork data cannot be found, it will be replaced with a placeholder provided by media player --- boringNotch/managers/MusicManager.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 57899fd..4b3d69d 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -173,7 +173,13 @@ class MusicManager: ObservableObject { let artist = information["kMRMediaRemoteNowPlayingInfoArtist"] as? String ?? "" let album = information["kMRMediaRemoteNowPlayingInfoAlbum"] as? String ?? "" let duration = information["kMRMediaRemoteNowPlayingInfoDuration"] as? TimeInterval ?? lastMusicItem?.duration ?? 0 - let artworkData = information["kMRMediaRemoteNowPlayingInfoArtworkData"] as? Data + + + guard let artworkData = information["kMRMediaRemoteNowPlayingInfoArtworkData"] as? Data else { + let placeholder = AppIconAsNSImage(for: bundleIdentifier)?.pngRepresentation + return (title, artist, album, duration, placeholder) + } + return (title, artist, album, duration, artworkData) } @@ -195,6 +201,8 @@ class MusicManager: ObservableObject { self.songTitle = newInfo.title self.album = newInfo.album self.songDuration = newInfo.duration + print(newInfo.duration) + } private func updateArtwork(_ artworkData: Data?) { From 7e0f475812291dc434fd43e17397fb21478243b5 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Tue, 5 Nov 2024 02:00:36 -0500 Subject: [PATCH 43/74] rewrite logic for detirmining when to change album art and when to use app icon as a placeholder --- boringNotch/managers/MusicManager.swift | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 4b3d69d..256c035 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -173,18 +173,13 @@ class MusicManager: ObservableObject { let artist = information["kMRMediaRemoteNowPlayingInfoArtist"] as? String ?? "" let album = information["kMRMediaRemoteNowPlayingInfoAlbum"] as? String ?? "" let duration = information["kMRMediaRemoteNowPlayingInfoDuration"] as? TimeInterval ?? lastMusicItem?.duration ?? 0 - - - guard let artworkData = information["kMRMediaRemoteNowPlayingInfoArtworkData"] as? Data else { - let placeholder = AppIconAsNSImage(for: bundleIdentifier)?.pngRepresentation - return (title, artist, album, duration, placeholder) - } - + let artworkData = information["kMRMediaRemoteNowPlayingInfoArtworkData"] as? Data return (title, artist, album, duration, artworkData) } private func updateMusicState(newInfo: (title: String, artist: String, album: String, duration: TimeInterval, artworkData: Data?), state: Int?) { + print((newInfo.title, newInfo.artist, newInfo.album) != (lastMusicItem?.title, lastMusicItem?.artist, lastMusicItem?.album)) if((newInfo.artworkData != nil && newInfo.artworkData != lastMusicItem?.artworkData) || (newInfo.title, newInfo.artist, newInfo.album) != (lastMusicItem?.title, lastMusicItem?.artist, lastMusicItem?.album)) { updateArtwork(newInfo.artworkData) self.lastMusicItem?.artworkData = newInfo.artworkData @@ -201,12 +196,10 @@ class MusicManager: ObservableObject { self.songTitle = newInfo.title self.album = newInfo.album self.songDuration = newInfo.duration - print(newInfo.duration) - } private func updateArtwork(_ artworkData: Data?) { - if let artworkData = artworkData, + if let artworkData = artworkData ?? AppIconAsNSImage(for: bundleIdentifier)?.tiffRepresentation, let artworkImage = NSImage(data: artworkData) { self.usingAppIconForArtwork = false self.updateAlbumArt(newAlbumArt: artworkImage) From 524d80f1c4a236dc842c8d503018efbbe50602fb Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Tue, 5 Nov 2024 02:01:31 -0500 Subject: [PATCH 44/74] Modify MusicSliderView to display hours if applicable --- boringNotch/components/Notch/NotchHomeView.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 4f64af8..4fce922 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -233,9 +233,6 @@ struct MusicSliderView: View { } func timeString(from seconds: Double) -> String { - if seconds.isNaN || seconds.isInfinite { - return "--:--" - } let totalMinutes = Int(seconds) / 60 let remainingSeconds = Int(seconds) % 60 let hours = totalMinutes / 60 From fd5dd70fb6ce84951c53bd6a7078eec173cb34f0 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:27:38 -0500 Subject: [PATCH 45/74] Hide mini app icon when app icon is being used for artwork --- .../components/Notch/NotchHomeView.swift | 42 ++++--------------- boringNotch/managers/MusicManager.swift | 15 +++++-- 2 files changed, 20 insertions(+), 37 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 4fce922..13bd745 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -38,39 +38,15 @@ struct NotchHomeView: View { ) .clipped() .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? vm.musicPlayerSizes.image.cornerRadius.opened.inset! : vm.musicPlayerSizes.image.cornerRadius.closed.inset!)) - .scaleEffect(x: 1.4, y: 1.4) - .rotationEffect(.degrees(92)) - .blur(radius: 35) - .opacity(min(0.6, 1 - max(musicManager.albumArt.getBrightness(), 0.3))) - .onAppear { - print(musicManager.albumArt.getBrightness()) - } - } - - Button { - musicManager.openMusicApp() - } label: { - ZStack(alignment: .bottomTrailing) { - Color.clear - .aspectRatio(1, contentMode: .fit) - .background( - Image(nsImage: musicManager.albumArt) - .resizable() - .aspectRatio(contentMode: .fill) - ) - .clipped() - .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? vm.musicPlayerSizes.image.cornerRadius.opened.inset! : vm.musicPlayerSizes.image.cornerRadius.closed.inset!)) - .opacity(musicManager.isPlaying ? 1 : 0.4) - .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) - - if vm.notchState == .open && !musicManager.usingAppIconForArtwork { - AppIcon(for: musicManager.bundleIdentifier ?? "com.apple.Music") - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: 30, height: 30) - .offset(x: 10, y: 10) - .transition(.scale.combined(with: .opacity).animation(.bouncy.delay(0.3))) - } + .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) + + if vm.notchState == .open && !musicManager.usingAppIconForArtwork { + AppIcon(for: musicManager.bundleIdentifier) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 30, height: 30) + .offset(x: 10, y: 10) + .transition(.scale.combined(with: .opacity).animation(.bouncy.delay(0.3))) } .scaleEffect(musicManager.isPlaying ? 1 : 0.85) } diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 256c035..1dac4b7 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -40,7 +40,6 @@ class MusicManager: ObservableObject { @Published var timestampDate: Date = Date() @Published var playbackRate: Double = 0 @ObservedObject var detector: FullscreenMediaDetector - @ObservedObject var coordinator = BoringViewCoordinator.shared @Published var usingAppIconForArtwork: Bool = false var nowPlaying: NowPlaying @@ -163,7 +162,7 @@ class MusicManager: ObservableObject { // MARK: - Helper Methods private func updateBundleIdentifier(_ bundle: String?) { if let bundle = bundle { - self.bundleIdentifier = bundle + self.bundleIdentifier = bundle == "com.apple.WebKit.GPU" ? "com.apple.Safari" : bundle } } @@ -199,16 +198,24 @@ class MusicManager: ObservableObject { } private func updateArtwork(_ artworkData: Data?) { - if let artworkData = artworkData ?? AppIconAsNSImage(for: bundleIdentifier)?.tiffRepresentation, + if let artworkData = artworkData, let artworkImage = NSImage(data: artworkData) { self.usingAppIconForArtwork = false self.updateAlbumArt(newAlbumArt: artworkImage) - } else if let appIconImage = AppIconAsNSImage(for: bundleIdentifier ?? nowPlaying.appBundleIdentifier ?? "") { + } else if let appIconImage = AppIconAsNSImage(for: bundleIdentifier) { self.usingAppIconForArtwork = true self.updateAlbumArt(newAlbumArt: appIconImage) } } + private func updatePlaybackState(_ state: Int?) { + if let state = state { + self.musicIsPaused(state: state == 1, setIdle: true) + } else if self.isPlaying { + self.musicIsPaused(state: false, setIdle: true) + } + } + func musicIsPaused(state: Bool, bypass: Bool = false, setIdle: Bool = false) { if musicToggledManually && !bypass { return } From f9e55f8d76719c789cba8970129403c8eee635e1 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 6 Nov 2024 19:23:07 -0500 Subject: [PATCH 46/74] Use the isPlaying function rather than guessing based on playback rate is this is sometimes 0 even though something is playing --- boringNotch/managers/MusicManager.swift | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 1dac4b7..e927c4d 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -191,6 +191,9 @@ class MusicManager: ObservableObject { MRMediaRemoteGetNowPlayingApplicationIsPlaying(DispatchQueue.main) { [weak self] isPlaying in self?.musicIsPaused(state: isPlaying, setIdle: true) } + + if !self.isPlaying { return } + self.artistName = newInfo.artist self.songTitle = newInfo.title self.album = newInfo.album @@ -208,14 +211,6 @@ class MusicManager: ObservableObject { } } - private func updatePlaybackState(_ state: Int?) { - if let state = state { - self.musicIsPaused(state: state == 1, setIdle: true) - } else if self.isPlaying { - self.musicIsPaused(state: false, setIdle: true) - } - } - func musicIsPaused(state: Bool, bypass: Bool = false, setIdle: Bool = false) { if musicToggledManually && !bypass { return } From 4aef50cdc4b4284944093b525c25c41c7c66743b Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 6 Nov 2024 19:25:22 -0500 Subject: [PATCH 47/74] Prevent crashes due to non-finite duration --- boringNotch/components/Notch/NotchHomeView.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 13bd745..236414e 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -209,6 +209,9 @@ struct MusicSliderView: View { } func timeString(from seconds: Double) -> String { + if seconds.isNaN || seconds.isInfinite { + return "--:--" + } let totalMinutes = Int(seconds) / 60 let remainingSeconds = Int(seconds) % 60 let hours = totalMinutes / 60 From aaddd84f4f96b761713829f5053924f48caff590 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 6 Nov 2024 19:36:18 -0500 Subject: [PATCH 48/74] Remove old debug print statements --- boringNotch/components/Notch/NotchHomeView.swift | 3 --- boringNotch/managers/MusicManager.swift | 1 - 2 files changed, 4 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 236414e..13bd745 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -209,9 +209,6 @@ struct MusicSliderView: View { } func timeString(from seconds: Double) -> String { - if seconds.isNaN || seconds.isInfinite { - return "--:--" - } let totalMinutes = Int(seconds) / 60 let remainingSeconds = Int(seconds) % 60 let hours = totalMinutes / 60 diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index e927c4d..9b9b632 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -178,7 +178,6 @@ class MusicManager: ObservableObject { } private func updateMusicState(newInfo: (title: String, artist: String, album: String, duration: TimeInterval, artworkData: Data?), state: Int?) { - print((newInfo.title, newInfo.artist, newInfo.album) != (lastMusicItem?.title, lastMusicItem?.artist, lastMusicItem?.album)) if((newInfo.artworkData != nil && newInfo.artworkData != lastMusicItem?.artworkData) || (newInfo.title, newInfo.artist, newInfo.album) != (lastMusicItem?.title, lastMusicItem?.artist, lastMusicItem?.album)) { updateArtwork(newInfo.artworkData) self.lastMusicItem?.artworkData = newInfo.artworkData From 86058f38b3decbbffab9d744946a3acbc4f47d22 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 6 Nov 2024 19:47:21 -0500 Subject: [PATCH 49/74] adjust states inside thread for new playing detection --- boringNotch/managers/MusicManager.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 9b9b632..743a4f4 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -189,14 +189,14 @@ class MusicManager: ObservableObject { self.lastMusicItem = (title: newInfo.title, artist: newInfo.artist, album: newInfo.album, duration: newInfo.duration, artworkData: lastMusicItem?.artworkData) MRMediaRemoteGetNowPlayingApplicationIsPlaying(DispatchQueue.main) { [weak self] isPlaying in self?.musicIsPaused(state: isPlaying, setIdle: true) + + guard isPlaying else { return } + + self?.artistName = newInfo.artist + self?.songTitle = newInfo.title + self?.album = newInfo.album + self?.songDuration = newInfo.duration } - - if !self.isPlaying { return } - - self.artistName = newInfo.artist - self.songTitle = newInfo.title - self.album = newInfo.album - self.songDuration = newInfo.duration } private func updateArtwork(_ artworkData: Data?) { From 08ff0b6651439dcce8db7b40eed18360ca87c3a3 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 6 Nov 2024 20:43:10 -0500 Subject: [PATCH 50/74] Update bundle identifier logic for more accurate icon --- boringNotch/components/Notch/NotchHomeView.swift | 2 +- boringNotch/managers/MusicManager.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 13bd745..0903d1b 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -41,7 +41,7 @@ struct NotchHomeView: View { .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) if vm.notchState == .open && !musicManager.usingAppIconForArtwork { - AppIcon(for: musicManager.bundleIdentifier) + AppIcon(for: musicManager.bundleIdentifier ?? "com.apple.Music") .resizable() .aspectRatio(contentMode: .fill) .frame(width: 30, height: 30) diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 743a4f4..4d23c56 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -162,7 +162,7 @@ class MusicManager: ObservableObject { // MARK: - Helper Methods private func updateBundleIdentifier(_ bundle: String?) { if let bundle = bundle { - self.bundleIdentifier = bundle == "com.apple.WebKit.GPU" ? "com.apple.Safari" : bundle + self.bundleIdentifier = bundle } } @@ -204,7 +204,7 @@ class MusicManager: ObservableObject { let artworkImage = NSImage(data: artworkData) { self.usingAppIconForArtwork = false self.updateAlbumArt(newAlbumArt: artworkImage) - } else if let appIconImage = AppIconAsNSImage(for: bundleIdentifier) { + } else if let appIconImage = AppIconAsNSImage(for: bundleIdentifier ?? nowPlaying.appBundleIdentifier ?? "") { self.usingAppIconForArtwork = true self.updateAlbumArt(newAlbumArt: appIconImage) } From 96019ae164549a7a0a9a3c5c66e73755c7785f50 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:10:39 -0500 Subject: [PATCH 51/74] allow timeline to update while paused, for the case when the back skip button is pressed or the source modifies the elapsedTime after being paused --- boringNotch/components/Notch/NotchHomeView.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 0903d1b..b5248c2 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -166,7 +166,8 @@ struct NotchHomeView: View { } private func updateSliderValue() { - guard !dragging, musicManager.timestampDate > lastDragged else { return } + guard !dragging, musicManager.timestampDate > lastDragged, + musicManager.timestampDate > musicManager.lastUpdated else { return } let currentTime = Date() let timeDifference = (musicManager.isPlaying ? currentTime.timeIntervalSince(musicManager.timestampDate) : musicManager.lastUpdated.timeIntervalSince(musicManager.timestampDate)) // Calculate the real-time elapsed time From ea5e59c34c38e42eb29c4faf3e502f65c043de53 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:37:37 -0500 Subject: [PATCH 52/74] More fixes --- boringNotch/components/Notch/NotchHomeView.swift | 3 +-- boringNotch/managers/MusicManager.swift | 11 ++++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index b5248c2..0903d1b 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -166,8 +166,7 @@ struct NotchHomeView: View { } private func updateSliderValue() { - guard !dragging, musicManager.timestampDate > lastDragged, - musicManager.timestampDate > musicManager.lastUpdated else { return } + guard !dragging, musicManager.timestampDate > lastDragged else { return } let currentTime = Date() let timeDifference = (musicManager.isPlaying ? currentTime.timeIntervalSince(musicManager.timestampDate) : musicManager.lastUpdated.timeIntervalSince(musicManager.timestampDate)) // Calculate the real-time elapsed time diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index 4d23c56..f28b5cb 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -189,14 +189,11 @@ class MusicManager: ObservableObject { self.lastMusicItem = (title: newInfo.title, artist: newInfo.artist, album: newInfo.album, duration: newInfo.duration, artworkData: lastMusicItem?.artworkData) MRMediaRemoteGetNowPlayingApplicationIsPlaying(DispatchQueue.main) { [weak self] isPlaying in self?.musicIsPaused(state: isPlaying, setIdle: true) - - guard isPlaying else { return } - - self?.artistName = newInfo.artist - self?.songTitle = newInfo.title - self?.album = newInfo.album - self?.songDuration = newInfo.duration } + self.artistName = newInfo.artist + self.songTitle = newInfo.title + self.album = newInfo.album + self.songDuration = newInfo.duration } private func updateArtwork(_ artworkData: Data?) { From b4a80949e59d34d2855b4d6aa604769f8a4e8dd8 Mon Sep 17 00:00:00 2001 From: Friedrich Stoltzfus Date: Thu, 7 Nov 2024 13:09:33 -0500 Subject: [PATCH 53/74] added shortcut ability to toggle notch open --- boringNotch/boringNotchApp.swift | 42 +++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index 836cb9b..4da8f6b 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -198,25 +198,33 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } - if !Defaults[.showOnAllDisplays] { - window = BoringNotchWindow( - contentRect: NSRect(x: 0, y: 0, width: openNotchSize.width + 20, height: openNotchSize.height + 30), - styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], - backing: .buffered, - defer: false - ) - - window.contentView = NSHostingView(rootView: ContentView(batteryModel: .init(vm: self.vm)).environmentObject(vm).environmentObject(MusicManager(vm: vm)!)) - - adjustWindowPosition(changeAlpha: true) - - window.orderFrontRegardless() - - NotchSpaceManager.shared.notchSpace.windows.insert(window) - } else { - adjustWindowPosition() + KeyboardShortcuts.onKeyDown(for: .toggleNotchOpen) { [weak self] in + guard let self = self else { return } + switch self.vm.notchState { + case .closed: + self.vm.open() + self.closeNotchWorkItem?.cancel() + + let workItem = DispatchWorkItem { + self.vm.close() + } + self.closeNotchWorkItem = workItem + + DispatchQueue.main.asyncAfter(deadline: .now() + 3.0, execute: workItem) + case .open: + self.closeNotchWorkItem?.cancel() + self.closeNotchWorkItem = nil + self.vm.close() + } } + window = BoringNotchWindow( + contentRect: NSRect(x: 0, y: 0, width: sizing.size.opened.width! + 20, height: sizing.size.opened.height! + 30), + styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], + backing: .buffered, + defer: false + ) + if coordinator.firstLaunch { DispatchQueue.main.async { self.openWindow(id: "onboarding") From 60e3ad490720cc23fec610546308bc98ff131bf1 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Sun, 17 Nov 2024 22:43:52 -0500 Subject: [PATCH 54/74] modify screen lock monitoring --- boringNotch/boringNotchApp.swift | 33 ++------------------------------ 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index 4da8f6b..e0023c3 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -123,39 +123,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { NotificationCenter.default.addObserver(forName: Notification.Name.notchHeightChanged, object: nil, queue: nil) { [weak self] _ in self?.adjustWindowPosition() } - - NotificationCenter.default.addObserver(forName: Notification.Name.showOnAllDisplaysChanged, object: nil, queue: nil) { [weak self] _ in - if(!Defaults[.showOnAllDisplays]) { - self?.window = BoringNotchWindow( - contentRect: NSRect(x: 0, y: 0, width: openNotchSize.width + 20, height: openNotchSize.height + 30), - styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], - backing: .buffered, - defer: false - ) - - if let windowValues = self?.windows.values { - for window in windowValues { - window.close() - } - } - - self?.window.contentView = NSHostingView(rootView: ContentView(batteryModel: .init(vm: self!.vm)).environmentObject(self!.vm).environmentObject(MusicManager(vm: self!.vm)!)) - - self?.adjustWindowPosition(changeAlpha: true) - - self?.window.orderFrontRegardless() - - NotchSpaceManager.shared.notchSpace.windows.insert(self!.window) - } else { - self?.window.close() - self?.windows = [:] - self?.adjustWindowPosition() - } - } - + DistributedNotificationCenter.default().addObserver(self, selector: #selector(onScreenLocked(_:)), name: NSNotification.Name(rawValue: "com.apple.screenIsLocked"), object: nil) DistributedNotificationCenter.default().addObserver(self, selector: #selector(onScreenUnlocked(_:)), name: NSNotification.Name(rawValue: "com.apple.screenIsUnlocked"), object: nil) - + KeyboardShortcuts.onKeyDown(for: .toggleSneakPeek) { [weak self] in guard let self = self else { return } From bcc5fe610cffb2977a91322b63e37820c40cd4bc Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Sun, 17 Nov 2024 23:02:17 -0500 Subject: [PATCH 55/74] Changed selectedScreen to preferredScreen and added a seperate selectedDisplay so that boringNotch remembers the last manually selected screen and tries to use it if it is available --- boringNotch/boringNotchApp.swift | 64 ++++--------------- .../components/Settings/SettingsView.swift | 11 +--- boringNotch/models/BoringViewModel.swift | 19 ++++++ 3 files changed, 33 insertions(+), 61 deletions(-) diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index e0023c3..6060db9 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -229,58 +229,20 @@ class AppDelegate: NSObject, NSApplicationDelegate { } @objc func adjustWindowPosition(changeAlpha: Bool = false) { - if Defaults[.showOnAllDisplays] { - for screen in NSScreen.screens { - if windows[screen] == nil { - let viewModel: BoringViewModel = .init(screen: screen.localizedName) - let window = BoringNotchWindow( - contentRect: NSRect(x: 0, y: 0, width: openNotchSize.width + 20, height: openNotchSize.height + 30), - styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], - backing: .buffered, - defer: false - ) - window.contentView = NSHostingView( - rootView: ContentView(batteryModel: .init(vm: viewModel)) - .environmentObject(viewModel) - .environmentObject(MusicManager(vm: viewModel)!) - ) - windows[screen] = window - viewModels[screen] = viewModel - window.orderFrontRegardless() - NotchSpaceManager.shared.notchSpace.windows.insert(window) - } - if let window = windows[screen] { - window.alphaValue = changeAlpha ? 0 : 1 - DispatchQueue.main.async { - window.setFrameOrigin(screen.frame.origin.applying(CGAffineTransform(translationX: (screen.frame.width / 2) - window.frame.width / 2, y: screen.frame.height - window.frame.height))) - window.alphaValue = 1 - } - } - if let viewModel = viewModels[screen] { - if viewModel.notchState == .closed { - viewModel.close() - } - } - } - } else { - if !NSScreen.screens.contains(where: {$0.localizedName == coordinator.preferredScreen}) { - coordinator.selectedScreen = NSScreen.main?.localizedName ?? "Unknown" - } + if !NSScreen.screens.contains(where: {$0.localizedName == vm.preferredScreen}) { + vm.selectedScreen = NSScreen.main?.localizedName ?? "Unknown" + } + + let selectedScreen = NSScreen.screens.first(where: {$0.localizedName == vm.selectedScreen}) + closedNotchSize = setNotchSize(screen: selectedScreen?.localizedName) + + if let screenFrame = selectedScreen { + window.alphaValue = changeAlpha ? 0 : 1 + window.makeKeyAndOrderFront(nil) - let selectedScreen = NSScreen.screens.first(where: {$0.localizedName == coordinator.selectedScreen}) - vm.notchSize = getClosedNotchSize(screen: selectedScreen?.localizedName) - - if let screenFrame = selectedScreen { - window.alphaValue = changeAlpha ? 0 : 1 - window.makeKeyAndOrderFront(nil) - - DispatchQueue.main.async {[weak self] in - self!.window.setFrameOrigin(screenFrame.frame.origin.applying(CGAffineTransform(translationX: (screenFrame.frame.width / 2) - self!.window.frame.width / 2, y: screenFrame.frame.height - self!.window.frame.height))) - self!.window.alphaValue = 1 - } - } - if vm.notchState == .closed { - vm.close() + DispatchQueue.main.async {[weak self] in + self!.window.setFrameOrigin(screenFrame.frame.origin.applying(CGAffineTransform(translationX: (screenFrame.frame.width / 2) - self!.window.frame.width / 2, y: screenFrame.frame.height - self!.window.frame.height))) + self!.window.alphaValue = 1 } } } diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index ec62304..2e752da 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -146,16 +146,7 @@ struct GeneralSettings: View { Section { Defaults.Toggle("Menubar icon", key: .menubarIcon) LaunchAtLogin.Toggle("Launch at login") - Defaults.Toggle(key: .showOnAllDisplays) { - HStack { - Text("Show on all displays") - customBadge(text: "Beta") - } - } - .onChange(of: showOnAllDisplays) { - NotificationCenter.default.post(name: Notification.Name.showOnAllDisplaysChanged, object: nil) - } - Picker("Show on a specific display", selection: $coordinator.preferredScreen) { + Picker("Show on a specific display", selection: $vm.preferredScreen) { ForEach(screens, id: \.self) { screen in Text(screen) } diff --git a/boringNotch/models/BoringViewModel.swift b/boringNotch/models/BoringViewModel.swift index 19e06fe..f38b9c1 100644 --- a/boringNotch/models/BoringViewModel.swift +++ b/boringNotch/models/BoringViewModel.swift @@ -60,7 +60,25 @@ class BoringViewModel: NSObject, ObservableObject { } } } + + @AppStorage("hudReplacement") var hudReplacement: Bool = true { + didSet { + toggleHudReplacement() + } + } + @AppStorage("preferred_screen_name") var preferredScreen = NSScreen.main?.localizedName ?? "Unknown" { + didSet { + selectedScreen = preferredScreen + NotificationCenter.default.post(name: Notification.Name.selectedScreenChanged, object: nil) + } + } + + @Published var selectedScreen: String = NSScreen.main?.localizedName ?? "Unknown" + + @AppStorage("currentMicStatus") var currentMicStatus: Bool = true + var notifier: TheBoringWorkerNotifier = .init() + deinit { destroy() } @@ -86,6 +104,7 @@ class BoringViewModel: NSObject, ObservableObject { } .assign(to: \.anyDropZoneTargeting, on: self) .store(in: &cancellables) + self.selectedScreen = preferredScreen } func open() { From c035401b6c93ecc5d77a2508635e7b42bbb1e9ca Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Sun, 17 Nov 2024 23:09:57 -0500 Subject: [PATCH 56/74] Add a Default Key and settings for show on all displays --- boringNotch/components/Settings/SettingsView.swift | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index 2e752da..5c96245 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -103,12 +103,8 @@ struct GeneralSettings: View { //@State var nonNotchHeightMode: NonNotchHeightMode = .matchRealNotchSize @Default(.nonNotchHeight) var nonNotchHeight @Default(.nonNotchHeightMode) var nonNotchHeightMode - @Default(.notchHeight) var notchHeight - @Default(.notchHeightMode) var notchHeightMode @Default(.showOnAllDisplays) var showOnAllDisplays - @Default(.enableGestures) var enableGestures - @Default(.openNotchOnHover) var openNotchOnHover - + var body: some View { Form { Section { @@ -146,13 +142,15 @@ struct GeneralSettings: View { Section { Defaults.Toggle("Menubar icon", key: .menubarIcon) LaunchAtLogin.Toggle("Launch at login") + Defaults.Toggle("Show on all displays", key: .showOnAllDisplays) Picker("Show on a specific display", selection: $vm.preferredScreen) { ForEach(screens, id: \.self) { screen in Text(screen) } } - .onChange(of: NSScreen.screens) { - screens = NSScreen.screens.compactMap({$0.localizedName}) + .disabled(showOnAllDisplays) + .onChange(of: NSScreen.screens) { old, new in + screens = new.compactMap({$0.localizedName}) } } header: { Text("System features") From 151830575fec8a52bda3a73a6c12ef7f9b249718 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:53:39 -0500 Subject: [PATCH 57/74] Add all displays as a beta feature --- boringNotch/boringNotchApp.swift | 161 +++++++++++------- .../components/Settings/SettingsView.swift | 10 +- 2 files changed, 110 insertions(+), 61 deletions(-) diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index 6060db9..3df3543 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -78,7 +78,6 @@ struct DynamicNotchApp: App { class AppDelegate: NSObject, NSApplicationDelegate { var statusItem: NSStatusItem? var windows: [NSScreen: NSWindow] = [:] - var viewModels: [NSScreen: BoringViewModel] = [:] var window: NSWindow! let vm: BoringViewModel = .init() @ObservedObject var coordinator = BoringViewCoordinator.shared @@ -123,15 +122,44 @@ class AppDelegate: NSObject, NSApplicationDelegate { NotificationCenter.default.addObserver(forName: Notification.Name.notchHeightChanged, object: nil, queue: nil) { [weak self] _ in self?.adjustWindowPosition() } - + + NotificationCenter.default.addObserver(forName: Notification.Name.showOnAllDisplaysChanged, object: nil, queue: nil) { [weak self] _ in + if(!Defaults[.showOnAllDisplays]) { + self?.window = BoringNotchWindow( + contentRect: NSRect(x: 0, y: 0, width: self!.sizing.size.opened.width! + 20, height: self!.sizing.size.opened.height! + 30), + styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], + backing: .buffered, + defer: false + ) + + if let windowValues = self?.windows.values { + for window in windowValues { + window.close() + } + } + + self?.window.contentView = NSHostingView(rootView: ContentView(batteryModel: .init(vm: self!.vm)).environmentObject(self!.vm).environmentObject(MusicManager(vm: self!.vm)!)) + + self?.adjustWindowPosition(changeAlpha: true) + + self?.window.orderFrontRegardless() + + NotchSpaceManager.shared.notchSpace.windows.insert(self!.window) + } else { + self?.window.close() + self?.windows = [:] + self?.adjustWindowPosition() + } + } + DistributedNotificationCenter.default().addObserver(self, selector: #selector(onScreenLocked(_:)), name: NSNotification.Name(rawValue: "com.apple.screenIsLocked"), object: nil) DistributedNotificationCenter.default().addObserver(self, selector: #selector(onScreenUnlocked(_:)), name: NSNotification.Name(rawValue: "com.apple.screenIsUnlocked"), object: nil) - + KeyboardShortcuts.onKeyDown(for: .toggleSneakPeek) { [weak self] in guard let self = self else { return } - self.coordinator.toggleSneakPeek( - status: !self.coordinator.sneakPeek.show, + self.vm.toggleSneakPeek( + status: !self.vm.sneakPeek.show, type: .music, duration: 3.0 ) @@ -139,25 +167,13 @@ class AppDelegate: NSObject, NSApplicationDelegate { KeyboardShortcuts.onKeyDown(for: .toggleNotchOpen) { [weak self] in guard let self = self else { return } - - let mouseLocation = NSEvent.mouseLocation - - var viewModel = self.vm; - - if(Defaults[.showOnAllDisplays]) { - for screen in NSScreen.screens { - if screen.frame.contains(mouseLocation) { - viewModel = viewModels[screen] ?? viewModel - } - } - } - switch viewModel.notchState { + switch self.vm.notchState { case .closed: - viewModel.open() + self.vm.open() self.closeNotchWorkItem?.cancel() let workItem = DispatchWorkItem { - viewModel.close() + self.vm.close() } self.closeNotchWorkItem = workItem @@ -165,38 +181,30 @@ class AppDelegate: NSObject, NSApplicationDelegate { case .open: self.closeNotchWorkItem?.cancel() self.closeNotchWorkItem = nil - viewModel.close() + self.vm.close() } } - KeyboardShortcuts.onKeyDown(for: .toggleNotchOpen) { [weak self] in - guard let self = self else { return } - switch self.vm.notchState { - case .closed: - self.vm.open() - self.closeNotchWorkItem?.cancel() - - let workItem = DispatchWorkItem { - self.vm.close() - } - self.closeNotchWorkItem = workItem - - DispatchQueue.main.asyncAfter(deadline: .now() + 3.0, execute: workItem) - case .open: - self.closeNotchWorkItem?.cancel() - self.closeNotchWorkItem = nil - self.vm.close() - } + if !Defaults[.showOnAllDisplays] { + window = BoringNotchWindow( + contentRect: NSRect(x: 0, y: 0, width: sizing.size.opened.width! + 20, height: sizing.size.opened.height! + 30), + styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], + backing: .buffered, + defer: false + ) + + window.contentView = NSHostingView(rootView: ContentView(batteryModel: .init(vm: self.vm)).environmentObject(vm).environmentObject(MusicManager(vm: vm)!)) + + adjustWindowPosition(changeAlpha: true) + + window.orderFrontRegardless() + + NotchSpaceManager.shared.notchSpace.windows.insert(window) + } else { + adjustWindowPosition() } - window = BoringNotchWindow( - contentRect: NSRect(x: 0, y: 0, width: sizing.size.opened.width! + 20, height: sizing.size.opened.height! + 30), - styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], - backing: .buffered, - defer: false - ) - - if coordinator.firstLaunch { + if vm.firstLaunch { DispatchQueue.main.async { self.openWindow(id: "onboarding") } @@ -229,20 +237,53 @@ class AppDelegate: NSObject, NSApplicationDelegate { } @objc func adjustWindowPosition(changeAlpha: Bool = false) { - if !NSScreen.screens.contains(where: {$0.localizedName == vm.preferredScreen}) { - vm.selectedScreen = NSScreen.main?.localizedName ?? "Unknown" - } - - let selectedScreen = NSScreen.screens.first(where: {$0.localizedName == vm.selectedScreen}) - closedNotchSize = setNotchSize(screen: selectedScreen?.localizedName) - - if let screenFrame = selectedScreen { - window.alphaValue = changeAlpha ? 0 : 1 - window.makeKeyAndOrderFront(nil) + if Defaults[.showOnAllDisplays] { + for screen in NSScreen.screens { + if windows[screen] == nil { + let window = BoringNotchWindow( + contentRect: NSRect(x: 0, y: 0, width: sizing.size.opened.width! + 20, height: sizing.size.opened.height! + 30), + styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], + backing: .buffered, + defer: false + ) + window.contentView = NSHostingView( + rootView: ContentView(batteryModel: .init(vm: self.vm)) + .environmentObject(vm) + .environmentObject(MusicManager(vm: vm)!) + ) + windows[screen] = window + window.orderFrontRegardless() + NotchSpaceManager.shared.notchSpace.windows.insert(window) + } + if let window = windows[screen] { + window.alphaValue = changeAlpha ? 0 : 1 + DispatchQueue.main.async { + window.setFrameOrigin(screen.frame.origin.applying(CGAffineTransform(translationX: (screen.frame.width / 2) - window.frame.width / 2, y: screen.frame.height - window.frame.height))) + window.alphaValue = 1 + } + } + } + } else { + if !NSScreen.screens.contains(where: {$0.localizedName == vm.preferredScreen}) { + vm.selectedScreen = NSScreen.main?.localizedName ?? "Unknown" + } - DispatchQueue.main.async {[weak self] in - self!.window.setFrameOrigin(screenFrame.frame.origin.applying(CGAffineTransform(translationX: (screenFrame.frame.width / 2) - self!.window.frame.width / 2, y: screenFrame.frame.height - self!.window.frame.height))) - self!.window.alphaValue = 1 + let selectedScreen = NSScreen.screens.first(where: {$0.localizedName == vm.selectedScreen}) + closedNotchSize = setNotchSize(screen: selectedScreen?.localizedName) + + if let screenFrame = selectedScreen { + window.alphaValue = changeAlpha ? 0 : 1 + window.makeKeyAndOrderFront(nil) + + DispatchQueue.main.async {[weak self] in + self!.window.setFrameOrigin(screenFrame.frame.origin.applying(CGAffineTransform(translationX: (screenFrame.frame.width / 2) - self!.window.frame.width / 2, y: screenFrame.frame.height - self!.window.frame.height))) + self!.window.alphaValue = 1 + } + } + // TODO: optimize animation here, especially for custom height slider + // TODO: don't animate if the window is changing screens + if vm.notchState == .closed { + vm.close() } } } diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index 5c96245..fde8e4f 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -142,7 +142,15 @@ struct GeneralSettings: View { Section { Defaults.Toggle("Menubar icon", key: .menubarIcon) LaunchAtLogin.Toggle("Launch at login") - Defaults.Toggle("Show on all displays", key: .showOnAllDisplays) + Defaults.Toggle(key: .showOnAllDisplays) { + HStack { + Text("Show on all displays") + customBadge(text: "Beta") + } + } + .onChange(of: showOnAllDisplays) { + NotificationCenter.default.post(name: Notification.Name.showOnAllDisplaysChanged, object: nil) + } Picker("Show on a specific display", selection: $vm.preferredScreen) { ForEach(screens, id: \.self) { screen in Text(screen) From ed6b855de06aee076271a55fd7abe565cc3c2fe9 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Sun, 17 Nov 2024 18:55:19 -0500 Subject: [PATCH 58/74] Dynamically change settings views based on preferences and fix deprecated onChange calls --- boringNotch/components/Settings/SettingsView.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index fde8e4f..ecb9be4 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -103,8 +103,9 @@ struct GeneralSettings: View { //@State var nonNotchHeightMode: NonNotchHeightMode = .matchRealNotchSize @Default(.nonNotchHeight) var nonNotchHeight @Default(.nonNotchHeightMode) var nonNotchHeightMode - @Default(.showOnAllDisplays) var showOnAllDisplays - + @Default(.enableGestures) var enableGestures + @Default(.openNotchOnHover) var openNotchOnHover + var body: some View { Form { Section { @@ -156,9 +157,8 @@ struct GeneralSettings: View { Text(screen) } } - .disabled(showOnAllDisplays) - .onChange(of: NSScreen.screens) { old, new in - screens = new.compactMap({$0.localizedName}) + .onChange(of: NSScreen.screens) { + screens = NSScreen.screens.compactMap({$0.localizedName}) } } header: { Text("System features") @@ -282,7 +282,7 @@ struct GeneralSettings: View { Section { Defaults.Toggle("Enable haptics", key: .enableHaptics) Defaults.Toggle("Open notch on hover", key: .openNotchOnHover) - Toggle("Remember last tab", isOn: $coordinator.openLastTabByDefault) + Toggle("Remember last tab", isOn: $vm.openLastTabByDefault) if openNotchOnHover { Slider(value: $minimumHoverDuration, in: 0...1, step: 0.1) { HStack { From beab22ebaa47304561ce10b505664ec2200599e9 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 20 Nov 2024 20:52:30 -0500 Subject: [PATCH 59/74] Refactor the code by transferring certain elements from BoringViewModel to BoringView Coordinator. This will help isolate the singleton-required code from the ViewModel that is now specific to each window. --- BoringViewCoordinator.swift | 167 +++++++++++++++++++++++ boringNotch.xcodeproj/project.pbxproj | 1 + boringNotch/models/BoringViewModel.swift | 24 +--- 3 files changed, 170 insertions(+), 22 deletions(-) create mode 100644 BoringViewCoordinator.swift diff --git a/BoringViewCoordinator.swift b/BoringViewCoordinator.swift new file mode 100644 index 0000000..0d3962e --- /dev/null +++ b/BoringViewCoordinator.swift @@ -0,0 +1,167 @@ +// +// BoringViewCoordinator.swift +// boringNotch +// +// Created by Alexander on 2024-11-20. +// + +import Combine +import SwiftUI +import TheBoringWorkerNotifier +import Defaults + +enum SneakContentType { + case brightness + case volume + case backlight + case music + case mic + case battery + case download +} + +struct sneakPeek { + var show: Bool = false + var type: SneakContentType = .music + var value: CGFloat = 0 + var icon: String = "" +} + +struct SharedSneakPeek: Codable { + var show: Bool + var type: String + var value: String + var icon: String +} + +class BoringViewCoordinator: ObservableObject { + static let shared = BoringViewCoordinator() + var notifier: TheBoringWorkerNotifier = .init() + + @Published var currentView: NotchViews = .home + private var sneakPeekDispatch: DispatchWorkItem? + + @AppStorage("firstLaunch") var firstLaunch: Bool = true + @AppStorage("showWhatsNew") var showWhatsNew: Bool = true + @AppStorage("musicLiveActivity") var showMusicLiveActivityOnClosed: Bool = true + @AppStorage("currentMicStatus") var currentMicStatus: Bool = true + + @AppStorage("alwaysShowTabs") var alwaysShowTabs: Bool = true { + didSet { + if !alwaysShowTabs { + openLastTabByDefault = false + if TrayDrop.shared.isEmpty || !Defaults[.openShelfByDefault] { + currentView = .home + } + } + } + } + + @AppStorage("openLastTabByDefault") var openLastTabByDefault: Bool = false { + didSet { + if openLastTabByDefault { + alwaysShowTabs = true + } + } + } + + @AppStorage("hudReplacement") var hudReplacement: Bool = true { + didSet { + notifier.postNotification(name: notifier.toggleHudReplacementNotification.name, userInfo: nil) + } + } + + @AppStorage("preferred_screen_name") var preferredScreen = NSScreen.main?.localizedName ?? "Unknown" { + didSet { + selectedScreen = preferredScreen + NotificationCenter.default.post(name: Notification.Name.selectedScreenChanged, object: nil) + } + } + + @Published var selectedScreen: String = NSScreen.main?.localizedName ?? "Unknown" + + @Published var optionKeyPressed: Bool = true + + private init() { + self.selectedScreen = preferredScreen + notifier = TheBoringWorkerNotifier() + } + + func setupWorkersNotificationObservers() { + notifier.setupObserver(notification: notifier.micStatusNotification, handler: initialMicStatus) + notifier.setupObserver(notification: notifier.sneakPeakNotification, handler: sneakPeekEvent) + } + + @objc func sneakPeekEvent(_ notification: Notification) { + let decoder = JSONDecoder() + if let decodedData = try? decoder.decode(SharedSneakPeek.self, from: notification.userInfo?.first?.value as! Data) { + let contentType = decodedData.type == "brightness" ? SneakContentType.brightness : decodedData.type == "volume" ? SneakContentType.volume : decodedData.type == "backlight" ? SneakContentType.backlight : decodedData.type == "mic" ? SneakContentType.mic : SneakContentType.brightness + + let value = CGFloat((NumberFormatter().number(from: decodedData.value) ?? 0.0).floatValue) + let icon = decodedData.icon + + print(decodedData) + + toggleSneakPeek(status: decodedData.show, type: contentType, value: value, icon: icon) + + } else { + print("Failed to decode JSON data") + } + } + + func toggleSneakPeek(status: Bool, type: SneakContentType, duration: TimeInterval = 1.5, value: CGFloat = 0, icon: String = "") { + self.sneakPeekDuration = duration + if type != .music { + //close() + if !hudReplacement { + return + } + } + DispatchQueue.main.async { + withAnimation(.smooth) { + self.sneakPeek.show = status + self.sneakPeek.type = type + self.sneakPeek.value = value + self.sneakPeek.icon = icon + } + } + + if type == .mic { + currentMicStatus = value == 1 + } + } + + private var sneakPeekDuration: TimeInterval = 1.5 + @Published var sneakPeek: sneakPeek = .init() { + didSet { + if sneakPeek.show { + sneakPeekDispatch?.cancel() + + sneakPeekDispatch = DispatchWorkItem { [weak self] in + guard let self = self else { return } + withAnimation { + self.toggleSneakPeek(status: false, type: SneakContentType.music) + self.sneakPeekDuration = 1.5 + } + } + DispatchQueue.main + .asyncAfter( + deadline: .now() + self.sneakPeekDuration, + execute: sneakPeekDispatch! + ) + } + } + } + + @objc func initialMicStatus(_ notification: Notification) { + currentMicStatus = notification.userInfo?.first?.value as! Bool + } + + func toggleMic() { + notifier.postNotification(name: notifier.toggleMicNotification.name, userInfo: nil) + } + + func showEmpty() { + currentView = .home + } +} diff --git a/boringNotch.xcodeproj/project.pbxproj b/boringNotch.xcodeproj/project.pbxproj index ce0a1f6..3f2f42b 100644 --- a/boringNotch.xcodeproj/project.pbxproj +++ b/boringNotch.xcodeproj/project.pbxproj @@ -328,6 +328,7 @@ 14CEF4092C5CAED200855D72 = { isa = PBXGroup; children = ( + 11CC44A12CEE614100C7244B /* BoringViewCoordinator.swift */, 14CEF4142C5CAED300855D72 /* boringNotch */, 14CEF4132C5CAED300855D72 /* Products */, 14D031EC2C689DB70096E6A1 /* Frameworks */, diff --git a/boringNotch/models/BoringViewModel.swift b/boringNotch/models/BoringViewModel.swift index f38b9c1..8d18e3e 100644 --- a/boringNotch/models/BoringViewModel.swift +++ b/boringNotch/models/BoringViewModel.swift @@ -60,25 +60,7 @@ class BoringViewModel: NSObject, ObservableObject { } } } - - @AppStorage("hudReplacement") var hudReplacement: Bool = true { - didSet { - toggleHudReplacement() - } - } - @AppStorage("preferred_screen_name") var preferredScreen = NSScreen.main?.localizedName ?? "Unknown" { - didSet { - selectedScreen = preferredScreen - NotificationCenter.default.post(name: Notification.Name.selectedScreenChanged, object: nil) - } - } - - @Published var selectedScreen: String = NSScreen.main?.localizedName ?? "Unknown" - - @AppStorage("currentMicStatus") var currentMicStatus: Bool = true - var notifier: TheBoringWorkerNotifier = .init() - deinit { destroy() } @@ -104,13 +86,11 @@ class BoringViewModel: NSObject, ObservableObject { } .assign(to: \.anyDropZoneTargeting, on: self) .store(in: &cancellables) - self.selectedScreen = preferredScreen } func open() { - withAnimation(.smooth.speed(2)) { - self.notchSize = .init(width: Sizes().size.opened.width!, height: Sizes().size.opened.height!) - self.notchMetastability = true + withAnimation(.bouncy) { + self.notchSize = CGSize(width: openNotchSize.width, height: openNotchSize.height) self.notchState = .open } } From dc4aa8e1af856764bb3d54f03f06dc9d1ded25cd Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 20 Nov 2024 20:55:12 -0500 Subject: [PATCH 60/74] Use new BoringView Coordinator, refactor matters.swift and sizes, and add new show on all displays beta option --- boringNotch/ContentView.swift | 8 +-- boringNotch/Localizable.xcstrings | 2 +- boringNotch/boringNotchApp.swift | 56 ++++++++++++------- .../components/Notch/NotchHomeView.swift | 35 +++++++++--- .../components/Settings/SettingsView.swift | 4 +- boringNotch/managers/MusicManager.swift | 1 + 6 files changed, 72 insertions(+), 34 deletions(-) diff --git a/boringNotch/ContentView.swift b/boringNotch/ContentView.swift index 9fa56ca..596e467 100644 --- a/boringNotch/ContentView.swift +++ b/boringNotch/ContentView.swift @@ -36,15 +36,14 @@ struct ContentView: View { var body: some View { ZStack(alignment: .top) { NotchLayout() - .padding(.horizontal, vm.notchState == .open ? Defaults[.cornerRadiusScaling] ? (vm.sizes.cornerRadius.opened.inset! - 5) : (vm.sizes.cornerRadius.closed.inset! - 5) : 12) + .padding(.horizontal, vm.notchState == .open ? Defaults[.cornerRadiusScaling] ? (cornerRadiusInsets.opened) : (cornerRadiusInsets.closed - 5) : 12) .padding([.horizontal, .bottom], vm.notchState == .open ? 12 : 0) .frame(maxWidth: (((musicManager.isPlaying || !musicManager.isPlayerIdle) && vm.notchState == .closed && coordinator.showMusicLiveActivityOnClosed) || (vm.expandingView.show && (vm.expandingView.type == .battery)) || Defaults[.inlineHUD]) ? nil : vm.notchSize.width + ((hoverAnimation || (vm.notchState == .closed)) ? 20 : 0) + gestureProgress, maxHeight: ((coordinator.sneakPeek.show && coordinator.sneakPeek.type != .music) || (coordinator.sneakPeek.show && coordinator.sneakPeek.type == .music && vm.notchState == .closed)) ? nil : vm.notchSize.height + (hoverAnimation ? 8 : 0) + gestureProgress / 3, alignment: .top) .background(.black) .mask { NotchShape(cornerRadius: ((vm.notchState == .open) && Defaults[.cornerRadiusScaling]) ? cornerRadiusInsets.opened : cornerRadiusInsets.closed) } - .frame(width: vm.notchState == .closed ? (((musicManager.isPlaying || !musicManager.isPlayerIdle) && vm.showMusicLiveActivityOnClosed) || (vm.expandingView.show && (vm.expandingView.type == .battery)) || (Defaults[.inlineHUD] && vm.sneakPeek.show && vm.sneakPeek.type != .music)) ? nil : Sizes().size.closed.width! + (hoverAnimation ? 20 : 0) + gestureProgress : nil, height: vm.notchState == .closed ? Sizes().size.closed.height! + (hoverAnimation ? 8 : 0) + gestureProgress / 3 : nil, alignment: .top) - .padding(.bottom, vm.notchState == .open ? 30 : 0) // Safe area to ensure the notch does not close if the cursor is within 30px of the notch from the bottom. + .frame(width: vm.notchState == .closed ? (((musicManager.isPlaying || !musicManager.isPlayerIdle) && coordinator.showMusicLiveActivityOnClosed) || (vm.expandingView.show && (vm.expandingView.type == .battery)) || (Defaults[.inlineHUD] && coordinator.sneakPeek.show && coordinator.sneakPeek.type != .music)) ? nil : vm.closedNotchSize.width + (hoverAnimation ? 20 : 0) + gestureProgress : nil, height: vm.notchState == .closed ? vm.closedNotchSize.height + (hoverAnimation ? 8 : 0) + gestureProgress / 3 : nil, alignment: .top) .conditionalModifier(Defaults[.openNotchOnHover]) { view in view .onHover { hovering in @@ -284,8 +283,7 @@ struct ContentView: View { .aspectRatio(contentMode: .fill) ) .clipped() - .clipShape(RoundedRectangle(cornerRadius: vm.musicPlayerSizes.image.cornerRadius.closed.inset!)) - .opacity(musicManager.isPlaying ? 1 : 0.4) + .clipShape(RoundedRectangle(cornerRadius: MusicPlayerImageSizes.cornerRadiusInset.closed)) .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) .frame(width: max(0, vm.closedNotchSize.height - 12), height: max(0, vm.closedNotchSize.height - 12)) } diff --git a/boringNotch/Localizable.xcstrings b/boringNotch/Localizable.xcstrings index b715c63..535826e 100644 --- a/boringNotch/Localizable.xcstrings +++ b/boringNotch/Localizable.xcstrings @@ -839,4 +839,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index 3df3543..836cb9b 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -78,6 +78,7 @@ struct DynamicNotchApp: App { class AppDelegate: NSObject, NSApplicationDelegate { var statusItem: NSStatusItem? var windows: [NSScreen: NSWindow] = [:] + var viewModels: [NSScreen: BoringViewModel] = [:] var window: NSWindow! let vm: BoringViewModel = .init() @ObservedObject var coordinator = BoringViewCoordinator.shared @@ -126,7 +127,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { NotificationCenter.default.addObserver(forName: Notification.Name.showOnAllDisplaysChanged, object: nil, queue: nil) { [weak self] _ in if(!Defaults[.showOnAllDisplays]) { self?.window = BoringNotchWindow( - contentRect: NSRect(x: 0, y: 0, width: self!.sizing.size.opened.width! + 20, height: self!.sizing.size.opened.height! + 30), + contentRect: NSRect(x: 0, y: 0, width: openNotchSize.width + 20, height: openNotchSize.height + 30), styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], backing: .buffered, defer: false @@ -158,8 +159,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { KeyboardShortcuts.onKeyDown(for: .toggleSneakPeek) { [weak self] in guard let self = self else { return } - self.vm.toggleSneakPeek( - status: !self.vm.sneakPeek.show, + self.coordinator.toggleSneakPeek( + status: !self.coordinator.sneakPeek.show, type: .music, duration: 3.0 ) @@ -167,13 +168,25 @@ class AppDelegate: NSObject, NSApplicationDelegate { KeyboardShortcuts.onKeyDown(for: .toggleNotchOpen) { [weak self] in guard let self = self else { return } - switch self.vm.notchState { + + let mouseLocation = NSEvent.mouseLocation + + var viewModel = self.vm; + + if(Defaults[.showOnAllDisplays]) { + for screen in NSScreen.screens { + if screen.frame.contains(mouseLocation) { + viewModel = viewModels[screen] ?? viewModel + } + } + } + switch viewModel.notchState { case .closed: - self.vm.open() + viewModel.open() self.closeNotchWorkItem?.cancel() let workItem = DispatchWorkItem { - self.vm.close() + viewModel.close() } self.closeNotchWorkItem = workItem @@ -181,13 +194,13 @@ class AppDelegate: NSObject, NSApplicationDelegate { case .open: self.closeNotchWorkItem?.cancel() self.closeNotchWorkItem = nil - self.vm.close() + viewModel.close() } } if !Defaults[.showOnAllDisplays] { window = BoringNotchWindow( - contentRect: NSRect(x: 0, y: 0, width: sizing.size.opened.width! + 20, height: sizing.size.opened.height! + 30), + contentRect: NSRect(x: 0, y: 0, width: openNotchSize.width + 20, height: openNotchSize.height + 30), styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], backing: .buffered, defer: false @@ -204,7 +217,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { adjustWindowPosition() } - if vm.firstLaunch { + if coordinator.firstLaunch { DispatchQueue.main.async { self.openWindow(id: "onboarding") } @@ -240,18 +253,20 @@ class AppDelegate: NSObject, NSApplicationDelegate { if Defaults[.showOnAllDisplays] { for screen in NSScreen.screens { if windows[screen] == nil { + let viewModel: BoringViewModel = .init(screen: screen.localizedName) let window = BoringNotchWindow( - contentRect: NSRect(x: 0, y: 0, width: sizing.size.opened.width! + 20, height: sizing.size.opened.height! + 30), + contentRect: NSRect(x: 0, y: 0, width: openNotchSize.width + 20, height: openNotchSize.height + 30), styleMask: [.borderless, .nonactivatingPanel, .utilityWindow, .hudWindow], backing: .buffered, defer: false ) window.contentView = NSHostingView( - rootView: ContentView(batteryModel: .init(vm: self.vm)) - .environmentObject(vm) - .environmentObject(MusicManager(vm: vm)!) + rootView: ContentView(batteryModel: .init(vm: viewModel)) + .environmentObject(viewModel) + .environmentObject(MusicManager(vm: viewModel)!) ) windows[screen] = window + viewModels[screen] = viewModel window.orderFrontRegardless() NotchSpaceManager.shared.notchSpace.windows.insert(window) } @@ -262,14 +277,19 @@ class AppDelegate: NSObject, NSApplicationDelegate { window.alphaValue = 1 } } + if let viewModel = viewModels[screen] { + if viewModel.notchState == .closed { + viewModel.close() + } + } } } else { - if !NSScreen.screens.contains(where: {$0.localizedName == vm.preferredScreen}) { - vm.selectedScreen = NSScreen.main?.localizedName ?? "Unknown" + if !NSScreen.screens.contains(where: {$0.localizedName == coordinator.preferredScreen}) { + coordinator.selectedScreen = NSScreen.main?.localizedName ?? "Unknown" } - let selectedScreen = NSScreen.screens.first(where: {$0.localizedName == vm.selectedScreen}) - closedNotchSize = setNotchSize(screen: selectedScreen?.localizedName) + let selectedScreen = NSScreen.screens.first(where: {$0.localizedName == coordinator.selectedScreen}) + vm.notchSize = getClosedNotchSize(screen: selectedScreen?.localizedName) if let screenFrame = selectedScreen { window.alphaValue = changeAlpha ? 0 : 1 @@ -280,8 +300,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { self!.window.alphaValue = 1 } } - // TODO: optimize animation here, especially for custom height slider - // TODO: don't animate if the window is changing screens if vm.notchState == .closed { vm.close() } diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 0903d1b..f08de08 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -23,12 +23,33 @@ struct NotchHomeView: View { let albumArtNamespace: Namespace.ID var body: some View { - Group { - if !vm.firstLaunch { - HStack(alignment: .top, spacing: 30) { - HStack { - ZStack(alignment: .bottomTrailing) { - if Defaults[.lightingEffect] { + if !coordinator.firstLaunch { + HStack(alignment: .top, spacing: 30) { + HStack { + ZStack(alignment: .bottomTrailing) { + if Defaults[.lightingEffect] { + Color.clear + .aspectRatio(1, contentMode: .fit) + .background( + Image(nsImage: musicManager.albumArt) + .resizable() + .aspectRatio(contentMode: .fill) + ) + .clipped() + .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? MusicPlayerImageSizes.cornerRadiusInset.opened : MusicPlayerImageSizes.cornerRadiusInset.closed)) + .scaleEffect(x: 1.3, y: 2.8) + .rotationEffect(.degrees(92)) + .blur(radius: 35) + .opacity(min(0.6, 1 - max(musicManager.albumArt.getBrightness(), 0.3))) + .onAppear { + print(musicManager.albumArt.getBrightness()) + } + } + + Button { + musicManager.openMusicApp() + } label: { + ZStack(alignment: .bottomTrailing) { Color.clear .aspectRatio(1, contentMode: .fit) .background( @@ -37,7 +58,7 @@ struct NotchHomeView: View { .aspectRatio(contentMode: .fill) ) .clipped() - .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? vm.musicPlayerSizes.image.cornerRadius.opened.inset! : vm.musicPlayerSizes.image.cornerRadius.closed.inset!)) + .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? MusicPlayerImageSizes.cornerRadiusInset.opened : MusicPlayerImageSizes.cornerRadiusInset.closed)) .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) if vm.notchState == .open && !musicManager.usingAppIconForArtwork { diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index ecb9be4..47d3d59 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -152,7 +152,7 @@ struct GeneralSettings: View { .onChange(of: showOnAllDisplays) { NotificationCenter.default.post(name: Notification.Name.showOnAllDisplaysChanged, object: nil) } - Picker("Show on a specific display", selection: $vm.preferredScreen) { + Picker("Show on a specific display", selection: $coordinator.preferredScreen) { ForEach(screens, id: \.self) { screen in Text(screen) } @@ -282,7 +282,7 @@ struct GeneralSettings: View { Section { Defaults.Toggle("Enable haptics", key: .enableHaptics) Defaults.Toggle("Open notch on hover", key: .openNotchOnHover) - Toggle("Remember last tab", isOn: $vm.openLastTabByDefault) + Toggle("Remember last tab", isOn: $coordinator.openLastTabByDefault) if openNotchOnHover { Slider(value: $minimumHoverDuration, in: 0...1, step: 0.1) { HStack { diff --git a/boringNotch/managers/MusicManager.swift b/boringNotch/managers/MusicManager.swift index f28b5cb..57899fd 100644 --- a/boringNotch/managers/MusicManager.swift +++ b/boringNotch/managers/MusicManager.swift @@ -40,6 +40,7 @@ class MusicManager: ObservableObject { @Published var timestampDate: Date = Date() @Published var playbackRate: Double = 0 @ObservedObject var detector: FullscreenMediaDetector + @ObservedObject var coordinator = BoringViewCoordinator.shared @Published var usingAppIconForArtwork: Bool = false var nowPlaying: NowPlaying From 438d89e0455dd3c6aa65f25a0638d564662579e7 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Wed, 20 Nov 2024 20:55:58 -0500 Subject: [PATCH 61/74] Move BoringViewCoordinator into folder --- BoringViewCoordinator.swift | 167 -------------------------- boringNotch.xcodeproj/project.pbxproj | 1 - 2 files changed, 168 deletions(-) delete mode 100644 BoringViewCoordinator.swift diff --git a/BoringViewCoordinator.swift b/BoringViewCoordinator.swift deleted file mode 100644 index 0d3962e..0000000 --- a/BoringViewCoordinator.swift +++ /dev/null @@ -1,167 +0,0 @@ -// -// BoringViewCoordinator.swift -// boringNotch -// -// Created by Alexander on 2024-11-20. -// - -import Combine -import SwiftUI -import TheBoringWorkerNotifier -import Defaults - -enum SneakContentType { - case brightness - case volume - case backlight - case music - case mic - case battery - case download -} - -struct sneakPeek { - var show: Bool = false - var type: SneakContentType = .music - var value: CGFloat = 0 - var icon: String = "" -} - -struct SharedSneakPeek: Codable { - var show: Bool - var type: String - var value: String - var icon: String -} - -class BoringViewCoordinator: ObservableObject { - static let shared = BoringViewCoordinator() - var notifier: TheBoringWorkerNotifier = .init() - - @Published var currentView: NotchViews = .home - private var sneakPeekDispatch: DispatchWorkItem? - - @AppStorage("firstLaunch") var firstLaunch: Bool = true - @AppStorage("showWhatsNew") var showWhatsNew: Bool = true - @AppStorage("musicLiveActivity") var showMusicLiveActivityOnClosed: Bool = true - @AppStorage("currentMicStatus") var currentMicStatus: Bool = true - - @AppStorage("alwaysShowTabs") var alwaysShowTabs: Bool = true { - didSet { - if !alwaysShowTabs { - openLastTabByDefault = false - if TrayDrop.shared.isEmpty || !Defaults[.openShelfByDefault] { - currentView = .home - } - } - } - } - - @AppStorage("openLastTabByDefault") var openLastTabByDefault: Bool = false { - didSet { - if openLastTabByDefault { - alwaysShowTabs = true - } - } - } - - @AppStorage("hudReplacement") var hudReplacement: Bool = true { - didSet { - notifier.postNotification(name: notifier.toggleHudReplacementNotification.name, userInfo: nil) - } - } - - @AppStorage("preferred_screen_name") var preferredScreen = NSScreen.main?.localizedName ?? "Unknown" { - didSet { - selectedScreen = preferredScreen - NotificationCenter.default.post(name: Notification.Name.selectedScreenChanged, object: nil) - } - } - - @Published var selectedScreen: String = NSScreen.main?.localizedName ?? "Unknown" - - @Published var optionKeyPressed: Bool = true - - private init() { - self.selectedScreen = preferredScreen - notifier = TheBoringWorkerNotifier() - } - - func setupWorkersNotificationObservers() { - notifier.setupObserver(notification: notifier.micStatusNotification, handler: initialMicStatus) - notifier.setupObserver(notification: notifier.sneakPeakNotification, handler: sneakPeekEvent) - } - - @objc func sneakPeekEvent(_ notification: Notification) { - let decoder = JSONDecoder() - if let decodedData = try? decoder.decode(SharedSneakPeek.self, from: notification.userInfo?.first?.value as! Data) { - let contentType = decodedData.type == "brightness" ? SneakContentType.brightness : decodedData.type == "volume" ? SneakContentType.volume : decodedData.type == "backlight" ? SneakContentType.backlight : decodedData.type == "mic" ? SneakContentType.mic : SneakContentType.brightness - - let value = CGFloat((NumberFormatter().number(from: decodedData.value) ?? 0.0).floatValue) - let icon = decodedData.icon - - print(decodedData) - - toggleSneakPeek(status: decodedData.show, type: contentType, value: value, icon: icon) - - } else { - print("Failed to decode JSON data") - } - } - - func toggleSneakPeek(status: Bool, type: SneakContentType, duration: TimeInterval = 1.5, value: CGFloat = 0, icon: String = "") { - self.sneakPeekDuration = duration - if type != .music { - //close() - if !hudReplacement { - return - } - } - DispatchQueue.main.async { - withAnimation(.smooth) { - self.sneakPeek.show = status - self.sneakPeek.type = type - self.sneakPeek.value = value - self.sneakPeek.icon = icon - } - } - - if type == .mic { - currentMicStatus = value == 1 - } - } - - private var sneakPeekDuration: TimeInterval = 1.5 - @Published var sneakPeek: sneakPeek = .init() { - didSet { - if sneakPeek.show { - sneakPeekDispatch?.cancel() - - sneakPeekDispatch = DispatchWorkItem { [weak self] in - guard let self = self else { return } - withAnimation { - self.toggleSneakPeek(status: false, type: SneakContentType.music) - self.sneakPeekDuration = 1.5 - } - } - DispatchQueue.main - .asyncAfter( - deadline: .now() + self.sneakPeekDuration, - execute: sneakPeekDispatch! - ) - } - } - } - - @objc func initialMicStatus(_ notification: Notification) { - currentMicStatus = notification.userInfo?.first?.value as! Bool - } - - func toggleMic() { - notifier.postNotification(name: notifier.toggleMicNotification.name, userInfo: nil) - } - - func showEmpty() { - currentView = .home - } -} diff --git a/boringNotch.xcodeproj/project.pbxproj b/boringNotch.xcodeproj/project.pbxproj index 3f2f42b..ce0a1f6 100644 --- a/boringNotch.xcodeproj/project.pbxproj +++ b/boringNotch.xcodeproj/project.pbxproj @@ -328,7 +328,6 @@ 14CEF4092C5CAED200855D72 = { isa = PBXGroup; children = ( - 11CC44A12CEE614100C7244B /* BoringViewCoordinator.swift */, 14CEF4142C5CAED300855D72 /* boringNotch */, 14CEF4132C5CAED300855D72 /* Products */, 14D031EC2C689DB70096E6A1 /* Frameworks */, From f28895979993da1da859f289764ffea034bf3a22 Mon Sep 17 00:00:00 2001 From: Alexander Greco <33609792+Alexander5015@users.noreply.github.com> Date: Thu, 21 Nov 2024 02:19:12 -0500 Subject: [PATCH 62/74] Add the option to customize the size of the notch window on notched screens (seperate control from non-notched displays) --- boringNotch/Localizable.xcstrings | 2 +- boringNotch/components/Settings/SettingsView.swift | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/boringNotch/Localizable.xcstrings b/boringNotch/Localizable.xcstrings index 535826e..b715c63 100644 --- a/boringNotch/Localizable.xcstrings +++ b/boringNotch/Localizable.xcstrings @@ -839,4 +839,4 @@ } }, "version" : "1.0" -} \ No newline at end of file +} diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index 47d3d59..ec62304 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -103,6 +103,9 @@ struct GeneralSettings: View { //@State var nonNotchHeightMode: NonNotchHeightMode = .matchRealNotchSize @Default(.nonNotchHeight) var nonNotchHeight @Default(.nonNotchHeightMode) var nonNotchHeightMode + @Default(.notchHeight) var notchHeight + @Default(.notchHeightMode) var notchHeightMode + @Default(.showOnAllDisplays) var showOnAllDisplays @Default(.enableGestures) var enableGestures @Default(.openNotchOnHover) var openNotchOnHover From c64188ec6cc01f9e6dfc9a188d0fe9bafda1cb26 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Sun, 24 Nov 2024 16:00:20 +0530 Subject: [PATCH 63/74] Fixed Compiling Error --- .../components/Notch/NotchHomeView.swift | 172 +++++++++--------- 1 file changed, 87 insertions(+), 85 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index f08de08..3bbf1f2 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -23,33 +23,12 @@ struct NotchHomeView: View { let albumArtNamespace: Namespace.ID var body: some View { - if !coordinator.firstLaunch { - HStack(alignment: .top, spacing: 30) { - HStack { - ZStack(alignment: .bottomTrailing) { - if Defaults[.lightingEffect] { - Color.clear - .aspectRatio(1, contentMode: .fit) - .background( - Image(nsImage: musicManager.albumArt) - .resizable() - .aspectRatio(contentMode: .fill) - ) - .clipped() - .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? MusicPlayerImageSizes.cornerRadiusInset.opened : MusicPlayerImageSizes.cornerRadiusInset.closed)) - .scaleEffect(x: 1.3, y: 2.8) - .rotationEffect(.degrees(92)) - .blur(radius: 35) - .opacity(min(0.6, 1 - max(musicManager.albumArt.getBrightness(), 0.3))) - .onAppear { - print(musicManager.albumArt.getBrightness()) - } - } - - Button { - musicManager.openMusicApp() - } label: { - ZStack(alignment: .bottomTrailing) { + Group { + if !coordinator.firstLaunch { + HStack(alignment: .top, spacing: 30) { + HStack { + ZStack(alignment: .bottomTrailing) { + if Defaults[.lightingEffect] { Color.clear .aspectRatio(1, contentMode: .fit) .background( @@ -59,70 +38,93 @@ struct NotchHomeView: View { ) .clipped() .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? MusicPlayerImageSizes.cornerRadiusInset.opened : MusicPlayerImageSizes.cornerRadiusInset.closed)) - .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) - - if vm.notchState == .open && !musicManager.usingAppIconForArtwork { - AppIcon(for: musicManager.bundleIdentifier ?? "com.apple.Music") - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: 30, height: 30) - .offset(x: 10, y: 10) - .transition(.scale.combined(with: .opacity).animation(.bouncy.delay(0.3))) + .scaleEffect(x: 1.4, y: 1.4) + .rotationEffect(.degrees(92)) + .blur(radius: 35) + .opacity(min(0.6, 1 - max(musicManager.albumArt.getBrightness(), 0.3))) + .onAppear { + print(musicManager.albumArt.getBrightness()) + } + } + + + Button { + musicManager.openMusicApp() + } label: { + ZStack(alignment: .bottomTrailing) { + Color.clear + .aspectRatio(1, contentMode: .fit) + .background( + Image(nsImage: musicManager.albumArt) + .resizable() + .aspectRatio(contentMode: .fill) + ) + .clipped() + .clipShape(RoundedRectangle(cornerRadius: Defaults[.cornerRadiusScaling] ? MusicPlayerImageSizes.cornerRadiusInset.opened : MusicPlayerImageSizes.cornerRadiusInset.closed)) + .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) + + if vm.notchState == .open && !musicManager.usingAppIconForArtwork { + AppIcon(for: musicManager.bundleIdentifier ?? "com.apple.Music") + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 30, height: 30) + .offset(x: 10, y: 10) + .transition(.scale.combined(with: .opacity).animation(.bouncy.delay(0.3))) + } } .scaleEffect(musicManager.isPlaying ? 1 : 0.85) } .buttonStyle(PlainButtonStyle()) + .opacity(musicManager.isPlaying ? 1 : 0.4) + .scaleEffect(musicManager.isPlaying ? 1 : 0.85) } - Group { - if vm.notchState == .open { - VStack(alignment: .leading) { - GeometryReader { geo in - VStack(alignment: .leading, spacing: 4){ - VStack(alignment: .leading, spacing: 0) { - MarqueeText($musicManager.songTitle, font: .headline, nsFont: .headline, textColor: .white, frameWidth: geo.size.width) - MarqueeText( - $musicManager.artistName, - font: .headline, - nsFont: .headline, - textColor: Defaults[.playerColorTinting] ? Color(nsColor: musicManager.avgColor) - .ensureMinimumBrightness(factor: 0.6) : .gray, - frameWidth: geo.size.width - ) - .fontWeight(.medium) - } - - MusicSliderView(sliderValue: $sliderValue, - duration: $musicManager.songDuration, - lastDragged: $lastDragged, - color: musicManager.avgColor, - dragging: $dragging) { newValue in - musicManager.seekTrack(to: newValue) - } - .padding(.top, 5) - .frame(height: 36) - } + VStack(alignment: .leading) { + GeometryReader { geo in + VStack(alignment: .leading, spacing: 4){ + VStack(alignment: .leading, spacing: 0) { + MarqueeText($musicManager.songTitle, font: .headline, nsFont: .headline, textColor: .white, frameWidth: geo.size.width) + MarqueeText( + $musicManager.artistName, + font: .headline, + nsFont: .headline, + textColor: Defaults[.playerColorTinting] ? Color(nsColor: musicManager.avgColor) + .ensureMinimumBrightness(factor: 0.6) : .gray, + frameWidth: geo.size.width + ) + .fontWeight(.medium) } - .padding(.top, 10) - .padding(.leading, 5) - HStack(spacing: 8) { - HoverButton(icon: "backward.fill", scale: .medium) { - musicManager.previousTrack() - } - HoverButton(icon: musicManager.isPlaying ? "pause.fill" : "play.fill", scale: .large) { - print("tapped") - musicManager.togglePlayPause() - } - HoverButton(icon: "forward.fill", scale: .medium) { - musicManager.nextTrack() - } + + MusicSliderView(sliderValue: $sliderValue, + duration: $musicManager.songDuration, + lastDragged: $lastDragged, + color: musicManager.avgColor, + dragging: $dragging) { newValue in + musicManager.seekTrack(to: newValue) } - .frame(maxWidth: .infinity, alignment: .center) + .padding(.top, 5) + .frame(height: 36) + } + } + .padding(.top, 10) + .padding(.leading, 5) + + HStack(spacing: 8) { + HoverButton(icon: "backward.fill", scale: .medium) { + musicManager.previousTrack() + } + HoverButton(icon: musicManager.isPlaying ? "pause.fill" : "play.fill", scale: .large) { + print("tapped") + musicManager.togglePlayPause() + } + HoverButton(icon: "forward.fill", scale: .medium) { + musicManager.nextTrack() } - .buttonStyle(PlainButtonStyle()) - .frame(minWidth: 170) } + .frame(maxWidth: .infinity, alignment: .center) } + .buttonStyle(PlainButtonStyle()) + .frame(minWidth: 170) } if Defaults[.showCalendar] { @@ -130,10 +132,10 @@ struct NotchHomeView: View { .onContinuousHover { phase in if Defaults[.closeGestureEnabled] { switch phase { - case .active: - Defaults[.closeGestureEnabled] = false - case .ended: - Defaults[.closeGestureEnabled] = false + case .active: + Defaults[.closeGestureEnabled] = false + case .ended: + Defaults[.closeGestureEnabled] = false } } } @@ -173,8 +175,8 @@ struct NotchHomeView: View { timer?.cancel() } } - .transition(.opacity.combined(with: .blurReplace)) } + .transition(.opacity.combined(with: .blurReplace)) .blur(radius: vm.notchState == .closed ? 15 : 0) } From db2988a7eb20ec0b163b4639d3c8e268862867d1 Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Sun, 24 Nov 2024 16:23:06 +0530 Subject: [PATCH 64/74] Further Enhancements to Blur Animation --- boringNotch/components/Notch/NotchHomeView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index 3bbf1f2..f7acb31 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -174,10 +174,10 @@ struct NotchHomeView: View { // Stop the timer when the view disappears timer?.cancel() } + .transition(.opacity.animation(.smooth.speed(0.9)).combined(with: .blurReplace.animation(.smooth.speed(0.9))).combined(with: .move(edge: .top))) + .blur(radius: vm.notchState == .closed ? 30 : 0) } } - .transition(.opacity.combined(with: .blurReplace)) - .blur(radius: vm.notchState == .closed ? 15 : 0) } private func startTimer() { From ee4ea14378cef1f06301643b363e6d2ceb3f791f Mon Sep 17 00:00:00 2001 From: Om Chachad Date: Sat, 30 Nov 2024 00:28:40 +0530 Subject: [PATCH 65/74] Fixed Runtime Error --- boringNotch/components/Notch/NotchHomeView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/boringNotch/components/Notch/NotchHomeView.swift b/boringNotch/components/Notch/NotchHomeView.swift index f7acb31..9b45495 100644 --- a/boringNotch/components/Notch/NotchHomeView.swift +++ b/boringNotch/components/Notch/NotchHomeView.swift @@ -178,6 +178,7 @@ struct NotchHomeView: View { .blur(radius: vm.notchState == .closed ? 30 : 0) } } + .transition(.opacity.combined(with: .blurReplace)) } private func startTimer() { From 3dfbb3bbd0cf073e03fc232a10209c8c2afa7849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harsh=20Vardhan=20Goswami=20=F0=9F=90=B3?= Date: Thu, 19 Dec 2024 16:13:20 +0530 Subject: [PATCH 66/74] =?UTF-8?q?=E2=9C=A8=20CoriAI=20Code=20Review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/code-review.yml | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/code-review.yml diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml new file mode 100644 index 0000000..3d4d9f7 --- /dev/null +++ b/.github/workflows/code-review.yml @@ -0,0 +1,42 @@ +name: Cori AI Code Review + +on: + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - '**.md' + - 'LICENSE' + - '.gitignore' + +jobs: + code-review: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + actions: read + issues: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required to get full history for better context + + - name: ✹ CoriAI Code Review, PhD + uses: theboringhumane/cori-ai@1.2.0 # Uses the action from the current repository + with: + openai_api_key: ${{ secrets.OPENAI_API_KEY }} + openai_base_url: ${{ secrets.OPENAI_BASE_URL }} + github_token: ${{ secrets.GITHUB_TOKEN }} + model: 'gpt-4o-mini' + extra_prompt: | + Please focus on: + Boring notch is a dynamic island for macOS written in Swift. + + - Swift best practices and macos app development guidelines + - Security implications of the changes + - Performance optimizations + - Code maintainability and documentation + - Potential edge cases and error handling + - Use emojis in your comments and be very specific. + - Performance optimization of the app + - Aim for efficient working of the app \ No newline at end of file From aefd82498aa1c713264c1be6feb0c22714bafbbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harsh=20Vardhan=20Goswami=20=F0=9F=90=B3?= Date: Thu, 19 Dec 2024 16:43:28 +0530 Subject: [PATCH 67/74] Use cori-ai App --- .github/workflows/code-review.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 3d4d9f7..024175a 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -9,6 +9,16 @@ on: - '.gitignore' jobs: + cori_authentication: + runs-on: ubuntu-latest + steps: + - name: Cori Auth Step + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.CORI_APP_ID }} + private-key: ${{ secrets.CORI_APP_PRIVATE_KEY }} + code-review: runs-on: ubuntu-latest permissions: @@ -26,12 +36,11 @@ jobs: with: openai_api_key: ${{ secrets.OPENAI_API_KEY }} openai_base_url: ${{ secrets.OPENAI_BASE_URL }} - github_token: ${{ secrets.GITHUB_TOKEN }} + github_token: ${{ steps.generate-token.outputs.token }} model: 'gpt-4o-mini' extra_prompt: | Please focus on: Boring notch is a dynamic island for macOS written in Swift. - - Swift best practices and macos app development guidelines - Security implications of the changes - Performance optimizations @@ -39,4 +48,4 @@ jobs: - Potential edge cases and error handling - Use emojis in your comments and be very specific. - Performance optimization of the app - - Aim for efficient working of the app \ No newline at end of file + - Aim for efficient working of the app From 0a70dd06bdc681b9b6513b9f55072aca62bdc99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harsh=20Vardhan=20Goswami=20=F0=9F=90=B3?= Date: Thu, 19 Dec 2024 16:54:55 +0530 Subject: [PATCH 68/74] =?UTF-8?q?Update=20cori-ai=20=F0=9F=A4=96=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/code-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 024175a..40ef4d5 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -32,7 +32,7 @@ jobs: fetch-depth: 0 # Required to get full history for better context - name: ✹ CoriAI Code Review, PhD - uses: theboringhumane/cori-ai@1.2.0 # Uses the action from the current repository + uses: theboringhumane/cori-ai@1.2.1 # Uses the action from the current repository with: openai_api_key: ${{ secrets.OPENAI_API_KEY }} openai_base_url: ${{ secrets.OPENAI_BASE_URL }} From 13d4265b6a16a9c613f7351cd882935bcf93b4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harsh=20Vardhan=20Goswami=20=F0=9F=90=B3?= Date: Fri, 20 Dec 2024 11:59:58 +0530 Subject: [PATCH 69/74] =?UTF-8?q?=E2=9C=A8=20CoriAI=20Code=20Reviewer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/code-review.yml | 69 +++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 40ef4d5..e06f29c 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -1,4 +1,4 @@ -name: Cori AI Code Review +name: ✹ CoriAI Code Review on: pull_request: @@ -9,17 +9,7 @@ on: - '.gitignore' jobs: - cori_authentication: - runs-on: ubuntu-latest - steps: - - name: Cori Auth Step - id: generate-token - uses: actions/create-github-app-token@v1 - with: - app-id: ${{ vars.CORI_APP_ID }} - private-key: ${{ secrets.CORI_APP_PRIVATE_KEY }} - - code-review: + cori-ai-review: runs-on: ubuntu-latest permissions: contents: read @@ -31,8 +21,15 @@ jobs: with: fetch-depth: 0 # Required to get full history for better context + - name: Cori Auth Step + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.CORI_APP_ID }} + private-key: ${{ secrets.CORI_APP_PRIVATE_KEY }} + - name: ✹ CoriAI Code Review, PhD - uses: theboringhumane/cori-ai@1.2.1 # Uses the action from the current repository + uses: theboringhumane/cori-ai@1.2.3 # Uses the action from the current repository with: openai_api_key: ${{ secrets.OPENAI_API_KEY }} openai_base_url: ${{ secrets.OPENAI_BASE_URL }} @@ -40,12 +37,40 @@ jobs: model: 'gpt-4o-mini' extra_prompt: | Please focus on: - Boring notch is a dynamic island for macOS written in Swift. - - Swift best practices and macos app development guidelines - - Security implications of the changes - - Performance optimizations - - Code maintainability and documentation - - Potential edge cases and error handling - - Use emojis in your comments and be very specific. - - Performance optimization of the app - - Aim for efficient working of the app + You are a senior Swift/SwiftUI developer reviewing pull requests for the BoringNotch macOS application. This app customizes the MacBook notch area with + various features like music controls and visualizer, calendar, camera mirror. + When reviewing PRs, consider the following key aspects: + + 1. Project Structure Compliance + Verify new files follow the established directory structure as defined + Ensure file naming follows conventions + + 2. Dependencies Management + Check if new dependencies are properly added to the Package.resolved file + Verify minimum deployment target compatibility (macOS 14.2+) + + 3. Code Quality Standards + Verify compliance with SwiftLint rules + Check proper access control usage (private, fileprivate, internal, public) + Ensure documentation for public APIs + Verify proper error handling implementation + Check for memory management best practices + + 4. MacOS Specific Considerations + Verify proper entitlements usage + Check privacy permissions declarations + Ensure compatibility with notch-specific APIs + Verify proper window management for notch area + + Review Checklist + Does the code follow the project's architectural patterns? + Does the PR include proper documentation? + Are there any potential performance issues? + Does the code handle edge cases and error conditions? + Is the UI implementation consistent with existing design patterns? + Are accessibility features properly implemented? + Does the code handle system events and state changes appropriately? + Are there any potential memory leaks? + Does the PR include appropriate changelog entries? + + Mandatory to Use emojis in your comments add code examples as well with your comments (markdown format) \ No newline at end of file From a3b57483395fb40856a98179e0568f895db5fccd Mon Sep 17 00:00:00 2001 From: Thomas Havy Date: Tue, 17 Dec 2024 23:22:08 +0100 Subject: [PATCH 70/74] Update Settings view to support localization Added French localization for testing --- boringNotch/Localizable.xcstrings | 1969 ++++++++++++++--- .../components/Settings/SettingsView.swift | 126 +- boringNotch/enums/generic.swift | 20 +- 3 files changed, 1751 insertions(+), 364 deletions(-) diff --git a/boringNotch/Localizable.xcstrings b/boringNotch/Localizable.xcstrings index b715c63..5d8710b 100644 --- a/boringNotch/Localizable.xcstrings +++ b/boringNotch/Localizable.xcstrings @@ -48,6 +48,12 @@ "state" : "translated", "value" : "About" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "À Propos" + } } } }, @@ -58,47 +64,155 @@ "state" : "translated", "value" : "Accent color" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur d’accentuation" + } } } }, "Activate" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer" + } + } + } }, "Activate your license" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer votre license" + } + } + } }, "Activation" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activation" + } + } + } }, "Add" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouter" + } + } + } }, "Add manually" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouter manuellement" + } + } + } }, "Add new visualizer" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouter une visualisation" + } + } + } }, "Additional features" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "FonctionnalitĂ©s supplĂ©mentaires" + } + } + } }, "AirDrop" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "AirDrop" + } + } + } }, "AirDrop service not available" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le service AirDrop n’est pas disponible" + } + } + } }, "All-day" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toute la journĂ©e" + } + } + } }, "Always show tabs" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toujours afficher les onglets" + } + } + } }, "App icon" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "IcĂŽne de l’application" + } + } + } }, "Appearance" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apparence" + } + } + } + }, + "Autohide BoringNotch in fullscreen" : { + "comment" : "Toggle in the parameters to autohide Boring notch in fullscreen", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cacher BoringNotch en plein Ă©cran" + } + } + } }, "Automatically check for updates" : { "localizations" : { @@ -107,11 +221,24 @@ "state" : "translated", "value" : "Automatically check for updates" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "VĂ©rifier automatiquement les mises Ă  jour" + } } } }, "Automatically download updates" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "TĂ©lĂ©charger automatiquement les mises Ă  jour" + } + } + } }, "Battery" : { "localizations" : { @@ -120,11 +247,24 @@ "state" : "translated", "value" : "Battery" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Batterie" + } } } }, "Boost your productivity with Clipboard Manager" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Booster votre productivitĂ© avec le Clipboard Manager" + } + } + } }, "Boring Controls" : { "extractionState" : "stale", @@ -134,6 +274,12 @@ "state" : "translated", "value" : "Boring Controls" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boring ContrĂŽles" + } } } }, @@ -154,6 +300,12 @@ "state" : "translated", "value" : "Both" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Les deux" + } } } }, @@ -165,11 +317,24 @@ "state" : "translated", "value" : "Buy Me a Coffee" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Payez-moi un cafĂ©" + } } } }, "Cancel" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuler" + } + } + } }, "Charging" : { "localizations" : { @@ -178,6 +343,12 @@ "state" : "translated", "value" : "Charging" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chargement" + } } } }, @@ -189,562 +360,1531 @@ "state" : "translated", "value" : "Check for updates" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "VĂ©rifier les mises Ă  jours" + } } } }, "Check for Updates
" : { - - }, - "Circle" : { - - }, - "Close" : { - - }, - "Coming soon" : { "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Coming soon" + "value" : "VĂ©rifier les mises Ă  jours
" } } } }, - "Custom color" : { + "Circle" : { "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Custom color" + "value" : "Cercle" } } } }, - "Custom height" : { - - }, - "Custom music live activity animation" : { - - }, - "Custom notch size - %.0f" : { - - }, - "Custom vizualizers (Lottie)" : { - - }, - "Default" : { - - }, - "Description" : { - - }, - "Disable" : { - - }, - "Disabled" : { - - }, - "Download" : { - - }, - "Download icon style" : { + "Circular" : { + "extractionState" : "manual", "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Download icon style" + "value" : "Circulaire" } } } }, - "Download indicator style" : { + "Close" : { "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Download indicator style" + "value" : "Fermer" } } } }, - "Download indicators" : { - - }, - "Downloads" : { + "Close gesture" : { + "comment" : "Toggle in parameters to enable the closing gesture", "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Downloads" + "value" : "Geste de fermeture" } } } }, - "Drop files here" : { - - }, - "Easily copy, store, and manage your most-used content. Upgrade now for advanced features like multi-item storage and quick access!" : { - - }, - "Edit" : { - - }, - "Edit layout" : { - - }, - "Email address" : { - - }, - "Enable colored spectrograms" : { - "extractionState" : "stale", + "Coming soon" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enable colored spectrograms" + "value" : "Coming soon" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "BientĂŽt disponible" } } } }, - "Enable haptics" : { - "extractionState" : "stale", + "Corner radius scaling" : { + "comment" : "Toggle in parameters for corner radius scaling", "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Enable haptics" + "value" : "Mise Ă  l’échelle des coins" } } } }, - "Enable HUD replacement" : { - - }, - "Enable music live activity" : { - - }, - "Enhance your experience with HUDs" : { - - }, - "Enjoy your free time!" : { - - }, - "Error" : { - - }, - "ERROR: VIEW NOT DEFINED" : { - - }, - "Exclude apps" : { + "Custom color" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Exclude apps" + "value" : "Custom color" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur personnalisĂ©e" } } } }, - "Exit" : { - - }, - "Extensions" : { + "Custom height" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hauteur personnalisĂ©e" + } + } + } + }, + "Custom music live activity animation" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animation personnalisĂ©e pour les Live Activities musicales" + } + } + } + }, + "Custom notch size - %.0f" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille personnalisĂ©e de l’encoche - %.0f" + } + } + } + }, + "Custom vizualizers (Lottie)" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Visualisations personnalisĂ©es (Lottie)" + } + } + } + }, + "Default" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Par dĂ©faut" + } + } + } + }, + "Description" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Description" + } + } + } + }, + "Disable" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "DĂ©sactiver" + } + } + } + }, + "Disabled" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "DĂ©sactivĂ©" + } + } + } + }, + "Download" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "TĂ©lĂ©chargement" + } + } + } + }, + "Download icon style" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download icon style" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Style de l’icĂŽne de tĂ©lĂ©chargement" + } + } + } + }, + "Download indicator style" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download indicator style" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Style de l’indicateur de tĂ©lĂ©chargement" + } + } + } + }, + "Download indicators" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Indicateur de tĂ©lĂ©chargement" + } + } + } + }, + "Downloads" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Downloads" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "TĂ©lĂ©chargements" + } + } + } + }, + "Drop files here" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "DĂ©posez vos fichiers ici" + } + } + } + }, + "Easily copy, store, and manage your most-used content. Upgrade now for advanced features like multi-item storage and quick access!" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiez, gardez et gĂ©rer vos contenus les plus utilisĂ©s. Mettez Ă  niveau maintenant pour activer des fonctionnalitĂ©s avancĂ©es comme le stockage de plusieurs fichiers et les accĂšs rapides!" + } + } + } + }, + "Edit" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Éditer" + } + } + } + }, + "Edit layout" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Éditer la disposition" + } + } + } + }, + "Email address" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adresse email" + } + } + } + }, + "Enable blur effect behind album art" : { + "comment" : "Toggle in parameters to enable the blur effect behind album art", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer l’effet de flou derriĂšre la couverture d’album" + } + } + } + }, + "Enable boring mirror" : { + "comment" : "Toggle in parameters to enable Boring mirror", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer le Boring Mirror" + } + } + } + }, + "Enable colored spectrograms" : { + "comment" : "Toggle in parameters for colored spectrograms", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable colored spectrograms" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer les spectrogrammes en couleur" + } + } + } + }, + "Enable gestures" : { + "comment" : "Toggle in parameters to enable gestures", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer les gestes" + } + } + } + }, + "Enable glowing effect" : { + "comment" : "Toggle in parameters to enable the glowing effet for progress bars", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer l’effet de brillance" + } + } + } + }, + "Enable haptics" : { + "comment" : "Parameter toggle for enabling haptics", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable haptics" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer le retour haptique" + } + } + } + }, + "Enable HUD replacement" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer le remplacement de l’ATH" + } + } + } + }, + "Enable music live activity" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer le Live Activity pour la musique" + } + } + } + }, + "Enable Safari Downloads" : { + "comment" : "Toggle to in parameters to enable Safari downloads", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer les tĂ©lĂ©chargements Safari" + } + } + } + }, + "Enable shelf" : { + "comment" : "Toggle in parameters to enable shelf system", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer les Ă©tagĂšres" + } + } + } + }, + "Enable sneak peek" : { + "comment" : "Toggle in parameters to enable the Sneak Peek feature", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer Sneak Peek" + } + } + } + }, + "Enable window shadow" : { + "comment" : "Toggle in parameters for enabling the window shadow", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer les ombres de fenĂȘtre" + } + } + } + }, + "Enhance your experience with HUDs" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "AmĂ©liorer votre expĂ©rience avec des ATHs" + } + } + } + }, + "Enjoy your free time!" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Profitez de votre pĂ©riode d’essai!" + } + } + } + }, + "Error" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur" + } + } + } + }, + "ERROR: VIEW NOT DEFINED" : { }, - "Fullscreen media playback detection" : { - + "Exclude apps" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exclude apps" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exclure des applications" + } + } + } + }, + "Exit" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quitter" + } + } + } + }, + "Extensions" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Extensions" + } + } + } + }, + "Fullscreen media playback detection" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "DĂ©tection des contrĂŽles multimĂ©dia en plein Ă©cran" + } + } + } + }, + "General" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "General" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "GĂ©nĂ©ral" + } + } + } + }, + "Gesture control" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ContrĂŽle des gestes" + } + } + } + }, + "Gesture sensitivity" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "SensibilitĂ© des gestes" + } + } + } + }, + "Get started" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "DĂ©marrer" + } + } + } + }, + "GitHub" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "GitHub" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "GitHub" + } + } + } + }, + "Got it!" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compris!" + } + } + } + }, + "Gradient" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gradiant" + } + } + } + }, + "Hierarchical" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "HiĂ©rarchique" + } + } + } + }, + "High" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ÉlevĂ©" + } + } + } + }, + "HUD style" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Style de l’ATH" + } + } + } + }, + "HUDs" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ATHs" + } + } + } + }, + "In progress" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "En cours" + } + } + } + }, + "Inline" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "En ligne" + } + } + } + }, + "Install updates automatically" : { + "extractionState" : "stale", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Install updates automatically" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installer des mises Ă  jour automatiquement" + } + } + } + }, + "Installed extensions" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Extensions installĂ©es" + } + } + } + }, + "Key" : { + "extractionState" : "manual" + }, + "Later" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plus tard" + } + } + } + }, + "Launch at login" : { + "comment" : "Launch at login", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvrir Ă  la connexion" + } + } + } + }, + "License key" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ClĂ© de license" + } + } + } + }, + "Lottie JSON URL" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL du JSON Lottie" + } + } + } + }, + "Low" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Faible" + } + } + } }, - "General" : { + "Made with đŸ«¶đŸ» by not so boring not.people" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Made with đŸ«¶đŸ» by not so boring not.people" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fait avec đŸ«¶ par des personnes pas si « boring »!" + } + } + } + }, + "Match album art" : { + "extractionState" : "manual", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Faire correspondre Ă  la couverture d’album" + } + } + } + }, + "Match menubar height" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Faire correspondre avec la hauteur de la barre de menu" + } + } + } + }, + "Match real notch size" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Faire correspondre avec la hauteur de l’encoche" + } + } + } + }, + "Media" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Media" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "MĂ©dia" + } + } + } + }, + "Media change with horizontal gestures" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Changer le mĂ©dia avec un geste horizontal" + } + } + } + }, + "Media inactivity timeout" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Media inactivity timeout" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temps de dĂ©tection d’inactivitĂ© des mĂ©dias" + } + } + } + }, + "Media playback live activity" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Media playback live activity" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Live Activity de la lecture de mĂ©dia" + } + } + } + }, + "Medium" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Moyen" + } + } + } + }, + "Menubar icon" : { + "comment" : "Toggle in parameter to enable menubar icon", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menubar icon" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "IcĂŽne dans la barre de menu" + } + } + } + }, + "Mic %@" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Microphone %@" + } + } + } + }, + "Minimum hover duration" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temps minimum de survol" + } + } + } + }, + "Mirror" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mirroir" + } + } + } + }, + "Mirror shape" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forme du mirroir" + } + } + } + }, + "More" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plus" + } + } + } + }, + "muted" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "sourdine" + } + } + } + }, + "Name" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom" + } + } + } + }, + "Need Restart" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "RedĂ©marrage nĂ©cessaire" + } + } + } + }, + "No custom animation available" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune animation personnalisĂ©e disponible" + } + } + } + }, + "No custom visualizer" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune visualisation personnalisĂ©e disponible" + } + } + } + }, + "No events today" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun Ă©vĂšnement aujourd’hui" + } + } + } + }, + "No excluded apps" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune application exclue" + } + } + } + }, + "No excludes" : { + "extractionState" : "stale", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No excludes" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune exclusion" + } + } + } + }, + "No extension installed" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune extension installĂ©e" + } + } + } + }, + "Non-notch display height" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille d’écran sans encoche" + } + } + } + }, + "Notch behavior" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comportement de l’encoche" + } + } + } + }, + "Notch display height" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille d’écran avec encoche" + } + } + } + }, + "Notch Height" : { "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "General" + "value" : "Hauteur d’encoche" } } } }, - "Gesture control" : { - + "OK" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ok" + } + } + } }, - "Gesture sensitivity" : { - + "Onboarding" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Embarquement" + } + } + } }, - "Get started" : { - + "One or more files failed to load" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un ou plusieurs fichier n’a pas pu ĂȘtre chargĂ©" + } + } + } }, - "GitHub" : { + "Only app icon" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "GitHub" + "value" : "Only app icon" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seulement l’icĂŽne de l’application" } } } }, - "Got it!" : { - - }, - "Gradient" : { - - }, - "Hierarchical" : { - - }, - "High" : { - - }, - "HUD style" : { - - }, - "HUDs" : { - - }, - "In progress" : { - - }, - "Inline" : { - - }, - "Install updates automatically" : { - "extractionState" : "stale", + "Only download icon" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Install updates automatically" + "value" : "Only download icon" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seulement l’icĂŽne de tĂ©lĂ©chargement" } } } }, - "Installed extensions" : { - - }, - "Later" : { - - }, - "License key" : { - - }, - "Lottie JSON URL" : { - + "Open notch on hover" : { + "comment" : "Parameter toggle for opening notch on hover", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvrir en survolant" + } + } + } }, - "Low" : { - + "Open shelf tab by default if items added" : { + "comment" : "Toggle in parameters to open shelf by default if items are added", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvrir l’étagĂšre par dĂ©faut quand un Ă©lĂ©ment est ajoutĂ©" + } + } + } }, - "Made with đŸ«¶đŸ» by not so boring not.people" : { + "Percentage" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Made with đŸ«¶đŸ» by not so boring not.people" + "value" : "Percentage" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pourcentage" } } } }, - "Match menubar height" : { - + "Player tinting" : { + "comment" : "Toggle in parameters to tint the player", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Teinte du lecteur" + } + } + } }, - "Match real notch size" : { - + "Progress" : { + "extractionState" : "manual", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ProgrĂšs" + } + } + } }, - "Media" : { + "Progress bar" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Media" + "value" : "Progress bar" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Barre de progression" } } } }, - "Media change with horizontal gestures" : { - - }, - "Media inactivity timeout" : { + "Progressbar style" : { "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Media inactivity timeout" + "value" : "Style de la barre de progression" } } } }, - "Media playback live activity" : { + "Quit" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Media playback live activity" + "value" : "Quit" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quitter" } } } }, - "Medium" : { - + "Quit app" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quitter l’application" + } + } + } }, - "Menubar icon" : { + "Quit boring.notch" : { "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Menubar icon" + "value" : "Quit boring.notch" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quitter boring.notch" } } } }, - "Mic %@" : { - - }, - "Minimum hover duration" : { - - }, - "Mirror" : { - - }, - "Mirror shape" : { - - }, - "More" : { - - }, - "muted" : { - - }, - "Name" : { - - }, - "Need Restart" : { - - }, - "No custom animation available" : { - - }, - "No custom visualizer" : { - - }, - "No events today" : { - - }, - "No excluded apps" : { - + "Rectangular" : { + "extractionState" : "manual", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rectangulaire" + } + } + } }, - "No excludes" : { - "extractionState" : "stale", + "Release name" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "No excludes" + "value" : "Release name" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom de version" } } } }, - "No extension installed" : { - - }, - "Non-notch display height" : { - - }, - "Notch behavior" : { - - }, - "Notch display height" : { - - }, - "Notch Height" : { - - }, - "OK" : { - + "Remember last tab" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se souvenir du dernier onglet" + } + } + } }, - "Onboarding" : { - + "Restart" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "RedĂ©marrer" + } + } + } }, - "One or more files failed to load" : { - + "Restart Boring Notch" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "RedĂ©marrer Boring Notch" + } + } + } }, - "Only app icon" : { + "Running" : { "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Only app icon" + "value" : "En fonctionnement" } } } }, - "Only download icon" : { + "seconds" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Only download icon" + "value" : "seconds" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "secondes" } } } }, - "Percentage" : { + "selected" : { "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Percentage" + "value" : "sĂ©lĂ©ctionnĂ©" } } } }, - "Progress bar" : { + "Selected animation" : { "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Progress bar" + "value" : "Animation sĂ©lectionnĂ©e" } } } }, - "Progressbar style" : { - - }, - "Quit" : { + "Settings" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Quit" + "value" : "Settings" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ParamĂštres" } } } }, - "Quit app" : { - - }, - "Quit boring.notch" : { - "extractionState" : "stale", + "Settings icon in notch" : { + "comment" : "Toggle in parameters for settings icon in notch", "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Quit boring.notch" + "value" : "IcĂŽne des paramĂštres dans Notch" } } } }, - "Release name" : { + "Shelf" : { "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Release name" + "value" : "ÉtagĂšre" } } } }, - "Remember last tab" : { - - }, - "Restart" : { - - }, - "Restart Boring Notch" : { - - }, - "Running" : { - - }, - "seconds" : { - "extractionState" : "stale", + "Shortcuts" : { "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "seconds" + "value" : "Raccourcis" } } } }, - "selected" : { - - }, - "Selected animation" : { - - }, - "Settings" : { + "Show battery indicator" : { + "comment" : "Toggle in parameters to show battery indicator", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Settings" + "value" : "Show battery indicator" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher l’indicateur de batterie" } } } }, - "Shelf" : { - - }, - "Shortcuts" : { - - }, - "Show battery indicator" : { - "extractionState" : "stale", + "Show calendar" : { + "comment" : "Toggle in parameters to show the calendar in the expanded view", "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Show battery indicator" + "value" : "Montrer le calendrier" } } } }, "Show charging indicator" : { - "extractionState" : "stale", + "comment" : "Toogle in parameters to Show charging indicator", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", "value" : "Show charging indicator" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher l’indicateur de charge" + } } } }, "Show cool face animation while inactivity" : { - "extractionState" : "stale", + "comment" : "Toggle in parameters to show a face animation when inactive", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", "value" : "Show cool face animation while inactivity" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher une animation stylĂ©e quand inactif" + } } } }, "Show download progress" : { - "extractionState" : "stale", + "comment" : "Toggle in parameters to show the download progress in supported apps", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", "value" : "Show download progress" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher le status du tĂ©lĂ©chargement" + } } } }, "Show on a specific display" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher sur un Ă©cran spĂ©cifique" + } + } + } }, "Show on all displays" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher sur tous les Ă©crans" + } + } + } }, "Slider color" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur du curseur" + } + } + } }, "Sneak Peek shows the media title and artist under the notch for a few seconds." : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sneak Peek affiche le nom et l’artiste du mĂ©dia sous l’encoche pour quelques secondes." + } + } + } }, "Software updates" : { "localizations" : { @@ -753,17 +1893,44 @@ "state" : "translated", "value" : "Software updates" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mises Ă  jour" + } } } }, "Speed" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vitesse" + } + } + } }, "Square" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "CarrĂ©" + } + } + } }, "Stopped" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ArrĂȘtĂ©" + } + } + } }, "Support Us" : { "localizations" : { @@ -772,41 +1939,135 @@ "state" : "translated", "value" : "Support Us" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supportez-nous" + } } } }, "System features" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "FonctionnalitĂ©s systĂšme" + } + } + } }, "TheBoringNotch" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "TheBoringNotch" + } + } + } }, "Toggle Notch Open:" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permuter l’ouverture de l’encoche:" + } + } + } }, "Toggle Sneak Peek:" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permuter Sneak Peak:" + } + } + } }, "Transform your notch truly yours" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rendez l’encoche vraiment vĂŽtre" + } + } + } }, "Two-finger swipe up on notch to close, two-finger swipe down on notch to open when **Open notch on hover** option is disabled" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Faites glissez avec deux doigts vers le haut pour fermer Boring Notch, vers le bas pour l’ouvrir quand **Ouvrir l’encoche en survolant** est dĂ©sactivĂ©" + } + } + } }, "Uninstall" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "DĂ©sinstaller" + } + } + } }, "Unlock advanced features and improve your experience. Upgrade now for more customizations!" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "DĂ©bloquez des fonctionnalitĂ©s avancĂ©e pour amĂ©liorer votre expĂ©rience. Mettez Ă  jour maintenant pour plus de personnalisations!" + } + } + } }, "unmuted" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "actif" + } + } + } }, "Upgrade to Pro" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passez Ă  Pro" + } + } + } + }, + "Use accent color" : { + "comment" : "Toggle in parameters to show the accent color of progress bars", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utiliser la couleur d’accentuation" + } + } + } }, "Use music visualizer spectrogram" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utiliser le spectrogramme comme visualisateur musical" + } + } + } }, "Version" : { "localizations" : { @@ -815,6 +2076,12 @@ "state" : "translated", "value" : "Version" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" + } } } }, @@ -825,18 +2092,56 @@ "state" : "translated", "value" : "Version info" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Informations de version" + } } } }, "Welcome" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bienvenue" + } + } + } }, "Welcome window" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "FenĂȘtre de bienvenue" + } + } + } }, "What's New" : { - + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "NouveautĂ©s" + } + } + } + }, + "White" : { + "extractionState" : "manual", + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanc" + } + } + } } }, "version" : "1.0" -} +} \ No newline at end of file diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index ec62304..d2914ea 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -144,8 +144,13 @@ struct GeneralSettings: View { } Section { - Defaults.Toggle("Menubar icon", key: .menubarIcon) - LaunchAtLogin.Toggle("Launch at login") + Defaults.Toggle( + NSLocalizedString("Menubar icon", comment: "Toggle in parameter to enable menubar icon"), + key: .menubarIcon + ) + LaunchAtLogin.Toggle( + NSLocalizedString("Launch at login", comment:"Launch at login") + ) Defaults.Toggle(key: .showOnAllDisplays) { HStack { Text("Show on all displays") @@ -252,12 +257,18 @@ struct GeneralSettings: View { @ViewBuilder func gestureControls() -> some View { Section { - Defaults.Toggle("Enable gestures", key: .enableGestures) + Defaults.Toggle( + NSLocalizedString("Enable gestures", comment: "Toggle in parameters to enable gestures"), + key: .enableGestures + ) .disabled(!openNotchOnHover) if enableGestures { Toggle("Media change with horizontal gestures", isOn: .constant(false)) .disabled(true) - Defaults.Toggle("Close gesture", key: .closeGestureEnabled) + Defaults.Toggle( + NSLocalizedString("Close gesture", comment: "Toggle in parameters to enable the closing gesture" ), + key: .closeGestureEnabled + ) Slider(value: $gestureSensitivity, in: 100...300, step: 100) { HStack { Text("Gesture sensitivity") @@ -283,8 +294,14 @@ struct GeneralSettings: View { @ViewBuilder func NotchBehaviour() -> some View { Section { - Defaults.Toggle("Enable haptics", key: .enableHaptics) - Defaults.Toggle("Open notch on hover", key: .openNotchOnHover) + Defaults.Toggle( + NSLocalizedString("Enable haptics", comment: "Parameter toggle for enabling haptics"), + key: .enableHaptics + ) + Defaults.Toggle( + NSLocalizedString("Open notch on hover", comment: "Parameter toggle for opening notch on hover"), + key: .openNotchOnHover + ) Toggle("Remember last tab", isOn: $coordinator.openLastTabByDefault) if openNotchOnHover { Slider(value: $minimumHoverDuration, in: 0...1, step: 0.1) { @@ -309,8 +326,14 @@ struct Charge: View { var body: some View { Form { Section { - Defaults.Toggle("Show charging indicator", key: .chargingInfoAllowed) - Defaults.Toggle("Show battery indicator", key: .showBattery) + Defaults.Toggle( + NSLocalizedString("Show charging indicator", comment: "Toogle in parameters to Show charging indicator"), + key: .chargingInfoAllowed + ) + Defaults.Toggle( + NSLocalizedString("Show battery indicator", comment: "Toggle in parameters to show battery indicator"), + key: .showBattery + ) } header: { Text("General") } @@ -327,9 +350,15 @@ struct Downloads: View { Form { warningBadge("We don't support downloads yet", "It will be supported later on.") Section { - Defaults.Toggle("Show download progress", key: .enableDownloadListener) + Defaults.Toggle( + NSLocalizedString("Show download progress", comment: "Toggle in parameters to show the download progress in supported apps"), + key: .enableDownloadListener + ) .disabled(true) - Defaults.Toggle("Enable Safari Downloads", key: .enableSafariDownloads) + Defaults.Toggle( + NSLocalizedString("Enable Safari Downloads", comment: "Toggle to in parameters to enable Safari downloads"), + key: .enableSafariDownloads + ) .disabled(!Defaults[.enableDownloadListener]) Picker("Download indicator style", selection: $selectedDownloadIndicatorStyle) { Text("Progress bar") @@ -433,8 +462,13 @@ struct HUD: View { Text("Gradient") .tag(true) } - Defaults.Toggle("Enable glowing effect", key: .systemEventIndicatorShadow) - Defaults.Toggle("Use accent color", key: .systemEventIndicatorUseAccent) + Defaults.Toggle( + NSLocalizedString("Enable glowing effect", comment: "Toggle in parameters to enable the glowing effet for progress bars"), + key: .systemEventIndicatorShadow + ) + Defaults.Toggle( + NSLocalizedString("Use accent color", comment: "Toggle in parameters to show the accent color of progress bars"), + key: .systemEventIndicatorUseAccent) } header: { HStack { Text("Appearance") @@ -456,7 +490,10 @@ struct Media: View { "Enable music live activity", isOn: $coordinator.showMusicLiveActivityOnClosed.animation() ) - Defaults.Toggle("Enable sneak peek", key: .enableSneakPeek) + Defaults.Toggle( + NSLocalizedString("Enable sneak peek", comment: "Toggle in parameters to enable the Sneak Peek feature"), + key: .enableSneakPeek + ) HStack { Stepper(value: $waitInterval, in: 0...10, step: 1) { HStack { @@ -472,7 +509,10 @@ struct Media: View { } Section { - Defaults.Toggle("Autohide BoringNotch in fullscreen", key: .enableFullscreenMediaDetection) + Defaults.Toggle( + NSLocalizedString("Autohide BoringNotch in fullscreen", comment: "Toggle in the parameters to autohide Boring notch in fullscreen"), + key: .enableFullscreenMediaDetection + ) } header: { HStack { Text("Fullscreen media playback detection") @@ -578,8 +618,14 @@ struct Shelf: View { var body: some View { Form { Section { - Defaults.Toggle("Enable shelf", key: .boringShelf) - Defaults.Toggle("Open shelf tab by default if items added", key: .openShelfByDefault) + Defaults.Toggle( + NSLocalizedString("Enable shelf", comment: "Toggle in parameters to enable shelf system"), + key: .boringShelf + ) + Defaults.Toggle( + NSLocalizedString("Open shelf tab by default if items added", comment: "Toggle in parameters to open shelf by default if items are added"), + key: .openShelfByDefault + ) } header: { HStack { Text("General") @@ -732,21 +778,38 @@ struct Appearance: View { Form { Section { Toggle("Always show tabs", isOn: $coordinator.alwaysShowTabs) - Defaults.Toggle("Settings icon in notch", key: .settingsIconInNotch) - Defaults.Toggle("Enable window shadow", key: .enableShadow) - Defaults.Toggle("Corner radius scaling", key: .cornerRadiusScaling) + Defaults.Toggle( + NSLocalizedString("Settings icon in notch", comment: "Toggle in parameters for settings icon in notch"), + key: .settingsIconInNotch + ) + Defaults.Toggle( + NSLocalizedString("Enable window shadow", comment: "Toggle in parameters for enabling the window shadow"), + key: .enableShadow + ) + Defaults.Toggle( + NSLocalizedString("Corner radius scaling", comment: "Toggle in parameters for corner radius scaling"), + key: .cornerRadiusScaling + ) } header: { Text("General") } Section { - Defaults.Toggle("Enable colored spectrograms", key: .coloredSpectrogram) - Defaults - .Toggle("Player tinting", key: .playerColorTinting) - Defaults.Toggle("Enable blur effect behind album art", key: .lightingEffect) + Defaults.Toggle( + NSLocalizedString("Enable colored spectrograms", comment: "Toggle in parameters for colored spectrograms"), + key: .coloredSpectrogram + ) + Defaults.Toggle( + NSLocalizedString("Player tinting", comment: "Toggle in parameters to tint the player"), + key: .playerColorTinting + ) + Defaults.Toggle( + NSLocalizedString("Enable blur effect behind album art", comment: "Toggle in parameters to enable the blur effect behind album art"), + key: .lightingEffect + ) Picker("Slider color", selection: $sliderColor) { ForEach(SliderColorEnum.allCases, id: \.self) { option in - Text(option.rawValue) + Text(option.localizedName) } } } header: { @@ -922,7 +985,10 @@ struct Appearance: View { } Section { - Defaults.Toggle("Enable boring mirror", key: .showMirror) + Defaults.Toggle( + NSLocalizedString("Enable boring mirror", comment: "Toggle in parameters to enable Boring mirror"), + key: .showMirror + ) .disabled(!checkVideoInput()) Picker("Mirror shape", selection: $mirrorShape) { Text("Circle") @@ -930,8 +996,14 @@ struct Appearance: View { Text("Square") .tag(MirrorShapeEnum.rectangle) } - Defaults.Toggle("Show calendar", key: .showCalendar) - Defaults.Toggle("Show cool face animation while inactivity", key: .showNotHumanFace) + Defaults.Toggle( + NSLocalizedString("Show calendar", comment: "Toggle in parameters to show the calendar in the expanded view"), + key: .showCalendar + ) + Defaults.Toggle( + NSLocalizedString("Show cool face animation while inactivity", comment: "Toggle in parameters to show a face animation when inactive"), + key: .showNotHumanFace + ) .disabled(true) } header: { HStack { diff --git a/boringNotch/enums/generic.swift b/boringNotch/enums/generic.swift index e70b459..2b40c24 100644 --- a/boringNotch/enums/generic.swift +++ b/boringNotch/enums/generic.swift @@ -8,6 +8,16 @@ import Foundation import Defaults +protocol LocalizedEnum: RawRepresentable, CaseIterable where RawValue == String { + var localizedName: String { get } +} + +extension LocalizedEnum { + var localizedName: String { + NSLocalizedString(self.rawValue, comment: "") + } +} + public enum Style { case notch case floating @@ -40,29 +50,29 @@ enum SettingsEnum { case extensions } -enum DownloadIndicatorStyle: String, Defaults.Serializable { +enum DownloadIndicatorStyle: String, LocalizedEnum, Defaults.Serializable { case progress = "Progress" case percentage = "Percentage" } -enum DownloadIconStyle: String, Defaults.Serializable { +enum DownloadIconStyle: String, LocalizedEnum, Defaults.Serializable { case onlyAppIcon = "Only app icon" case onlyIcon = "Only download icon" case iconAndAppIcon = "Icon and app icon" } -enum MirrorShapeEnum: String, Defaults.Serializable { +enum MirrorShapeEnum: String, LocalizedEnum, Defaults.Serializable { case rectangle = "Rectangular" case circle = "Circular" } -enum WindowHeightMode: String, Defaults.Serializable { +enum WindowHeightMode: String, LocalizedEnum, Defaults.Serializable { case matchMenuBar = "Match menubar height" case matchRealNotchSize = "Match real notch height" case custom = "Custom height" } -enum SliderColorEnum: String, CaseIterable, Defaults.Serializable { +enum SliderColorEnum: String, CaseIterable, LocalizedEnum, Defaults.Serializable { case white = "White" case albumArt = "Match album art" case accent = "Accent color" From 3481e1882f051ea1572a07f9435d922f7ca02028 Mon Sep 17 00:00:00 2001 From: Thomas Havy Date: Wed, 18 Dec 2024 02:08:15 +0100 Subject: [PATCH 71/74] Started moving to keys for strings --- boringNotch/Localizable.xcstrings | 1645 +++++++++++------ boringNotch/boringNotchApp.swift | 4 +- .../components/Calender/BoringCalendar.swift | 6 +- .../Live activities/DownloadView.swift | 4 +- .../Live activities/InlineHUD.swift | 4 +- .../components/Notch/BoringExtrasMenu.swift | 8 +- .../components/Notch/NotchContentView.swift | 2 +- .../components/Notch/NotchShelfView.swift | 2 +- .../Onboarding/ActivationView.swift | 20 +- .../components/Onboarding/ProOnboarding.swift | 12 +- .../components/Settings/EditPanelView.swift | 4 +- .../components/Settings/SettingsView.swift | 237 +-- .../components/Settings/SoftwareUpdater.swift | 8 +- .../components/Shelf/Ext+FileProvider.swift | 2 +- .../components/Shelf/Ext+NSAlert.swift | 10 +- boringNotch/components/Tips/TipStore.swift | 12 +- .../components/Webcam/WebcamView.swift | 2 +- boringNotch/enums/generic.swift | 4 +- 18 files changed, 1266 insertions(+), 720 deletions(-) diff --git a/boringNotch/Localizable.xcstrings b/boringNotch/Localizable.xcstrings index 5d8710b..f5c773b 100644 --- a/boringNotch/Localizable.xcstrings +++ b/boringNotch/Localizable.xcstrings @@ -41,515 +41,599 @@ "‱ New feature 1" : { }, - "About" : { + "Activation" : { + "localizations" : { + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activation" + } + } + } + }, + "activation.header.subtitle" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "About" + "value" : "Transform your notch truly " } }, "fr" : { "stringUnit" : { - "state" : "translated", - "value" : "À Propos" + "state" : "needs_review", + "value" : "Rendez l’encoche vraiment vĂŽtre" } } } }, - "Accent color" : { + "activation.header.title" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Accent color" + "value" : "Activate your license" } }, "fr" : { "stringUnit" : { - "state" : "translated", - "value" : "Couleur d’accentuation" + "state" : "needs_review", + "value" : "Activer votre license" } } } }, - "Activate" : { + "activation.process.activate" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", + "value" : "Activate" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", "value" : "Activer" } } } }, - "Activate your license" : { + "activation.process.email" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Activer votre license" + "value" : "Email" } - } - } - }, - "Activation" : { - "localizations" : { + }, "fr" : { "stringUnit" : { - "state" : "translated", - "value" : "Activation" + "state" : "needs_review", + "value" : "Adresse email" } } } }, - "Add" : { + "activation.process.key" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Ajouter" + "value" : "License Key" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "ClĂ© de license" } } } }, - "Add manually" : { + "AirDrop" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ajouter manuellement" + "value" : "AirDrop" } } } }, - "Add new visualizer" : { + "AirDrop service not available" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ajouter une visualisation" + "value" : "Le service AirDrop n’est pas disponible" } } } }, - "Additional features" : { + "Appearance" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "FonctionnalitĂ©s supplĂ©mentaires" + "value" : "Apparence" } } } }, - "AirDrop" : { + "Boring Controls" : { + "extractionState" : "stale", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boring Controls" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "AirDrop" + "value" : "Boring ContrĂŽles" } } } }, - "AirDrop service not available" : { + "boring.notch" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Le service AirDrop n’est pas disponible" + "value" : "boring.notch" } } } }, - "All-day" : { + "Both" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Both" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Toute la journĂ©e" + "value" : "Les deux" } } } }, - "Always show tabs" : { + "Buy Me a Coffee" : { + "extractionState" : "stale", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buy Me a Coffee" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Toujours afficher les onglets" + "value" : "Payez-moi un cafĂ©" } } } }, - "App icon" : { + "calendar.all_day" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "IcĂŽne de l’application" + "value" : "All-day" } } } }, - "Appearance" : { + "calendar.enjoy_free_time" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Apparence" + "value" : "Enjoy your free time!" } } } }, - "Autohide BoringNotch in fullscreen" : { - "comment" : "Toggle in the parameters to autohide Boring notch in fullscreen", + "calendar.no_events" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Cacher BoringNotch en plein Ă©cran" + "value" : "No events today" } } } }, - "Automatically check for updates" : { + "Charging" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Automatically check for updates" + "value" : "Charging" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "VĂ©rifier automatiquement les mises Ă  jour" + "value" : "Chargement" } } } }, - "Automatically download updates" : { + "Circle" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "TĂ©lĂ©charger automatiquement les mises Ă  jour" + "value" : "Cercle" } } } }, - "Battery" : { + "Circular" : { + "extractionState" : "manual", "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Battery" + "value" : "Circulaire" } - }, - "fr" : { + } + } + }, + "clipboard_mgr.message" : { + "localizations" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Batterie" + "value" : "Easily copy, store, and manage your most-used content. Upgrade now for advanced features like multi-item storage and quick access!" } } } }, - "Boost your productivity with Clipboard Manager" : { + "clipboard_mgr.title" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Booster votre productivitĂ© avec le Clipboard Manager" + "value" : "Boost your productivity with Clipboard Manager" } } } }, - "Boring Controls" : { - "extractionState" : "stale", + "common.add" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Boring Controls" + "value" : "Add" } }, "fr" : { "stringUnit" : { - "state" : "translated", - "value" : "Boring ContrĂŽles" + "state" : "needs_review", + "value" : "Ajouter" } } } }, - "boring.notch" : { + "common.cancel" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "boring.notch" + "value" : "Cancel" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Annuler" } } } }, - "Both" : { + "common.charging" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Both" + "value" : "Charging" } - }, - "fr" : { + } + } + }, + "common.close" : { + "localizations" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Les deux" + "value" : "Close" } } } }, - "Buy Me a Coffee" : { - "extractionState" : "stale", + "common.coming_soon" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Buy Me a Coffee" + "value" : "Coming Soon" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Payez-moi un cafĂ©" + "value" : "BientĂŽt disponible" } } } }, - "Cancel" : { + "common.default" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Annuler" + "value" : "Default" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "DĂ©faut" } } } }, - "Charging" : { + "common.error" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Charging" + "value" : "Error" } - }, - "fr" : { + } + } + }, + "common.exit" : { + "localizations" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Chargement" + "value" : "Exit" } } } }, - "Check for updates" : { - "extractionState" : "stale", + "common.later" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Check for updates" + "value" : "Later" } - }, - "fr" : { + } + } + }, + "common.mirror" : { + "localizations" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "VĂ©rifier les mises Ă  jours" + "value" : "Mirror" } } } }, - "Check for Updates
" : { + "common.more" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "VĂ©rifier les mises Ă  jours
" + "value" : "More" } } } }, - "Circle" : { + "common.muted" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Cercle" + "value" : "Muted" } } } }, - "Circular" : { - "extractionState" : "manual", + "common.name" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Circulaire" + "value" : "Name" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Nom" } } } }, - "Close" : { + "common.ok" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Fermer" + "value" : "OK" } } } }, - "Close gesture" : { - "comment" : "Toggle in parameters to enable the closing gesture", + "common.quit" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Geste de fermeture" + "value" : "Quit" } } } }, - "Coming soon" : { + "common.quit_app" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Coming soon" + "value" : "Quit App" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "BientĂŽt disponible" + "value" : "Quitter l’application" } } } }, - "Corner radius scaling" : { - "comment" : "Toggle in parameters for corner radius scaling", + "common.restart_needed" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Mise Ă  l’échelle des coins" + "value" : "Restart needed" } } } }, - "Custom color" : { + "common.selected" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Custom color" + "value" : "Selected" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Couleur personnalisĂ©e" + "value" : "sĂ©lĂ©ctionnĂ©" } } } }, - "Custom height" : { + "common.settings" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Hauteur personnalisĂ©e" + "value" : "Settings" } } } }, - "Custom music live activity animation" : { + "common.speed" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Animation personnalisĂ©e pour les Live Activities musicales" + "value" : "Speed" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Vitesse" } } } }, - "Custom notch size - %.0f" : { + "common.unmuted" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Taille personnalisĂ©e de l’encoche - %.0f" + "value" : "unmuted" } } } }, - "Custom vizualizers (Lottie)" : { + "common.upgrade_pro" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Visualisations personnalisĂ©es (Lottie)" + "value" : "Upgrade to Pro" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Obtenir Pro" } } } }, - "Default" : { + "common.welcome" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Par dĂ©faut" + "value" : "Welcome" } } } }, - "Description" : { + "Custom color" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Custom color" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Description" + "value" : "Couleur personnalisĂ©e" } } } }, - "Disable" : { + "Custom notch size - %.0f" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "DĂ©sactiver" + "value" : "Taille personnalisĂ©e de l’encoche - %.0f" } } } }, - "Disabled" : { + "Default" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "DĂ©sactivĂ©" + "value" : "Par dĂ©faut" } } } }, - "Download" : { + "Description" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "TĂ©lĂ©chargement" + "value" : "Description" } } } @@ -598,36 +682,30 @@ }, "Downloads" : { "localizations" : { - "en" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Downloads" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "TĂ©lĂ©chargements" + "value" : "TĂ©lĂ©chargements" } } } }, - "Drop files here" : { + "downloads.download" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "DĂ©posez vos fichiers ici" + "value" : "Download" } } } }, - "Easily copy, store, and manage your most-used content. Upgrade now for advanced features like multi-item storage and quick access!" : { + "downloads.in_progress" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Copiez, gardez et gĂ©rer vos contenus les plus utilisĂ©s. Mettez Ă  niveau maintenant pour activer des fonctionnalitĂ©s avancĂ©es comme le stockage de plusieurs fichiers et les accĂšs rapides!" + "value" : "In progress" } } } @@ -642,634 +720,677 @@ } } }, - "Edit layout" : { + "edit_panel.edit_layout" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Éditer la disposition" + "value" : "Edit layout" } } } }, - "Email address" : { + "Enable glowing effect" : { + "comment" : "Toggle in parameters to enable the glowing effet for progress bars", "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Adresse email" + "value" : "Activer l’effet de brillance" } } } }, - "Enable blur effect behind album art" : { - "comment" : "Toggle in parameters to enable the blur effect behind album art", + "Enable HUD replacement" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer l’effet de flou derriĂšre la couverture d’album" + "value" : "Activer le remplacement de l’ATH" } } } }, - "Enable boring mirror" : { - "comment" : "Toggle in parameters to enable Boring mirror", + "Enable Safari Downloads" : { + "comment" : "Toggle to in parameters to enable Safari downloads", "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer le Boring Mirror" + "value" : "Activer les tĂ©lĂ©chargements Safari" } } } }, - "Enable colored spectrograms" : { - "comment" : "Toggle in parameters for colored spectrograms", + "ERROR: VIEW NOT DEFINED" : { + + }, + "Exclude apps" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enable colored spectrograms" + "value" : "Exclude apps" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer les spectrogrammes en couleur" - } - } - } - }, - "Enable gestures" : { - "comment" : "Toggle in parameters to enable gestures", - "localizations" : { - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Activer les gestes" + "value" : "Exclure des applications" } } } }, - "Enable glowing effect" : { - "comment" : "Toggle in parameters to enable the glowing effet for progress bars", + "General" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer l’effet de brillance" + "value" : "GĂ©nĂ©ral" } } } }, - "Enable haptics" : { - "comment" : "Parameter toggle for enabling haptics", + "GitHub" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enable haptics" + "value" : "GitHub" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer le retour haptique" + "value" : "GitHub" } } } }, - "Enable HUD replacement" : { + "Got it!" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer le remplacement de l’ATH" + "value" : "Compris!" } } } }, - "Enable music live activity" : { + "Gradient" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer le Live Activity pour la musique" + "value" : "Gradiant" } } } }, - "Enable Safari Downloads" : { - "comment" : "Toggle to in parameters to enable Safari downloads", + "Hierarchical" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer les tĂ©lĂ©chargements Safari" + "value" : "HiĂ©rarchique" } } } }, - "Enable shelf" : { - "comment" : "Toggle in parameters to enable shelf system", + "HUD style" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer les Ă©tagĂšres" + "value" : "Style de l’ATH" } } } }, - "Enable sneak peek" : { - "comment" : "Toggle in parameters to enable the Sneak Peek feature", + "HUDs" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer Sneak Peek" + "value" : "ATHs" } } } }, - "Enable window shadow" : { - "comment" : "Toggle in parameters for enabling the window shadow", + "Inline" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer les ombres de fenĂȘtre" + "value" : "En ligne" } } } }, - "Enhance your experience with HUDs" : { + "Key" : { + "extractionState" : "manual", "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "AmĂ©liorer votre expĂ©rience avec des ATHs" + "value" : "ClĂ©" } } } }, - "Enjoy your free time!" : { + "Match album art" : { + "extractionState" : "manual", "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Profitez de votre pĂ©riode d’essai!" + "value" : "Faire correspondre Ă  la couverture d’album" } } } }, - "Error" : { + "Mic %@" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Erreur" + "value" : "Microphone %@" } } } }, - "ERROR: VIEW NOT DEFINED" : { - - }, - "Exclude apps" : { + "No excluded apps" : { "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Exclude apps" - } - }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Exclure des applications" + "value" : "Aucune application exclue" } } } }, - "Exit" : { + "Onboarding" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Quitter" + "value" : "Embarquement" } } } }, - "Extensions" : { + "onboarding.get_started" : { + + }, + "One or more files failed to load" : { + "comment" : "Error message when one or more files failed to load", "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Extensions" + "value" : "Un ou plusieurs fichier n’a pas pu ĂȘtre chargĂ©" } } } }, - "Fullscreen media playback detection" : { + "Only app icon" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Only app icon" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "DĂ©tection des contrĂŽles multimĂ©dia en plein Ă©cran" + "value" : "Seulement l’icĂŽne de l’application" } } } }, - "General" : { + "Only download icon" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "General" + "value" : "Only download icon" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "GĂ©nĂ©ral" + "value" : "Seulement l’icĂŽne de tĂ©lĂ©chargement" } } } }, - "Gesture control" : { + "Percentage" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "ContrĂŽle des gestes" + "value" : "Percentage" } - } - } - }, - "Gesture sensitivity" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "SensibilitĂ© des gestes" + "value" : "Pourcentage" } } } }, - "Get started" : { + "PRO" : { + + }, + "Progress" : { + "extractionState" : "manual", "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "DĂ©marrer" + "value" : "ProgrĂšs" } } } }, - "GitHub" : { + "Progress bar" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "GitHub" + "value" : "Progress bar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "GitHub" + "value" : "Barre de progression" } } } }, - "Got it!" : { + "Progressbar style" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Compris!" + "value" : "Style de la barre de progression" } } } }, - "Gradient" : { + "Rectangular" : { + "extractionState" : "manual", "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Gradiant" + "value" : "Rectangulaire" } } } }, - "Hierarchical" : { + "Restart Boring Notch" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "HiĂ©rarchique" + "value" : "RedĂ©marrer Boring Notch" } } } }, - "High" : { + "setting.extensions.disabled" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "ÉlevĂ©" + "value" : "Disabled" } - } - } - }, - "HUD style" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Style de l’ATH" + "value" : "DĂ©sactivĂ©" } } } }, - "HUDs" : { + "Settings" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "ATHs" + "value" : "Settings" } - } - } - }, - "In progress" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "En cours" + "value" : "ParamĂštres" } } } }, - "Inline" : { + "settings.about.footer" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Made with đŸ«¶đŸ» by not so boring not.people" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "En ligne" + "value" : "Fait avec đŸ«¶ par des personnes pas si « boring »!" } } } }, - "Install updates automatically" : { - "extractionState" : "stale", + "settings.about.header.version" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Install updates automatically" + "value" : "Version" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Installer des mises Ă  jour automatiquement" + "value" : "Informations de version" } } } }, - "Installed extensions" : { + "settings.about.release_name" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Extensions installĂ©es" + "value" : "Release Name" } - } - } - }, - "Key" : { - "extractionState" : "manual" - }, - "Later" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Plus tard" + "value" : "Nom de version" } } } }, - "Launch at login" : { - "comment" : "Launch at login", + "settings.about.support_us" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Ouvrir Ă  la connexion" + "value" : "Support Us" } - } - } - }, - "License key" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "ClĂ© de license" + "value" : "Supportez-nous" } } } }, - "Lottie JSON URL" : { + "settings.about.version" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "URL du JSON Lottie" + "value" : "Version" } - } - } - }, - "Low" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Faible" + "value" : "Version" } } } }, - "Made with đŸ«¶đŸ» by not so boring not.people" : { + "settings.appearance.add_custom_visualizer" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Made with đŸ«¶đŸ» by not so boring not.people" + "value" : "Add new visualizer" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fait avec đŸ«¶ par des personnes pas si « boring »!" + "value" : "Ajouter une visualisation" } } } }, - "Match album art" : { - "extractionState" : "manual", + "settings.appearance.always_show_tabs" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always show tabs" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Faire correspondre Ă  la couverture d’album" + "value" : "Toujours afficher les onglets" } } } }, - "Match menubar height" : { + "settings.appearance.corner_radius_scaling" : { + "comment" : "Toggle in parameters for corner radius scaling", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Corner radius scaling" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Faire correspondre avec la hauteur de la barre de menu" + "value" : "Mise Ă  l’échelle des coins" } } } }, - "Match real notch size" : { + "settings.appearance.enable_blur_effect_album_art" : { + "comment" : "Toggle in parameters to enable the blur effect behind album art", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable blur effect behind album art" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Faire correspondre avec la hauteur de l’encoche" + "value" : "Activer l’effet de flou derriĂšre la couverture d’album" } } } }, - "Media" : { + "settings.appearance.enable_boring_mirror" : { + "comment" : "Toggle in parameters to enable Boring mirror", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Media" + "value" : "Enable boring mirror" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "MĂ©dia" + "value" : "Activer le Boring Mirror" } } } }, - "Media change with horizontal gestures" : { + "settings.appearance.enable_colored_spectrograms" : { + "comment" : "Toggle in parameters for colored spectrograms", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable colored spectrograms" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Changer le mĂ©dia avec un geste horizontal" + "value" : "Activer les spectrogrammes en couleur" } } } }, - "Media inactivity timeout" : { + "settings.appearance.enable_window_shadow" : { + "comment" : "Toggle in parameters for enabling the window shadow", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Media inactivity timeout" + "value" : "Enable window shadow" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Temps de dĂ©tection d’inactivitĂ© des mĂ©dias" + "value" : "Activer les ombres de fenĂȘtre" } } } }, - "Media playback live activity" : { + "settings.appearance.header.additional_features" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Media playback live activity" + "value" : "Additional features" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Live Activity de la lecture de mĂ©dia" + "value" : "FonctionnalitĂ©s supplĂ©mentaires" } } } }, - "Medium" : { + "settings.appearance.header.app_icon" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Icon" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Moyen" + "value" : "IcĂŽne de l’application" } } } }, - "Menubar icon" : { - "comment" : "Toggle in parameter to enable menubar icon", + "settings.appearance.header.custom_live_activity_anim" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Menubar icon" + "value" : "Custom music live activity animation" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "IcĂŽne dans la barre de menu" + "value" : "Animation personnalisĂ©e pour les Live Activities musicales" } } } }, - "Mic %@" : { + "settings.appearance.header.custom_visualizers" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Custom vizualizers (Lottie)" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Microphone %@" + "value" : "Visualisateurs personnalisĂ©s (Lottie)" } } } }, - "Minimum hover duration" : { + "settings.appearance.header.general" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "General" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Temps minimum de survol" + "value" : "GĂ©nĂ©ral" } } } }, - "Mirror" : { + "settings.appearance.header.media" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Media" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mirroir" + "value" : "MĂ©dia" } } } }, - "Mirror shape" : { + "settings.appearance.lottie_json_url" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lottie JSON URL" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL du JSON Lottie" + } + } + } + }, + "settings.appearance.mirror_shape" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mirror shape" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -1278,48 +1399,114 @@ } } }, - "More" : { + "settings.appearance.no_custom_visualizer" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No custom visualizer" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Plus" + "value" : "Aucune visualisation personnalisĂ©e disponible" } } } }, - "muted" : { + "settings.appearance.player_tinting" : { + "comment" : "Toggle in parameters to tint the player", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player tinting" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "sourdine" + "value" : "Teinte du lecteur" } } } }, - "Name" : { + "settings.appearance.settings_icon_in_notch" : { + "comment" : "Toggle in parameters for settings icon in notch", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings icon in notch" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Nom" + "value" : "IcĂŽne des paramĂštres dans Notch" } } } }, - "Need Restart" : { + "settings.appearance.show_calendar" : { + "comment" : "Toggle in parameters to show the calendar in the expanded view", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show calendar" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "RedĂ©marrage nĂ©cessaire" + "value" : "Montrer le calendrier" } } } }, - "No custom animation available" : { + "settings.appearance.show_idle_face_animation" : { + "comment" : "Toggle in parameters to show a face animation when inactive", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show cool face animation while inactivity" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher une animation stylĂ©e quand inactif" + } + } + } + }, + "settings.appearance.spectrogram_selected_animation" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Selected animation" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Animation sĂ©lectionnĂ©e" + } + } + } + }, + "settings.appearance.spectrogram_selected_animation_none" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No custom animation available" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -1328,168 +1515,242 @@ } } }, - "No custom visualizer" : { + "settings.appearance.use_music_visualizer_spectrogram" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Use music visualizer spectrogram" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune visualisation personnalisĂ©e disponible" + "value" : "Utiliser le spectrogramme comme visualisateur musical" } } } }, - "No events today" : { + "settings.battery.header.general" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "General" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucun Ă©vĂšnement aujourd’hui" + "value" : "GĂ©nĂ©ral" } } } }, - "No excluded apps" : { + "settings.battery.show_battery_indicator" : { + "comment" : "Toggle in parameters to show battery indicator", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show battery indicator" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune application exclue" + "value" : "Afficher l’indicateur de batterie" } } } }, - "No excludes" : { - "extractionState" : "stale", + "settings.battery.show_charging_indicator" : { + "comment" : "Toogle in parameters to Show charging indicator", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "No excludes" + "value" : "Show charging indicator" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune exclusion" + "value" : "Afficher l’indicateur de charge" } } } }, - "No extension installed" : { + "settings.extensions.add_manually" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add manually" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune extension installĂ©e" + "value" : "Ajouter manuellement" } } } }, - "Non-notch display height" : { + "settings.extensions.disable" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disable extension" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Taille d’écran sans encoche" + "value" : "DĂ©sactiver l'extension" } } } }, - "Notch behavior" : { + "settings.extensions.header.installed_extensions" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed extensions" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Comportement de l’encoche" + "value" : "Extensions installĂ©es" } } } }, - "Notch display height" : { + "settings.extensions.no_extensions_installed" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No extensions installed" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Taille d’écran avec encoche" + "value" : "Aucune extension d’installĂ©e" } } } }, - "Notch Height" : { + "settings.extensions.restart" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restart" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Hauteur d’encoche" + "value" : "RedĂ©marrer" } } } }, - "OK" : { + "settings.extensions.running" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Running" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ok" + "value" : "En fonctionnement" } } } }, - "Onboarding" : { + "settings.extensions.stopped" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stopped" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Embarquement" + "value" : "ArrĂȘtĂ©" } } } }, - "One or more files failed to load" : { + "settings.extensions.uninstall" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uninstall" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Un ou plusieurs fichier n’a pas pu ĂȘtre chargĂ©" + "value" : "DĂ©sinstaller" } } } }, - "Only app icon" : { + "settings.general.behavior.enable_haptics" : { + "comment" : "Parameter toggle for enabling haptics", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Only app icon" + "value" : "Enable haptics" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Seulement l’icĂŽne de l’application" + "value" : "Activer le retour haptique" } } } }, - "Only download icon" : { + "settings.general.behavior.hover_minimum_duration" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Only download icon" + "value" : "Minimum hover duration" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Seulement l’icĂŽne de tĂ©lĂ©chargement" + "value" : "Temps minimum de survol" } } } }, - "Open notch on hover" : { + "settings.general.behavior.open_on_hover" : { "comment" : "Parameter toggle for opening notch on hover", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open notch on hover" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -1498,625 +1759,883 @@ } } }, - "Open shelf tab by default if items added" : { - "comment" : "Toggle in parameters to open shelf by default if items are added", + "settings.general.behavior.remember_last_tab" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remember last tab" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ouvrir l’étagĂšre par dĂ©faut quand un Ă©lĂ©ment est ajoutĂ©" + "value" : "Se souvenir du dernier onglet" } } } }, - "Percentage" : { + "settings.general.gestures.close" : { + "comment" : "Toggle in parameters to enable the closing gesture", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Percentage" + "value" : "Close gesture" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Pourcentage" + "value" : "Geste de fermeture" } } } }, - "Player tinting" : { - "comment" : "Toggle in parameters to tint the player", + "settings.general.gestures.descriptions.open_close" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Two-finger swipe up on notch to close, two-finger swipe down on notch to open when **Open notch on hover** option is disabled" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Teinte du lecteur" + "value" : "Faites glissez avec deux doigts vers le haut pour fermer Boring Notch, vers le bas pour l’ouvrir quand **Ouvrir l’encoche en survolant** est dĂ©sactivĂ©" } } } }, - "Progress" : { - "extractionState" : "manual", + "settings.general.gestures.enable" : { + "comment" : "Toggle in parameters to enable gestures", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable gestures" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "ProgrĂšs" + "value" : "Activer les gestes" + } + } + } + }, + "settings.general.gestures.media_change_horizontal" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Media change with horizontal gestures" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Changer le mĂ©dia avec un geste horizontal" + } + } + } + }, + "settings.general.gestures.sensitivity" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesture sensitivity" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "SensibilitĂ© des gestes" + } + } + } + }, + "settings.general.gestures.sensitivity.high" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "High" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ÉlevĂ©e" + } + } + } + }, + "settings.general.gestures.sensitivity.low" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Low" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Faible" + } + } + } + }, + "settings.general.gestures.sensitivity.medium" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medium" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Moyenne" + } + } + } + }, + "settings.general.header.accent_color" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accent color" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur d’accentuation" + } + } + } + }, + "settings.general.header.behavior" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notch behavior" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comportement de l’encoche" + } + } + } + }, + "settings.general.header.gestures" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gestures" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ContrĂŽle des gestes" + } + } + } + }, + "settings.general.header.notch_height" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notch Height" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hauteur d’encoche" + } + } + } + }, + "settings.general.header.system_features" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "System Features" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "FonctionnalitĂ©s systĂšme" + } + } + } + }, + "settings.general.launch_at_login" : { + "comment" : "Toggle for parameter launch at login", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Launch at login" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvrir Ă  la connexion" + } + } + } + }, + "settings.general.menubar_icon" : { + "comment" : "Toggle in parameter to enable menubar icon", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menubar icon" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "IcĂŽne dans la barre de menu" + } + } + } + }, + "settings.general.non_notch_display_height" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Non-notch display height" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille d’écran sans encoche" } } } }, - "Progress bar" : { + "settings.general.notch_custom_height" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Progress bar" + "value" : "Custom height" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Barre de progression" + "value" : "Hauteur personnalisĂ©e" } } } }, - "Progressbar style" : { + "settings.general.notch_display_height" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notch display height" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Style de la barre de progression" + "value" : "Taille d’écran avec encoche" } } } }, - "Quit" : { + "settings.general.notch_match_display_height" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Quit" + "value" : "Match real notch size" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Quitter" + "value" : "Faire correspondre avec la hauteur de l’encoche" } } } }, - "Quit app" : { + "settings.general.notch_match_menubar_height" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Match menubar height" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Quitter l’application" + "value" : "Faire correspondre avec la hauteur de la barre de menu" } } } }, - "Quit boring.notch" : { - "extractionState" : "stale", + "settings.general.show_on_all_displays" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Quit boring.notch" + "value" : "Show on all displays" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Quitter boring.notch" + "value" : "Afficher sur tous les Ă©crans" } } } }, - "Rectangular" : { - "extractionState" : "manual", + "settings.general.show_on_specific_display" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show on a specific display" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rectangulaire" + "value" : "Afficher sur un Ă©cran spĂ©cifique" } } } }, - "Release name" : { + "settings.media.autohide_in_fullscreen" : { + "comment" : "Toggle in the parameters to autohide Boring notch in fullscreen", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Release name" + "value" : "Autohide BoringNotch in fullscreen" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Nom de version" + "value" : "Cacher BoringNotch en plein Ă©cran" } } } }, - "Remember last tab" : { + "settings.media.enable_live_activity" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Se souvenir du dernier onglet" + "value" : "Enable music live activity" } - } - } - }, - "Restart" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "RedĂ©marrer" + "value" : "Activer le Live Activity pour la musique" } } } }, - "Restart Boring Notch" : { + "settings.media.enable_sneak_peek" : { + "comment" : "Toggle in parameters to enable the Sneak Peek feature", "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "RedĂ©marrer Boring Notch" + "value" : "Enable sneak peek" } - } - } - }, - "Running" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "En fonctionnement" + "value" : "Activer Sneak Peek" } } } }, - "seconds" : { - "extractionState" : "stale", + "settings.media.header.fullscreen" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "seconds" + "value" : "Fullscreen media playback detection" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "secondes" + "value" : "DĂ©tection des contrĂŽles multimĂ©dia en plein Ă©cran" } } } }, - "selected" : { + "settings.media.header.live_activity" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "sĂ©lĂ©ctionnĂ©" + "value" : "Media playback live activity" } - } - } - }, - "Selected animation" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Animation sĂ©lectionnĂ©e" + "value" : "Live Activity de la lecture de mĂ©dia" } } } }, - "Settings" : { + "settings.media.timeout" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Settings" + "value" : "Media inactivity timeout" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "ParamĂštres" + "value" : "Temps de dĂ©tection d’inactivitĂ© des mĂ©dias" } } } }, - "Settings icon in notch" : { - "comment" : "Toggle in parameters for settings icon in notch", + "settings.shelf.enable_shelf" : { + "comment" : "Toggle in parameters to enable shelf system", "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "IcĂŽne des paramĂštres dans Notch" + "value" : "Enable shelf" } - } - } - }, - "Shelf" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "ÉtagĂšre" + "value" : "Activer les Ă©tagĂšres" } } } }, - "Shortcuts" : { + "settings.shelf.header.general" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "General" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Raccourcis" + "value" : "GĂ©nĂ©ral" } } } }, - "Show battery indicator" : { - "comment" : "Toggle in parameters to show battery indicator", + "settings.shelf.open_when_items_added" : { + "comment" : "Toggle in parameters to open shelf by default if items are added", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show battery indicator" + "value" : "Open shelf tab by default if items added" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher l’indicateur de batterie" + "value" : "Ouvrir l’étagĂšre par dĂ©faut quand un Ă©lĂ©ment est ajoutĂ©" } } } }, - "Show calendar" : { - "comment" : "Toggle in parameters to show the calendar in the expanded view", + "settings.shortcuts.header.media" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Media" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Montrer le calendrier" + "value" : "MĂ©dias" } } } }, - "Show charging indicator" : { - "comment" : "Toogle in parameters to Show charging indicator", + "settings.shortcuts.sneak_peek_description" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show charging indicator" + "value" : "Sneak Peek shows the media title and artist under the notch for a few seconds." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher l’indicateur de charge" + "value" : "Sneak Peek affiche le nom et l’artiste du mĂ©dia sous l’encoche pour quelques secondes." } } } }, - "Show cool face animation while inactivity" : { - "comment" : "Toggle in parameters to show a face animation when inactive", + "settings.shortcuts.toggle_notch" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show cool face animation while inactivity" + "value" : "Toggle Notch Open:" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher une animation stylĂ©e quand inactif" + "value" : "Permuter l’ouverture de l’encoche:" } } } }, - "Show download progress" : { - "comment" : "Toggle in parameters to show the download progress in supported apps", + "settings.shortcuts.toggle_sneak_peek" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show download progress" + "value" : "Toggle Sneak Peek:" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher le status du tĂ©lĂ©chargement" + "value" : "Permuter Sneak Peak:" } } } }, - "Show on a specific display" : { + "settings.tab.about" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher sur un Ă©cran spĂ©cifique" + "value" : "About" } - } - } - }, - "Show on all displays" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher sur tous les Ă©crans" + "value" : "À Propos" } } } }, - "Slider color" : { + "settings.tab.appearance" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Couleur du curseur" + "value" : "Appearance" } - } - } - }, - "Sneak Peek shows the media title and artist under the notch for a few seconds." : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Sneak Peek affiche le nom et l’artiste du mĂ©dia sous l’encoche pour quelques secondes." + "value" : "Apparence" } } } }, - "Software updates" : { + "settings.tab.battery" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Software updates" + "value" : "Battery" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mises Ă  jour" + "value" : "Batterie" } } } }, - "Speed" : { + "settings.tab.downloads" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Vitesse" + "value" : "Downloads" } - } - } - }, - "Square" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "CarrĂ©" + "value" : "TĂ©lĂ©chargements" } } } }, - "Stopped" : { + "settings.tab.extensions" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Extensions" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "ArrĂȘtĂ©" + "value" : "Extensions" } } } }, - "Support Us" : { + "settings.tab.general" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Support Us" + "value" : "General" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Supportez-nous" + "value" : "GĂ©nĂ©ral" } } } }, - "System features" : { + "settings.tab.huds" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "HUDs" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "FonctionnalitĂ©s systĂšme" + "value" : "ATHs" } } } }, - "TheBoringNotch" : { + "settings.tab.media" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Media" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "TheBoringNotch" + "value" : "MĂ©dia" } } } }, - "Toggle Notch Open:" : { + "settings.tab.shelf" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shelf" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Permuter l’ouverture de l’encoche:" + "value" : "ÉtagĂšre" } } } }, - "Toggle Sneak Peek:" : { + "settings.tab.shortcuts" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shortcuts" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Permuter Sneak Peak:" + "value" : "Raccourcis" } } } }, - "Transform your notch truly yours" : { + "settings.toolbar.onboarding" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onboarding" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rendez l’encoche vraiment vĂŽtre" + "value" : "Écran de bienvenue" } } } }, - "Two-finger swipe up on notch to close, two-finger swipe down on notch to open when **Open notch on hover** option is disabled" : { + "shelf.drop_files_here" : { + + }, + "Show download progress" : { + "comment" : "Toggle in parameters to show the download progress in supported apps", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show download progress" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Faites glissez avec deux doigts vers le haut pour fermer Boring Notch, vers le bas pour l’ouvrir quand **Ouvrir l’encoche en survolant** est dĂ©sactivĂ©" + "value" : "Afficher le status du tĂ©lĂ©chargement" } } } }, - "Uninstall" : { + "Slider color" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "DĂ©sinstaller" + "value" : "Couleur du curseur" } } } }, - "Unlock advanced features and improve your experience. Upgrade now for more customizations!" : { + "Square" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "DĂ©bloquez des fonctionnalitĂ©s avancĂ©e pour amĂ©liorer votre expĂ©rience. Mettez Ă  jour maintenant pour plus de personnalisations!" + "value" : "CarrĂ©" } } } }, - "unmuted" : { + "TheBoringNotch" : { "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "actif" + "value" : "TheBoringNotch" } } } }, - "Upgrade to Pro" : { + "tips.hud.message" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Passez Ă  Pro" + "value" : "Unlock advanced features and improve your experience. Upgrade now for more customizations!" } } } }, - "Use accent color" : { - "comment" : "Toggle in parameters to show the accent color of progress bars", + "tips.hud.title" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Utiliser la couleur d’accentuation" + "value" : "Enhance your experience with HUDs" } } } }, - "Use music visualizer spectrogram" : { + "updater.automatically_check_updates" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Utiliser le spectrogramme comme visualisateur musical" + "value" : "Automatically check for updates" } } } }, - "Version" : { + "updater.automatically_download_updates" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Version" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Version" + "value" : "Automatically download updates" } } } }, - "Version info" : { + "updater.check_for_updates" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Version info" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Informations de version" + "value" : "Check for updates" } } } }, - "Welcome" : { + "updater.header.check_updates" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Bienvenue" + "value" : "Software updates" } } } }, - "Welcome window" : { + "Use accent color" : { + "comment" : "Toggle in parameters to show the accent color of progress bars", "localizations" : { "fr" : { "stringUnit" : { "state" : "translated", - "value" : "FenĂȘtre de bienvenue" + "value" : "Utiliser la couleur d’accentuation" } } } diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index 836cb9b..b787058 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -26,7 +26,7 @@ struct DynamicNotchApp: App { var body: some Scene { MenuBarExtra("boring.notch", systemImage: "sparkle", isInserted: $showMenuBarIcon) { SettingsLink(label: { - Text("Settings") + Text("common.settings") }) .keyboardShortcut(KeyEquivalent(","), modifiers: .command) if false { @@ -51,7 +51,7 @@ struct DynamicNotchApp: App { NSApplication.shared.terminate(nil) } - Button("Quit", role: .destructive) { + Button("common.quit", role: .destructive) { NSApp.terminate(nil) } .keyboardShortcut(KeyEquivalent("Q"), modifiers: .command) diff --git a/boringNotch/components/Calender/BoringCalendar.swift b/boringNotch/components/Calender/BoringCalendar.swift index 45a0779..a7563b0 100644 --- a/boringNotch/components/Calender/BoringCalendar.swift +++ b/boringNotch/components/Calender/BoringCalendar.swift @@ -198,10 +198,10 @@ struct CalendarView: View { struct EmptyEventsView: View { var body: some View { ScrollView { - Text("No events today") + Text("calendar.no_events") .font(.headline) .foregroundStyle(.white) - Text("Enjoy your free time!") + Text("calendar.enjoy_free_time") .font(.subheadline) .foregroundColor(.gray) } @@ -220,7 +220,7 @@ struct EventListView: View { if isAllDayEvent( start: events[index].startDate, end: events[index].endDate) { - Text("All-day") + Text("calendar.all_day") } else { Text("\(events[index].startDate, style: .time)") Text("\(events[index].endDate, style: .time)") diff --git a/boringNotch/components/Live activities/DownloadView.swift b/boringNotch/components/Live activities/DownloadView.swift index 2e6104b..26a625a 100644 --- a/boringNotch/components/Live activities/DownloadView.swift +++ b/boringNotch/components/Live activities/DownloadView.swift @@ -36,8 +36,8 @@ struct DownloadArea: View { Image(.chrome).resizable().scaledToFit().frame(width: 30, height: 30) } VStack(alignment: .leading) { - Text("Download") - Text("In progress").font(.system(.footnote)).foregroundStyle(.gray) + Text("downloads.download") + Text("downloads.in_progress").font(.system(.footnote)).foregroundStyle(.gray) } } Spacer() diff --git a/boringNotch/components/Live activities/InlineHUD.swift b/boringNotch/components/Live activities/InlineHUD.swift index 19f9200..299ba87 100644 --- a/boringNotch/components/Live activities/InlineHUD.swift +++ b/boringNotch/components/Live activities/InlineHUD.swift @@ -68,7 +68,7 @@ struct InlineHUD: View { HStack { if (type == .mic) { - Text(value.isZero ? "muted" : "unmuted") + Text(value.isZero ? "common.muted" : "common.unmuted") .foregroundStyle(.gray) .lineLimit(1) .allowsTightening(true) @@ -79,7 +79,7 @@ struct InlineHUD: View { HStack { DraggableProgressBar(value: $value) if (type == .volume && value.isZero) { - Text("muted") + Text("common.muted") .font(.caption) .fontWeight(.medium) .foregroundStyle(.gray) diff --git a/boringNotch/components/Notch/BoringExtrasMenu.swift b/boringNotch/components/Notch/BoringExtrasMenu.swift index d1bceef..8b12fac 100644 --- a/boringNotch/components/Notch/BoringExtrasMenu.swift +++ b/boringNotch/components/Notch/BoringExtrasMenu.swift @@ -56,7 +56,7 @@ struct BoringExtrasMenu : View { NSWorkspace.shared.open(sponsorPage) }, icon: Image(systemName: "heart.fill"), - title: "Love Us" + title: "boring.love_us" ) } @@ -67,7 +67,7 @@ struct BoringExtrasMenu : View { VStack(spacing: 8) { Image(systemName: "gear").resizable() .aspectRatio(contentMode: .fit).frame(width:20) - Text("Settings").font(.body) + Text("common.settings").font(.body) } } }) @@ -82,7 +82,7 @@ struct BoringExtrasMenu : View { } }, icon: Image(systemName: "arrow.down.forward.and.arrow.up.backward"), - title: "Hide" + title: "common.hide" ) } @@ -96,7 +96,7 @@ struct BoringExtrasMenu : View { } }, icon: Image(systemName: "xmark"), - title: "Exit" + title: "common.exit" ) } } diff --git a/boringNotch/components/Notch/NotchContentView.swift b/boringNotch/components/Notch/NotchContentView.swift index 5cf5552..e758a8f 100644 --- a/boringNotch/components/Notch/NotchContentView.swift +++ b/boringNotch/components/Notch/NotchContentView.swift @@ -34,7 +34,7 @@ struct NotchContentView: View { HStack(spacing: 14) { if vm.notchState == .closed && vm.expandingView.show { if(vm.expandingView.type == .battery){ - Text("Charging").foregroundStyle(.white).padding(.leading, 4) + Text("common.charging").foregroundStyle(.white).padding(.leading, 4) } else { if vm.expandingView.browser == .safari { diff --git a/boringNotch/components/Notch/NotchShelfView.swift b/boringNotch/components/Notch/NotchShelfView.swift index 6bc8a36..81294d8 100644 --- a/boringNotch/components/Notch/NotchShelfView.swift +++ b/boringNotch/components/Notch/NotchShelfView.swift @@ -41,7 +41,7 @@ struct NotchShelfView: View { .foregroundStyle(.white, .gray) .imageScale(.large) - Text("Drop files here") + Text("shelf.drop_files_here") .foregroundStyle(.gray) .font(.system(.title3, design: .rounded)) .fontWeight(.medium) diff --git a/boringNotch/components/Onboarding/ActivationView.swift b/boringNotch/components/Onboarding/ActivationView.swift index d4306cb..7409da9 100644 --- a/boringNotch/components/Onboarding/ActivationView.swift +++ b/boringNotch/components/Onboarding/ActivationView.swift @@ -7,6 +7,14 @@ import SwiftUI +struct ActivationWindow_Previews: PreviewProvider { + static var previews: some View { + ActivationWindow() + .previewLayout(.sizeThatFits) + .padding() + } +} + struct ActivationWindow: View { @State private var email: String = "" @State private var key: String = "" @@ -18,19 +26,19 @@ struct ActivationWindow: View { .frame(height: 80) .padding(.top, 30) .padding(.bottom, 10) - Text("Activate your license") + Text("activation.header.title") .font(.largeTitle.bold()) .fontDesign(.rounded) - Text("Transform your notch truly yours") + Text("activation.header.subtitle") .foregroundStyle(.secondary) .font(.title2) .padding(.bottom, 20) Group { - TextField("Email address", text: $email) + TextField("activation.process.email", text: $email) .padding(.horizontal, 8) .padding(.vertical, 4) .background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 8)) - TextField("License key", text: $key) + TextField("activation.process.key", text: $key) .padding(.horizontal, 8) .padding(.vertical, 4) .background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 8)) @@ -43,7 +51,7 @@ struct ActivationWindow: View { HStack(alignment: .center) { HStack { Button {} label: { - Text("Cancel") + Text("common.cancel") .padding(.horizontal, 18) } .buttonStyle(AccessoryBarButtonStyle()) @@ -57,7 +65,7 @@ struct ActivationWindow: View { .blendMode(.overlay) HStack { Button {} label: { - Text("Activate") + Text("activation.process.activate") .padding(.horizontal, 18) } .buttonStyle(BorderedProminentButtonStyle()) diff --git a/boringNotch/components/Onboarding/ProOnboarding.swift b/boringNotch/components/Onboarding/ProOnboarding.swift index b049cb5..78ee762 100644 --- a/boringNotch/components/Onboarding/ProOnboarding.swift +++ b/boringNotch/components/Onboarding/ProOnboarding.swift @@ -8,6 +8,14 @@ import SwiftUI import SwiftUIIntrospect +struct ProOnboard_Previews: PreviewProvider { + static var previews: some View { + ProOnboard() + .previewLayout(.sizeThatFits) + .padding() + } +} + struct ProOnboard: View { var body: some View { ZStack(alignment: .top) { @@ -27,7 +35,7 @@ struct ProOnboard: View { .padding(.bottom, 8) Text("TheBoringNotch") .font(.system(.largeTitle, design: .serif)) - Text("Welcome") + Text("common.welcome") .font(.title) .foregroundStyle(.secondary) .padding(.bottom, 30) @@ -49,7 +57,7 @@ struct ProOnboard: View { Button { NSApp.keyWindow?.close() } label: { - Text("Get started") + Text("onboarding.get_started") .padding(.horizontal, 20) .padding(.vertical, 6) } diff --git a/boringNotch/components/Settings/EditPanelView.swift b/boringNotch/components/Settings/EditPanelView.swift index 0d3c025..dd0c034 100644 --- a/boringNotch/components/Settings/EditPanelView.swift +++ b/boringNotch/components/Settings/EditPanelView.swift @@ -12,14 +12,14 @@ struct EditPanelView: View { var body: some View { VStack { HStack { - Text("Edit layout") + Text("edit_panel.edit_layout") .font(.system(.largeTitle, design: .rounded)) .foregroundColor(.white.opacity(0.5)) Spacer() Button { exit(0) } label: { - Label("Close", systemImage: "xmark") + Label("common.close", systemImage: "xmark") } .controlSize(.extraLarge) .buttonStyle(AccessoryBarButtonStyle()) diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index d2914ea..5705c00 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -14,6 +14,17 @@ import SwiftUIIntrospect import AVFoundation import LottieUI +struct SettingsView_Previews: PreviewProvider { + static var previews: some View { + + let updaterController = SPUStandardUpdaterController(startingUpdater: false, updaterDelegate: nil, userDriverDelegate: nil) + + SettingsView(extensionManager: BoringExtensionManager(), updaterController: updaterController) + .previewLayout(.sizeThatFits) + .padding() + } +} + struct SettingsView: View { @Environment(\.scenePhase) private var scenePhase @StateObject var extensionManager = BoringExtensionManager() @@ -25,46 +36,46 @@ struct SettingsView: View { NavigationSplitView { List(selection: $selectedTab) { NavigationLink(destination: GeneralSettings()) { - Label("General", systemImage: "gear") + Label("settings.tab.general", systemImage: "gear") } NavigationLink(destination: Appearance()) { - Label("Appearance", systemImage: "eye") + Label("settings.tab.appearance", systemImage: "eye") } NavigationLink(destination: Media()) { - Label("Media", systemImage: "play.laptopcomputer") + Label("settings.tab.media", systemImage: "play.laptopcomputer") } if extensionManager.installedExtensions .contains( where: { $0.bundleIdentifier == hudExtension }) { NavigationLink(destination: HUD()) { - Label("HUDs", systemImage: "dial.medium.fill") + Label("settings.tab.huds", systemImage: "dial.medium.fill") } } NavigationLink(destination: Charge()) { - Label("Battery", systemImage: "battery.100.bolt") + Label("settings.tab.battery", systemImage: "battery.100.bolt") } if extensionManager.installedExtensions .contains( where: { $0.bundleIdentifier == downloadManagerExtension }) { NavigationLink(destination: Downloads()) { - Label("Downloads", systemImage: "square.and.arrow.down") + Label("settings.tab.downloads", systemImage: "square.and.arrow.down") } } NavigationLink(destination: Shelf()) { - Label("Shelf", systemImage: "books.vertical") + Label("settings.tab.shelf", systemImage: "books.vertical") } NavigationLink(destination: Shortcuts()) { - Label("Shortcuts", systemImage: "keyboard") + Label("settings.tab.shortcuts", systemImage: "keyboard") } NavigationLink(destination: Extensions()) { - Label("Extensions", systemImage: "puzzlepiece.extension") + Label("settings.tab.extensions", systemImage: "puzzlepiece.extension") } NavigationLink( destination: About(updaterController: updaterController) ) { - Label("About", systemImage: "info.circle") + Label("settings.tab.about", systemImage: "info.circle") } } .tint(Defaults[.accentColor]) @@ -140,27 +151,27 @@ struct GeneralSettings: View { .labelsHidden() } } header: { - Text("Accent color") + Text("settings.general.header.accent_color") } Section { Defaults.Toggle( - NSLocalizedString("Menubar icon", comment: "Toggle in parameter to enable menubar icon"), + NSLocalizedString("settings.general.menubar_icon", comment: "Toggle in parameter to enable menubar icon"), key: .menubarIcon ) LaunchAtLogin.Toggle( - NSLocalizedString("Launch at login", comment:"Launch at login") + NSLocalizedString("settings.general.launch_at_login", comment:"Toggle for parameter launch at login") ) Defaults.Toggle(key: .showOnAllDisplays) { HStack { - Text("Show on all displays") - customBadge(text: "Beta") + Text("settings.general.show_on_all_displays") + customBadge(text: "common.beta") } } .onChange(of: showOnAllDisplays) { NotificationCenter.default.post(name: Notification.Name.showOnAllDisplaysChanged, object: nil) } - Picker("Show on a specific display", selection: $coordinator.preferredScreen) { + Picker("settings.general.show_on_specific_display", selection: $coordinator.preferredScreen) { ForEach(screens, id: \.self) { screen in Text(screen) } @@ -169,20 +180,20 @@ struct GeneralSettings: View { screens = NSScreen.screens.compactMap({$0.localizedName}) } } header: { - Text("System features") + Text("settings.general.header.system_features") } Section { Picker(selection: $notchHeightMode, label: HStack { - Text("Notch display height") - customBadge(text: "Beta") + Text("settings.general.notch_display_height") + customBadge(text: "common.beta") }) { - Text("Match real notch size") + Text("settings.general.notch_match_display_height") .tag(WindowHeightMode.matchRealNotchSize) - Text("Match menubar height") + Text("settings.general.notch_match_menubar_height") .tag(WindowHeightMode.matchMenuBar) - Text("Custom height") + Text("settings.general.notch_custom_height") .tag(WindowHeightMode.custom) } .onChange(of: notchHeightMode) { @@ -204,12 +215,12 @@ struct GeneralSettings: View { NotificationCenter.default.post(name: Notification.Name.notchHeightChanged, object: nil) } } - Picker("Non-notch display height", selection: $nonNotchHeightMode) { - Text("Match menubar height") + Picker("settings.general.non_notch_display_height", selection: $nonNotchHeightMode) { + Text("settings.general.notch_match_menubar_height") .tag(WindowHeightMode.matchMenuBar) - Text("Match real notch size") + Text("settings.general.notch_match_display_height") .tag(WindowHeightMode.matchRealNotchSize) - Text("Custom height") + Text("settings.general.notch_custom_height") .tag(WindowHeightMode.custom) } .onChange(of: nonNotchHeightMode) { @@ -232,7 +243,7 @@ struct GeneralSettings: View { } } } header: { - Text("Notch Height") + Text("settings.general.header.notch_height") } NotchBehaviour() @@ -241,12 +252,12 @@ struct GeneralSettings: View { } .tint(Defaults[.accentColor]) .toolbar { - Button("Quit app") { + Button("common.quit_app") { NSApp.terminate(self) } .controlSize(.extraLarge) } - .navigationTitle("General") + .navigationTitle("settings.tab.general") .onChange(of: openNotchOnHover) { if !openNotchOnHover { enableGestures = true @@ -258,33 +269,33 @@ struct GeneralSettings: View { func gestureControls() -> some View { Section { Defaults.Toggle( - NSLocalizedString("Enable gestures", comment: "Toggle in parameters to enable gestures"), + NSLocalizedString("settings.general.gestures.enable", comment: "Toggle in parameters to enable gestures"), key: .enableGestures ) .disabled(!openNotchOnHover) if enableGestures { - Toggle("Media change with horizontal gestures", isOn: .constant(false)) + Toggle("settings.general.gestures.media_change_horizontal", isOn: .constant(false)) .disabled(true) Defaults.Toggle( - NSLocalizedString("Close gesture", comment: "Toggle in parameters to enable the closing gesture" ), + NSLocalizedString("settings.general.gestures.close", comment: "Toggle in parameters to enable the closing gesture" ), key: .closeGestureEnabled ) Slider(value: $gestureSensitivity, in: 100...300, step: 100) { HStack { - Text("Gesture sensitivity") + Text("settings.general.gestures.sensitivity") Spacer() - Text(Defaults[.gestureSensitivity] == 100 ? "High" : Defaults[.gestureSensitivity] == 200 ? "Medium" : "Low") + Text(Defaults[.gestureSensitivity] == 100 ? "settings.general.gestures.sensitivity.high" : Defaults[.gestureSensitivity] == 200 ? "settings.general.gestures.sensitivity.medium" : "settings.general.gestures.sensitivity.low") .foregroundStyle(.secondary) } } } } header: { HStack { - Text("Gesture control") - customBadge(text: "Beta") + Text("settings.general.header.gestures") + customBadge(text: "common.beta") } } footer: { - Text("Two-finger swipe up on notch to close, two-finger swipe down on notch to open when **Open notch on hover** option is disabled") + Text("settings.general.gestures.descriptions.open_close") .multilineTextAlignment(.trailing) .foregroundStyle(.secondary) .font(.caption) @@ -295,18 +306,18 @@ struct GeneralSettings: View { func NotchBehaviour() -> some View { Section { Defaults.Toggle( - NSLocalizedString("Enable haptics", comment: "Parameter toggle for enabling haptics"), + NSLocalizedString("settings.general.behavior.enable_haptics", comment: "Parameter toggle for enabling haptics"), key: .enableHaptics ) Defaults.Toggle( - NSLocalizedString("Open notch on hover", comment: "Parameter toggle for opening notch on hover"), + NSLocalizedString("settings.general.behavior.open_on_hover", comment: "Parameter toggle for opening notch on hover"), key: .openNotchOnHover ) - Toggle("Remember last tab", isOn: $coordinator.openLastTabByDefault) + Toggle("settings.general.behavior.remember_last_tab", isOn: $coordinator.openLastTabByDefault) if openNotchOnHover { Slider(value: $minimumHoverDuration, in: 0...1, step: 0.1) { HStack { - Text("Minimum hover duration") + Text("settings.general.behavior.hover_minimum_duration") Spacer() Text("\(minimumHoverDuration, specifier: "%.1f")s") .foregroundStyle(.secondary) @@ -317,7 +328,7 @@ struct GeneralSettings: View { } } } header: { - Text("Notch behavior") + Text("settings.general.header.behavior") } } } @@ -327,19 +338,19 @@ struct Charge: View { Form { Section { Defaults.Toggle( - NSLocalizedString("Show charging indicator", comment: "Toogle in parameters to Show charging indicator"), + NSLocalizedString("settings.battery.show_charging_indicator", comment: "Toogle in parameters to Show charging indicator"), key: .chargingInfoAllowed ) Defaults.Toggle( - NSLocalizedString("Show battery indicator", comment: "Toggle in parameters to show battery indicator"), + NSLocalizedString("settings.battery.show_battery_indicator", comment: "Toggle in parameters to show battery indicator"), key: .showBattery ) } header: { - Text("General") + Text("settings.battery.header.general") } } .tint(Defaults[.accentColor]) - .navigationTitle("Battery") + .navigationTitle("settings.tab.battery") } } @@ -487,17 +498,17 @@ struct Media: View { Form { Section { Toggle( - "Enable music live activity", + "settings.media.enable_live_activity", isOn: $coordinator.showMusicLiveActivityOnClosed.animation() ) Defaults.Toggle( - NSLocalizedString("Enable sneak peek", comment: "Toggle in parameters to enable the Sneak Peek feature"), + NSLocalizedString("settings.media.enable_sneak_peek", comment: "Toggle in parameters to enable the Sneak Peek feature"), key: .enableSneakPeek ) HStack { Stepper(value: $waitInterval, in: 0...10, step: 1) { HStack { - Text("Media inactivity timeout") + Text("settings.media.timeout") Spacer() Text("\(Defaults[.waitInterval], specifier: "%.0f") seconds") .foregroundStyle(.secondary) @@ -505,23 +516,23 @@ struct Media: View { } } } header: { - Text("Media playback live activity") + Text("settings.media.header.live_activity") } Section { Defaults.Toggle( - NSLocalizedString("Autohide BoringNotch in fullscreen", comment: "Toggle in the parameters to autohide Boring notch in fullscreen"), + NSLocalizedString("settings.media.autohide_in_fullscreen", comment: "Toggle in the parameters to autohide Boring notch in fullscreen"), key: .enableFullscreenMediaDetection ) } header: { HStack { - Text("Fullscreen media playback detection") - customBadge(text: "Beta") + Text("settings.media.header.fullscreen") + customBadge(text: "common.beta") } } } .tint(Defaults[.accentColor]) - .navigationTitle("Media") + .navigationTitle("settings.tab.media") } } @@ -534,13 +545,13 @@ struct About: View { Form { Section { HStack { - Text("Release name") + Text("settings.about.release_name") Spacer() Text(Defaults[.releaseName]) .foregroundStyle(.secondary) } HStack { - Text("Version") + Text("settings.about.version") Spacer() if (showBuildNumber) { Text("(\(Bundle.main.buildVersionNumber ?? ""))") @@ -555,7 +566,7 @@ struct About: View { } } } header: { - Text("Version info") + Text("settings.about.header.version") } UpdaterSettingsView(updater: updaterController.updater) @@ -568,7 +579,7 @@ struct About: View { VStack(spacing: 5) { Image(systemName: "cup.and.saucer.fill") .imageScale(.large) - Text("Support Us") + Text("settings.about.support_us") .foregroundStyle(.white) } .contentShape(Rectangle()) @@ -593,7 +604,7 @@ struct About: View { } VStack(spacing: 0) { Divider() - Text("Made with đŸ«¶đŸ» by not so boring not.people") + Text("settings.about.footer") .foregroundStyle(.secondary) .padding(.top, 5) .padding(.bottom, 7) @@ -603,14 +614,14 @@ struct About: View { .frame(maxWidth: .infinity, alignment: .center) } .toolbar { - Button("Welcome window") { + Button("settings.toolbar.onboarding") { openWindow(id: "onboarding") } .controlSize(.extraLarge) CheckForUpdatesView(updater: updaterController.updater) } .tint(Defaults[.accentColor]) - .navigationTitle("About") + .navigationTitle("settings.tab.about") } } @@ -619,21 +630,21 @@ struct Shelf: View { Form { Section { Defaults.Toggle( - NSLocalizedString("Enable shelf", comment: "Toggle in parameters to enable shelf system"), + NSLocalizedString("settings.shelf.enable_shelf", comment: "Toggle in parameters to enable shelf system"), key: .boringShelf ) Defaults.Toggle( - NSLocalizedString("Open shelf tab by default if items added", comment: "Toggle in parameters to open shelf by default if items are added"), + NSLocalizedString("settings.shelf.open_when_items_added", comment: "Toggle in parameters to open shelf by default if items are added"), key: .openShelfByDefault ) } header: { HStack { - Text("General") + Text("settings.shelf.header.general") } } } .tint(Defaults[.accentColor]) - .navigationTitle("Shelf") + .navigationTitle("settings.tab.shelf") } } @@ -663,7 +674,7 @@ struct Extensions: View { view .shadow(color: .green, radius: 3) } - Text(isExtensionRunning(item.bundleIdentifier) ? "Running" : item.status == .disabled ? "Disabled" : "Stopped") + Text(isExtensionRunning(item.bundleIdentifier) ? "settings.extensions.running" : item.status == .disabled ? "setting.extensions.disabled" : "settings.extensions.stopped") .contentTransition(.numericText()) .foregroundStyle(.secondary) .font(.footnote) @@ -671,7 +682,7 @@ struct Extensions: View { .frame(width: 60, alignment: .leading) Menu(content: { - Button("Restart") { + Button("settings.extensions.restart") { let ws = NSWorkspace.shared if let ext = ws.runningApplications.first(where: {$0.bundleIdentifier == item.bundleIdentifier}) { @@ -683,7 +694,7 @@ struct Extensions: View { } } .keyboardShortcut("R", modifiers: .command) - Button("Disable") { + Button("settings.extensions.disable") { if let ext = NSWorkspace.shared.runningApplications.first(where: {$0.bundleIdentifier == item.bundleIdentifier}) { ext.terminate() } @@ -691,7 +702,7 @@ struct Extensions: View { } .keyboardShortcut("D", modifiers: .command) Divider() - Button("Uninstall", role: .destructive) { + Button("settings.extensions.uninstall", role: .destructive) { // } }, label: { @@ -712,7 +723,7 @@ struct Extensions: View { } label: { HStack(spacing: 3) { Image(systemName: "plus") - Text("Add manually") + Text("settings.extensions.add_manually") } .foregroundStyle(.secondary) } @@ -737,14 +748,14 @@ struct Extensions: View { .buttonStyle(PlainButtonStyle()) .overlay { if extensionManager.installedExtensions.isEmpty { - Text("No extension installed") + Text("settings.extensions.no_extensions_installed") .foregroundStyle(Color(.secondaryLabelColor)) .padding(.bottom, 22) } } } header: { HStack(spacing: 0) { - Text("Installed extensions") + Text("settings.extensions.header.installed_extensions") if !extensionManager.installedExtensions.isEmpty { Text(" – \(extensionManager.installedExtensions.count)") .foregroundStyle(.secondary) @@ -753,7 +764,7 @@ struct Extensions: View { } } .tint(Defaults[.accentColor]) - .navigationTitle("Extensions") + .navigationTitle("settings.tab.extensions") //TipsView() //.padding(.horizontal, 19) } @@ -777,34 +788,34 @@ struct Appearance: View { var body: some View { Form { Section { - Toggle("Always show tabs", isOn: $coordinator.alwaysShowTabs) + Toggle("settings.appearance.always_show_tabs", isOn: $coordinator.alwaysShowTabs) Defaults.Toggle( - NSLocalizedString("Settings icon in notch", comment: "Toggle in parameters for settings icon in notch"), + NSLocalizedString("settings.appearance.settings_icon_in_notch", comment: "Toggle in parameters for settings icon in notch"), key: .settingsIconInNotch ) Defaults.Toggle( - NSLocalizedString("Enable window shadow", comment: "Toggle in parameters for enabling the window shadow"), + NSLocalizedString("settings.appearance.enable_window_shadow", comment: "Toggle in parameters for enabling the window shadow"), key: .enableShadow ) Defaults.Toggle( - NSLocalizedString("Corner radius scaling", comment: "Toggle in parameters for corner radius scaling"), + NSLocalizedString("settings.appearance.corner_radius_scaling", comment: "Toggle in parameters for corner radius scaling"), key: .cornerRadiusScaling ) } header: { - Text("General") + Text("settings.appearance.header.general") } Section { Defaults.Toggle( - NSLocalizedString("Enable colored spectrograms", comment: "Toggle in parameters for colored spectrograms"), + NSLocalizedString("settings.appearance.enable_colored_spectrograms", comment: "Toggle in parameters for colored spectrograms"), key: .coloredSpectrogram ) Defaults.Toggle( - NSLocalizedString("Player tinting", comment: "Toggle in parameters to tint the player"), + NSLocalizedString("settings.appearance.player_tinting", comment: "Toggle in parameters to tint the player"), key: .playerColorTinting ) Defaults.Toggle( - NSLocalizedString("Enable blur effect behind album art", comment: "Toggle in parameters to enable the blur effect behind album art"), + NSLocalizedString("settings.appearance.enable_blur_effect_album_art", comment: "Toggle in parameters to enable the blur effect behind album art"), key: .lightingEffect ) Picker("Slider color", selection: $sliderColor) { @@ -813,19 +824,19 @@ struct Appearance: View { } } } header: { - Text("Media") + Text("settings.appearance.header.media") } Section { Toggle( - "Use music visualizer spectrogram", + "settings.appearance.use_music_visualizer_spectrogram", isOn: $useMusicVisualizer.animation() ) .disabled(true) if !useMusicVisualizer { if customVisualizers.count > 0 { Picker( - "Selected animation", + "settings.appearance.spectrogram_selected_animation", selection: $selectedVisualizer ) { ForEach( @@ -838,17 +849,17 @@ struct Appearance: View { } } else { HStack { - Text("Selected animation") + Text("settings.appearance.spectrogram_selected_animation") Spacer() - Text("No custom animation available") + Text("settings.appearance.spectrogram_selected_animation_none") .foregroundStyle(.secondary) } } } } header: { HStack { - Text("Custom music live activity animation") - customBadge(text: "Coming soon") + Text("settings.appearance.header.custom_live_activity_anim") + customBadge(text: "common.coming_soon") } } @@ -861,7 +872,7 @@ struct Appearance: View { Text(visualizer.name) Spacer(minLength: 0) if selectedVisualizer == visualizer { - Text("selected") + Text("common.selected") .font(.caption) .fontWeight(.medium) .foregroundStyle(.secondary) @@ -921,20 +932,20 @@ struct Appearance: View { .buttonStyle(PlainButtonStyle()) .overlay { if customVisualizers.isEmpty { - Text("No custom visualizer") + Text("settings.appearance.no_custom_visualizer") .foregroundStyle(Color(.secondaryLabelColor)) .padding(.bottom, 22) } } .sheet(isPresented: $isPresented) { VStack(alignment: .leading) { - Text("Add new visualizer") + Text("settings.appearance.add_custom_visualizer") .font(.largeTitle.bold()) .padding(.vertical) - TextField("Name", text: $name) - TextField("Lottie JSON URL", text: $url) + TextField("common.name", text: $name) + TextField("settings.appearance.lottie_json_url", text: $url) HStack { - Text("Speed") + Text("common.speed") Spacer(minLength: 80) Text("\(speed, specifier: "%.1f")s") .multilineTextAlignment(.trailing) @@ -946,7 +957,7 @@ struct Appearance: View { Button { isPresented.toggle() } label: { - Text("Cancel") + Text("common.cancel") .frame(maxWidth: .infinity, alignment: .center) } @@ -964,7 +975,7 @@ struct Appearance: View { isPresented.toggle() } label: { - Text("Add") + Text("common.add") .frame(maxWidth: .infinity, alignment: .center) } .buttonStyle(BorderedProminentButtonStyle()) @@ -976,7 +987,7 @@ struct Appearance: View { } } header: { HStack(spacing: 0) { - Text("Custom vizualizers (Lottie)") + Text("settings.appearance.header.custom_visualizers") if !Defaults[.customVisualizers].isEmpty { Text(" – \(Defaults[.customVisualizers].count)") .foregroundStyle(.secondary) @@ -986,28 +997,28 @@ struct Appearance: View { Section { Defaults.Toggle( - NSLocalizedString("Enable boring mirror", comment: "Toggle in parameters to enable Boring mirror"), + NSLocalizedString("settings.appearance.enable_boring_mirror", comment: "Toggle in parameters to enable Boring mirror"), key: .showMirror ) .disabled(!checkVideoInput()) - Picker("Mirror shape", selection: $mirrorShape) { + Picker("settings.appearance.mirror_shape", selection: $mirrorShape) { Text("Circle") .tag(MirrorShapeEnum.circle) Text("Square") .tag(MirrorShapeEnum.rectangle) } Defaults.Toggle( - NSLocalizedString("Show calendar", comment: "Toggle in parameters to show the calendar in the expanded view"), + NSLocalizedString("settings.appearance.show_calendar", comment: "Toggle in parameters to show the calendar in the expanded view"), key: .showCalendar ) Defaults.Toggle( - NSLocalizedString("Show cool face animation while inactivity", comment: "Toggle in parameters to show a face animation when inactive"), + NSLocalizedString("settings.appearance.show_idle_face_animation", comment: "Toggle in parameters to show a face animation when inactive"), key: .showNotHumanFace ) .disabled(true) } header: { HStack { - Text("Additional features") + Text("settings.appearance.header.additional_features") } } @@ -1027,7 +1038,7 @@ struct Appearance: View { ) ) - Text("Default") + Text("common.default") .fontWeight(.medium) .font(.caption) .foregroundStyle(icon == selectedIcon ? .white : .secondary) @@ -1050,13 +1061,13 @@ struct Appearance: View { .disabled(true) } header: { HStack { - Text("App icon") - customBadge(text: "Coming soon") + Text("settings.appearance.header.app_icon") + customBadge(text: "common.coming_soon") } } } .tint(Defaults[.accentColor]) - .navigationTitle("Appearance") + .navigationTitle("settings.tab.appearance") } func checkVideoInput() -> Bool { @@ -1072,26 +1083,26 @@ struct Shortcuts: View { var body: some View { Form { Section { - KeyboardShortcuts.Recorder("Toggle Sneak Peek:", name: .toggleSneakPeek) + KeyboardShortcuts.Recorder("settings.shortcuts.toggle_sneak_peek", name: .toggleSneakPeek) } header: { - Text("Media") + Text("settings.shortcuts.header.media") } footer: { - Text("Sneak Peek shows the media title and artist under the notch for a few seconds.") + Text("settings.shortcuts.sneak_peek_description") .multilineTextAlignment(.trailing) .foregroundStyle(.secondary) .font(.caption) } Section { - KeyboardShortcuts.Recorder("Toggle Notch Open:", name: .toggleNotchOpen) + KeyboardShortcuts.Recorder("settings.shortcuts.toggle_notch", name: .toggleNotchOpen) } } .tint(Defaults[.accentColor]) - .navigationTitle("Shortcuts") + .navigationTitle("settings.tab.shortcuts") } } func proFeatureBadge() -> some View { - Text("Upgrade to Pro") + Text("common.upgrade_pro") .foregroundStyle(Color(red: 0.545, green: 0.196, blue: 0.98)) .font(.footnote.bold()) .padding(.vertical, 3) @@ -1100,7 +1111,7 @@ func proFeatureBadge() -> some View { } func comingSoonTag() -> some View { - Text("Coming soon") + Text("common.coming_soon") .foregroundStyle(.secondary) .font(.footnote.bold()) .padding(.vertical, 3) diff --git a/boringNotch/components/Settings/SoftwareUpdater.swift b/boringNotch/components/Settings/SoftwareUpdater.swift index f9db680..ddeca5b 100644 --- a/boringNotch/components/Settings/SoftwareUpdater.swift +++ b/boringNotch/components/Settings/SoftwareUpdater.swift @@ -29,7 +29,7 @@ struct CheckForUpdatesView: View { } var body: some View { - Button("Check for Updates
", action: updater.checkForUpdates) + Button("updater.check_for_updates", action: updater.checkForUpdates) .disabled(!checkForUpdatesViewModel.canCheckForUpdates) } } @@ -48,19 +48,19 @@ struct UpdaterSettingsView: View { var body: some View { Section { - Toggle("Automatically check for updates", isOn: $automaticallyChecksForUpdates) + Toggle("updater.automatically_check_updates", isOn: $automaticallyChecksForUpdates) .onChange(of: automaticallyChecksForUpdates) { _, newValue in updater.automaticallyChecksForUpdates = newValue } - Toggle("Automatically download updates", isOn: $automaticallyDownloadsUpdates) + Toggle("updater.automatically_download_updates", isOn: $automaticallyDownloadsUpdates) .disabled(!automaticallyChecksForUpdates) .onChange(of: automaticallyDownloadsUpdates) { _, newValue in updater.automaticallyDownloadsUpdates = newValue } } header: { HStack { - Text("Software updates") + Text("updater.header.check_updates") } } } diff --git a/boringNotch/components/Shelf/Ext+FileProvider.swift b/boringNotch/components/Shelf/Ext+FileProvider.swift index c755079..b07dac2 100644 --- a/boringNotch/components/Shelf/Ext+FileProvider.swift +++ b/boringNotch/components/Shelf/Ext+FileProvider.swift @@ -52,7 +52,7 @@ extension [NSItemProvider] { } guard urls.count == count else { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - NSAlert.popError(NSLocalizedString("One or more files failed to load", comment: "")) + NSAlert.popError(NSLocalizedString("One or more files failed to load", comment: "Error message when one or more files failed to load")) } return nil } diff --git a/boringNotch/components/Shelf/Ext+NSAlert.swift b/boringNotch/components/Shelf/Ext+NSAlert.swift index 4c72ab7..d8fca6e 100644 --- a/boringNotch/components/Shelf/Ext+NSAlert.swift +++ b/boringNotch/components/Shelf/Ext+NSAlert.swift @@ -10,20 +10,20 @@ import Cocoa extension NSAlert { static func popError(_ error: String) { let alert = NSAlert() - alert.messageText = NSLocalizedString("Error", comment: "") + alert.messageText = NSLocalizedString("common.error", comment: "") alert.alertStyle = .critical alert.informativeText = error - alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) + alert.addButton(withTitle: NSLocalizedString("common.ok", comment: "")) alert.runModal() } static func popRestart(_ error: String, completion: @escaping () -> Void) { let alert = NSAlert() - alert.messageText = NSLocalizedString("Need Restart", comment: "") + alert.messageText = NSLocalizedString("common.restart_needed", comment: "") alert.alertStyle = .critical alert.informativeText = error - alert.addButton(withTitle: NSLocalizedString("Exit", comment: "")) - alert.addButton(withTitle: NSLocalizedString("Later", comment: "")) + alert.addButton(withTitle: NSLocalizedString("common.exit", comment: "")) + alert.addButton(withTitle: NSLocalizedString("common.later", comment: "")) let response = alert.runModal() if response == .alertFirstButtonReturn { completion() diff --git a/boringNotch/components/Tips/TipStore.swift b/boringNotch/components/Tips/TipStore.swift index dc6d655..3c7ef2c 100644 --- a/boringNotch/components/Tips/TipStore.swift +++ b/boringNotch/components/Tips/TipStore.swift @@ -10,12 +10,12 @@ import TipKit struct HUDsTip: Tip { var title: Text { - Text("Enhance your experience with HUDs") + Text("tips.hud.title") } var message: Text? { - Text("Unlock advanced features and improve your experience. Upgrade now for more customizations!") + Text("tips.hud.message") } @@ -25,19 +25,19 @@ struct HUDsTip: Tip { var actions: [Action] { Action { - Text("More") + Text("common.more") } } } struct CBTip: Tip { var title: Text { - Text("Boost your productivity with Clipboard Manager") + Text("clipboard_mgr.title") } var message: Text? { - Text("Easily copy, store, and manage your most-used content. Upgrade now for advanced features like multi-item storage and quick access!") + Text("clipboard_mgr.message") } @@ -47,7 +47,7 @@ struct CBTip: Tip { var actions: [Action] { Action { - Text("More") + Text("common.more") } } } diff --git a/boringNotch/components/Webcam/WebcamView.swift b/boringNotch/components/Webcam/WebcamView.swift index 53dca73..125dbdb 100644 --- a/boringNotch/components/Webcam/WebcamView.swift +++ b/boringNotch/components/Webcam/WebcamView.swift @@ -34,7 +34,7 @@ struct CameraPreviewView: View { Image(systemName: "web.camera") .foregroundStyle(.gray) .font(.system(size: geometry.size.width/3.5)) - Text("Mirror") + Text("common.mirror") .font(.caption2) .foregroundColor(.gray) } diff --git a/boringNotch/enums/generic.swift b/boringNotch/enums/generic.swift index 2b40c24..08b907c 100644 --- a/boringNotch/enums/generic.swift +++ b/boringNotch/enums/generic.swift @@ -67,9 +67,9 @@ enum MirrorShapeEnum: String, LocalizedEnum, Defaults.Serializable { } enum WindowHeightMode: String, LocalizedEnum, Defaults.Serializable { - case matchMenuBar = "Match menubar height" + case matchMenuBar = "settings.general.notch_match_menubar_height" case matchRealNotchSize = "Match real notch height" - case custom = "Custom height" + case custom = "settings.general.notch_custom_height" } enum SliderColorEnum: String, CaseIterable, LocalizedEnum, Defaults.Serializable { From 60bb6403d0686975c5f7b64e4e21f2f5dd0975a9 Mon Sep 17 00:00:00 2001 From: Thomas Havy Date: Wed, 18 Dec 2024 14:44:17 +0100 Subject: [PATCH 72/74] Updated strings to have keys instead of english --- boringNotch/ContentView.swift | 6 +- boringNotch/Localizable.xcstrings | 935 +++++++++--------- boringNotch/boringNotchApp.swift | 8 +- .../SystemEventIndicatorModifier.swift | 2 +- .../components/Onboarding/ProOnboarding.swift | 2 +- .../components/Settings/SettingsView.swift | 64 +- boringNotch/components/Shelf/AirDrop.swift | 4 +- .../components/Shelf/AirDropView.swift | 2 +- .../components/Shelf/Ext+FileProvider.swift | 2 +- boringNotch/components/WhatsNewView.swift | 4 +- boringNotch/enums/generic.swift | 10 +- 11 files changed, 494 insertions(+), 545 deletions(-) diff --git a/boringNotch/ContentView.swift b/boringNotch/ContentView.swift index 596e467..d508881 100644 --- a/boringNotch/ContentView.swift +++ b/boringNotch/ContentView.swift @@ -157,10 +157,10 @@ struct ContentView: View { .sensoryFeedback(.alignment, trigger: haptics) .contextMenu { SettingsLink(label: { - Text("Settings") + Text("common.settings") }) .keyboardShortcut(KeyEquivalent(","), modifiers: .command) - Button("Edit") { + Button("common.edit") { let dn = DynamicNotch(content: EditPanelView()) dn.toggle() } @@ -196,7 +196,7 @@ struct ContentView: View { if vm.expandingView.type == .battery && vm.expandingView.show && vm.notchState == .closed { HStack(spacing: 0) { HStack { - Text("Charging") + Text("battery.charging") .font(.subheadline) } diff --git a/boringNotch/Localizable.xcstrings b/boringNotch/Localizable.xcstrings index f5c773b..03a4dad 100644 --- a/boringNotch/Localizable.xcstrings +++ b/boringNotch/Localizable.xcstrings @@ -2,7 +2,7 @@ "sourceLanguage" : "en", "strings" : { " – %lld" : { - + "shouldTranslate" : false }, "(%@)" : { "localizations" : { @@ -12,44 +12,35 @@ "value" : "(%@)" } } - } + }, + "shouldTranslate" : false }, "%.0f seconds" : { - + "shouldTranslate" : false }, "%.1fs" : { - + "shouldTranslate" : false }, "%@" : { - + "shouldTranslate" : false }, "%d%%" : { - + "shouldTranslate" : false }, "%lld" : { - + "shouldTranslate" : false }, "%lld%%" : { - + "shouldTranslate" : false }, "‱ Bug fixes" : { - + "shouldTranslate" : false }, "‱ Improved performance" : { - + "shouldTranslate" : false }, "‱ New feature 1" : { - - }, - "Activation" : { - "localizations" : { - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Activation" - } - } - } + "shouldTranslate" : false }, "activation.header.subtitle" : { "localizations" : { @@ -131,32 +122,32 @@ } } }, - "AirDrop" : { + "airdrop.airdrop_service_unavailable" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "AirDrop" + "value" : "AirDrop service not available" } } } }, - "AirDrop service not available" : { + "airdrop.name" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Le service AirDrop n’est pas disponible" + "value" : "AirDrop" } } } }, - "Appearance" : { + "battery.charging" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Apparence" + "value" : "Charging" } } } @@ -186,23 +177,8 @@ "value" : "boring.notch" } } - } - }, - "Both" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Both" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Les deux" - } - } - } + }, + "shouldTranslate" : false }, "Buy Me a Coffee" : { "extractionState" : "stale", @@ -251,39 +227,22 @@ } } }, - "Charging" : { + "changelog.got_it" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Charging" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Chargement" + "value" : "Got it!" } } } }, - "Circle" : { + "changelog.whats_new" : { "localizations" : { - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cercle" - } - } - } - }, - "Circular" : { - "extractionState" : "manual", - "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Circulaire" + "value" : "What’s New" } } } @@ -324,6 +283,16 @@ } } }, + "common.app_name" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "TheBoringNotch" + } + } + } + }, "common.cancel" : { "localizations" : { "en" : { @@ -392,6 +361,16 @@ } } }, + "common.edit" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit" + } + } + } + }, "common.error" : { "localizations" : { "en" : { @@ -412,280 +391,245 @@ } } }, - "common.later" : { + "common.gradient" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Later" + "value" : "Gradient" } } } }, - "common.mirror" : { + "common.hierarchical" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Mirror" + "value" : "Hierarchical" } } } }, - "common.more" : { + "common.inline" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "More" + "value" : "Inline" } } } }, - "common.muted" : { + "common.later" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Muted" + "value" : "Later" } } } }, - "common.name" : { + "common.load_file_error" : { + "comment" : "Error message when one or more files failed to load", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Name" - } - }, - "fr" : { - "stringUnit" : { - "state" : "needs_review", - "value" : "Nom" + "value" : "One or more files failed to load" } } } }, - "common.ok" : { + "common.mic %@" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "OK" + "value" : "Mic %@" } } } }, - "common.quit" : { + "common.mirror" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Quit" + "value" : "Mirror" } } } }, - "common.quit_app" : { + "common.more" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Quit App" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Quitter l’application" + "value" : "More" } } } }, - "common.restart_needed" : { + "common.muted" : { + "comment" : "Mic muted", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Restart needed" + "value" : "muted" } } } }, - "common.selected" : { + "common.name" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Selected" + "value" : "Name" } }, "fr" : { "stringUnit" : { - "state" : "translated", - "value" : "sĂ©lĂ©ctionnĂ©" + "state" : "needs_review", + "value" : "Nom" } } } }, - "common.settings" : { + "common.ok" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Settings" + "value" : "OK" } } } }, - "common.speed" : { + "common.quit" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Speed" - } - }, - "fr" : { - "stringUnit" : { - "state" : "needs_review", - "value" : "Vitesse" + "value" : "Quit" } } } }, - "common.unmuted" : { + "common.quit_app" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "unmuted" + "value" : "Quit App" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quitter l’application" } } } }, - "common.upgrade_pro" : { + "common.restart_app" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Upgrade to Pro" - } - }, - "fr" : { - "stringUnit" : { - "state" : "needs_review", - "value" : "Obtenir Pro" + "value" : "Restart Boring Notch" } } } }, - "common.welcome" : { + "common.restart_needed" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Welcome" + "value" : "Restart needed" } } } }, - "Custom color" : { + "common.selected" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Custom color" + "value" : "Selected" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Couleur personnalisĂ©e" + "value" : "sĂ©lĂ©ctionnĂ©" } } } }, - "Custom notch size - %.0f" : { + "common.settings" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Taille personnalisĂ©e de l’encoche - %.0f" + "value" : "Settings" } } } }, - "Default" : { + "common.speed" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Par dĂ©faut" + "value" : "Speed" } - } - } - }, - "Description" : { - "localizations" : { + }, "fr" : { "stringUnit" : { - "state" : "translated", - "value" : "Description" + "state" : "needs_review", + "value" : "Vitesse" } } } }, - "Download icon style" : { + "common.unmuted" : { + "comment" : "Mic unmuted", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Download icon style" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Style de l’icĂŽne de tĂ©lĂ©chargement" + "value" : "unmuted" } } } }, - "Download indicator style" : { + "common.upgrade_pro" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Download indicator style" + "value" : "Upgrade to Pro" } }, "fr" : { "stringUnit" : { - "state" : "translated", - "value" : "Style de l’indicateur de tĂ©lĂ©chargement" - } - } - } - }, - "Download indicators" : { - "localizations" : { - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Indicateur de tĂ©lĂ©chargement" + "state" : "needs_review", + "value" : "Obtenir Pro" } } } }, - "Downloads" : { + "common.welcome" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "TĂ©lĂ©chargements" + "value" : "Welcome" } } } @@ -710,16 +654,6 @@ } } }, - "Edit" : { - "localizations" : { - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Éditer" - } - } - } - }, "edit_panel.edit_layout" : { "localizations" : { "en" : { @@ -730,612 +664,550 @@ } } }, - "Enable glowing effect" : { - "comment" : "Toggle in parameters to enable the glowing effet for progress bars", + "ERROR: VIEW NOT DEFINED" : { + + }, + "GitHub" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Activer l’effet de brillance" + "value" : "GitHub" } - } - } - }, - "Enable HUD replacement" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer le remplacement de l’ATH" + "value" : "GitHub" } } - } + }, + "shouldTranslate" : false }, - "Enable Safari Downloads" : { - "comment" : "Toggle to in parameters to enable Safari downloads", + "onboarding.get_started" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Activer les tĂ©lĂ©chargements Safari" + "value" : "Get started" } } } }, - "ERROR: VIEW NOT DEFINED" : { - - }, - "Exclude apps" : { + "setting.extensions.disabled" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Exclude apps" + "value" : "Disabled" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Exclure des applications" + "value" : "DĂ©sactivĂ©" } } } }, - "General" : { + "setting.extensions.extension_description" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "GĂ©nĂ©ral" + "value" : "Description" } } } }, - "GitHub" : { + "settings.about.footer" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "GitHub" + "value" : "Made with đŸ«¶đŸ» by not so boring not.people" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "GitHub" + "value" : "Fait avec đŸ«¶ par des personnes pas si « boring »!" } } } }, - "Got it!" : { + "settings.about.header.version" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Compris!" + "value" : "Version" } - } - } - }, - "Gradient" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Gradiant" + "value" : "Informations de version" } } } }, - "Hierarchical" : { + "settings.about.release_name" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "HiĂ©rarchique" + "value" : "Release Name" } - } - } - }, - "HUD style" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Style de l’ATH" + "value" : "Nom de version" } } } }, - "HUDs" : { + "settings.about.support_us" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "ATHs" + "value" : "Support Us" } - } - } - }, - "Inline" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "En ligne" + "value" : "Supportez-nous" } } } }, - "Key" : { - "extractionState" : "manual", + "settings.about.version" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "ClĂ©" + "value" : "Version" } - } - } - }, - "Match album art" : { - "extractionState" : "manual", - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Faire correspondre Ă  la couverture d’album" + "value" : "Version" } } } }, - "Mic %@" : { + "settings.appearance.add_custom_visualizer" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Microphone %@" + "value" : "Add new visualizer" } - } - } - }, - "No excluded apps" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune application exclue" + "value" : "Ajouter une visualisation" } } } }, - "Onboarding" : { + "settings.appearance.always_show_tabs" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Embarquement" + "value" : "Always show tabs" } - } - } - }, - "onboarding.get_started" : { - - }, - "One or more files failed to load" : { - "comment" : "Error message when one or more files failed to load", - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Un ou plusieurs fichier n’a pas pu ĂȘtre chargĂ©" + "value" : "Toujours afficher les onglets" } } } }, - "Only app icon" : { + "settings.appearance.corner_radius_scaling" : { + "comment" : "Toggle in parameters for corner radius scaling", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Only app icon" + "value" : "Corner radius scaling" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Seulement l’icĂŽne de l’application" + "value" : "Mise Ă  l’échelle des coins" } } } }, - "Only download icon" : { + "settings.appearance.enable_blur_effect_album_art" : { + "comment" : "Toggle in parameters to enable the blur effect behind album art", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Only download icon" + "value" : "Enable blur effect behind album art" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Seulement l’icĂŽne de tĂ©lĂ©chargement" + "value" : "Activer l’effet de flou derriĂšre la couverture d’album" } } } }, - "Percentage" : { + "settings.appearance.enable_boring_mirror" : { + "comment" : "Toggle in parameters to enable Boring mirror", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Percentage" + "value" : "Enable boring mirror" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Pourcentage" - } - } - } - }, - "PRO" : { - - }, - "Progress" : { - "extractionState" : "manual", - "localizations" : { - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "ProgrĂšs" + "value" : "Activer le Boring Mirror" } } } }, - "Progress bar" : { + "settings.appearance.enable_colored_spectrograms" : { + "comment" : "Toggle in parameters for colored spectrograms", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Progress bar" + "value" : "Enable colored spectrograms" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Barre de progression" - } - } - } - }, - "Progressbar style" : { - "localizations" : { - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Style de la barre de progression" + "value" : "Activer les spectrogrammes en couleur" } } } }, - "Rectangular" : { - "extractionState" : "manual", + "settings.appearance.enable_window_shadow" : { + "comment" : "Toggle in parameters for enabling the window shadow", "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Rectangulaire" + "value" : "Enable window shadow" } - } - } - }, - "Restart Boring Notch" : { - "localizations" : { + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "RedĂ©marrer Boring Notch" + "value" : "Activer les ombres de fenĂȘtre" } } } }, - "setting.extensions.disabled" : { + "settings.appearance.header.additional_features" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Disabled" + "value" : "Additional features" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "DĂ©sactivĂ©" + "value" : "FonctionnalitĂ©s supplĂ©mentaires" } } } }, - "Settings" : { + "settings.appearance.header.app_icon" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Settings" + "value" : "App Icon" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "ParamĂštres" + "value" : "IcĂŽne de l’application" } } } }, - "settings.about.footer" : { + "settings.appearance.header.custom_live_activity_anim" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Made with đŸ«¶đŸ» by not so boring not.people" + "value" : "Custom music live activity animation" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fait avec đŸ«¶ par des personnes pas si « boring »!" + "value" : "Animation personnalisĂ©e pour les Live Activities musicales" } } } }, - "settings.about.header.version" : { + "settings.appearance.header.custom_visualizers" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Version" + "value" : "Custom vizualizers (Lottie)" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Informations de version" + "value" : "Visualisateurs personnalisĂ©s (Lottie)" } } } }, - "settings.about.release_name" : { + "settings.appearance.header.general" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Release Name" + "value" : "General" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Nom de version" + "value" : "GĂ©nĂ©ral" } } } }, - "settings.about.support_us" : { + "settings.appearance.header.media" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Support Us" + "value" : "Media" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Supportez-nous" + "value" : "MĂ©dia" } } } }, - "settings.about.version" : { + "settings.appearance.lottie_json_url" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Version" + "value" : "Lottie JSON URL" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Version" + "value" : "URL du JSON Lottie" } } } }, - "settings.appearance.add_custom_visualizer" : { + "settings.appearance.mirror_shape" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Add new visualizer" + "value" : "Mirror shape" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ajouter une visualisation" + "value" : "Forme du mirroir" } } } }, - "settings.appearance.always_show_tabs" : { + "settings.appearance.mirror_shape.circle" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Always show tabs" + "value" : "Circle" } - }, - "fr" : { + } + } + }, + "settings.appearance.mirror_shape.square" : { + "localizations" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Toujours afficher les onglets" + "value" : "Square" } } } }, - "settings.appearance.corner_radius_scaling" : { - "comment" : "Toggle in parameters for corner radius scaling", + "settings.appearance.no_custom_visualizer" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Corner radius scaling" + "value" : "No custom visualizer" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mise Ă  l’échelle des coins" + "value" : "Aucune visualisation personnalisĂ©e disponible" } } } }, - "settings.appearance.enable_blur_effect_album_art" : { - "comment" : "Toggle in parameters to enable the blur effect behind album art", + "settings.appearance.player_tinting" : { + "comment" : "Toggle in parameters to tint the player", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enable blur effect behind album art" + "value" : "Player tinting" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer l’effet de flou derriĂšre la couverture d’album" + "value" : "Teinte du lecteur" } } } }, - "settings.appearance.enable_boring_mirror" : { - "comment" : "Toggle in parameters to enable Boring mirror", + "settings.appearance.settings_icon_in_notch" : { + "comment" : "Toggle in parameters for settings icon in notch", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enable boring mirror" + "value" : "Settings icon in notch" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer le Boring Mirror" + "value" : "IcĂŽne des paramĂštres dans Notch" } } } }, - "settings.appearance.enable_colored_spectrograms" : { - "comment" : "Toggle in parameters for colored spectrograms", + "settings.appearance.show_calendar" : { + "comment" : "Toggle in parameters to show the calendar in the expanded view", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enable colored spectrograms" + "value" : "Show calendar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer les spectrogrammes en couleur" + "value" : "Montrer le calendrier" } } } }, - "settings.appearance.enable_window_shadow" : { - "comment" : "Toggle in parameters for enabling the window shadow", + "settings.appearance.show_idle_face_animation" : { + "comment" : "Toggle in parameters to show a face animation when inactive", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enable window shadow" + "value" : "Show cool face animation while inactivity" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Activer les ombres de fenĂȘtre" + "value" : "Afficher une animation stylĂ©e quand inactif" } } } }, - "settings.appearance.header.additional_features" : { + "settings.appearance.spectrogram_selected_animation" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Additional features" + "value" : "Selected animation" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "FonctionnalitĂ©s supplĂ©mentaires" + "value" : "Animation sĂ©lectionnĂ©e" } } } }, - "settings.appearance.header.app_icon" : { + "settings.appearance.spectrogram_selected_animation_none" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "App Icon" + "value" : "No custom animation available" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "IcĂŽne de l’application" + "value" : "Aucune animation personnalisĂ©e disponible" } } } }, - "settings.appearance.header.custom_live_activity_anim" : { + "settings.appearance.use_music_visualizer_spectrogram" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Custom music live activity animation" + "value" : "Use music visualizer spectrogram" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Animation personnalisĂ©e pour les Live Activities musicales" + "value" : "Utiliser le spectrogramme comme visualisateur musical" } } } }, - "settings.appearance.header.custom_visualizers" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Custom vizualizers (Lottie)" - } - }, - "fr" : { + "settings.appearence.media.slider_color" : { + "localizations" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Visualisateurs personnalisĂ©s (Lottie)" + "value" : "Slider color" } } } }, - "settings.appearance.header.general" : { + "settings.battery.header.general" : { "localizations" : { "en" : { "stringUnit" : { @@ -1351,232 +1223,200 @@ } } }, - "settings.appearance.header.media" : { + "settings.battery.show_battery_indicator" : { + "comment" : "Toggle in parameters to show battery indicator", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Media" + "value" : "Show battery indicator" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "MĂ©dia" + "value" : "Afficher l’indicateur de batterie" } } } }, - "settings.appearance.lottie_json_url" : { + "settings.battery.show_charging_indicator" : { + "comment" : "Toogle in parameters to Show charging indicator", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Lottie JSON URL" + "value" : "Show charging indicator" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "URL du JSON Lottie" + "value" : "Afficher l’indicateur de charge" } } } }, - "settings.appearance.mirror_shape" : { + "settings.downloaders.header.excluded_apps" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Mirror shape" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Forme du mirroir" + "value" : "Excluded apps" } } } }, - "settings.appearance.no_custom_visualizer" : { + "settings.downloads.enable_safari_dl" : { + "comment" : "Toggle to in parameters to enable Safari downloads", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "No custom visualizer" + "value" : "Enable Safari downloads" } }, "fr" : { "stringUnit" : { - "state" : "translated", - "value" : "Aucune visualisation personnalisĂ©e disponible" + "state" : "needs_review", + "value" : "Activer les tĂ©lĂ©chargements Safari" } } } }, - "settings.appearance.player_tinting" : { - "comment" : "Toggle in parameters to tint the player", + "settings.downloads.header.indicators" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Player tinting" + "value" : "Download indicators" } }, "fr" : { "stringUnit" : { - "state" : "translated", - "value" : "Teinte du lecteur" + "state" : "needs_review", + "value" : "Indicateur de tĂ©lĂ©chargement" } } } }, - "settings.appearance.settings_icon_in_notch" : { - "comment" : "Toggle in parameters for settings icon in notch", + "settings.downloads.icon_style" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Settings icon in notch" + "value" : "settings.downloads.icon_style" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "IcĂŽne des paramĂštres dans Notch" + "value" : "Style de l’icĂŽne de tĂ©lĂ©chargement" } } } }, - "settings.appearance.show_calendar" : { - "comment" : "Toggle in parameters to show the calendar in the expanded view", + "settings.downloads.indicator_style" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show calendar" + "value" : "settings.downloads.indicator_style" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Montrer le calendrier" + "value" : "Style de l’indicateur de tĂ©lĂ©chargement" } } } }, - "settings.appearance.show_idle_face_animation" : { - "comment" : "Toggle in parameters to show a face animation when inactive", + "settings.downloads.no_excluded_apps" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show cool face animation while inactivity" + "value" : "No excluded apps" } }, "fr" : { "stringUnit" : { - "state" : "translated", - "value" : "Afficher une animation stylĂ©e quand inactif" + "state" : "needs_review", + "value" : "Aucune application exclue" } } } }, - "settings.appearance.spectrogram_selected_animation" : { + "settings.downloads.show_progress" : { + "comment" : "Toggle in parameters to show the download progress in supported apps", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Selected animation" + "value" : "settings.downloads.show_progress" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Animation sĂ©lectionnĂ©e" + "value" : "Afficher le status du tĂ©lĂ©chargement" } } } }, - "settings.appearance.spectrogram_selected_animation_none" : { + "settings.downloads.style.app_icon_only" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "No custom animation available" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aucune animation personnalisĂ©e disponible" + "value" : "Only app icon" } } } }, - "settings.appearance.use_music_visualizer_spectrogram" : { + "settings.downloads.style.both_app_icon" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Use music visualizer spectrogram" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Utiliser le spectrogramme comme visualisateur musical" + "value" : "Only download icon" } } } }, - "settings.battery.header.general" : { + "settings.downloads.style.icon_only" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "General" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "GĂ©nĂ©ral" + "value" : "Both" } } } }, - "settings.battery.show_battery_indicator" : { - "comment" : "Toggle in parameters to show battery indicator", + "settings.downloads.style.percentage" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show battery indicator" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Afficher l’indicateur de batterie" + "value" : "Percentage" } } } }, - "settings.battery.show_charging_indicator" : { - "comment" : "Toogle in parameters to Show charging indicator", + "settings.downloads.style.progress_bar" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show charging indicator" + "value" : "settings.downloads.style.progress_bar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher l’indicateur de charge" + "value" : "Barre de progression" } } } @@ -1709,6 +1549,16 @@ } } }, + "settings.general.accent_color.custom_color" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Custom color" + } + } + } + }, "settings.general.behavior.enable_haptics" : { "comment" : "Parameter toggle for enabling haptics", "localizations" : { @@ -1775,6 +1625,16 @@ } } }, + "settings.general.custom_notch_size %.0f" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Custom notch size - %.0f" + } + } + } + }, "settings.general.gestures.close" : { "comment" : "Toggle in parameters to enable the closing gesture", "localizations" : { @@ -2131,6 +1991,108 @@ } } }, + "settings.hud.enable_replacement" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable HUD replacement" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Activer le remplacement de l’ATH" + } + } + } + }, + "settings.hud.header.appearance" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appearance" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Apparence" + } + } + } + }, + "settings.hud.header.general" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "General" + } + } + } + }, + "settings.hud.progressbar_style" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Progressbar style" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Style de la barre de progression" + } + } + } + }, + "settings.hud.progressbar_style.enable_glowing" : { + "comment" : "Toggle in parameters to enable the glowing effet for progress bars", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable glowing" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Activer l’effet de brillance" + } + } + } + }, + "settings.hud.progressbar_style.use_accent_color" : { + "comment" : "Toggle in parameters to show the accent color of progress bars", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Use accent colour" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Utiliser la couleur d’accentuation" + } + } + } + }, + "settings.hud.style" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "HUDs style" + } + } + } + }, "settings.media.autohide_in_fullscreen" : { "comment" : "Toggle in the parameters to autohide Boring notch in fullscreen", "localizations" : { @@ -2364,7 +2326,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Appearance" + "value" : "settings.hud.header.appearance" } }, "fr" : { @@ -2520,51 +2482,50 @@ } }, "shelf.drop_files_here" : { - - }, - "Show download progress" : { - "comment" : "Toggle in parameters to show the download progress in supported apps", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show download progress" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Afficher le status du tĂ©lĂ©chargement" + "value" : "Drop files here" } } } }, - "Slider color" : { + "slidercolor.match_accent" : { + "extractionState" : "manual", "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Couleur du curseur" + "value" : "Match accent" } } } }, - "Square" : { + "slidercolor.match_album_art" : { + "extractionState" : "manual", "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "CarrĂ©" + "value" : "Match album art" } } } }, - "TheBoringNotch" : { + "slidercolor.white" : { + "extractionState" : "manual", "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "TheBoringNotch" + "value" : "White" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Blanc" } } } @@ -2629,34 +2590,22 @@ } } }, - "Use accent color" : { - "comment" : "Toggle in parameters to show the accent color of progress bars", - "localizations" : { - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Utiliser la couleur d’accentuation" - } - } - } - }, - "What's New" : { + "windowtitle.activation" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "NouveautĂ©s" + "value" : "Activation" } } } }, - "White" : { - "extractionState" : "manual", + "windowtitle.onboarding" : { "localizations" : { - "fr" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Blanc" + "value" : "Onboarding" } } } diff --git a/boringNotch/boringNotchApp.swift b/boringNotch/boringNotchApp.swift index b787058..58b356a 100644 --- a/boringNotch/boringNotchApp.swift +++ b/boringNotch/boringNotchApp.swift @@ -30,13 +30,13 @@ struct DynamicNotchApp: App { }) .keyboardShortcut(KeyEquivalent(","), modifiers: .command) if false { - Button("Activate License") { + Button("activation.activate_license") { openWindow(id: "activation") } } CheckForUpdatesView(updater: updaterController.updater) Divider() - Button("Restart Boring Notch") { + Button("common.restart_app") { guard let bundleIdentifier = Bundle.main.bundleIdentifier else { return } let workspace = NSWorkspace.shared @@ -61,13 +61,13 @@ struct DynamicNotchApp: App { SettingsView(updaterController: updaterController) } - Window("Onboarding", id: "onboarding") { + Window("windowtitle.onboarding", id: "onboarding") { ProOnboard() } .windowStyle(.hiddenTitleBar) .windowResizability(.contentSize) - Window("Activation", id: "activation") { + Window("windowtitle.activation", id: "activation") { ActivationWindow() } .windowStyle(.hiddenTitleBar) diff --git a/boringNotch/components/Live activities/SystemEventIndicatorModifier.swift b/boringNotch/components/Live activities/SystemEventIndicatorModifier.swift index fc203d6..a7dbceb 100644 --- a/boringNotch/components/Live activities/SystemEventIndicatorModifier.swift +++ b/boringNotch/components/Live activities/SystemEventIndicatorModifier.swift @@ -61,7 +61,7 @@ struct SystemEventIndicatorModifier: View { if (eventType != .mic) { DraggableProgressBar(value: $value) } else { - Text("Mic \(value > 0 ? "unmuted" : "muted")") + Text("common.mic \(value > 0 ? NSLocalizedString("common.unmuted", comment: "Mic unmuted") : NSLocalizedString("common.muted", comment: "Mic muted"))") .foregroundStyle(.gray) .lineLimit(1) .allowsTightening(true) diff --git a/boringNotch/components/Onboarding/ProOnboarding.swift b/boringNotch/components/Onboarding/ProOnboarding.swift index 78ee762..fc34827 100644 --- a/boringNotch/components/Onboarding/ProOnboarding.swift +++ b/boringNotch/components/Onboarding/ProOnboarding.swift @@ -33,7 +33,7 @@ struct ProOnboard: View { .aspectRatio(contentMode: .fit) .frame(width: 100, height: 100) .padding(.bottom, 8) - Text("TheBoringNotch") + Text("common.app_name") .font(.system(.largeTitle, design: .serif)) Text("common.welcome") .font(.title) diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index 5705c00..c727cbb 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -147,7 +147,7 @@ struct GeneralSettings: View { .buttonStyle(PlainButtonStyle()) } Spacer() - ColorPicker("Custom color", selection: $accentColor) + ColorPicker("settings.general.accent_color.custom_color", selection: $accentColor) .labelsHidden() } } header: { @@ -209,7 +209,7 @@ struct GeneralSettings: View { } if notchHeightMode == .custom { Slider(value: $nonNotchHeight, in: 15...45, step: 1) { - Text("Custom notch size - \(nonNotchHeight, specifier: "%.0f")") + Text("settings.general.custom_notch_size \(nonNotchHeight, specifier: "%.0f")") } .onChange(of: nonNotchHeight) { NotificationCenter.default.post(name: Notification.Name.notchHeightChanged, object: nil) @@ -236,7 +236,7 @@ struct GeneralSettings: View { } if nonNotchHeightMode == .custom { Slider(value: $nonNotchHeight, in: 10...40, step: 1) { - Text("Custom notch size - \(nonNotchHeight, specifier: "%.0f")") + Text("settings.general.custom_notch_size \(nonNotchHeight, specifier: "%.0f")") } .onChange(of: nonNotchHeight) { NotificationCenter.default.post(name: Notification.Name.notchHeightChanged, object: nil) @@ -362,33 +362,33 @@ struct Downloads: View { warningBadge("We don't support downloads yet", "It will be supported later on.") Section { Defaults.Toggle( - NSLocalizedString("Show download progress", comment: "Toggle in parameters to show the download progress in supported apps"), + NSLocalizedString("settings.downloads.show_progress", comment: "Toggle in parameters to show the download progress in supported apps"), key: .enableDownloadListener ) .disabled(true) Defaults.Toggle( - NSLocalizedString("Enable Safari Downloads", comment: "Toggle to in parameters to enable Safari downloads"), + NSLocalizedString("settings.downloads.enable_safari_dl", comment: "Toggle to in parameters to enable Safari downloads"), key: .enableSafariDownloads ) .disabled(!Defaults[.enableDownloadListener]) - Picker("Download indicator style", selection: $selectedDownloadIndicatorStyle) { - Text("Progress bar") + Picker("settings.downloads.indicator_style", selection: $selectedDownloadIndicatorStyle) { + Text("settings.downloads.style.progress_bar") .tag(DownloadIndicatorStyle.progress) - Text("Percentage") + Text("settings.downloads.style.percentage") .tag(DownloadIndicatorStyle.percentage) } - Picker("Download icon style", selection: $selectedDownloadIconStyle) { - Text("Only app icon") + Picker("settings.downloads.icon_style", selection: $selectedDownloadIconStyle) { + Text("settings.downloads.style.app_icon_only") .tag(DownloadIconStyle.onlyAppIcon) - Text("Only download icon") + Text("settings.downloads.style.icon_only") .tag(DownloadIconStyle.onlyIcon) - Text("Both") + Text("settings.downloads.style.both_app_icon") .tag(DownloadIconStyle.iconAndAppIcon) } } header: { HStack { - Text("Download indicators") + Text("settings.downloads.header.indicators") comingSoonTag() } } @@ -401,7 +401,7 @@ struct Downloads: View { .frame(minHeight: 96) .overlay { if true { - Text("No excluded apps") + Text("settings.downloads.no_excluded_apps") .foregroundStyle(Color(.secondaryLabelColor)) } } @@ -429,14 +429,14 @@ struct Downloads: View { } } header: { HStack (spacing: 4){ - Text("Exclude apps") + Text("settings.downloaders.header.excluded_apps") comingSoonTag() } } } .tint(Defaults[.accentColor]) - .navigationTitle("Downloads") + .navigationTitle("settings.tab.downloads") } } @@ -448,15 +448,15 @@ struct HUD: View { var body: some View { Form { Section { - Toggle("Enable HUD replacement", isOn: $coordinator.hudReplacement) + Toggle("settings.hud.enable_replacement", isOn: $coordinator.hudReplacement) } header: { - Text("General") + Text("settings.hud.header.general") } Section { - Picker("HUD style", selection: $inlineHUD) { - Text("Default") + Picker("settings.hud.style", selection: $inlineHUD) { + Text("common.default") .tag(false) - Text("Inline") + Text("common.inline") .tag(true) } .onChange(of: Defaults[.inlineHUD]) { @@ -467,27 +467,27 @@ struct HUD: View { } } } - Picker("Progressbar style", selection: $enableGradient) { - Text("Hierarchical") + Picker("settings.hud.progressbar_style", selection: $enableGradient) { + Text("common.hierarchical") .tag(false) - Text("Gradient") + Text("common.gradient") .tag(true) } Defaults.Toggle( - NSLocalizedString("Enable glowing effect", comment: "Toggle in parameters to enable the glowing effet for progress bars"), + NSLocalizedString("settings.hud.progressbar_style.enable_glowing", comment: "Toggle in parameters to enable the glowing effet for progress bars"), key: .systemEventIndicatorShadow ) Defaults.Toggle( - NSLocalizedString("Use accent color", comment: "Toggle in parameters to show the accent color of progress bars"), + NSLocalizedString("settings.hud.progressbar_style.use_accent_color", comment: "Toggle in parameters to show the accent color of progress bars"), key: .systemEventIndicatorUseAccent) } header: { HStack { - Text("Appearance") + Text("settings.hud.header.appearance") } } } .tint(Defaults[.accentColor]) - .navigationTitle("HUDs") + .navigationTitle("settings.tab.huds") } } @@ -663,7 +663,7 @@ struct Extensions: View { .frame(width: 24, height: 24) Text(item.name) ListItemPopover { - Text("Description") + Text("setting.extensions.extension_description") } Spacer(minLength: 0) HStack(spacing: 6) { @@ -818,7 +818,7 @@ struct Appearance: View { NSLocalizedString("settings.appearance.enable_blur_effect_album_art", comment: "Toggle in parameters to enable the blur effect behind album art"), key: .lightingEffect ) - Picker("Slider color", selection: $sliderColor) { + Picker("settings.appearence.media.slider_color", selection: $sliderColor) { ForEach(SliderColorEnum.allCases, id: \.self) { option in Text(option.localizedName) } @@ -1002,9 +1002,9 @@ struct Appearance: View { ) .disabled(!checkVideoInput()) Picker("settings.appearance.mirror_shape", selection: $mirrorShape) { - Text("Circle") + Text("settings.appearance.mirror_shape.circle") .tag(MirrorShapeEnum.circle) - Text("Square") + Text("settings.appearance.mirror_shape.square") .tag(MirrorShapeEnum.rectangle) } Defaults.Toggle( diff --git a/boringNotch/components/Shelf/AirDrop.swift b/boringNotch/components/Shelf/AirDrop.swift index a46f7c5..d1d7119 100644 --- a/boringNotch/components/Shelf/AirDrop.swift +++ b/boringNotch/components/Shelf/AirDrop.swift @@ -26,12 +26,12 @@ class AirDrop: NSObject, NSSharingServiceDelegate { private func sendEx(_ files: [URL]) throws { guard let service = NSSharingService(named: .sendViaAirDrop) else { throw NSError(domain: "AirDrop", code: 1, userInfo: [ - NSLocalizedDescriptionKey: NSLocalizedString("AirDrop service not available", comment: ""), + NSLocalizedDescriptionKey: NSLocalizedString("airdrop.airdrop_service_unavailable", comment: ""), ]) } guard service.canPerform(withItems: files) else { throw NSError(domain: "AirDrop", code: 2, userInfo: [ - NSLocalizedDescriptionKey: NSLocalizedString("AirDrop service not available", comment: ""), + NSLocalizedDescriptionKey: NSLocalizedString("airdrop.airdrop_service_unavailable", comment: ""), ]) } service.delegate = self diff --git a/boringNotch/components/Shelf/AirDropView.swift b/boringNotch/components/Shelf/AirDropView.swift index dd000ff..b0131bb 100644 --- a/boringNotch/components/Shelf/AirDropView.swift +++ b/boringNotch/components/Shelf/AirDropView.swift @@ -37,7 +37,7 @@ struct AirDropView: View { var dropLabel: some View { VStack(spacing: 8) { Image(systemName: "airplayaudio") - Text("AirDrop") + Text("airdrop.name") } .foregroundStyle(.gray) .font(.system(.headline, design: .rounded)) diff --git a/boringNotch/components/Shelf/Ext+FileProvider.swift b/boringNotch/components/Shelf/Ext+FileProvider.swift index b07dac2..ec3fa72 100644 --- a/boringNotch/components/Shelf/Ext+FileProvider.swift +++ b/boringNotch/components/Shelf/Ext+FileProvider.swift @@ -52,7 +52,7 @@ extension [NSItemProvider] { } guard urls.count == count else { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - NSAlert.popError(NSLocalizedString("One or more files failed to load", comment: "Error message when one or more files failed to load")) + NSAlert.popError(NSLocalizedString("common.load_file_error", comment: "Error message when one or more files failed to load")) } return nil } diff --git a/boringNotch/components/WhatsNewView.swift b/boringNotch/components/WhatsNewView.swift index 154558a..7002e71 100644 --- a/boringNotch/components/WhatsNewView.swift +++ b/boringNotch/components/WhatsNewView.swift @@ -12,7 +12,7 @@ struct WhatsNewView: View { var body: some View { VStack(spacing: 20) { - Text("What's New") + Text("changelog.whats_new") .font(.largeTitle) VStack(alignment: .leading, spacing: 10) { @@ -21,7 +21,7 @@ struct WhatsNewView: View { Text("‱ Bug fixes") } - Button("Got it!") { + Button("changelog.got_it") { isPresented = false } } diff --git a/boringNotch/enums/generic.swift b/boringNotch/enums/generic.swift index 08b907c..95b9ef6 100644 --- a/boringNotch/enums/generic.swift +++ b/boringNotch/enums/generic.swift @@ -62,8 +62,8 @@ enum DownloadIconStyle: String, LocalizedEnum, Defaults.Serializable { } enum MirrorShapeEnum: String, LocalizedEnum, Defaults.Serializable { - case rectangle = "Rectangular" - case circle = "Circular" + case rectangle = "settings.mirror.shape.rectangle" + case circle = "settings.mirror.shape.circle" } enum WindowHeightMode: String, LocalizedEnum, Defaults.Serializable { @@ -73,7 +73,7 @@ enum WindowHeightMode: String, LocalizedEnum, Defaults.Serializable { } enum SliderColorEnum: String, CaseIterable, LocalizedEnum, Defaults.Serializable { - case white = "White" - case albumArt = "Match album art" - case accent = "Accent color" + case white = "slidercolor.white" + case albumArt = "slidercolor.match_album_art" + case accent = "slidercolor.match_accent" } From 3d55c90c373e2b2164b31181762f4bc19ba8ca87 Mon Sep 17 00:00:00 2001 From: Thomas Havy Date: Wed, 18 Dec 2024 15:07:40 +0100 Subject: [PATCH 73/74] Improved translataion, fixed badges --- boringNotch/Localizable.xcstrings | 415 +++++++++++++++++- .../components/Settings/SettingsView.swift | 12 +- 2 files changed, 400 insertions(+), 27 deletions(-) diff --git a/boringNotch/Localizable.xcstrings b/boringNotch/Localizable.xcstrings index 03a4dad..828c8ad 100644 --- a/boringNotch/Localizable.xcstrings +++ b/boringNotch/Localizable.xcstrings @@ -52,7 +52,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Rendez l’encoche vraiment vĂŽtre" } } @@ -68,7 +68,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Activer votre license" } } @@ -84,7 +84,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Activer" } } @@ -100,7 +100,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Adresse email" } } @@ -116,7 +116,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "ClĂ© de license" } } @@ -129,6 +129,12 @@ "state" : "translated", "value" : "AirDrop service not available" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Service AirDrop indisponible" + } } } }, @@ -139,6 +145,12 @@ "state" : "translated", "value" : "AirDrop" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "AirDrop" + } } } }, @@ -149,6 +161,12 @@ "state" : "translated", "value" : "Charging" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "En charge" + } } } }, @@ -204,6 +222,12 @@ "state" : "translated", "value" : "All-day" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toute la journĂ©e" + } } } }, @@ -214,6 +238,12 @@ "state" : "translated", "value" : "Enjoy your free time!" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Profitez du temps libre!" + } } } }, @@ -224,6 +254,12 @@ "state" : "translated", "value" : "No events today" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucun Ă©vĂšnement" + } } } }, @@ -234,6 +270,12 @@ "state" : "translated", "value" : "Got it!" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compris!" + } } } }, @@ -244,6 +286,12 @@ "state" : "translated", "value" : "What’s New" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quoi de neuf?" + } } } }, @@ -254,6 +302,12 @@ "state" : "translated", "value" : "Easily copy, store, and manage your most-used content. Upgrade now for advanced features like multi-item storage and quick access!" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiez, sauvegardez et managez vos fichiers les plus souvent utilisĂ©s. Passez Ă  Pro dĂšs maintenant pour dĂ©verrouiller des fonctionnalitĂ©s telles que le stockage de plusieurs Ă©lĂ©ments et l’accĂšs rapide!" + } } } }, @@ -264,6 +318,12 @@ "state" : "translated", "value" : "Boost your productivity with Clipboard Manager" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boostez votre productivity avec le Clipboard Manager" + } } } }, @@ -277,7 +337,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Ajouter" } } @@ -291,6 +351,24 @@ "value" : "TheBoringNotch" } } + }, + "shouldTranslate" : false + }, + "common.beta" : { + "comment" : "Beta badge", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beta" + } + }, + "fr" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "Beta" + } + } } }, "common.cancel" : { @@ -303,7 +381,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Annuler" } } @@ -316,6 +394,12 @@ "state" : "translated", "value" : "Charging" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "En charge" + } } } }, @@ -326,10 +410,17 @@ "state" : "translated", "value" : "Close" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermer" + } } } }, "common.coming_soon" : { + "comment" : "Coming soon badge", "localizations" : { "en" : { "stringUnit" : { @@ -355,7 +446,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "DĂ©faut" } } @@ -368,6 +459,12 @@ "state" : "translated", "value" : "Edit" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Éditer" + } } } }, @@ -378,6 +475,12 @@ "state" : "translated", "value" : "Error" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur" + } } } }, @@ -388,6 +491,12 @@ "state" : "translated", "value" : "Exit" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermer" + } } } }, @@ -398,6 +507,12 @@ "state" : "translated", "value" : "Gradient" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gradiant" + } } } }, @@ -408,6 +523,12 @@ "state" : "translated", "value" : "Hierarchical" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "HiĂ©rarchiquement" + } } } }, @@ -418,6 +539,12 @@ "state" : "translated", "value" : "Inline" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "En ligne" + } } } }, @@ -428,6 +555,12 @@ "state" : "translated", "value" : "Later" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plus tard" + } } } }, @@ -439,6 +572,12 @@ "state" : "translated", "value" : "One or more files failed to load" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un ou plusieurs fichiers n’ont pas pu ĂȘtre chargĂ©s" + } } } }, @@ -449,6 +588,12 @@ "state" : "translated", "value" : "Mic %@" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mic %@" + } } } }, @@ -459,6 +604,12 @@ "state" : "translated", "value" : "Mirror" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mirroir" + } } } }, @@ -469,6 +620,12 @@ "state" : "translated", "value" : "More" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plus" + } } } }, @@ -480,6 +637,12 @@ "state" : "translated", "value" : "muted" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "fermĂ©" + } } } }, @@ -493,7 +656,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Nom" } } @@ -506,6 +669,12 @@ "state" : "translated", "value" : "OK" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ok" + } } } }, @@ -516,6 +685,12 @@ "state" : "translated", "value" : "Quit" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermer" + } } } }, @@ -542,6 +717,12 @@ "state" : "translated", "value" : "Restart Boring Notch" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "RedĂ©marrer Boring Noth" + } } } }, @@ -552,6 +733,12 @@ "state" : "translated", "value" : "Restart needed" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "RedĂ©marrage requis" + } } } }, @@ -578,6 +765,12 @@ "state" : "translated", "value" : "Settings" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ParamĂštres" + } } } }, @@ -591,7 +784,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Vitesse" } } @@ -605,6 +798,12 @@ "state" : "translated", "value" : "unmuted" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "fermĂ©" + } } } }, @@ -618,7 +817,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Obtenir Pro" } } @@ -631,6 +830,12 @@ "state" : "translated", "value" : "Welcome" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bienvenue" + } } } }, @@ -641,6 +846,12 @@ "state" : "translated", "value" : "Download" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "TĂ©lĂ©chargement" + } } } }, @@ -651,6 +862,12 @@ "state" : "translated", "value" : "In progress" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "En cours" + } } } }, @@ -661,11 +878,17 @@ "state" : "translated", "value" : "Edit layout" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Éditer la disposition" + } } } }, "ERROR: VIEW NOT DEFINED" : { - + "shouldTranslate" : false }, "GitHub" : { "localizations" : { @@ -691,6 +914,12 @@ "state" : "translated", "value" : "Get started" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commencer" + } } } }, @@ -717,6 +946,12 @@ "state" : "translated", "value" : "Description" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Description" + } } } }, @@ -1052,6 +1287,12 @@ "state" : "translated", "value" : "Circle" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cercle" + } } } }, @@ -1062,6 +1303,12 @@ "state" : "translated", "value" : "Square" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "CarrĂ©" + } } } }, @@ -1204,6 +1451,12 @@ "state" : "translated", "value" : "Slider color" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur du curseur" + } } } }, @@ -1264,6 +1517,12 @@ "state" : "translated", "value" : "Excluded apps" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Applications exclues" + } } } }, @@ -1278,7 +1537,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Activer les tĂ©lĂ©chargements Safari" } } @@ -1294,7 +1553,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Indicateur de tĂ©lĂ©chargement" } } @@ -1342,7 +1601,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Aucune application exclue" } } @@ -1372,6 +1631,12 @@ "state" : "translated", "value" : "Only app icon" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seulement l’icĂŽne de l’app" + } } } }, @@ -1382,6 +1647,12 @@ "state" : "translated", "value" : "Only download icon" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seulement l’icĂŽne du tĂ©lĂ©chargement" + } } } }, @@ -1392,6 +1663,12 @@ "state" : "translated", "value" : "Both" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Les deux" + } } } }, @@ -1402,6 +1679,12 @@ "state" : "translated", "value" : "Percentage" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pourcentage" + } } } }, @@ -1556,6 +1839,12 @@ "state" : "translated", "value" : "Custom color" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur personnalisĂ©e" + } } } }, @@ -1632,6 +1921,12 @@ "state" : "translated", "value" : "Custom notch size - %.0f" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille personnalisĂ© de l’encoche - %.0f" + } } } }, @@ -2001,7 +2296,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Activer le remplacement de l’ATH" } } @@ -2017,7 +2312,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Apparence" } } @@ -2030,6 +2325,12 @@ "state" : "translated", "value" : "General" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "GĂ©nĂ©ral" + } } } }, @@ -2043,7 +2344,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Style de la barre de progression" } } @@ -2060,7 +2361,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Activer l’effet de brillance" } } @@ -2077,7 +2378,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Utiliser la couleur d’accentuation" } } @@ -2090,6 +2391,12 @@ "state" : "translated", "value" : "HUDs style" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Style des ATHs" + } } } }, @@ -2488,6 +2795,12 @@ "state" : "translated", "value" : "Drop files here" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glissez des fichiers ici" + } } } }, @@ -2499,6 +2812,12 @@ "state" : "translated", "value" : "Match accent" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur d'accentuation" + } } } }, @@ -2510,6 +2829,12 @@ "state" : "translated", "value" : "Match album art" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur de la couverture d’album" + } } } }, @@ -2524,7 +2849,7 @@ }, "fr" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "Blanc" } } @@ -2537,6 +2862,12 @@ "state" : "translated", "value" : "Unlock advanced features and improve your experience. Upgrade now for more customizations!" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "DĂ©bloquez des fonctionnalitĂ©s avancĂ©es et amĂ©liorez votre expĂ©rience. Mettez Ă  niveau maintenant pour plus de personalisations!" + } } } }, @@ -2547,6 +2878,12 @@ "state" : "translated", "value" : "Enhance your experience with HUDs" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "AmĂ©liorez votre expĂ©rience avec les ATHs" + } } } }, @@ -2557,6 +2894,12 @@ "state" : "translated", "value" : "Automatically check for updates" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "VĂ©rifier automatiquement les mises Ă  jour" + } } } }, @@ -2567,6 +2910,12 @@ "state" : "translated", "value" : "Automatically download updates" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "TĂ©lĂ©chargez automatiquement les mises Ă  jour" + } } } }, @@ -2577,6 +2926,12 @@ "state" : "translated", "value" : "Check for updates" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "VĂ©rifier les mises Ă  jour" + } } } }, @@ -2587,6 +2942,12 @@ "state" : "translated", "value" : "Software updates" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mises Ă  jour" + } } } }, @@ -2597,6 +2958,12 @@ "state" : "translated", "value" : "Activation" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activation" + } } } }, @@ -2607,6 +2974,12 @@ "state" : "translated", "value" : "Onboarding" } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prise en main" + } } } } diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index c727cbb..c8b03e8 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -165,7 +165,7 @@ struct GeneralSettings: View { Defaults.Toggle(key: .showOnAllDisplays) { HStack { Text("settings.general.show_on_all_displays") - customBadge(text: "common.beta") + customBadge(text: NSLocalizedString("common.beta", comment: "Beta badge")) } } .onChange(of: showOnAllDisplays) { @@ -187,7 +187,7 @@ struct GeneralSettings: View { Picker(selection: $notchHeightMode, label: HStack { Text("settings.general.notch_display_height") - customBadge(text: "common.beta") + customBadge(text: NSLocalizedString("common.beta", comment: "Beta badge")) }) { Text("settings.general.notch_match_display_height") .tag(WindowHeightMode.matchRealNotchSize) @@ -292,7 +292,7 @@ struct GeneralSettings: View { } header: { HStack { Text("settings.general.header.gestures") - customBadge(text: "common.beta") + customBadge(text: NSLocalizedString("common.beta", comment: "Beta badge")) } } footer: { Text("settings.general.gestures.descriptions.open_close") @@ -527,7 +527,7 @@ struct Media: View { } header: { HStack { Text("settings.media.header.fullscreen") - customBadge(text: "common.beta") + customBadge(text: NSLocalizedString("common.beta", comment: "Beta badge")) } } } @@ -859,7 +859,7 @@ struct Appearance: View { } header: { HStack { Text("settings.appearance.header.custom_live_activity_anim") - customBadge(text: "common.coming_soon") + customBadge(text: NSLocalizedString("common.coming_soon", comment: "Coming soon badge")) } } @@ -1062,7 +1062,7 @@ struct Appearance: View { } header: { HStack { Text("settings.appearance.header.app_icon") - customBadge(text: "common.coming_soon") + customBadge(text: NSLocalizedString("common.coming_soon", comment: "Coming soon badge")) } } } From c1b1a0d6880cb35661fd7fdc7121fff595a5adea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harsh=20Vardhan=20Goswami=20=F0=9F=90=B3?= Date: Sun, 22 Dec 2024 11:51:35 +0530 Subject: [PATCH 74/74] Updated guidelines --- .github/workflows/code-review.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index e06f29c..0160119 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -29,7 +29,7 @@ jobs: private-key: ${{ secrets.CORI_APP_PRIVATE_KEY }} - name: ✹ CoriAI Code Review, PhD - uses: theboringhumane/cori-ai@1.2.3 # Uses the action from the current repository + uses: theboringhumane/cori-ai@1.2.10 # Uses the action from the current repository with: openai_api_key: ${{ secrets.OPENAI_API_KEY }} openai_base_url: ${{ secrets.OPENAI_BASE_URL }} @@ -73,4 +73,6 @@ jobs: Are there any potential memory leaks? Does the PR include appropriate changelog entries? - Mandatory to Use emojis in your comments add code examples as well with your comments (markdown format) \ No newline at end of file + IMPORTANT GUIDELINES + - Mandatory to Use emojis in your comments add code examples as well with your comments (markdown format) + - Do not repeat the commit message or description or what user is doing right, just point out issues and things which can be improved. Do not bombard with too much comments. \ No newline at end of file