No Internet Connection — Redirecting to offline page...

AppForge Studio

Convert any website into a native-ready mobile app • Upload icon & splash screen • Smart offline handling

App Branding

App Package

How it works: Generate a zip containing an offline-capable web app wrapper (HTML/JS/CSS + manifest). The generated app includes automatic offline detection + redirect, your icon & splash screen. Ready to wrap with Capacitor / PWABuilder or use as PWA.

Current saved assets:

No assets uploaded yet
Built-in offline detection: The generated app will show "No Internet" screen automatically & redirect when offline.
⭐ After ZIP generation, you can use Capacitor, Cordova, or simply host as PWA.
Offline

📡 No Internet

Please check your connection and try again.

'; file_put_contents('offline.html', $offlineHtml); } // ---------- ENDPOINT: api_upload_assets.php ---------- if ($path == 'api_upload_assets.php') { header('Content-Type: application/json'); $response = ['success' => false]; $saved_icon = ''; $saved_splash = ''; // Save app_url in session? we can store in a json file $config_file = 'app_config.json'; $config = []; if (file_exists($config_file)) { $config = json_decode(file_get_contents($config_file), true); } if (isset($_POST['app_url'])) { $config['website_url'] = $_POST['app_url']; } // handle icon upload if (isset($_FILES['app_icon']) && $_FILES['app_icon']['error'] === UPLOAD_ERR_OK) { $ext = pathinfo($_FILES['app_icon']['name'], PATHINFO_EXTENSION); $icon_name = 'icon_' . time() . '.' . $ext; $target = $upload_dir . $icon_name; if (move_uploaded_file($_FILES['app_icon']['tmp_name'], $target)) { $config['icon_path'] = $target; $saved_icon = $target; $response['icon_url'] = $target; } else { $response['error'] = 'Icon upload failed'; echo json_encode($response); exit; } } if (isset($_FILES['splash_image']) && $_FILES['splash_image']['error'] === UPLOAD_ERR_OK) { $ext = pathinfo($_FILES['splash_image']['name'], PATHINFO_EXTENSION); $splash_name = 'splash_' . time() . '.' . $ext; $target = $upload_dir . $splash_name; if (move_uploaded_file($_FILES['splash_image']['tmp_name'], $target)) { $config['splash_path'] = $target; $saved_splash = $target; $response['splash_url'] = $target; } else { $response['error'] = 'Splash upload failed'; echo json_encode($response); exit; } } file_put_contents($config_file, json_encode($config)); $response['success'] = true; echo json_encode($response); exit; } // ---------- ENDPOINT: api_get_assets.php ---------- if ($path == 'api_get_assets.php') { header('Content-Type: application/json'); $config_file = 'app_config.json'; $data = ['icon' => '', 'splash' => '', 'website_url' => '']; if (file_exists($config_file)) { $cfg = json_decode(file_get_contents($config_file), true); if (isset($cfg['icon_path'])) $data['icon'] = $cfg['icon_path']; if (isset($cfg['splash_path'])) $data['splash'] = $cfg['splash_path']; if (isset($cfg['website_url'])) $data['website_url'] = $cfg['website_url']; } echo json_encode($data); exit; } // ---------- ENDPOINT: api_generate_app.php (returns ZIP) ---------- if ($path == 'api_generate_app.php') { $input = json_decode(file_get_contents('php://input'), true); if (!$input) die('Invalid request'); $appName = preg_replace('/[^a-zA-Z0-9 ]/', '', $input['app_name']); $shortName = preg_replace('/[^a-zA-Z0-9]/', '', $input['short_name']); $websiteUrl = filter_var($input['website_url'], FILTER_VALIDATE_URL); $iconRel = isset($input['icon_path']) ? $input['icon_path'] : ''; $splashRel = isset($input['splash_path']) ? $input['splash_path'] : ''; if (!$websiteUrl) die('Invalid website URL'); // Create temporary directory for building $tempDir = sys_get_temp_dir() . '/app_build_' . uniqid(); mkdir($tempDir); // Create index.html for the wrapper app (offline detection + splash + webview) $indexContent = ''.$appName.'
splash

⚠️ No Internet Connection

Please reconnect and try again.

'; file_put_contents($tempDir . '/index.html', $indexContent); // manifest.json $manifest = [ 'name' => $appName, 'short_name' => $shortName, 'start_url' => '/index.html', 'display' => 'standalone', 'background_color' => '#ffffff', 'icons' => [] ]; if ($iconRel && file_exists($iconRel)) { $manifest['icons'][] = ['src' => $iconRel, 'sizes' => '512x512', 'type' => 'image/png']; } else { $manifest['icons'][] = ['src' => 'data:image/svg+xml,...', 'sizes' => 'any']; } file_put_contents($tempDir . '/manifest.json', json_encode($manifest)); // copy assets if ($iconRel && file_exists($iconRel)) copy($iconRel, $tempDir . '/' . basename($iconRel)); if ($splashRel && file_exists($splashRel)) copy($splashRel, $tempDir . '/' . basename($splashRel)); // create ZIP $zipFile = sys_get_temp_dir() . '/' . $shortName . '_app.zip'; $zip = new ZipArchive(); if ($zip->open($zipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) { $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($tempDir), RecursiveIteratorIterator::LEAVES_ONLY); foreach ($files as $name => $file) { if (!$file->isDir()) { $filePath = $file->getRealPath(); $relativePath = substr($filePath, strlen($tempDir) + 1); $zip->addFile($filePath, $relativePath); } } $zip->close(); } // clean temp array_map('unlink', glob("$tempDir/*.*")); rmdir($tempDir); header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename="' . $shortName . '_app.zip"'); header('Content-Length: ' . filesize($zipFile)); readfile($zipFile); unlink($zipFile); exit; } // If none matched, show main HTML (already output before PHP blocks). But we need to avoid printing again. // Since this file already outputs HTML before PHP, the rest is fine. We just ensure no extra output. ?>