Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
ae6ac33
Modify dark mode
DCM4711 Jan 24, 2026
1fd5027
Show star rating in Currently-Playing view
DCM4711 Jan 24, 2026
19a9478
Show star ratings in song-list views
DCM4711 Jan 24, 2026
a152c7c
Show/hide lyrics by clicking on album art in currently-playing view
DCM4711 Jan 24, 2026
717d183
When showing lyrics jump to the current lrc lyrics position without s…
DCM4711 Jan 24, 2026
f88490a
Keep current pause/play state when clicking next (>>) or prev (<<) song
DCM4711 Jan 24, 2026
29f733b
Design change for rating stars in currently-playing view
DCM4711 Jan 24, 2026
f5f8ac6
Fix small bug where music is briefly heard when clicking >> or << whi…
DCM4711 Jan 24, 2026
0e6a29b
Change color to black for Play and Shuffle buttons
DCM4711 Jan 24, 2026
296a607
REAL FIX for small bug where music is briefly heard when clicking >> …
DCM4711 Jan 24, 2026
ae2c8c9
Fix to light unselected rating stars in Light theme
DCM4711 Jan 24, 2026
8440e61
Have solid colors for non lrc lyrics
DCM4711 Jan 24, 2026
0fa5ab2
Change app name to Musify
DCM4711 Jan 24, 2026
2c0b3ff
Show color gradient on currently-playing viewer on iPad
DCM4711 Jan 25, 2026
4a872ef
Back background for the two buttonson currently-playing view in Dark …
DCM4711 Jan 25, 2026
c691504
Another try to stop paused music to play briefly wehn tapping next or…
DCM4711 Jan 25, 2026
6906534
Place heart rating button next to star rating buttons (In currently-p…
DCM4711 Jan 25, 2026
41cc1af
Consolidate the two settings buttons in currently-playing view; Remov…
DCM4711 Jan 25, 2026
4accdd6
Prevent Favorite and (second) Settings button from reappearing by cli…
DCM4711 Jan 25, 2026
f36a1e6
Add confirmation dialog before clearing the queue
DCM4711 Jan 25, 2026
79ca53a
Show total song time in currently-playing view
DCM4711 Jan 25, 2026
a8d5b52
New LRC lyrics viewer
DCM4711 Jan 25, 2026
6ada8c3
Fix progress bar not showing when playing non-cached songs
DCM4711 Jan 27, 2026
059e24f
Appicon redesign
DCM4711 Jan 27, 2026
b87495e
New loading screen
DCM4711 Jan 28, 2026
f9ee3ff
Move 'Resync Library' from Account settings to Library settings
DCM4711 Jan 28, 2026
0ae8007
Hide 'Manual Playback' item from Settings > Player, Stream & Scrobble…
DCM4711 Jan 28, 2026
c42a936
Removed 'Song Playback Resume' setting and always remember position o…
DCM4711 Jan 28, 2026
b89e79c
Fix fade in glitch of non LRC lyrics
DCM4711 Jan 29, 2026
b489623
Small design change in lyrics view
DCM4711 Jan 29, 2026
a735484
Fix of LRC lyrics where the last text block is not highlighted
DCM4711 Jan 29, 2026
1ad7e41
Fade in/out of LRC lyrics view
DCM4711 Jan 29, 2026
0d4cd97
Use the same font weight for LRC inactive and highlighted lyrics
DCM4711 Jan 29, 2026
46c630c
Scale up album art when playing the song
DCM4711 Jan 29, 2026
daa1924
Fix short album art scale-up/down when opening currently-playing view…
DCM4711 Jan 29, 2026
b79ce80
If caching of songs is disabled (stram only mode), still cache the cu…
DCM4711 Jan 29, 2026
d720e7d
Show track number if more than one song of the same album is in curre…
DCM4711 Jan 29, 2026
bf23c05
Small fix of placeholder image flash being shown
DCM4711 Jan 29, 2026
b054c40
Fix small audio play after restart of the app and opening currently-p…
DCM4711 Jan 29, 2026
8f020fb
Make Amperfy reference in Settings
DCM4711 Jan 29, 2026
7c53066
Name change at 'Add Account' view
DCM4711 Jan 29, 2026
d76280a
Indicate when Replay Gain is used
DCM4711 Jan 30, 2026
70f7c60
Display db adjustment instead of RG when replay gain is used
DCM4711 Jan 30, 2026
3b9f3d5
Specify Preamp value in settings when using ReplayGain
DCM4711 Jan 30, 2026
c341330
Show song info button
DCM4711 Jan 30, 2026
c4fd99b
Change of the song info view
DCM4711 Jan 31, 2026
da199ac
More reliable deleteing of scrub cached songs
DCM4711 Jan 31, 2026
ea387b6
Better info at Song Info view
DCM4711 Jan 31, 2026
9d56f37
More reliable scrub song caching and indication
DCM4711 Jan 31, 2026
2ae9486
Fix playcount display in Sing Info view
DCM4711 Jan 31, 2026
20441c0
Add MacoOS icns file
DCM4711 Jan 31, 2026
e295ecd
Show a X button in the Song Info view when running on MacOS
DCM4711 Jan 31, 2026
148873f
Remove temporarily cached files from 'Downloaded' list when being rem…
DCM4711 Feb 1, 2026
d3091e3
Remove scrobble setting; Always send scrobble (incr. playcount) updat…
DCM4711 Feb 1, 2026
79ae2c9
Hide 'Lyrics smooth scrolling' (enabled by default)
DCM4711 Feb 1, 2026
dd4d866
Allow copy of Song ID in Song Info view
DCM4711 Feb 1, 2026
031bbd0
Allow download/deletion of song in Song Info view
DCM4711 Feb 1, 2026
51ddeba
Assets folder added
DCM4711 Feb 1, 2026
2e7ba3f
Use modified Dark theme also in Grid views
DCM4711 Feb 1, 2026
f7a37ac
README modified
DCM4711 Feb 1, 2026
19a22e1
Fix typo
DCM4711 Feb 1, 2026
047c6c0
README fix
DCM4711 Feb 1, 2026
7dd57f2
Merge master: Subsonic scrobble API time parameter fix
DCM4711 Feb 1, 2026
3bad057
Make sure current song position is always 00:00 when starting a new song
DCM4711 Feb 1, 2026
1a99bd7
Album cover zoom-in at Play adjusted to 10% (works on MacOS and iPadOS)
DCM4711 Feb 1, 2026
a0384f0
Show color gradient background in currently playing view on iPadOS
DCM4711 Feb 1, 2026
7a7103c
Make gradient background in currently playing view darker in Dark mode
DCM4711 Feb 1, 2026
6cbabd7
Disable the 'Auto cache played Songs' by default
DCM4711 Feb 1, 2026
7755c9e
Multiple performance optimizations
DCM4711 Feb 2, 2026
9967f59
Remove temporary caching again
DCM4711 Feb 3, 2026
fc6a920
Move song info button (i) to the bottom and remove (redundant) volume…
DCM4711 Feb 3, 2026
0fe631f
Change currently playing bottom buttons and artist name to Theme-Color
DCM4711 Feb 3, 2026
4bdabbc
Darker color gradient background in dark mode
DCM4711 Feb 3, 2026
3868dd6
Make dark theme slightly darker
DCM4711 Feb 3, 2026
07c16e6
Light mode adjustments
DCM4711 Feb 3, 2026
ea28219
Move some settings items to more logical locations
DCM4711 Feb 3, 2026
338c701
Removed User Queue feature; Rename 'Insert Context Queue' to 'Play Ne…
DCM4711 Feb 3, 2026
77eec35
Don't replace queue when clicking on a single song. Insert song into …
DCM4711 Feb 3, 2026
d3c9a49
Reworked context menu of song-list views; Add 'Delete from Playlist' …
DCM4711 Feb 3, 2026
7e14fe1
Allow clearing of entire queue
DCM4711 Feb 3, 2026
85d77cb
Added CarPlay and Siri entitlement; Exception handling for streamed s…
DCM4711 Feb 4, 2026
4b784c5
Fix bug when directly clicking on a song to insert it into queue and …
DCM4711 Feb 4, 2026
2a58bb0
Another try to prevent streamed songs from not playing
DCM4711 Feb 4, 2026
471e909
Fix rare missing back '<' button eg. after clicking 'Show ALbum'
DCM4711 Feb 4, 2026
3687f36
Don't allow to set star ratings when in offline mode
DCM4711 Feb 4, 2026
3883dfb
Don't overlap artist name and star rating in song-list views
DCM4711 Feb 4, 2026
fe78fc1
Merge master into custom-features
DCM4711 Feb 4, 2026
ff8d83c
Redesign of song-info view
DCM4711 Feb 6, 2026
04d0401
Changes at README
DCM4711 Feb 6, 2026
a3d8b0e
Changes at README
DCM4711 Feb 6, 2026
6949611
Changes at README
DCM4711 Feb 6, 2026
ccc7fc1
Changes at README
DCM4711 Feb 6, 2026
a9e0cdd
Changes at README
DCM4711 Feb 6, 2026
e8eabb1
MacOS: Remember window position and size; Use same window size for cu…
DCM4711 Feb 7, 2026
3ca75fe
MacOS-fix: Show (i) button on currently-playing view in theme color
DCM4711 Feb 7, 2026
a222deb
Redesign of MacOS player; Always show queue; Splitview when lyrics ar…
DCM4711 Feb 7, 2026
d442aa6
Change (i) button size
DCM4711 Feb 8, 2026
8221bd5
Show additional info in song info view (Path/Filename/Comment/Genre)
DCM4711 Feb 15, 2026
ee0a2ca
Small change
DCM4711 Feb 15, 2026
63eb4dc
Show star rating in CarPlay
DCM4711 Mar 6, 2026
bf76578
Fix lyrics view on MacOS
DCM4711 Mar 6, 2026
30f0351
Rename from Amperfy to Musify
DCM4711 Mar 12, 2026
bce1452
License text changes
DCM4711 Mar 12, 2026
dbb0412
Carplay design changes
DCM4711 Mar 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Amperfy/Amperfy.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.carplay-audio</key>
<true/>
<key>com.apple.developer.siri</key>
<true/>
<key>com.apple.developer.carplay-audio</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
Expand Down
6 changes: 3 additions & 3 deletions Amperfy/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ let defaultWindowActivityType = "amperfy.main"

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
static let name = "Amperfy"
static let name = "Musify"
static var version: String {
(Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String) ?? ""
}
Expand Down Expand Up @@ -236,8 +236,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func restartByUser() {
Task {
await localNotificationManager.notifyDebugAndWait(
title: "Amperfy Restart",
body: "Tap to reopen Amperfy"
title: "Musify Restart",
body: "Tap to reopen Musify"
)
stopForInit()
// close Amperfy
Expand Down
30 changes: 29 additions & 1 deletion Amperfy/AppDelegateMainMenuExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ extension AppDelegate {
guard builder.system == .main else { return }

#if targetEnvironment(macCatalyst) // ok
// Replace About menu with custom Musify version info
builder.replace(menu: .about, with: UIMenu(options: .displayInline, children: [
UIAction(title: "About Musify") { _ in
self.showCustomAboutPanel()
},
]))

// Add File menu
let fileMenus = [
UIMenu(options: .displayInline, children: [
Expand Down Expand Up @@ -104,13 +111,34 @@ extension AppDelegate {

builder.insertChild(UIMenu(options: .displayInline, children: [
UIAction(title: "Report an issue on GitHub") { _ in
if let url = URL(string: "https://github.com/BLeeEZ/amperfy/issues") {
if let url = URL(string: "https://github.com/DCM4711/amperfy/issues") {
UIApplication.shared.open(url)
}
},
]), atStartOfMenu: .help)
}

private func showCustomAboutPanel() {
let message = """
Version \(musifyVersion)
Based on Amperfy Version \(AppDelegate.version) (Build \(AppDelegate.buildNumber))

© 2026 DonkeyCat GmbH
A fork of Amperfy by Maximilian Bauer
Licensed under GPLv3
"""
let alert = UIAlertController(title: "Musify", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))

if let windowScene = UIApplication.shared.connectedScenes
.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene,
let rootVC = windowScene.windows.first?.rootViewController {
var topVC = rootVC
while let presented = topVC.presentedViewController { topVC = presented }
topVC.present(alert, animated: true)
}
}

@objc
private func keyCommandPause() {
player.pause()
Expand Down
6 changes: 3 additions & 3 deletions Amperfy/AppIntentVocabulary.plist
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
<string>INPlayMediaIntent</string>
<key>IntentExamples</key>
<array>
<string>Play the example band in Amperfy</string>
<string>Play playlist example in Amperfy</string>
<string>Play title by artist in Amperfy</string>
<string>Play the example band in Musify</string>
<string>Play playlist example in Musify</string>
<string>Play title by artist in Musify</string>
</array>
</dict>
</array>
Expand Down
87 changes: 75 additions & 12 deletions Amperfy/CarPlay/CarPlayNowPlayingExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ extension CarPlaySceneDelegate {
playerQueueSection.updateSections(createPlayerQueueSections())
}

func installArtworkStarOverlay() {
AmperKit.shared.nowPlayingInfoCenterHandler?.artworkTransform = { artwork, playable in
guard let song = playable.asSong else { return artwork }
return CarPlaySceneDelegate.overlayStarRating(on: artwork, rating: song.rating)
}
}

func removeArtworkStarOverlay() {
AmperKit.shared.nowPlayingInfoCenterHandler?.artworkTransform = nil
}

func configureNowPlayingTemplate() {
var buttons: [CPNowPlayingButton] = []
buttons.append(
Expand All @@ -50,25 +61,29 @@ extension CarPlaySceneDelegate {
)
if let currentlyPlaying = appDelegate.player.currentlyPlaying,
!currentlyPlaying.isRadio {
let isFavorite = appDelegate.player.currentlyPlaying?.isFavorite ?? false
let currentRating = currentlyPlaying.asSong?.rating ?? 0
let starImage = UIImage(systemName: currentRating > 0 ? "star.fill" : "star")!
buttons.append(
CPNowPlayingImageButton(
image: isFavorite ? .heartFill : .heartEmpty,
image: starImage,
handler: { [weak self] button in
guard let self = self else { return }
guard let playableInfo = appDelegate.player.currentlyPlaying,
let account = playableInfo.account else { return }
guard let song = appDelegate.player.currentlyPlaying?.asSong,
let account = song.account else { return }
let newRating = (song.rating + 1) % 6
song.rating = newRating
Task { @MainActor in
do {
try await playableInfo
.remoteToggleFavorite(
syncer: self.appDelegate
.getMeta(account.info).librarySyncer
)
try await self.appDelegate.getMeta(account.info)
.librarySyncer.setRating(song: song, rating: newRating)
} catch {
self.appDelegate.eventLogger.report(topic: "Toggle Favorite", error: error)
self.appDelegate.eventLogger.report(topic: "Song Rating", error: error)
}
self.configureNowPlayingTemplate()
// Refresh artwork to update star overlay
if let playable = self.appDelegate.player.currentlyPlaying {
AmperKit.shared.nowPlayingInfoCenterHandler?.refreshNowPlayingInfo(playable: playable)
}
}
}
)
Expand All @@ -93,13 +108,61 @@ extension CarPlaySceneDelegate {
CPListSection(items: availablePlaybackRates),
])
interfaceController?.pushTemplate(playbackRateTemplate, animated: true, completion: nil)

})
)
CPNowPlayingTemplate.shared.updateNowPlayingButtons(buttons)
CPNowPlayingTemplate.shared.isUpNextButtonEnabled = true
}

/// Composites a star rating bar at the bottom of the artwork image.
static func overlayStarRating(on artwork: UIImage, rating: Int) -> UIImage {
let artworkSize = artwork.size
guard artworkSize.width > 0, artworkSize.height > 0 else { return artwork }

let starCount = 5
let starPointSize: CGFloat = artworkSize.width * 0.07
let starSpacing: CGFloat = starPointSize * 0.4
let bottomPadding: CGFloat = artworkSize.height * 0.03

let config = UIImage.SymbolConfiguration(pointSize: starPointSize, weight: .medium)
let filledStar = UIImage(systemName: "star.fill")!.withConfiguration(config)
let emptyStar = UIImage(systemName: "star")!.withConfiguration(config)
let starSize = filledStar.size

let totalStarsWidth = CGFloat(starCount) * starSize.width
+ CGFloat(starCount - 1) * starSpacing
let starsX = (artworkSize.width - totalStarsWidth) / 2
let starsY = artworkSize.height - starSize.height - bottomPadding

let renderer = UIGraphicsImageRenderer(size: artworkSize)
return renderer.image { ctx in
artwork.draw(in: CGRect(origin: .zero, size: artworkSize))

let bgRect = CGRect(
x: 0,
y: starsY - bottomPadding,
width: artworkSize.width,
height: starSize.height + bottomPadding * 2
)
UIColor.black.withAlphaComponent(0.90).setFill()
ctx.fill(bgRect)

let goldenYellow = UIColor(red: 1.0, green: 0.84, blue: 0.0, alpha: 1.0)
let paleWhite = UIColor(white: 1.0, alpha: 0.3)

for i in 0..<starCount {
let x = starsX + CGFloat(i) * (starSize.width + starSpacing)
if i < rating {
let tintedStar = filledStar.withTintColor(goldenYellow, renderingMode: .alwaysOriginal)
tintedStar.draw(at: CGPoint(x: x, y: starsY))
} else {
let tintedStar = emptyStar.withTintColor(paleWhite, renderingMode: .alwaysOriginal)
tintedStar.draw(at: CGPoint(x: x, y: starsY))
}
}
}
}

func displayNowPlaying(immediately: Bool = false, completion: @escaping (() -> ())) {
configureNowPlayingTemplate()
if immediately {
Expand Down Expand Up @@ -196,7 +259,7 @@ extension CarPlaySceneDelegate: CPNowPlayingTemplateObserver {
)
listItem.handler = { [weak self] item, completion in
guard let self = self else { completion(); return }
appDelegate.player.play(playerIndex: playerIndex)
appDelegate.player.play(playerIndex: playerIndex, autoStartPlayback: nil)
Task { @MainActor in
guard let _ = try? await interfaceController?.popTemplate(animated: true) else { return }
completion()
Expand Down
2 changes: 2 additions & 0 deletions Amperfy/CarPlay/CarPlaySceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate {
CPNowPlayingTemplate.shared.add(self)
self.interfaceController = interfaceController
self.interfaceController?.delegate = self
self.installArtworkStarOverlay()
self.configureNowPlayingTemplate()

accountNotificationHandler?
Expand Down Expand Up @@ -171,6 +172,7 @@ class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate {
os_log("CarPlay: no account available -> do nothing", log: self.log, type: .info)
return
}
self.removeArtworkStarOverlay()
self.interfaceController = nil
appDelegate.notificationHandler.remove(self, name: .fetchControllerSortChanged, object: nil)
appDelegate.notificationHandler.remove(self, name: .offlineModeChanged, object: nil)
Expand Down
8 changes: 5 additions & 3 deletions Amperfy/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleDisplayName</key>
<string>Musify</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<string>Musify</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
Expand All @@ -39,9 +41,9 @@
<array>
<dict>
<key>INAlternativeAppName</key>
<string>Amperfy Music</string>
<string>Musify Music</string>
<key>INAlternativeAppNamePronunciationHint</key>
<string>amperfy music</string>
<string>musify music</string>
</dict>
</array>
<key>INIntentsSupported</key>
Expand Down
2 changes: 1 addition & 1 deletion Amperfy/Intents/IntentManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ public class IntentManager {
documentation.append(
XCallbackActionDocu(
name: "SetOfflineMode",
description: "Sets the Amperfy offline mode to active/inactive",
description: "Sets the Musify offline mode to active/inactive",
exampleURLs: [
"amperfy://x-callback-url/setOfflineMode?offlineMode=1",
],
Expand Down
21 changes: 21 additions & 0 deletions Amperfy/MiniPlayerSceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,27 @@ class MiniPlayerSceneDelegate: UIResponder, UIWindowSceneDelegate {

window?.rootViewController = PopupPlayerVC()
window?.makeKeyAndVisible()

applyMainWindowFrame(windowScene: windowScene)
}

private func applyMainWindowFrame(windowScene: UIWindowScene) {
#if targetEnvironment(macCatalyst)
let defaults = UserDefaults.standard
let width = defaults.double(forKey: SceneDelegate.mainWindowFrameWidthKey)
let height = defaults.double(forKey: SceneDelegate.mainWindowFrameHeightKey)
guard width > 0, height > 0 else { return }
let x = defaults.double(forKey: SceneDelegate.mainWindowFrameXKey)
let y = defaults.double(forKey: SceneDelegate.mainWindowFrameYKey)
let frame = CGRect(x: x, y: y, width: width, height: height)
windowScene.requestGeometryUpdate(.Mac(systemFrame: frame))
os_log(
"MiniPlayer: applied main window frame: %s",
log: self.log,
type: .info,
frame.debugDescription
)
#endif
}

/** Called when the user activates your application by selecting a shortcut on the Home Screen,
Expand Down
39 changes: 39 additions & 0 deletions Amperfy/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
static let mainWindowSize = CGSizeMake(1168, 688) // 2560 x 1600
#endif

static let mainWindowFrameXKey = "mainWindowFrameX"
static let mainWindowFrameYKey = "mainWindowFrameY"
static let mainWindowFrameWidthKey = "mainWindowFrameWidth"
static let mainWindowFrameHeightKey = "mainWindowFrameHeight"

public lazy var log = {
AmperKit.shared.log
}()
Expand Down Expand Up @@ -123,6 +128,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {

window?.makeKeyAndVisible()

restoreMainWindowFrame(windowScene: windowScene)

appDelegate.setAppAppearanceMode(style: appDelegate.storage.settings.user.appearanceMode)
AmperfyAppShortcuts.updateAppShortcutParameters()
}
Expand Down Expand Up @@ -172,6 +179,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
os_log("sceneWillResignActive", log: self.log, type: .info)
saveMainWindowFrame(scene: scene)
guard appDelegate.isNormalInteraction else {
return
}
Expand All @@ -192,6 +200,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {

// Save changes in the application's managed object context when the application transitions to the background.
os_log("sceneDidEnterBackground", log: self.log, type: .info)
saveMainWindowFrame(scene: scene)
AmperKit.shared.threadPerformanceMonitor.isInForeground = false
guard appDelegate.isNormalInteraction else {
return
Expand Down Expand Up @@ -259,4 +268,34 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, didUpdate userActivity: NSUserActivity) {
os_log("didUpdate userActivity: %s", log: self.log, type: .info, userActivity.activityType)
}

// MARK: - Window Frame Persistence

private func saveMainWindowFrame(scene: UIScene) {
#if targetEnvironment(macCatalyst)
guard let windowScene = scene as? UIWindowScene else { return }
let frame = windowScene.effectiveGeometry.systemFrame
guard frame.width > 0, frame.height > 0 else { return }
let defaults = UserDefaults.standard
defaults.set(Double(frame.origin.x), forKey: Self.mainWindowFrameXKey)
defaults.set(Double(frame.origin.y), forKey: Self.mainWindowFrameYKey)
defaults.set(Double(frame.width), forKey: Self.mainWindowFrameWidthKey)
defaults.set(Double(frame.height), forKey: Self.mainWindowFrameHeightKey)
os_log("Saved main window frame: %s", log: self.log, type: .info, frame.debugDescription)
#endif
}

private func restoreMainWindowFrame(windowScene: UIWindowScene) {
#if targetEnvironment(macCatalyst)
let defaults = UserDefaults.standard
let width = defaults.double(forKey: Self.mainWindowFrameWidthKey)
let height = defaults.double(forKey: Self.mainWindowFrameHeightKey)
guard width > 0, height > 0 else { return }
let x = defaults.double(forKey: Self.mainWindowFrameXKey)
let y = defaults.double(forKey: Self.mainWindowFrameYKey)
let frame = CGRect(x: x, y: y, width: width, height: height)
windowScene.requestGeometryUpdate(.Mac(systemFrame: frame))
os_log("Restored main window frame: %s", log: self.log, type: .info, frame.debugDescription)
#endif
}
}
3 changes: 2 additions & 1 deletion Amperfy/Screens/Basics/AnimatedGradientLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ class PopupAnimatedGradientLayer {
if inStyle == .dark {
return [
coloredCornerColor.getWithLightness(of: lightnessDarkMode).cgColor,
UIColor.black.cgColor,
// 90% black (10% white) for custom dark mode
UIColor(white: 0.1, alpha: 1.0).cgColor,
]
} else {
return [
Expand Down
Loading