New Giftopia App Icons and Splashes #236

Open
jose wants to merge 7 commits from giftopia-app-icon into master
29 changed files with 144 additions and 49 deletions

View File

@@ -1360,7 +1360,7 @@ npm run assets:validate
**Source Assets (Required):** **Source Assets (Required):**
- `resources/icon.png` - App icon source - `resources/icon.png` - App icon source
- `resources/splash.png` - Splash screen 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 Resources (Generated):**
- `android/app/src/main/res/drawable/splash.png` - Splash screen drawable - `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 4. **Native Build**: Platform-specific compilation
5. **Package Creation**: APK/IPA generation 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 ## Architecture Environment Configuration
### Environment Files ### Environment Files

View File

@@ -199,7 +199,7 @@ icon and splash screen generation across all platforms.
### Asset Sources ### Asset Sources
- **Single source of truth**: `resources/` directory (Capacitor default) - **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 - **Format**: PNG or SVG files for optimal quality
### Asset Generation ### Asset Generation

View File

@@ -2,9 +2,9 @@
"icon": { "icon": {
"android": { "android": {
"adaptive": { "adaptive": {
"background": "#121212", "background": "resources/android/icon/icon-background.png",
"foreground": "resources/icon.png", "foreground": "resources/android/icon/icon-foreground.png",
"monochrome": "resources/icon.png" "monochrome": "resources/android/icon/icon-monochrome.png"
}, },
"target": "android/app/src/main/res" "target": "android/app/src/main/res"
}, },
@@ -22,7 +22,7 @@
"scale": "cover", "scale": "cover",
"target": "android/app/src/main/res" "target": "android/app/src/main/res"
}, },
"darkSource": "resources/splash_dark.png", "darkSource": "resources/splash-dark.png",
"ios": { "ios": {
"target": "ios/App/App/Assets.xcassets", "target": "ios/App/App/Assets.xcassets",
"useStoryBoard": true "useStoryBoard": true

View File

@@ -22,7 +22,7 @@
"scale": "cover", "scale": "cover",
"target": "android/app/src/main/res" "target": "android/app/src/main/res"
}, },
"darkSource": "resources/splash_dark.png", "darkSource": "resources/splash-dark.png",
"ios": { "ios": {
"target": "ios/App/App/Assets.xcassets", "target": "ios/App/App/Assets.xcassets",
"useStoryBoard": true "useStoryBoard": true

View File

@@ -47,7 +47,7 @@ npm run build:android:studio
#### Source Assets (Required) #### Source Assets (Required)
- `resources/icon.png` - App icon source - `resources/icon.png` - App icon source
- `resources/splash.png` - Splash screen 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 Resources (Generated)
- `android/app/src/main/res/drawable/splash.png` - Splash screen drawable - `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 # Copy source assets to assets directory
cp resources/icon.png assets/ cp resources/icon.png assets/
cp resources/splash.png assets/ cp resources/splash.png assets/
cp resources/splash_dark.png assets/ cp resources/splash-dark.png assets/
# Generate assets manually # Generate assets manually
npx @capacitor/assets generate npx @capacitor/assets generate
# Clean up # 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 ## Future Enhancements

View File

@@ -89,7 +89,7 @@ system to a standardized, single-source asset configuration approach using
resources/ # Image sources ONLY resources/ # Image sources ONLY
icon.png icon.png
splash.png splash.png
splash_dark.png splash-dark.png
config/assets/ # Versioned config & schema config/assets/ # Versioned config & schema
capacitor-assets.config.json capacitor-assets.config.json

View File

@@ -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
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

View File

@@ -56,7 +56,10 @@ npx capacitor-assets generate --web
## Configuration ## 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 ## Version Control

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 624 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 624 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 624 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
resources/splash-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 624 KiB

View File

@@ -92,7 +92,7 @@ function generateAssetConfig(): AssetConfig {
}, },
splash: { splash: {
source: "resources/splash.png", source: "resources/splash.png",
darkSource: "resources/splash_dark.png", darkSource: "resources/splash-dark.png",
android: { android: {
scale: "cover", scale: "cover",
target: "android/app/src/main/res" target: "android/app/src/main/res"
@@ -138,7 +138,7 @@ function validateSourceFiles(): void {
const requiredFiles = [ const requiredFiles = [
'resources/icon.png', 'resources/icon.png',
'resources/splash.png', 'resources/splash.png',
'resources/splash_dark.png' 'resources/splash-dark.png'
]; ];
const missingFiles = requiredFiles.filter(file => { const missingFiles = requiredFiles.filter(file => {

View File

@@ -230,8 +230,8 @@ validate_android_assets() {
missing_assets+=("resources/splash.png") missing_assets+=("resources/splash.png")
fi fi
if [ ! -f "resources/splash_dark.png" ]; then if [ ! -f "resources/splash-dark.png" ]; then
missing_assets+=("resources/splash_dark.png") missing_assets+=("resources/splash-dark.png")
fi fi
if [ ${#missing_assets[@]} -gt 0 ]; then if [ ${#missing_assets[@]} -gt 0 ]; then
@@ -278,14 +278,14 @@ validate_android_assets() {
# Copy source assets to assets directory for capacitor-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/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.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 # Generate assets
if npx @capacitor/assets generate >/dev/null 2>&1; then if npx @capacitor/assets generate >/dev/null 2>&1; then
log_success "Android assets regenerated successfully" log_success "Android assets regenerated successfully"
# Clean up temporary assets # 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 # Verify the resources were created
local verification_failed=false local verification_failed=false

View File

@@ -172,12 +172,12 @@ check_ios_resources() {
log_info "Checking iOS resources..." log_info "Checking iOS resources..."
# Check for required assets # Check for required assets
if [ ! -f "assets/icon.png" ]; then if [ ! -f "resources/icon.png" ]; then
log_warn "App icon not found at assets/icon.png" log_warn "App icon not found at resources/icon.png"
fi fi
if [ ! -f "assets/splash.png" ]; then if [ ! -f "resources/splash.png" ]; then
log_warn "Splash screen not found at assets/splash.png" log_warn "Splash screen not found at resources/splash.png"
fi fi
# Check for iOS-specific files # Check for iOS-specific files
@@ -192,6 +192,118 @@ check_ios_resources() {
log_success "iOS resource check completed" 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 # Function to clean iOS build
clean_ios_build() { clean_ios_build() {
log_info "Cleaning iOS build artifacts..." log_info "Cleaning iOS build artifacts..."
@@ -424,8 +536,7 @@ fi
# Handle assets-only mode # Handle assets-only mode
if [ "$ASSETS_ONLY" = true ]; then if [ "$ASSETS_ONLY" = true ]; then
log_info "Assets-only mode: generating assets" log_info "Assets-only mode: generating assets"
ensure_ios_capacitor_asset_directories safe_execute "Generating assets" "generate_ios_assets" || exit 7
safe_execute "Generating assets" "npx capacitor-assets generate --ios" || exit 7
log_success "Assets generation completed successfully!" log_success "Assets generation completed successfully!"
exit 0 exit 0
fi 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 safe_execute "Syncing with Capacitor" "run_cap_sync_with_workaround" || exit 6
# Step 7: Generate assets # Step 7: Generate assets
ensure_ios_capacitor_asset_directories safe_execute "Generating assets" "generate_ios_assets" || exit 7
safe_execute "Generating assets" "npx capacitor-assets generate --ios" || exit 7
# Step 8: Build iOS app # Step 8: Build iOS app
safe_execute "Building iOS app" "build_ios_app" || exit 5 safe_execute "Building iOS app" "build_ios_app" || exit 5