diff --git a/apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/1024-dark.png b/apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/1024-dark.png new file mode 100644 index 000000000000..3f18c86c2b7f Binary files /dev/null and b/apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/1024-dark.png differ diff --git a/apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json b/apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json index ded24327aca1..3da2462b6b74 100644 --- a/apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1 +1 @@ -{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"idiom":"watch","filename":"172.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"86x86","expected-size":"172","role":"quickLook"},{"idiom":"watch","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"40x40","expected-size":"80","role":"appLauncher"},{"idiom":"watch","filename":"88.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"40mm","scale":"2x","size":"44x44","expected-size":"88","role":"appLauncher"},{"idiom":"watch","filename":"102.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"51x51","expected-size":"102","role":"appLauncher"},{"idiom":"watch","filename":"108.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"54x54","expected-size":"108","role":"appLauncher"},{"idiom":"watch","filename":"92.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"41mm","scale":"2x","size":"46x46","expected-size":"92","role":"appLauncher"},{"idiom":"watch","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"50x50","expected-size":"100","role":"appLauncher"},{"idiom":"watch","filename":"196.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"98x98","expected-size":"196","role":"quickLook"},{"idiom":"watch","filename":"216.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"108x108","expected-size":"216","role":"quickLook"},{"idiom":"watch","filename":"234.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"117x117","expected-size":"234","role":"quickLook"},{"idiom":"watch","filename":"258.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"129x129","expected-size":"258","role":"quickLook"},{"idiom":"watch","filename":"48.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"24x24","expected-size":"48","role":"notificationCenter"},{"idiom":"watch","filename":"55.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"27.5x27.5","expected-size":"55","role":"notificationCenter"},{"idiom":"watch","filename":"66.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"33x33","expected-size":"66","role":"notificationCenter"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"3x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"2x"},{"size":"1024x1024","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch-marketing","scale":"1x"}]} +{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"idiom":"watch","filename":"172.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"86x86","expected-size":"172","role":"quickLook"},{"idiom":"watch","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"40x40","expected-size":"80","role":"appLauncher"},{"idiom":"watch","filename":"88.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"40mm","scale":"2x","size":"44x44","expected-size":"88","role":"appLauncher"},{"idiom":"watch","filename":"102.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"51x51","expected-size":"102","role":"appLauncher"},{"idiom":"watch","filename":"108.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"54x54","expected-size":"108","role":"appLauncher"},{"idiom":"watch","filename":"92.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"41mm","scale":"2x","size":"46x46","expected-size":"92","role":"appLauncher"},{"idiom":"watch","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"50x50","expected-size":"100","role":"appLauncher"},{"idiom":"watch","filename":"196.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"98x98","expected-size":"196","role":"quickLook"},{"idiom":"watch","filename":"216.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"108x108","expected-size":"216","role":"quickLook"},{"idiom":"watch","filename":"234.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"117x117","expected-size":"234","role":"quickLook"},{"idiom":"watch","filename":"258.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"129x129","expected-size":"258","role":"quickLook"},{"idiom":"watch","filename":"48.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"24x24","expected-size":"48","role":"notificationCenter"},{"idiom":"watch","filename":"55.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"27.5x27.5","expected-size":"55","role":"notificationCenter"},{"idiom":"watch","filename":"66.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"33x33","expected-size":"66","role":"notificationCenter"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"3x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"2x"},{"size":"1024x1024","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch-marketing","scale":"1x"},{"appearances":[{"appearance":"luminosity","value":"dark"}],"filename":"1024-dark.png","idiom":"universal","platform":"ios","size":"1024x1024"},{"appearances":[{"appearance":"luminosity","value":"tinted"}],"idiom":"universal","platform":"ios","size":"1024x1024"}],"info":{"author":"xcode","version":1}} diff --git a/apps/ios/Sources/Assets.xcassets/AppIconDebug.appiconset/1024-dark.png b/apps/ios/Sources/Assets.xcassets/AppIconDebug.appiconset/1024-dark.png new file mode 100644 index 000000000000..be22477f581d Binary files /dev/null and b/apps/ios/Sources/Assets.xcassets/AppIconDebug.appiconset/1024-dark.png differ diff --git a/apps/ios/Sources/Assets.xcassets/AppIconDebug.appiconset/Contents.json b/apps/ios/Sources/Assets.xcassets/AppIconDebug.appiconset/Contents.json index ded24327aca1..3da2462b6b74 100644 --- a/apps/ios/Sources/Assets.xcassets/AppIconDebug.appiconset/Contents.json +++ b/apps/ios/Sources/Assets.xcassets/AppIconDebug.appiconset/Contents.json @@ -1 +1 @@ -{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"idiom":"watch","filename":"172.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"86x86","expected-size":"172","role":"quickLook"},{"idiom":"watch","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"40x40","expected-size":"80","role":"appLauncher"},{"idiom":"watch","filename":"88.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"40mm","scale":"2x","size":"44x44","expected-size":"88","role":"appLauncher"},{"idiom":"watch","filename":"102.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"51x51","expected-size":"102","role":"appLauncher"},{"idiom":"watch","filename":"108.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"54x54","expected-size":"108","role":"appLauncher"},{"idiom":"watch","filename":"92.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"41mm","scale":"2x","size":"46x46","expected-size":"92","role":"appLauncher"},{"idiom":"watch","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"50x50","expected-size":"100","role":"appLauncher"},{"idiom":"watch","filename":"196.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"98x98","expected-size":"196","role":"quickLook"},{"idiom":"watch","filename":"216.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"108x108","expected-size":"216","role":"quickLook"},{"idiom":"watch","filename":"234.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"117x117","expected-size":"234","role":"quickLook"},{"idiom":"watch","filename":"258.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"129x129","expected-size":"258","role":"quickLook"},{"idiom":"watch","filename":"48.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"24x24","expected-size":"48","role":"notificationCenter"},{"idiom":"watch","filename":"55.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"27.5x27.5","expected-size":"55","role":"notificationCenter"},{"idiom":"watch","filename":"66.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"33x33","expected-size":"66","role":"notificationCenter"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"3x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"2x"},{"size":"1024x1024","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch-marketing","scale":"1x"}]} +{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"idiom":"watch","filename":"172.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"86x86","expected-size":"172","role":"quickLook"},{"idiom":"watch","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"40x40","expected-size":"80","role":"appLauncher"},{"idiom":"watch","filename":"88.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"40mm","scale":"2x","size":"44x44","expected-size":"88","role":"appLauncher"},{"idiom":"watch","filename":"102.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"51x51","expected-size":"102","role":"appLauncher"},{"idiom":"watch","filename":"108.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"54x54","expected-size":"108","role":"appLauncher"},{"idiom":"watch","filename":"92.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"41mm","scale":"2x","size":"46x46","expected-size":"92","role":"appLauncher"},{"idiom":"watch","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"50x50","expected-size":"100","role":"appLauncher"},{"idiom":"watch","filename":"196.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"98x98","expected-size":"196","role":"quickLook"},{"idiom":"watch","filename":"216.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"108x108","expected-size":"216","role":"quickLook"},{"idiom":"watch","filename":"234.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"117x117","expected-size":"234","role":"quickLook"},{"idiom":"watch","filename":"258.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"49mm","scale":"2x","size":"129x129","expected-size":"258","role":"quickLook"},{"idiom":"watch","filename":"48.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"24x24","expected-size":"48","role":"notificationCenter"},{"idiom":"watch","filename":"55.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"27.5x27.5","expected-size":"55","role":"notificationCenter"},{"idiom":"watch","filename":"66.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"45mm","scale":"2x","size":"33x33","expected-size":"66","role":"notificationCenter"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"3x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"2x"},{"size":"1024x1024","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch-marketing","scale":"1x"},{"appearances":[{"appearance":"luminosity","value":"dark"}],"filename":"1024-dark.png","idiom":"universal","platform":"ios","size":"1024x1024"},{"appearances":[{"appearance":"luminosity","value":"tinted"}],"idiom":"universal","platform":"ios","size":"1024x1024"}],"info":{"author":"xcode","version":1}} diff --git a/apps/ios/scripts/app-icon-debug-dark.swift b/apps/ios/scripts/app-icon-debug-dark.swift new file mode 100644 index 000000000000..5a8212a41b59 --- /dev/null +++ b/apps/ios/scripts/app-icon-debug-dark.swift @@ -0,0 +1,182 @@ +import CoreGraphics +import Foundation +import ImageIO +import UniformTypeIdentifiers + +func fail(_ message: String) -> Never { + FileHandle.standardError.write(Data("app-icon-debug-dark: \(message)\n".utf8)) + exit(1) +} + +guard CommandLine.arguments.count == 3 else { + fail("usage: app-icon-debug-dark.swift ") +} + +let sourceURL = URL(fileURLWithPath: CommandLine.arguments[1]) +let outputURL = URL(fileURLWithPath: CommandLine.arguments[2]) + +guard + let imageSource = CGImageSourceCreateWithURL(sourceURL as CFURL, nil), + let sourceImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) +else { + fail("cannot read \(sourceURL.path)") +} + +let width = sourceImage.width +let height = sourceImage.height +guard width == 1024, height == 1024 else { + fail("source must be 1024x1024, got \(width)x\(height)") +} + +let colorSpace = CGColorSpace(name: CGColorSpace.sRGB)! +let bitmapInfo = + CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue +var pixels = [UInt8](repeating: 0, count: width * height * 4) +guard + let context = CGContext( + data: &pixels, + width: width, + height: height, + bitsPerComponent: 8, + bytesPerRow: width * 4, + space: colorSpace, + bitmapInfo: bitmapInfo) +else { + fail("cannot create an RGBA rendering context") +} +context.draw(sourceImage, in: CGRect(x: 0, y: 0, width: width, height: height)) + +func isInsetBackground(pixel: Int) -> Bool { + let red = Int(pixels[pixel]) + let green = Int(pixels[pixel + 1]) + let blue = Int(pixels[pixel + 2]) + return pixels[pixel + 3] == 255 + && min(red, green, blue) >= 224 + && max(red, green, blue) - min(red, green, blue) <= 4 +} + +// The Debug master is raster-only. Flood-filling from the inset panel keeps the +// disconnected white bug glyph and the full-bleed construction frame intact. +var visited = [Bool](repeating: false, count: width * height) +var cleared = [Bool](repeating: false, count: width * height) +var queue = [128 * width + 128] +visited[queue[0]] = true +var cursor = 0 +var clearedCount = 0 + +while cursor < queue.count { + let index = queue[cursor] + cursor += 1 + let pixel = index * 4 + guard isInsetBackground(pixel: pixel) else { continue } + + cleared[index] = true + clearedCount += 1 + pixels[pixel] = 0 + pixels[pixel + 1] = 0 + pixels[pixel + 2] = 0 + pixels[pixel + 3] = 0 + + let x = index % width + let y = index / width + if x > 0 { + let neighbor = index - 1 + if !visited[neighbor] { + visited[neighbor] = true + queue.append(neighbor) + } + } + if x + 1 < width { + let neighbor = index + 1 + if !visited[neighbor] { + visited[neighbor] = true + queue.append(neighbor) + } + } + if y > 0 { + let neighbor = index - width + if !visited[neighbor] { + visited[neighbor] = true + queue.append(neighbor) + } + } + if y + 1 < height { + let neighbor = index + width + if !visited[neighbor] { + visited[neighbor] = true + queue.append(neighbor) + } + } +} + +guard clearedCount > 300_000 else { + fail("expected a connected light inset panel, cleared only \(clearedCount) pixels") +} + +// The source artwork was antialiased against white. Remove that matte from the +// two-pixel perimeter so the transparent Dark panel cannot produce a white halo. +var unmattedCount = 0 +for index in 0..<(width * height) where !cleared[index] { + let x = index % width + let y = index / width + var touchesBackground = false + + for deltaY in -2...2 where !touchesBackground { + for deltaX in -2...2 { + let neighborX = x + deltaX + let neighborY = y + deltaY + if neighborX >= 0, + neighborX < width, + neighborY >= 0, + neighborY < height, + cleared[neighborY * width + neighborX] + { + touchesBackground = true + break + } + } + } + + guard touchesBackground else { continue } + let pixel = index * 4 + let matte = min(pixels[pixel], pixels[pixel + 1], pixels[pixel + 2]) + guard matte >= 96 else { continue } + + pixels[pixel] -= matte + pixels[pixel + 1] -= matte + pixels[pixel + 2] -= matte + pixels[pixel + 3] = 255 - matte + unmattedCount += 1 +} + +guard unmattedCount > 1_000 else { + fail("expected white-matted inset edges, unmatted only \(unmattedCount) pixels") +} + +guard + let provider = CGDataProvider(data: Data(pixels) as CFData), + let outputImage = CGImage( + width: width, + height: height, + bitsPerComponent: 8, + bitsPerPixel: 32, + bytesPerRow: width * 4, + space: colorSpace, + bitmapInfo: CGBitmapInfo(rawValue: bitmapInfo), + provider: provider, + decode: nil, + shouldInterpolate: false, + intent: .defaultIntent), + let destination = CGImageDestinationCreateWithURL( + outputURL as CFURL, + UTType.png.identifier as CFString, + 1, + nil) +else { + fail("cannot create \(outputURL.path)") +} + +CGImageDestinationAddImage(destination, outputImage, nil) +guard CGImageDestinationFinalize(destination) else { + fail("cannot write \(outputURL.path)") +} diff --git a/apps/ios/scripts/app-icon-variants.sh b/apps/ios/scripts/app-icon-variants.sh new file mode 100755 index 000000000000..aa090b6f0282 --- /dev/null +++ b/apps/ios/scripts/app-icon-variants.sh @@ -0,0 +1,187 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "$0")/../../.." && pwd)" +release_icon_set="$repo_root/apps/ios/Sources/Assets.xcassets/AppIcon.appiconset" +debug_icon_set="$repo_root/apps/ios/Sources/Assets.xcassets/AppIconDebug.appiconset" +source_svg="$repo_root/ui/public/favicon.svg" +debug_renderer="$repo_root/apps/ios/scripts/app-icon-debug-dark.swift" + +require_command() { + local command_name="$1" + local install_hint="$2" + if ! command -v "$command_name" >/dev/null 2>&1; then + echo "app-icon-variants: missing $command_name; $install_hint" >&2 + exit 1 + fi +} + +preflight() { + require_command node "install the repository's required Node.js version" + require_command xcrun "install Xcode command-line tools" + if [[ ! -x /usr/bin/sips ]]; then + echo "app-icon-variants: /usr/bin/sips is required; run this generator on macOS" >&2 + exit 1 + fi + if ! xcrun --find swift >/dev/null 2>&1; then + echo "app-icon-variants: Swift is required; select a complete Xcode toolchain" >&2 + exit 1 + fi +} + +render_release_dark_icon() { + local output="$1" + /usr/bin/sips -z 1024 1024 -s format png "$source_svg" --out "$output" >/dev/null +} + +render_debug_dark_icon() { + local output="$1" + xcrun swift "$debug_renderer" "$debug_icon_set/1024.png" "$output" +} + +check_manifests() { + node - \ + "$release_icon_set/Contents.json" "$release_icon_set" \ + "$debug_icon_set/Contents.json" "$debug_icon_set" <<'NODE' +const fs = require("node:fs"); +const path = require("node:path"); + +const inputs = process.argv.slice(2); +const luminosity = (image) => + image.appearances?.find((entry) => entry.appearance === "luminosity")?.value; + +for (let index = 0; index < inputs.length; index += 2) { + const manifestPath = inputs[index]; + const iconSetPath = inputs[index + 1]; + const catalogName = path.basename(iconSetPath, ".appiconset"); + const { images } = JSON.parse(fs.readFileSync(manifestPath, "utf8")); + const dark = images.filter((image) => luminosity(image) === "dark"); + const tinted = images.filter((image) => luminosity(image) === "tinted"); + const marketing = images.filter( + (image) => + image.idiom === "ios-marketing" && + image.size === "1024x1024" && + image.scale === "1x", + ); + + if ( + marketing.length !== 1 || + marketing[0].filename !== "1024.png" || + dark.length !== 1 || + dark[0].filename !== "1024-dark.png" || + dark[0].idiom !== "universal" || + dark[0].platform !== "ios" || + dark[0].size !== "1024x1024" || + tinted.length !== 1 || + Object.hasOwn(tinted[0], "filename") || + tinted[0].idiom !== "universal" || + tinted[0].platform !== "ios" || + tinted[0].size !== "1024x1024" + ) { + throw new Error( + `${catalogName} must declare its existing Default image, one custom Dark image, and one automatic Tinted slot`, + ); + } + + for (const filename of new Set(images.flatMap((image) => image.filename ?? []))) { + if (!fs.existsSync(path.join(iconSetPath, filename))) { + throw new Error(`${catalogName} references missing file: ${filename}`); + } + } +} +NODE +} + +check_pngs() { + node - \ + "$release_icon_set/1024.png" opaque any \ + "$release_icon_set/1024-dark.png" alpha srgb \ + "$debug_icon_set/1024.png" opaque srgb \ + "$debug_icon_set/1024-dark.png" alpha srgb <<'NODE' +const fs = require("node:fs"); + +const inputs = process.argv.slice(2); +const pngSignature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]); + +for (let index = 0; index < inputs.length; index += 3) { + const imagePath = inputs[index]; + const alphaExpectation = inputs[index + 1]; + const profileExpectation = inputs[index + 2]; + const data = fs.readFileSync(imagePath); + if (!data.subarray(0, 8).equals(pngSignature)) { + throw new Error(`${imagePath} is not a PNG`); + } + + let offset = 8; + let header; + let hasSRGBChunk = false; + let iccProfileName; + while (offset + 12 <= data.length) { + const length = data.readUInt32BE(offset); + const type = data.toString("ascii", offset + 4, offset + 8); + const body = data.subarray(offset + 8, offset + 8 + length); + if (type === "IHDR") { + header = { + width: body.readUInt32BE(0), + height: body.readUInt32BE(4), + bitDepth: body[8], + colorType: body[9], + }; + } else if (type === "sRGB") { + hasSRGBChunk = true; + } else if (type === "iCCP") { + iccProfileName = body.subarray(0, body.indexOf(0)).toString("latin1"); + } + offset += 12 + length; + if (type === "IEND") break; + } + + if ( + !header || + header.width !== 1024 || + header.height !== 1024 || + header.bitDepth !== 8 + ) { + throw new Error(`${imagePath} must be an 8-bit 1024x1024 PNG`); + } + + const hasAlphaChannel = header.colorType === 4 || header.colorType === 6; + if ((alphaExpectation === "alpha") !== hasAlphaChannel) { + throw new Error(`${imagePath} has an unexpected PNG alpha-channel shape`); + } + + if ( + profileExpectation === "srgb" && + !hasSRGBChunk && + !iccProfileName?.toLowerCase().startsWith("srgb") + ) { + throw new Error(`${imagePath} must declare an sRGB color profile`); + } +} +NODE +} + +case "${1:-check}" in + generate) + preflight + render_release_dark_icon "$release_icon_set/1024-dark.png" + render_debug_dark_icon "$debug_icon_set/1024-dark.png" + ;; + check) + preflight + check_manifests + check_pngs + + temp_dir="$(mktemp -d /tmp/openclaw-app-icon-variants.XXXXXX)" + trap 'rm -rf "$temp_dir"' EXIT + render_release_dark_icon "$temp_dir/1024-dark.png" + render_debug_dark_icon "$temp_dir/1024-debug-dark.png" + cmp "$release_icon_set/1024-dark.png" "$temp_dir/1024-dark.png" + cmp "$debug_icon_set/1024-dark.png" "$temp_dir/1024-debug-dark.png" + echo "AppIcon and AppIconDebug Default, Dark, and automatic Tinted variants are valid." + ;; + *) + echo "usage: $0 [generate|check]" >&2 + exit 2 + ;; +esac