mirror of
https://github.com/AeThex-Corporation/AeThex-OS.git
synced 2026-04-26 01:37:19 +00:00
Merge pull request #4 from AeThex-Corporation/claude/setup-capacitor-live-reload-ws9Rg
Add Capacitor live reload support for mobile development
This commit is contained in:
commit
72e42e2eed
10 changed files with 854 additions and 545 deletions
8
android/.gitignore
vendored
8
android/.gitignore
vendored
|
|
@ -52,10 +52,10 @@ captures/
|
||||||
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
|
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
|
||||||
.idea/navEditor.xml
|
.idea/navEditor.xml
|
||||||
|
|
||||||
# Keystore files
|
# Keystore files - DO NOT commit these
|
||||||
# Uncomment the following lines if you do not want to check your keystore files in.
|
*.jks
|
||||||
#*.jks
|
*.keystore
|
||||||
#*.keystore
|
keystore.properties
|
||||||
|
|
||||||
# External native build folder generated in Android Studio 2.2 and later
|
# External native build folder generated in Android Studio 2.2 and later
|
||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
// Load keystore properties for release signing
|
||||||
|
def keystorePropertiesFile = rootProject.file('keystore.properties')
|
||||||
|
def keystoreProperties = new Properties()
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.aethex.os"
|
namespace = "com.aethex.os"
|
||||||
compileSdk = rootProject.ext.compileSdkVersion
|
compileSdk = rootProject.ext.compileSdkVersion
|
||||||
|
|
@ -16,10 +23,24 @@ android {
|
||||||
ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
|
ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
storeFile file(keystoreProperties['storeFile'])
|
||||||
|
storePassword keystoreProperties['storePassword']
|
||||||
|
keyAlias keystoreProperties['keyAlias']
|
||||||
|
keyPassword keystoreProperties['keyPassword']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled true
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
shrinkResources true
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
signingConfig signingConfigs.release
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
0
android/gradlew
vendored
Normal file → Executable file
0
android/gradlew
vendored
Normal file → Executable file
7
android/keystore.properties.example
Normal file
7
android/keystore.properties.example
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Copy this file to keystore.properties and fill in your values
|
||||||
|
# DO NOT commit keystore.properties to version control
|
||||||
|
|
||||||
|
storeFile=app/aethex-release.keystore
|
||||||
|
storePassword=your-store-password
|
||||||
|
keyAlias=aethex
|
||||||
|
keyPassword=your-key-password
|
||||||
|
|
@ -1,12 +1,22 @@
|
||||||
import type { CapacitorConfig } from '@capacitor/cli';
|
import type { CapacitorConfig } from '@capacitor/cli';
|
||||||
|
|
||||||
|
// Live reload configuration
|
||||||
|
// Set CAPACITOR_LIVE_RELOAD=true and CAPACITOR_SERVER_URL to enable
|
||||||
|
const isLiveReload = process.env.CAPACITOR_LIVE_RELOAD === 'true';
|
||||||
|
const serverUrl = process.env.CAPACITOR_SERVER_URL || 'http://192.168.1.100:5000';
|
||||||
|
|
||||||
const config: CapacitorConfig = {
|
const config: CapacitorConfig = {
|
||||||
appId: 'com.aethex.os',
|
appId: 'com.aethex.os',
|
||||||
appName: 'AeThex OS',
|
appName: 'AeThex OS',
|
||||||
webDir: 'dist/public',
|
webDir: 'dist/public',
|
||||||
server: {
|
server: {
|
||||||
androidScheme: 'https',
|
androidScheme: 'https',
|
||||||
iosScheme: 'https'
|
iosScheme: 'https',
|
||||||
|
// Live reload: point to dev server instead of bundled assets
|
||||||
|
...(isLiveReload && {
|
||||||
|
url: serverUrl,
|
||||||
|
cleartext: true, // Allow HTTP for local development
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
SplashScreen: {
|
SplashScreen: {
|
||||||
|
|
@ -42,8 +52,18 @@ const config: CapacitorConfig = {
|
||||||
android: {
|
android: {
|
||||||
allowMixedContent: true,
|
allowMixedContent: true,
|
||||||
captureInput: true,
|
captureInput: true,
|
||||||
webContentsDebuggingEnabled: true
|
webContentsDebuggingEnabled: true,
|
||||||
}
|
// Allow cleartext (HTTP) for live reload
|
||||||
|
...(isLiveReload && {
|
||||||
|
allowMixedContent: true,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
ios: {
|
||||||
|
// iOS-specific live reload settings
|
||||||
|
...(isLiveReload && {
|
||||||
|
limitsNavigationsToAppBoundDomains: false,
|
||||||
|
}),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|
|
||||||
1120
package-lock.json
generated
1120
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -12,6 +12,11 @@
|
||||||
"build:mobile": "npm run build && npx cap sync",
|
"build:mobile": "npm run build && npx cap sync",
|
||||||
"android": "npx cap open android",
|
"android": "npx cap open android",
|
||||||
"ios": "npx cap open ios",
|
"ios": "npx cap open ios",
|
||||||
|
"cap:live-reload": "tsx script/capacitor-live-reload.ts",
|
||||||
|
"cap:live-reload:android": "tsx script/capacitor-live-reload.ts --android",
|
||||||
|
"cap:live-reload:ios": "tsx script/capacitor-live-reload.ts --ios",
|
||||||
|
"cap:production": "tsx script/capacitor-production.ts",
|
||||||
|
"dev:mobile": "npm run cap:live-reload && concurrently \"npm run dev\" \"echo 'Dev server started. Open Android Studio or Xcode to run the app.'\"",
|
||||||
"start": "NODE_ENV=production node dist/index.js",
|
"start": "NODE_ENV=production node dist/index.js",
|
||||||
"check": "tsc",
|
"check": "tsc",
|
||||||
"db:push": "drizzle-kit push",
|
"db:push": "drizzle-kit push",
|
||||||
|
|
|
||||||
130
script/capacitor-live-reload.ts
Normal file
130
script/capacitor-live-reload.ts
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
#!/usr/bin/env tsx
|
||||||
|
/**
|
||||||
|
* Capacitor Live Reload Setup Script
|
||||||
|
*
|
||||||
|
* This script configures Capacitor for live reload development by:
|
||||||
|
* 1. Detecting the local network IP address
|
||||||
|
* 2. Setting environment variables for the Capacitor config
|
||||||
|
* 3. Running cap sync to update native projects
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* npx tsx script/capacitor-live-reload.ts [--port 5000] [--ip 192.168.1.100]
|
||||||
|
* npm run cap:live-reload
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { execSync, spawn } from 'child_process';
|
||||||
|
import { networkInterfaces } from 'os';
|
||||||
|
|
||||||
|
interface Options {
|
||||||
|
port: number;
|
||||||
|
ip?: string;
|
||||||
|
platform?: 'android' | 'ios' | 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLocalIP(): string {
|
||||||
|
const nets = networkInterfaces();
|
||||||
|
const results: string[] = [];
|
||||||
|
|
||||||
|
for (const name of Object.keys(nets)) {
|
||||||
|
const netList = nets[name];
|
||||||
|
if (!netList) continue;
|
||||||
|
|
||||||
|
for (const net of netList) {
|
||||||
|
// Skip internal and non-IPv4 addresses
|
||||||
|
if (net.family === 'IPv4' && !net.internal) {
|
||||||
|
results.push(net.address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer common local network ranges
|
||||||
|
const preferred = results.find(ip =>
|
||||||
|
ip.startsWith('192.168.') ||
|
||||||
|
ip.startsWith('10.') ||
|
||||||
|
ip.startsWith('172.')
|
||||||
|
);
|
||||||
|
|
||||||
|
return preferred || results[0] || 'localhost';
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseArgs(): Options {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const options: Options = {
|
||||||
|
port: 5000,
|
||||||
|
platform: 'all'
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
const arg = args[i];
|
||||||
|
const next = args[i + 1];
|
||||||
|
|
||||||
|
if (arg === '--port' && next) {
|
||||||
|
options.port = parseInt(next, 10);
|
||||||
|
i++;
|
||||||
|
} else if (arg === '--ip' && next) {
|
||||||
|
options.ip = next;
|
||||||
|
i++;
|
||||||
|
} else if (arg === '--android') {
|
||||||
|
options.platform = 'android';
|
||||||
|
} else if (arg === '--ios') {
|
||||||
|
options.platform = 'ios';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(command: string, env?: Record<string, string>): void {
|
||||||
|
console.log(`\n> ${command}\n`);
|
||||||
|
execSync(command, {
|
||||||
|
stdio: 'inherit',
|
||||||
|
env: { ...process.env, ...env }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const options = parseArgs();
|
||||||
|
const ip = options.ip || getLocalIP();
|
||||||
|
const serverUrl = `http://${ip}:${options.port}`;
|
||||||
|
|
||||||
|
console.log('========================================');
|
||||||
|
console.log(' Capacitor Live Reload Setup');
|
||||||
|
console.log('========================================');
|
||||||
|
console.log(` Server URL: ${serverUrl}`);
|
||||||
|
console.log(` Platform: ${options.platform}`);
|
||||||
|
console.log('========================================\n');
|
||||||
|
|
||||||
|
// Set environment variables for Capacitor config
|
||||||
|
const env = {
|
||||||
|
CAPACITOR_LIVE_RELOAD: 'true',
|
||||||
|
CAPACITOR_SERVER_URL: serverUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sync Capacitor with live reload configuration
|
||||||
|
console.log('Syncing Capacitor with live reload configuration...');
|
||||||
|
|
||||||
|
if (options.platform === 'all') {
|
||||||
|
run('npx cap sync', env);
|
||||||
|
} else {
|
||||||
|
run(`npx cap sync ${options.platform}`, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n========================================');
|
||||||
|
console.log(' Live Reload Setup Complete!');
|
||||||
|
console.log('========================================');
|
||||||
|
console.log('\nNext steps:');
|
||||||
|
console.log(` 1. Start the dev server: npm run dev`);
|
||||||
|
console.log(` 2. Open your IDE:`);
|
||||||
|
console.log(` - Android Studio: npm run android`);
|
||||||
|
console.log(` - Xcode: npm run ios`);
|
||||||
|
console.log(` 3. Run the app on your device`);
|
||||||
|
console.log('\nThe app will connect to your dev server at:');
|
||||||
|
console.log(` ${serverUrl}`);
|
||||||
|
console.log('\nMake sure:');
|
||||||
|
console.log(' - Your device is on the same network as this machine');
|
||||||
|
console.log(' - Firewall allows connections on port ' + options.port);
|
||||||
|
console.log(' - The dev server is running before launching the app');
|
||||||
|
console.log('========================================\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
59
script/capacitor-production.ts
Normal file
59
script/capacitor-production.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
#!/usr/bin/env tsx
|
||||||
|
/**
|
||||||
|
* Capacitor Production Sync Script
|
||||||
|
*
|
||||||
|
* Reverts Capacitor configuration from live reload mode back to production mode.
|
||||||
|
* This rebuilds the web assets and syncs them to native projects.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* npx tsx script/capacitor-production.ts [--android | --ios]
|
||||||
|
* npm run cap:production
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
|
function parseArgs(): { platform: 'android' | 'ios' | 'all' } {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (args.includes('--android')) return { platform: 'android' };
|
||||||
|
if (args.includes('--ios')) return { platform: 'ios' };
|
||||||
|
return { platform: 'all' };
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(command: string): void {
|
||||||
|
console.log(`\n> ${command}\n`);
|
||||||
|
execSync(command, { stdio: 'inherit' });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const { platform } = parseArgs();
|
||||||
|
|
||||||
|
console.log('========================================');
|
||||||
|
console.log(' Capacitor Production Build');
|
||||||
|
console.log('========================================');
|
||||||
|
console.log(` Platform: ${platform}`);
|
||||||
|
console.log('========================================\n');
|
||||||
|
|
||||||
|
// Build production assets
|
||||||
|
console.log('Building production assets...');
|
||||||
|
run('npm run build');
|
||||||
|
|
||||||
|
// Sync without live reload environment variables
|
||||||
|
console.log('\nSyncing Capacitor (production mode)...');
|
||||||
|
if (platform === 'all') {
|
||||||
|
run('npx cap sync');
|
||||||
|
} else {
|
||||||
|
run(`npx cap sync ${platform}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n========================================');
|
||||||
|
console.log(' Production Build Complete!');
|
||||||
|
console.log('========================================');
|
||||||
|
console.log('\nThe app is now configured to use bundled assets.');
|
||||||
|
console.log('Open your IDE to build and run:');
|
||||||
|
console.log(' - Android Studio: npm run android');
|
||||||
|
console.log(' - Xcode: npm run ios');
|
||||||
|
console.log('========================================\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
|
|
@ -44,11 +44,20 @@ export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
cors: true,
|
cors: true,
|
||||||
host: "0.0.0.0",
|
host: "0.0.0.0",
|
||||||
hmr: {
|
port: 5000,
|
||||||
host: "orange-journey-wvwxgw4r6vrf577-5000.app.github.dev",
|
// HMR configuration - use different settings for Capacitor live reload vs cloud dev
|
||||||
protocol: "wss",
|
hmr: process.env.CAPACITOR_LIVE_RELOAD === 'true'
|
||||||
clientPort: 443,
|
? {
|
||||||
},
|
// For Capacitor live reload: use default WebSocket on same host
|
||||||
|
protocol: 'ws',
|
||||||
|
host: undefined, // Uses request host automatically
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
// For cloud development (GitHub Codespaces, Replit, etc.)
|
||||||
|
host: "orange-journey-wvwxgw4r6vrf577-5000.app.github.dev",
|
||||||
|
protocol: "wss",
|
||||||
|
clientPort: 443,
|
||||||
|
},
|
||||||
allowedHosts: true,
|
allowedHosts: true,
|
||||||
fs: {
|
fs: {
|
||||||
strict: true,
|
strict: true,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue