Add Capacitor live reload for quick device testing

- Update capacitor.config.ts to support live reload via environment variables
  (CAPACITOR_LIVE_RELOAD and CAPACITOR_SERVER_URL)
- Add script/capacitor-live-reload.ts to auto-detect local IP and configure sync
- Add script/capacitor-production.ts to revert to production bundled assets
- Add npm scripts: cap:live-reload, cap:production, dev:mobile
- Update vite.config.ts to use appropriate HMR settings for local vs cloud dev

https://claude.ai/code/session_01WzGEr7t8hWFyiANo22iokS
This commit is contained in:
Claude 2026-01-31 21:12:44 +00:00
parent c963ed51ee
commit 2278fa2849
No known key found for this signature in database
5 changed files with 231 additions and 8 deletions

View file

@ -1,12 +1,22 @@
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 = {
appId: 'com.aethex.os',
appName: 'AeThex OS',
webDir: 'dist/public',
server: {
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: {
SplashScreen: {
@ -42,8 +52,18 @@ const config: CapacitorConfig = {
android: {
allowMixedContent: 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;

View file

@ -12,6 +12,11 @@
"build:mobile": "npm run build && npx cap sync",
"android": "npx cap open android",
"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",
"check": "tsc",
"db:push": "drizzle-kit push",

View 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);

View 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);

View file

@ -44,7 +44,16 @@ export default defineConfig({
server: {
cors: true,
host: "0.0.0.0",
hmr: {
port: 5000,
// HMR configuration - use different settings for Capacitor live reload vs cloud dev
hmr: process.env.CAPACITOR_LIVE_RELOAD === 'true'
? {
// 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,