Compare commits

..

1 Commits

Author SHA1 Message Date
AppCakes 304f3a8ca9 build: 6e92ee4f-08ae-4917-b7c6-d7db21480fec 2026-07-11 10:30:55 +00:00
68 changed files with 4298 additions and 752 deletions
Vendored
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -4,7 +4,7 @@ android {
namespace = "io.ionic.starter" namespace = "io.ionic.starter"
compileSdk = rootProject.ext.compileSdkVersion compileSdk = rootProject.ext.compileSdkVersion
defaultConfig { defaultConfig {
applicationId "io.demoapp.app" applicationId "com.basedeployer.app"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1 versionCode 1
+1 -1
View File
@@ -25,7 +25,7 @@
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="io.demoapp.app" /> <data android:scheme="com.basedeployer.app" />
</intent-filter> </intent-filter>
</activity> </activity>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 804 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 771 B

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 804 B

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 16 KiB

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="splashBackground">#FFFFFF</color>
</resources>
+2 -2
View File
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<resources> <resources>
<string name="app_name">DemoApp</string> <string name="app_name">Base Deployer</string>
<string name="title_activity_main">DemoApp</string> <string name="title_activity_main">Base Deployer</string>
<string name="package_name">io.ionic.starter</string> <string name="package_name">io.ionic.starter</string>
<string name="custom_url_scheme">io.ionic.starter</string> <string name="custom_url_scheme">io.ionic.starter</string>
</resources> </resources>
+2 -4
View File
@@ -16,9 +16,7 @@
</style> </style>
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen"> <style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/splashBackground</item> <item name="android:background">@drawable/splash</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_logo</item>
<item name="postSplashScreenTheme">@style/AppTheme.NoActionBar</item>
</style> </style>
</resources> </resources>
+2 -2
View File
@@ -1,8 +1,8 @@
import type { CapacitorConfig } from '@capacitor/cli'; import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = { const config: CapacitorConfig = {
appId: 'io.demoapp.app', appId: 'com.basedeployer.app',
appName: 'DemoApp', appName: 'Base Deployer',
webDir: 'dist', webDir: 'dist',
plugins: { plugins: {
FirebaseAuthentication: { FirebaseAuthentication: {
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+2 -2
View File
@@ -308,7 +308,7 @@
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = io.demoapp.app; PRODUCT_BUNDLE_IDENTIFIER = com.basedeployer.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -330,7 +330,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = io.demoapp.app; PRODUCT_BUNDLE_IDENTIFIER = com.basedeployer.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -1,122 +1,14 @@
{ {
"images": [ "images" : [
{ {
"idiom": "iphone", "filename" : "AppIcon-512@2x.png",
"scale": "1x", "idiom" : "universal",
"size": "20x20", "platform" : "ios",
"filename": "icon-20.png" "size" : "1024x1024"
},
{
"idiom": "ipad",
"scale": "1x",
"size": "20x20",
"filename": "icon-20.png"
},
{
"idiom": "iphone",
"scale": "1x",
"size": "29x29",
"filename": "icon-29.png"
},
{
"idiom": "ipad",
"scale": "1x",
"size": "29x29",
"filename": "icon-29.png"
},
{
"idiom": "ipad",
"scale": "1x",
"size": "40x40",
"filename": "icon-40.png"
},
{
"idiom": "iphone",
"scale": "2x",
"size": "20x20",
"filename": "icon-40.png"
},
{
"idiom": "iphone",
"scale": "2x",
"size": "29x29",
"filename": "icon-58.png"
},
{
"idiom": "ipad",
"scale": "2x",
"size": "29x29",
"filename": "icon-58.png"
},
{
"idiom": "iphone",
"scale": "3x",
"size": "20x20",
"filename": "icon-60.png"
},
{
"idiom": "ipad",
"scale": "1x",
"size": "76x76",
"filename": "icon-76.png"
},
{
"idiom": "iphone",
"scale": "2x",
"size": "40x40",
"filename": "icon-80.png"
},
{
"idiom": "ipad",
"scale": "2x",
"size": "40x40",
"filename": "icon-80.png"
},
{
"idiom": "iphone",
"scale": "3x",
"size": "29x29",
"filename": "icon-87.png"
},
{
"idiom": "iphone",
"scale": "2x",
"size": "60x60",
"filename": "icon-120.png"
},
{
"idiom": "iphone",
"scale": "3x",
"size": "40x40",
"filename": "icon-120.png"
},
{
"idiom": "ipad",
"scale": "2x",
"size": "76x76",
"filename": "icon-152.png"
},
{
"idiom": "ipad",
"scale": "2x",
"size": "83.5x83.5",
"filename": "icon-167.png"
},
{
"idiom": "iphone",
"scale": "3x",
"size": "60x60",
"filename": "icon-180.png"
},
{
"idiom": "ios-marketing",
"scale": "1x",
"size": "1024x1024",
"filename": "icon-1024.png"
} }
], ],
"info": { "info" : {
"author": "appsuite", "author" : "xcode",
"version": 1 "version" : 1
} }
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

+17 -7
View File
@@ -1,13 +1,23 @@
{ {
"images": [ "images" : [
{ {
"idiom": "universal", "idiom" : "universal",
"filename": "splash.png", "filename" : "splash-2732x2732-2.png",
"scale": "1x" "scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "splash-2732x2732-1.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "splash-2732x2732.png",
"scale" : "3x"
} }
], ],
"info": { "info" : {
"author": "appsuite", "version" : 1,
"version": 1 "author" : "xcode"
} }
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

+3 -3
View File
@@ -7,7 +7,7 @@
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>DemoApp</string> <string>Base Deployer</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@@ -51,10 +51,10 @@
<array> <array>
<dict> <dict>
<key>CFBundleURLName</key> <key>CFBundleURLName</key>
<string>io.demoapp.app</string> <string>com.basedeployer.app</string>
<key>CFBundleURLSchemes</key> <key>CFBundleURLSchemes</key>
<array> <array>
<string>io.demoapp.app</string> <string>com.basedeployer.app</string>
</array> </array>
</dict> </dict>
</array> </array>
-363
View File
@@ -1,363 +0,0 @@
workflows:
android-debug:
name: Android Debug APK
max_build_duration: 45
instance_type: mac_mini_m2
environment:
node: 22
java: 21
cache:
cache_paths:
- $HOME/.gradle/caches
- $HOME/.gradle/wrapper
scripts:
- name: Strip unused scaffold packages
script: |
for pkg in "@capacitor/google-maps" "@capacitor/push-notifications" "@capacitor-firebase/authentication"; do
if ! grep -rq "$pkg" src/; then
node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.dependencies['${pkg}'];fs.writeFileSync('package.json',JSON.stringify(p,null,2));"
echo "Stripped $pkg (not used in src/)"
else
echo "Keeping $pkg (used in src/)"
fi
done
- name: Install dependencies
script: rm -f package-lock.json && npm install --legacy-peer-deps
- name: Build web assets
script: npm run build
- name: Write Firebase config
script: |
if [ -n "$GOOGLE_SERVICES_JSON" ]; then
echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json
echo "Wrote google-services.json ($(wc -c < android/app/google-services.json) bytes)"
else
echo "GOOGLE_SERVICES_JSON is empty — skipping"
fi
if [ -n "$GOOGLE_SERVICE_INFO_PLIST" ]; then
echo "$GOOGLE_SERVICE_INFO_PLIST" | base64 --decode > ios/App/App/GoogleService-Info.plist
echo "Wrote GoogleService-Info.plist ($(wc -c < ios/App/App/GoogleService-Info.plist) bytes)"
fi
- name: Capacitor sync
script: npx cap sync android
- name: Set up debug keystore
script: |
if [ -n "$CM_DEBUG_KEYSTORE" ]; then
echo "$CM_DEBUG_KEYSTORE" | base64 --decode > /tmp/debug-keystore.p12
fi
- name: Build debug APK
script: |
cd android
if [ -n "$CM_DEBUG_KEYSTORE" ]; then
./gradlew assembleDebug \
-Pandroid.injected.signing.store.file=/tmp/debug-keystore.p12 \
-Pandroid.injected.signing.store.password=$CM_DEBUG_KEYSTORE_PASSWORD \
-Pandroid.injected.signing.key.alias=debug \
-Pandroid.injected.signing.key.password=$CM_DEBUG_KEYSTORE_PASSWORD \
--no-daemon
else
./gradlew assembleDebug --no-daemon
fi
artifacts:
- android/app/build/outputs/apk/debug/app-debug.apk
android-release-apk:
name: Android Release APK
max_build_duration: 45
instance_type: mac_mini_m2
environment:
node: 22
java: 21
cache:
cache_paths:
- $HOME/.gradle/caches
- $HOME/.gradle/wrapper
scripts:
- name: Strip unused scaffold packages
script: |
for pkg in "@capacitor/google-maps" "@capacitor/push-notifications" "@capacitor-firebase/authentication"; do
if ! grep -rq "$pkg" src/; then
node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.dependencies['${pkg}'];fs.writeFileSync('package.json',JSON.stringify(p,null,2));"
echo "Stripped $pkg (not used in src/)"
else
echo "Keeping $pkg (used in src/)"
fi
done
- name: Install dependencies
script: rm -f package-lock.json && npm install --legacy-peer-deps
- name: Build web assets
script: npm run build
- name: Write Firebase config
script: |
if [ -n "$GOOGLE_SERVICES_JSON" ]; then
echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json
fi
- name: Capacitor sync
script: npx cap sync android
- name: Set up keystore
script: echo "$CM_KEYSTORE" | base64 --decode > /tmp/keystore.jks
- name: Build Release APK
script: |
cd android
./gradlew assembleRelease \
-Pandroid.injected.signing.store.file=/tmp/keystore.jks \
-Pandroid.injected.signing.store.password=$CM_KEYSTORE_PASSWORD \
-Pandroid.injected.signing.key.alias=$CM_KEY_ALIAS \
-Pandroid.injected.signing.key.password=$CM_KEY_PASSWORD \
--no-daemon
artifacts:
- android/app/build/outputs/apk/release/app-release.apk
android-release-aab:
name: Android Release AAB
max_build_duration: 45
instance_type: mac_mini_m2
environment:
node: 22
java: 21
cache:
cache_paths:
- $HOME/.gradle/caches
- $HOME/.gradle/wrapper
scripts:
- name: Strip unused scaffold packages
script: |
for pkg in "@capacitor/google-maps" "@capacitor/push-notifications" "@capacitor-firebase/authentication"; do
if ! grep -rq "$pkg" src/; then
node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.dependencies['${pkg}'];fs.writeFileSync('package.json',JSON.stringify(p,null,2));"
echo "Stripped $pkg (not used in src/)"
else
echo "Keeping $pkg (used in src/)"
fi
done
- name: Install dependencies
script: rm -f package-lock.json && npm install --legacy-peer-deps
- name: Build web assets
script: npm run build
- name: Write Firebase config
script: |
if [ -n "$GOOGLE_SERVICES_JSON" ]; then
echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json
fi
- name: Capacitor sync
script: npx cap sync android
- name: Set up keystore
script: echo "$CM_KEYSTORE" | base64 --decode > /tmp/keystore.jks
- name: Build Release AAB
script: |
cd android
./gradlew bundleRelease \
-Pandroid.injected.signing.store.file=/tmp/keystore.jks \
-Pandroid.injected.signing.store.password=$CM_KEYSTORE_PASSWORD \
-Pandroid.injected.signing.key.alias=$CM_KEY_ALIAS \
-Pandroid.injected.signing.key.password=$CM_KEY_PASSWORD \
--no-daemon
artifacts:
- android/app/build/outputs/bundle/release/app-release.aab
android-release:
name: Android Publish to Google Play
max_build_duration: 60
instance_type: mac_mini_m2
environment:
node: 22
java: 21
cache:
cache_paths:
- $HOME/.gradle/caches
- $HOME/.gradle/wrapper
scripts:
- name: Strip unused scaffold packages
script: |
for pkg in "@capacitor/google-maps" "@capacitor/push-notifications" "@capacitor-firebase/authentication"; do
if ! grep -rq "$pkg" src/; then
node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.dependencies['${pkg}'];fs.writeFileSync('package.json',JSON.stringify(p,null,2));"
echo "Stripped $pkg (not used in src/)"
else
echo "Keeping $pkg (used in src/)"
fi
done
- name: Install dependencies
script: rm -f package-lock.json && npm install --legacy-peer-deps
- name: Build web assets
script: npm run build
- name: Write Firebase config
script: |
if [ -n "$GOOGLE_SERVICES_JSON" ]; then
echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json
fi
- name: Capacitor sync
script: npx cap sync android
- name: Set up keystore
script: echo "$CM_KEYSTORE" | base64 --decode > /tmp/keystore.jks
- name: Build Release AAB
script: |
cd android
./gradlew bundleRelease \
-Pandroid.injected.signing.store.file=/tmp/keystore.jks \
-Pandroid.injected.signing.store.password=$CM_KEYSTORE_PASSWORD \
-Pandroid.injected.signing.key.alias=$CM_KEY_ALIAS \
-Pandroid.injected.signing.key.password=$CM_KEY_PASSWORD \
--no-daemon
publishing:
google_play:
credentials: $GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS
track: $GOOGLE_PLAY_TRACK
submit_as_draft: false
artifacts:
- android/app/build/outputs/bundle/release/app-release.aab
ios-debug:
name: iOS Debug IPA
max_build_duration: 60
instance_type: mac_mini_m2
environment:
node: 22
xcode: latest
scripts:
- name: Strip unused scaffold packages
script: |
for pkg in "@capacitor/google-maps" "@capacitor/push-notifications" "@capacitor-firebase/authentication"; do
if ! grep -rq "$pkg" src/; then
node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.dependencies['${pkg}'];fs.writeFileSync('package.json',JSON.stringify(p,null,2));"
echo "Stripped $pkg (not used in src/)"
else
echo "Keeping $pkg (used in src/)"
fi
done
- name: Install dependencies
script: rm -f package-lock.json && npm install --legacy-peer-deps
- name: Build web assets
script: npm run build
- name: Capacitor sync
script: npx cap sync ios
- name: Write Firebase config
script: |
if [ -n "$GOOGLE_SERVICE_INFO_PLIST" ]; then
echo "$GOOGLE_SERVICE_INFO_PLIST" | base64 --decode > ios/App/App/GoogleService-Info.plist
ruby -e "
require 'xcodeproj'
project = Xcodeproj::Project.open('ios/App/App.xcodeproj')
target = project.targets.find { |t| t.name == 'App' }
app_group = project.main_group['App']
unless app_group.files.find { |f| f.path == 'GoogleService-Info.plist' }
file_ref = app_group.new_reference('GoogleService-Info.plist')
target.resources_build_phase.add_file_reference(file_ref)
project.save
end
"
fi
- name: Capacitor sync
script: npx cap sync ios
- name: Set up signing
script: |
keychain initialize
if [ -n "$APP_STORE_CONNECT_PRIVATE_KEY" ]; then
app-store-connect fetch-signing-files \
$(xcode-project detect-bundle-id) \
--type IOS_APP_DEVELOPMENT \
--certificate-key=@env:APP_STORE_CONNECT_PRIVATE_KEY \
--issuer-id=$APP_STORE_CONNECT_ISSUER_ID \
--key-id=$APP_STORE_CONNECT_KEY_IDENTIFIER \
--create
xcode-project use-profiles
elif [ -n "$CM_CERTIFICATE" ]; then
echo "$CM_CERTIFICATE" | base64 --decode > /tmp/cert.p12
keychain add-certificates \
--certificate /tmp/cert.p12 \
--certificate-password "$CM_CERTIFICATE_PASSWORD"
mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
echo "$CM_PROVISIONING_PROFILE" | base64 --decode > "$HOME/Library/MobileDevice/Provisioning Profiles/profile.mobileprovision"
xcode-project use-profiles
else
echo "No signing credentials configured — build will fail at signing"
exit 1
fi
- name: Build IPA
script: |
xcode-project build-ipa \
--workspace ios/App/App.xcworkspace \
--scheme App
artifacts:
- build/ios/ipa/*.ipa
ios-release:
name: iOS Release IPA
max_build_duration: 90
instance_type: mac_mini_m2
environment:
node: 22
xcode: latest
scripts:
- name: Strip unused scaffold packages
script: |
for pkg in "@capacitor/google-maps" "@capacitor/push-notifications" "@capacitor-firebase/authentication"; do
if ! grep -rq "$pkg" src/; then
node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.dependencies['${pkg}'];fs.writeFileSync('package.json',JSON.stringify(p,null,2));"
echo "Stripped $pkg (not used in src/)"
else
echo "Keeping $pkg (used in src/)"
fi
done
- name: Install dependencies
script: rm -f package-lock.json && npm install --legacy-peer-deps
- name: Build web assets
script: npm run build
- name: Capacitor sync
script: npx cap sync ios
- name: Write Firebase config
script: |
if [ -n "$GOOGLE_SERVICE_INFO_PLIST" ]; then
echo "$GOOGLE_SERVICE_INFO_PLIST" | base64 --decode > ios/App/App/GoogleService-Info.plist
ruby -e "
require 'xcodeproj'
project = Xcodeproj::Project.open('ios/App/App.xcodeproj')
target = project.targets.find { |t| t.name == 'App' }
app_group = project.main_group['App']
unless app_group.files.find { |f| f.path == 'GoogleService-Info.plist' }
file_ref = app_group.new_reference('GoogleService-Info.plist')
target.resources_build_phase.add_file_reference(file_ref)
project.save
end
"
fi
- name: Capacitor sync
script: npx cap sync ios
- name: Set up signing
script: |
keychain initialize
if [ -n "$APP_STORE_CONNECT_PRIVATE_KEY" ]; then
app-store-connect fetch-signing-files \
$(xcode-project detect-bundle-id) \
--type IOS_APP_STORE \
--certificate-key=@env:APP_STORE_CONNECT_PRIVATE_KEY \
--issuer-id=$APP_STORE_CONNECT_ISSUER_ID \
--key-id=$APP_STORE_CONNECT_KEY_IDENTIFIER \
--create
xcode-project use-profiles
else
echo "$CM_CERTIFICATE" | base64 --decode > /tmp/cert.p12
keychain add-certificates \
--certificate /tmp/cert.p12 \
--certificate-password "$CM_CERTIFICATE_PASSWORD"
mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
echo "$CM_PROVISIONING_PROFILE" | base64 --decode > "$HOME/Library/MobileDevice/Provisioning Profiles/profile.mobileprovision"
xcode-project use-profiles
fi
- name: Build IPA
script: |
xcode-project build-ipa \
--workspace ios/App/App.xcworkspace \
--scheme App \
--config Release
- name: Publish
script: |
if [ "$SUBMIT_TO_TESTFLIGHT" = "true" ] || [ "$SUBMIT_TO_APP_STORE" = "true" ]; then
app-store-connect publish \
--certificate-key=@env:APP_STORE_CONNECT_PRIVATE_KEY \
--issuer-id=$APP_STORE_CONNECT_ISSUER_ID \
--key-id=$APP_STORE_CONNECT_KEY_IDENTIFIER \
--submit-to-testflight=$SUBMIT_TO_TESTFLIGHT \
--submit-to-app-store=$SUBMIT_TO_APP_STORE
fi
artifacts:
- build/ios/ipa/*.ipa
+8 -1
View File
@@ -18,12 +18,17 @@
"@capacitor/haptics": "8.0.2", "@capacitor/haptics": "8.0.2",
"@capacitor/ios": "8.3.4", "@capacitor/ios": "8.3.4",
"@capacitor/keyboard": "8.0.3", "@capacitor/keyboard": "8.0.3",
"@capacitor/preferences": "^8.0.1",
"@capacitor/push-notifications": "^8.1.1", "@capacitor/push-notifications": "^8.1.1",
"@capacitor/status-bar": "8.0.2", "@capacitor/status-bar": "8.0.2",
"@hookform/resolvers": "^3.9.0",
"@ionic/pwa-elements": "^3.0.0", "@ionic/pwa-elements": "^3.0.0",
"@ionic/react": "8.8.7", "@ionic/react": "8.8.7",
"@ionic/react-router": "8.8.7", "@ionic/react-router": "8.8.7",
"@supabase/supabase-js": "^2.0.0", "@reown/appkit": "^1.8.21",
"@reown/appkit-adapter-wagmi": "^1.8.21",
"@supabase/supabase-js": "^2.108.1",
"@tanstack/react-query": "^5.101.1",
"firebase": "^11.0.0", "firebase": "^11.0.0",
"ionicons": "^7.4.0", "ionicons": "^7.4.0",
"katex": "^0.16.0", "katex": "^0.16.0",
@@ -33,6 +38,8 @@
"react-katex": "^3.0.1", "react-katex": "^3.0.1",
"react-router": "^5.3.4", "react-router": "^5.3.4",
"react-router-dom": "^5.3.4", "react-router-dom": "^5.3.4",
"viem": "^2.53.1",
"wagmi": "^3.6.18",
"zod": "^3.24.0", "zod": "^3.24.0",
"zustand": "^5.0.0" "zustand": "^5.0.0"
}, },
BIN
View File
Binary file not shown.
+53 -9
View File
@@ -2,6 +2,13 @@ import React, { useEffect } from 'react';
import { Redirect, Route } from 'react-router-dom'; import { Redirect, Route } from 'react-router-dom';
import { IonApp, IonRouterOutlet } from '@ionic/react'; import { IonApp, IonRouterOutlet } from '@ionic/react';
import { IonReactRouter } from '@ionic/react-router'; import { IonReactRouter } from '@ionic/react-router';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { WagmiProvider } from 'wagmi';
import { WagmiAdapter } from '@reown/appkit-adapter-wagmi';
import { base, type AppKitNetwork } from '@reown/appkit/networks';
import { createAppKit } from '@reown/appkit/react';
import { setStatusBarStyle, Style } from './utils/statusBar'; import { setStatusBarStyle, Style } from './utils/statusBar';
import '@ionic/react/css/core.css'; import '@ionic/react/css/core.css';
@@ -19,20 +26,57 @@ import './theme/variables.css';
import Home from './pages/Home'; import Home from './pages/Home';
// Setup QueryClient
const queryClient = new QueryClient();
// Setup AppKit
const projectId =
import.meta.env.VITE_WALLETCONNECT_PROJECT_ID ||
'01e0a2bf8d37317e040edb7878d70804';
const metadata = {
name: 'Base Deployer',
description: 'Base Mainnet GM dApp',
url: 'https://basedeployer.app',
icons: ['https://avatars.githubusercontent.com/u/37784886'],
};
const networks = [base as any] as [AppKitNetwork, ...AppKitNetwork[]];
const wagmiAdapter = new WagmiAdapter({
networks,
projectId,
ssr: false,
});
createAppKit({
adapters: [wagmiAdapter],
networks,
projectId,
metadata,
features: {
analytics: true,
},
});
const App: React.FC = () => { const App: React.FC = () => {
useEffect(() => { useEffect(() => {
setStatusBarStyle(Style.Light); setStatusBarStyle(Style.Dark); // Dark background, light icons
}, []); }, []);
return ( return (
<IonApp> <WagmiProvider config={wagmiAdapter.wagmiConfig}>
<IonReactRouter> <QueryClientProvider client={queryClient}>
<IonRouterOutlet> <IonApp>
<Route path="/home" component={Home} exact /> <IonReactRouter>
<Route exact path="/" render={() => <Redirect to="/home" />} /> <IonRouterOutlet>
</IonRouterOutlet> <Route path="/home" component={Home} exact />
</IonReactRouter> <Redirect exact from="/" to="/home" />
</IonApp> </IonRouterOutlet>
</IonReactRouter>
</IonApp>
</QueryClientProvider>
</WagmiProvider>
); );
}; };
+10 -5
View File
@@ -5,7 +5,7 @@ import { Capacitor } from '@capacitor/core';
import App from './App'; import App from './App';
// ── Ionic initialisation ────────────────────────────────────────────────────── // ── Ionic initialisation ──────────────────────────────────────────────────────
// ?mode=md|ios overrides (used by AppSuite preview); otherwise follow the platform. // ?mode=md|ios overrides (used by Appcakes preview); otherwise follow the platform.
const _urlMode = new URLSearchParams(window.location.search).get('mode'); const _urlMode = new URLSearchParams(window.location.search).get('mode');
const _platform = Capacitor.getPlatform(); // 'ios' | 'android' | 'web' const _platform = Capacitor.getPlatform(); // 'ios' | 'android' | 'web'
setupIonicReact({ setupIonicReact({
@@ -13,7 +13,7 @@ setupIonicReact({
}); });
// Derive the studio parent origin dynamically so this works in both dev // Derive the studio parent origin dynamically so this works in both dev
// (parent at localhost:3000) and production (parent at appcakes.qqura.com). // (parent at localhost:3000) and production (parent at studio.appcakes.dev).
const studioOrigin = (() => { const studioOrigin = (() => {
try { try {
if (document.referrer) return new URL(document.referrer).origin; if (document.referrer) return new URL(document.referrer).origin;
@@ -49,7 +49,7 @@ if (import.meta.hot) {
} }
// ── Session bridge ──────────────────────────────────────────────────────────── // ── Session bridge ────────────────────────────────────────────────────────────
// Post auth session to the AppSuite parent frame so the AI can test auth-protected // Post auth session to the Appcakes parent frame so the AI can test auth-protected
// Edge Functions. Uses import.meta.glob so Vite never errors if supabase.ts is absent. // Edge Functions. Uses import.meta.glob so Vite never errors if supabase.ts is absent.
(async () => { (async () => {
const mods = import.meta.glob('./supabase.ts', { eager: false }); const mods = import.meta.glob('./supabase.ts', { eager: false });
@@ -73,12 +73,17 @@ if (import.meta.hot) {
})(); })();
// ── Runtime error reporting ─────────────────────────────────────────────────── // ── Runtime error reporting ───────────────────────────────────────────────────
// Forward uncaught errors to the AppSuite dev server so the AI can read them. // Forward uncaught errors to the Appcakes dev server so the AI can read them.
// Extract projectId from the preview URL (/preview/{projectId}/) so errors are
// scoped per-project on the server rather than mixed into a global queue.
const _previewMatch = window.location.pathname.match(/\/preview\/([^/]+)/);
const _projectId = _previewMatch?.[1] ?? null;
function reportError(message: string, stack?: string) { function reportError(message: string, stack?: string) {
fetch(`${studioOrigin}/api/agent/runtime-error`, { fetch(`${studioOrigin}/api/agent/runtime-error`, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, stack }), body: JSON.stringify({ message, stack, projectId: _projectId }),
}).catch(() => {}); }).catch(() => {});
} }
+1152 -26
View File
File diff suppressed because it is too large Load Diff
+51
View File
@@ -0,0 +1,51 @@
// Ambient test globals for optional *.test.* files in the editor / tsc.
// No test runner is installed in the template; stubs avoid spurious TS errors.
interface Matchers<R = void> {
toBeDefined(): R;
toBe(expected: unknown): R;
toEqual(expected: unknown): R;
toBeTruthy(): R;
toBeFalsy(): R;
toBeNull(): R;
toContain(item: unknown): R;
toHaveLength(length: number): R;
toMatch(expected: string | RegExp): R;
toThrow(expected?: string | RegExp | Error): R;
not: Matchers<R>;
}
interface Expect {
<T = unknown>(actual: T): Matchers;
}
declare const expect: Expect;
type TestCaseFn = (
name: string,
fn?: () => void | Promise<void>,
timeout?: number,
) => void;
declare const test: TestCaseFn;
declare const it: TestCaseFn;
declare const describe: TestCaseFn;
declare function beforeEach(fn: () => void | Promise<void>): void;
declare function afterEach(fn: () => void | Promise<void>): void;
declare function beforeAll(fn: () => void | Promise<void>): void;
declare function afterAll(fn: () => void | Promise<void>): void;
declare module "@testing-library/react" {
export function render(
ui: unknown,
options?: object,
): {
baseElement: Element;
container: Element;
unmount(): void;
rerender(ui: unknown): void;
};
export const screen: Record<string, (...args: unknown[]) => Element>;
export function cleanup(): void;
}
+54 -121
View File
@@ -1,78 +1,78 @@
/* Ionic CSS Variables — customise to match your app's brand */ /* Ionic CSS Variables — customise to match your app's brand */
:root { :root {
--ion-color-primary: #5e6ad2; --ion-color-primary: #2667ff;
--ion-color-primary-rgb: 94, 106, 210; --ion-color-primary-rgb: 38, 103, 255;
--ion-color-primary-contrast: #ffffff; --ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255; --ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #535db9; --ion-color-primary-shade: #215be0;
--ion-color-primary-tint: #6e79d7; --ion-color-primary-tint: #3c76ff;
--ion-color-secondary: #7a89f4; --ion-color-secondary: #7cc7ff;
--ion-color-secondary-rgb: 122, 137, 244; --ion-color-secondary-rgb: 124, 199, 255;
--ion-color-secondary-contrast: #ffffff; --ion-color-secondary-contrast: #0a1020;
--ion-color-secondary-contrast-rgb: 255, 255, 255; --ion-color-secondary-contrast-rgb: 10, 16, 32;
--ion-color-secondary-shade: #6b79d7; --ion-color-secondary-shade: #6dafdf;
--ion-color-secondary-tint: #8895f5; --ion-color-secondary-tint: #89cdff;
--ion-color-tertiary: #8f78d8; --ion-color-tertiary: #9fd6ff;
--ion-color-tertiary-rgb: 143, 120, 216; --ion-color-tertiary-rgb: 159, 214, 255;
--ion-color-tertiary-contrast: #ffffff; --ion-color-tertiary-contrast: #0a1020;
--ion-color-tertiary-contrast-rgb: 255, 255, 255; --ion-color-tertiary-contrast-rgb: 10, 16, 32;
--ion-color-tertiary-shade: #7e6ac0; --ion-color-tertiary-shade: #8cbcb;
--ion-color-tertiary-tint: #9a86dc; --ion-color-tertiary-tint: #a9daff;
--ion-color-success: #3aa67a; --ion-color-success: #15803d;
--ion-color-success-rgb: 58, 166, 122; --ion-color-success-rgb: 21, 128, 61;
--ion-color-success-contrast: #ffffff; --ion-color-success-contrast: #ffffff;
--ion-color-success-contrast-rgb: 255, 255, 255; --ion-color-success-contrast-rgb: 255, 255, 255;
--ion-color-success-shade: #33926b; --ion-color-success-shade: #127136;
--ion-color-success-tint: #4eb088; --ion-color-success-tint: #2c8d50;
--ion-color-warning: #dfb25a; --ion-color-warning: #eab308;
--ion-color-warning-rgb: 223, 178, 90; --ion-color-warning-rgb: 234, 179, 8;
--ion-color-warning-contrast: #2d2520; --ion-color-warning-contrast: #0a1020;
--ion-color-warning-contrast-rgb: 45, 37, 32; --ion-color-warning-contrast-rgb: 10, 16, 32;
--ion-color-warning-shade: #c49d4f; --ion-color-warning-shade: #ce9d07;
--ion-color-warning-tint: #e2bb6b; --ion-color-warning-tint: #ecbb21;
--ion-color-danger: #cf6679; --ion-color-danger: #c2410c;
--ion-color-danger-rgb: 207, 102, 121; --ion-color-danger-rgb: 194, 65, 12;
--ion-color-danger-contrast: #ffffff; --ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255; --ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #b65a6a; --ion-color-danger-shade: #ab390a;
--ion-color-danger-tint: #d57587; --ion-color-danger-tint: #c85424;
--ion-color-dark: #1f2430; --ion-color-dark: #0a1020;
--ion-color-dark-rgb: 31, 36, 48; --ion-color-dark-rgb: 10, 16, 32;
--ion-color-dark-contrast: #ffffff; --ion-color-dark-contrast: #ffffff;
--ion-color-dark-contrast-rgb: 255, 255, 255; --ion-color-dark-contrast-rgb: 255, 255, 255;
--ion-color-dark-shade: #1b202a; --ion-color-dark-shade: #090e1c;
--ion-color-dark-tint: #353946; --ion-color-dark-tint: #222836;
--ion-color-medium: #7f8697; --ion-color-medium: #6b7a99;
--ion-color-medium-rgb: 127, 134, 151; --ion-color-medium-rgb: 107, 122, 153;
--ion-color-medium-contrast: #ffffff; --ion-color-medium-contrast: #ffffff;
--ion-color-medium-contrast-rgb: 255, 255, 255; --ion-color-medium-contrast-rgb: 255, 255, 255;
--ion-color-medium-shade: #707686; --ion-color-medium-shade: #5e6b87;
--ion-color-medium-tint: #8c93a2; --ion-color-medium-tint: #7a87a3;
--ion-color-light: #f3f5fb; --ion-color-light: #f8fbff;
--ion-color-light-rgb: 243, 245, 251; --ion-color-light-rgb: 248, 251, 255;
--ion-color-light-contrast: #1f2430; --ion-color-light-contrast: #0a1020;
--ion-color-light-contrast-rgb: 31, 36, 48; --ion-color-light-contrast-rgb: 10, 16, 32;
--ion-color-light-shade: #d6d8dd; --ion-color-light-shade: #dadde0;
--ion-color-light-tint: #f4f6fb; --ion-color-light-tint: #f9fbff;
--app-background: #ffffff; --app-background: #0a1020;
--app-surface: #ffffff; --app-surface: #121b36;
--app-surface-secondary: #f6f7fb; --app-surface-secondary: #f7faff;
--app-text-primary: #202532; --app-text-primary: #f8fbff;
--app-text-secondary: rgba(32, 37, 50, 0.68); --app-text-secondary: rgba(248, 251, 255, 0.7);
--app-text-tertiary: rgba(32, 37, 50, 0.46); --app-text-tertiary: rgba(248, 251, 255, 0.5);
--app-accent-soft: rgba(94, 106, 210, 0.12); --app-accent-soft: rgba(38, 103, 255, 0.12);
--app-accent-softer: rgba(94, 106, 210, 0.08); --app-accent-softer: rgba(38, 103, 255, 0.08);
--app-success-soft: rgba(58, 166, 122, 0.12); --app-success-soft: rgba(21, 128, 61, 0.12);
--app-shadow: 0 18px 48px rgba(82, 95, 140, 0.08); --app-shadow: 0 18px 48px rgba(0, 0, 0, 0.4);
} }
body { body {
@@ -83,70 +83,3 @@ body {
ion-content.home-content { ion-content.home-content {
--background: var(--app-background); --background: var(--app-background);
} }
.home-toolbar {
--background: rgba(255, 255, 255, 0.92);
--border-width: 0;
--border-color: transparent;
--color: var(--app-text-primary);
box-shadow: none;
backdrop-filter: blur(18px);
}
ion-header.home-header::after {
display: none;
}
.home-shell {
padding: calc(88px + var(--ion-safe-area-top)) 24px
calc(32px + var(--ion-safe-area-bottom));
display: flex;
flex-direction: column;
}
.minimal-home-shell {
min-height: 100%;
justify-content: flex-start;
}
.minimal-welcome-card {
text-align: left;
display: flex;
flex-direction: column;
gap: 10px;
}
.subtle-welcome-card {
max-width: 420px;
margin: 0;
padding: 28px 24px;
background: var(--app-surface-secondary);
border-radius: 28px;
}
.minimal-welcome-card h1 {
margin: 0;
color: var(--app-text-primary);
font-size: 32px;
line-height: 1.12;
letter-spacing: -0.03em;
font-weight: 700;
}
.minimal-subtitle {
margin: 0;
color: var(--app-text-secondary);
font-size: 15px;
line-height: 1.55;
}
@media (min-width: 680px) {
.home-shell {
max-width: 720px;
margin: 0 auto;
}
.minimal-welcome-card h1 {
font-size: 40px;
}
}
-18
View File
@@ -1,18 +0,0 @@
ext {
minSdkVersion = 24
compileSdkVersion = 36
targetSdkVersion = 36
androidxActivityVersion = '1.11.0'
androidxAppCompatVersion = '1.7.1'
androidxCoordinatorLayoutVersion = '1.3.0'
androidxCoreVersion = '1.17.0'
androidxFragmentVersion = '1.8.9'
coreSplashScreenVersion = '1.2.0'
androidxWebkitVersion = '1.14.0'
junitVersion = '4.13.2'
androidxJunitVersion = '1.3.0'
androidxEspressoCoreVersion = '3.7.0'
cordovaAndroidVersion = '14.0.1'
rgcfaIncludeGoogle = true
androidxCredentialsVersion = '1.3.0'
}
+11 -1
View File
@@ -17,8 +17,18 @@ const injectRouterBasename: Plugin = {
}, },
}; };
const hideScrollbars: Plugin = {
name: 'hide-scrollbars',
transformIndexHtml(html) {
return html.replace(
'</head>',
'<style>*{scrollbar-width:none!important;-ms-overflow-style:none!important}*::-webkit-scrollbar{display:none!important}</style></head>',
);
},
};
export default defineConfig({ export default defineConfig({
plugins: [injectRouterBasename, react()], plugins: [injectRouterBasename, hideScrollbars, react()],
server: { server: {
host: '0.0.0.0', host: '0.0.0.0',
port: 8100, port: 8100,
+2919 -64
View File
File diff suppressed because it is too large Load Diff