diff --git a/BUILDING.md b/BUILDING.md index 2b5803f0..040b2fec 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1360,7 +1360,7 @@ npm run assets:validate **Source Assets (Required):** - `resources/icon.png` - App icon source - `resources/splash.png` - Splash screen source -- `resources/splash_dark.png` - Dark mode splash source +- `resources/splash-dark.png` - Dark mode splash source **Android Resources (Generated):** - `android/app/src/main/res/drawable/splash.png` - Splash screen drawable @@ -1604,6 +1604,11 @@ Use the commands above to check and fix code quality issues. 4. **Native Build**: Platform-specific compilation 5. **Package Creation**: APK/IPA generation +`resources/` is the canonical source for app icons and splash screens. +Do not keep a legacy top-level `assets/` directory unless it is intentionally +used: `@capacitor/assets` prioritizes `assets/` over `resources/`, which can +prevent the canonical assets from being discovered. + ## Architecture Environment Configuration ### Environment Files diff --git a/README.md b/README.md index 642c7a44..446f2d69 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ icon and splash screen generation across all platforms. ### Asset Sources - **Single source of truth**: `resources/` directory (Capacitor default) -- **Source files**: `icon.png`, `splash.png`, `splash_dark.png` +- **Source files**: `icon.png`, `splash.png`, `splash-dark.png` - **Format**: PNG or SVG files for optimal quality ### Asset Generation diff --git a/capacitor-assets.config.json b/capacitor-assets.config.json index 92bd0414..50f79541 100644 --- a/capacitor-assets.config.json +++ b/capacitor-assets.config.json @@ -2,9 +2,9 @@ "icon": { "android": { "adaptive": { - "background": "#121212", - "foreground": "resources/icon.png", - "monochrome": "resources/icon.png" + "background": "resources/android/icon/icon-background.png", + "foreground": "resources/android/icon/icon-foreground.png", + "monochrome": "resources/android/icon/icon-monochrome.png" }, "target": "android/app/src/main/res" }, @@ -22,7 +22,7 @@ "scale": "cover", "target": "android/app/src/main/res" }, - "darkSource": "resources/splash_dark.png", + "darkSource": "resources/splash-dark.png", "ios": { "target": "ios/App/App/Assets.xcassets", "useStoryBoard": true diff --git a/config/assets/capacitor-assets.config.json b/config/assets/capacitor-assets.config.json index 92bd0414..69b8f2d8 100644 --- a/config/assets/capacitor-assets.config.json +++ b/config/assets/capacitor-assets.config.json @@ -22,7 +22,7 @@ "scale": "cover", "target": "android/app/src/main/res" }, - "darkSource": "resources/splash_dark.png", + "darkSource": "resources/splash-dark.png", "ios": { "target": "ios/App/App/Assets.xcassets", "useStoryBoard": true diff --git a/doc/android-asset-validation.md b/doc/android-asset-validation.md index db77aeb8..b34b8a1e 100644 --- a/doc/android-asset-validation.md +++ b/doc/android-asset-validation.md @@ -47,7 +47,7 @@ npm run build:android:studio #### Source Assets (Required) - `resources/icon.png` - App icon source - `resources/splash.png` - Splash screen source -- `resources/splash_dark.png` - Dark mode splash source +- `resources/splash-dark.png` - Dark mode splash source #### Android Resources (Generated) - `android/app/src/main/res/drawable/splash.png` - Splash screen drawable @@ -201,13 +201,13 @@ mkdir -p android/app/src/main/res/mipmap-{mdpi,hdpi,xhdpi,xxhdpi,xxxhdpi} # Copy source assets to assets directory cp resources/icon.png assets/ cp resources/splash.png assets/ -cp resources/splash_dark.png assets/ +cp resources/splash-dark.png assets/ # Generate assets manually npx @capacitor/assets generate # Clean up -rm assets/icon.png assets/splash.png assets/splash_dark.png +rm assets/icon.png assets/splash.png assets/splash-dark.png ``` ## Future Enhancements diff --git a/doc/asset-migration-plan.md b/doc/asset-migration-plan.md index 86c0954e..79ce8b65 100644 --- a/doc/asset-migration-plan.md +++ b/doc/asset-migration-plan.md @@ -89,7 +89,7 @@ system to a standardized, single-source asset configuration approach using resources/ # Image sources ONLY icon.png splash.png - splash_dark.png + splash-dark.png config/assets/ # Versioned config & schema capacitor-assets.config.json diff --git a/ios/App/App/Assets.xcassets/SplashDark.imageset/Contents.json b/ios/App/App/Assets.xcassets/SplashDark.imageset/Contents.json deleted file mode 100644 index 0b268f6f..00000000 --- a/ios/App/App/Assets.xcassets/SplashDark.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "splash@1x.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "splash@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "splash@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@1x.png b/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@1x.png deleted file mode 100644 index 79fc756e..00000000 Binary files a/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@1x.png and /dev/null differ diff --git a/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@2x.png b/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@2x.png deleted file mode 100644 index 03f44f99..00000000 Binary files a/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@2x.png and /dev/null differ diff --git a/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@3x.png b/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@3x.png deleted file mode 100644 index a1dbc775..00000000 Binary files a/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@3x.png and /dev/null differ diff --git a/resources/README.md b/resources/README.md index d8290cd3..631685aa 100644 --- a/resources/README.md +++ b/resources/README.md @@ -56,7 +56,10 @@ npx capacitor-assets generate --web ## Configuration -Asset generation is configured in `capacitor-assets.config.json` at the project root. +`resources/` is this project's canonical asset source. `@capacitor/assets` +prioritizes a top-level `assets/` directory over `resources/`, so a legacy +`assets/` directory can prevent these assets from being discovered. Remove that +directory when it is empty or obsolete. ## Version Control diff --git a/resources/android/icon/icon-background.png b/resources/android/icon/icon-background.png new file mode 100644 index 00000000..c438daa9 Binary files /dev/null and b/resources/android/icon/icon-background.png differ diff --git a/resources/android/icon/icon-foreground.png b/resources/android/icon/icon-foreground.png new file mode 100644 index 00000000..7d5e968a Binary files /dev/null and b/resources/android/icon/icon-foreground.png differ diff --git a/resources/android/icon/icon-monochrome.png b/resources/android/icon/icon-monochrome.png new file mode 100644 index 00000000..58986b0f Binary files /dev/null and b/resources/android/icon/icon-monochrome.png differ diff --git a/resources/android/icon/icon.png b/resources/android/icon/icon.png index f7b4d9d2..3788494e 100644 Binary files a/resources/android/icon/icon.png and b/resources/android/icon/icon.png differ diff --git a/resources/android/splash/splash.png b/resources/android/splash/splash.png index 5a29ffdd..5a8a6e0a 100644 Binary files a/resources/android/splash/splash.png and b/resources/android/splash/splash.png differ diff --git a/resources/android/splash/splash_dark.png b/resources/android/splash/splash_dark.png index d5d24209..0f3b8d23 100644 Binary files a/resources/android/splash/splash_dark.png and b/resources/android/splash/splash_dark.png differ diff --git a/resources/icon.png b/resources/icon.png index f7b4d9d2..3788494e 100644 Binary files a/resources/icon.png and b/resources/icon.png differ diff --git a/resources/ios/icon/icon-dark.png b/resources/ios/icon/icon-dark.png new file mode 100644 index 00000000..1efa0164 Binary files /dev/null and b/resources/ios/icon/icon-dark.png differ diff --git a/resources/ios/icon/icon.png b/resources/ios/icon/icon.png index f7b4d9d2..3788494e 100644 Binary files a/resources/ios/icon/icon.png and b/resources/ios/icon/icon.png differ diff --git a/resources/ios/splash/splash.png b/resources/ios/splash/splash.png index 5a29ffdd..5a8a6e0a 100644 Binary files a/resources/ios/splash/splash.png and b/resources/ios/splash/splash.png differ diff --git a/resources/ios/splash/splash_dark.png b/resources/ios/splash/splash_dark.png index d5d24209..0f3b8d23 100644 Binary files a/resources/ios/splash/splash_dark.png and b/resources/ios/splash/splash_dark.png differ diff --git a/resources/splash-dark.png b/resources/splash-dark.png new file mode 100644 index 00000000..0f3b8d23 Binary files /dev/null and b/resources/splash-dark.png differ diff --git a/resources/splash.png b/resources/splash.png index 5a29ffdd..5a8a6e0a 100644 Binary files a/resources/splash.png and b/resources/splash.png differ diff --git a/resources/splash_dark.png b/resources/splash_dark.png deleted file mode 100644 index d5d24209..00000000 Binary files a/resources/splash_dark.png and /dev/null differ diff --git a/resources/web/icon/icon.png b/resources/web/icon/icon.png index f7b4d9d2..3788494e 100644 Binary files a/resources/web/icon/icon.png and b/resources/web/icon/icon.png differ diff --git a/scripts/assets-config.ts b/scripts/assets-config.ts index baaadce5..30f924af 100644 --- a/scripts/assets-config.ts +++ b/scripts/assets-config.ts @@ -92,7 +92,7 @@ function generateAssetConfig(): AssetConfig { }, splash: { source: "resources/splash.png", - darkSource: "resources/splash_dark.png", + darkSource: "resources/splash-dark.png", android: { scale: "cover", target: "android/app/src/main/res" @@ -138,7 +138,7 @@ function validateSourceFiles(): void { const requiredFiles = [ 'resources/icon.png', 'resources/splash.png', - 'resources/splash_dark.png' + 'resources/splash-dark.png' ]; const missingFiles = requiredFiles.filter(file => { diff --git a/scripts/build-android.sh b/scripts/build-android.sh index 4a5fc808..6b18c015 100755 --- a/scripts/build-android.sh +++ b/scripts/build-android.sh @@ -230,8 +230,8 @@ validate_android_assets() { missing_assets+=("resources/splash.png") fi - if [ ! -f "resources/splash_dark.png" ]; then - missing_assets+=("resources/splash_dark.png") + if [ ! -f "resources/splash-dark.png" ]; then + missing_assets+=("resources/splash-dark.png") fi if [ ${#missing_assets[@]} -gt 0 ]; then @@ -278,14 +278,14 @@ validate_android_assets() { # Copy source assets to assets directory for capacitor-assets cp resources/icon.png assets/ 2>/dev/null || log_warn "Could not copy icon.png" cp resources/splash.png assets/ 2>/dev/null || log_warn "Could not copy splash.png" - cp resources/splash_dark.png assets/ 2>/dev/null || log_warn "Could not copy splash_dark.png" + cp resources/splash-dark.png assets/ 2>/dev/null || log_warn "Could not copy splash-dark.png" # Generate assets if npx @capacitor/assets generate >/dev/null 2>&1; then log_success "Android assets regenerated successfully" # Clean up temporary assets - rm -f assets/icon.png assets/splash.png assets/splash_dark.png + rm -f assets/icon.png assets/splash.png assets/splash-dark.png # Verify the resources were created local verification_failed=false diff --git a/scripts/build-ios.sh b/scripts/build-ios.sh index eac5f9a5..11e9a872 100755 --- a/scripts/build-ios.sh +++ b/scripts/build-ios.sh @@ -172,12 +172,12 @@ check_ios_resources() { log_info "Checking iOS resources..." # Check for required assets - if [ ! -f "assets/icon.png" ]; then - log_warn "App icon not found at assets/icon.png" + if [ ! -f "resources/icon.png" ]; then + log_warn "App icon not found at resources/icon.png" fi - if [ ! -f "assets/splash.png" ]; then - log_warn "Splash screen not found at assets/splash.png" + if [ ! -f "resources/splash.png" ]; then + log_warn "Splash screen not found at resources/splash.png" fi # Check for iOS-specific files @@ -192,6 +192,118 @@ check_ios_resources() { log_success "iOS resource check completed" } +# iOS app icon appearance variants (Dark, Tinted, …). +# Luminosity appearance values match Apple's asset catalog format (iOS 18+). +readonly _IOS_APP_ICON_APPEARANCE_LUMINOSITY="luminosity" +readonly _IOS_APP_ICON_APPEARANCE_DARK="dark" +readonly _IOS_APP_ICON_APPEARANCE_TINTED="tinted" + +# Each row: source_file|luminosity_value|dest_filename +# To add a variant: append one row and place the source PNG under resources/ios/. +readonly _IOS_APP_ICON_APPEARANCE_VARIANTS=( + "resources/ios/icon/icon-dark.png|${_IOS_APP_ICON_APPEARANCE_DARK}|AppIcon-Dark.png" + "resources/ios/icon/icon-tinted.png|${_IOS_APP_ICON_APPEARANCE_TINTED}|AppIcon-Tinted.png" +) + +# Update AppIcon.appiconset/Contents.json with one 1024×1024 appearance entry. +# Preserves all other images (including capacitor-assets output); replaces any +# existing entry for the same luminosity appearance. +_update_appicon_contents_for_appearance() { + local contents_json="$1" + local dest_filename="$2" + local luminosity_value="$3" + + local tmp_file + tmp_file="$(mktemp)" + + if ! jq --arg filename "$dest_filename" \ + --arg appearance "$_IOS_APP_ICON_APPEARANCE_LUMINOSITY" \ + --arg value "$luminosity_value" \ + ' + .images |= map(select((.appearances[0].value // "") != $value)) + | .images += [{ + "appearances": [{ + "appearance": $appearance, + "value": $value + }], + "filename": $filename, + "idiom": "universal", + "platform": "ios", + "size": "1024x1024" + }] + ' "$contents_json" > "$tmp_file"; then + rm -f "$tmp_file" + log_error "Failed to update AppIcon Contents.json for appearance: $luminosity_value" + return 1 + fi + + mv "$tmp_file" "$contents_json" +} + +# Install one appearance variant when its source PNG exists. +_apply_ios_app_icon_appearance_variant() { + local appiconset_dir="$1" + local contents_json="$2" + local source_file="$3" + local luminosity_value="$4" + local dest_filename="$5" + + if [ ! -f "$source_file" ]; then + log_info "iOS app icon appearance variant not found ($source_file) — skipping" + return 0 + fi + + if [ ! -d "$appiconset_dir" ]; then + log_warn "AppIcon.appiconset not found — skipping appearance variant ($luminosity_value)" + return 0 + fi + + if ! cp "$source_file" "$appiconset_dir/$dest_filename"; then + log_error "Failed to copy $source_file to $appiconset_dir/$dest_filename" + return 1 + fi + + log_info "Installed iOS app icon appearance variant: $luminosity_value ($dest_filename)" + _update_appicon_contents_for_appearance "$contents_json" "$dest_filename" "$luminosity_value" +} + +# Post-process capacitor-assets iOS icons: copy optional Dark/Tinted sources and +# register them in AppIcon.appiconset/Contents.json. +apply_ios_app_icon_appearances() { + local appiconset_dir="ios/App/App/Assets.xcassets/AppIcon.appiconset" + local contents_json="$appiconset_dir/Contents.json" + + if [ ! -f "$contents_json" ]; then + log_info "AppIcon Contents.json not found — skipping appearance variants" + return 0 + fi + + if ! command -v jq &> /dev/null; then + log_error "jq is required to install iOS app icon appearance variants (install with: brew install jq)" + return 1 + fi + + local variant source_file luminosity_value dest_filename + for variant in "${_IOS_APP_ICON_APPEARANCE_VARIANTS[@]}"; do + IFS='|' read -r source_file luminosity_value dest_filename <<< "$variant" + _apply_ios_app_icon_appearance_variant \ + "$appiconset_dir" "$contents_json" \ + "$source_file" "$luminosity_value" "$dest_filename" + done +} + +# Generate iOS assets (capacitor-assets), then apply optional appearance variants. +generate_ios_assets() { + ensure_ios_capacitor_asset_directories + if [ -d "assets" ]; then + log_warn "@capacitor/assets prioritizes the top-level assets/ directory over resources/." + log_warn "This project intentionally uses resources/ as the canonical asset source." + log_warn "Remove the legacy assets/ directory if it is empty or obsolete." + fi + npx capacitor-assets generate --ios + apply_ios_app_icon_appearances +} + # Function to clean iOS build clean_ios_build() { log_info "Cleaning iOS build artifacts..." @@ -424,8 +536,7 @@ fi # Handle assets-only mode if [ "$ASSETS_ONLY" = true ]; then log_info "Assets-only mode: generating assets" - ensure_ios_capacitor_asset_directories - safe_execute "Generating assets" "npx capacitor-assets generate --ios" || exit 7 + safe_execute "Generating assets" "generate_ios_assets" || exit 7 log_success "Assets generation completed successfully!" exit 0 fi @@ -574,8 +685,7 @@ safe_execute "Installing CocoaPods dependencies" "run_pod_install_with_workaroun safe_execute "Syncing with Capacitor" "run_cap_sync_with_workaround" || exit 6 # Step 7: Generate assets -ensure_ios_capacitor_asset_directories -safe_execute "Generating assets" "npx capacitor-assets generate --ios" || exit 7 +safe_execute "Generating assets" "generate_ios_assets" || exit 7 # Step 8: Build iOS app safe_execute "Building iOS app" "build_ios_app" || exit 5