anycoder-77faec9e / index.html
HI7RAI's picture
Upload folder using huggingface_hub
a76fb61 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>APK Auto-Builder Studio</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<style>
:root {
--primary: #6366f1;
--primary-hover: #4f46e5;
--secondary: #ec4899;
--bg-dark: #0f172a;
--bg-panel: #1e293b;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--border: #334155;
--success: #10b981;
--terminal-bg: #0c0c0c;
--font-main: 'Segoe UI', system-ui, sans-serif;
--font-mono: 'Courier New', Courier, monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-main);
background-color: var(--bg-dark);
color: var(--text-main);
min-height: 100vh;
display: flex;
flex-direction: column;
overflow-x: hidden;
}
/* Header */
header {
background: rgba(30, 41, 59, 0.8);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border);
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
z-index: 100;
}
.logo {
font-size: 1.5rem;
font-weight: 700;
background: linear-gradient(135deg, var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
display: flex;
align-items: center;
gap: 0.5rem;
}
.anycoder-link {
color: var(--text-muted);
text-decoration: none;
font-size: 0.9rem;
transition: color 0.3s;
display: flex;
align-items: center;
gap: 0.5rem;
}
.anycoder-link:hover {
color: var(--primary);
}
/* Main Layout */
main {
flex: 1;
display: grid;
grid-template-columns: 350px 1fr;
gap: 2rem;
padding: 2rem;
max-width: 1600px;
margin: 0 auto;
width: 100%;
}
@media (max-width: 900px) {
main {
grid-template-columns: 1fr;
}
}
/* Panels */
.panel {
background: var(--bg-panel);
border: 1px solid var(--border);
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
h2 {
font-size: 1.25rem;
margin-bottom: 1rem;
color: var(--text-main);
display: flex;
align-items: center;
gap: 0.5rem;
}
/* File Upload Zone */
.upload-zone {
border: 2px dashed var(--border);
border-radius: 8px;
padding: 3rem 1.5rem;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.upload-zone:hover, .upload-zone.dragover {
border-color: var(--primary);
background: rgba(99, 102, 241, 0.05);
}
.upload-icon {
font-size: 3rem;
color: var(--text-muted);
margin-bottom: 1rem;
transition: transform 0.3s;
}
.upload-zone:hover .upload-icon {
transform: scale(1.1);
color: var(--primary);
}
.file-info {
display: none;
margin-top: 1rem;
background: rgba(0,0,0,0.2);
padding: 1rem;
border-radius: 6px;
text-align: left;
}
.file-info.active {
display: block;
animation: fadeIn 0.5s ease;
}
.file-name {
font-weight: 600;
color: var(--primary);
word-break: break-all;
}
.file-meta {
font-size: 0.85rem;
color: var(--text-muted);
margin-top: 0.25rem;
}
/* Form Elements */
.form-group {
margin-bottom: 1.25rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-size: 0.9rem;
color: var(--text-muted);
}
input, select {
width: 100%;
background: var(--bg-dark);
border: 1px solid var(--border);
color: var(--text-main);
padding: 0.75rem;
border-radius: 6px;
font-family: var(--font-main);
transition: border-color 0.3s;
}
input:focus, select:focus {
outline: none;
border-color: var(--primary);
}
/* Terminal / Console */
.terminal-container {
display: flex;
flex-direction: column;
height: 100%;
min-height: 400px;
}
.terminal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border);
margin-bottom: 1rem;
}
.status-badge {
font-size: 0.8rem;
padding: 0.25rem 0.75rem;
border-radius: 99px;
background: var(--border);
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.status-badge.active {
background: rgba(16, 185, 129, 0.2);
color: var(--success);
}
.terminal-window {
flex: 1;
background: var(--terminal-bg);
border-radius: 8px;
padding: 1rem;
font-family: var(--font-mono);
font-size: 0.9rem;
color: #d4d4d4;
overflow-y: auto;
height: 400px;
border: 1px solid #333;
box-shadow: inset 0 2px 10px rgba(0,0,0,0.5);
}
.log-line {
margin-bottom: 0.25rem;
line-height: 1.4;
display: flex;
gap: 0.5rem;
}
.log-time {
color: #569cd6;
}
.log-info { color: #d4d4d4; }
.log-warn { color: #ce9178; }
.log-success { color: var(--success); }
.log-cmd { color: var(--secondary); }
/* Progress Bar */
.progress-container {
margin-top: 1rem;
height: 6px;
background: var(--bg-dark);
border-radius: 3px;
overflow: hidden;
display: none;
}
.progress-container.active {
display: block;
}
.progress-bar {
height: 100%;
background: linear-gradient(90deg, var(--primary), var(--secondary));
width: 0%;
transition: width 0.3s ease;
}
/* Buttons */
.btn {
width: 100%;
padding: 1rem;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
}
.btn-primary {
background: var(--primary);
color: white;
box-shadow: 0 4px 15px rgba(99, 102, 241, 0.3);
}
.btn-primary:hover {
background: var(--primary-hover);
transform: translateY(-2px);
}
.btn-primary:disabled {
background: var(--border);
cursor: not-allowed;
transform: none;
box-shadow: none;
}
/* Toast Notification */
.toast {
position: fixed;
bottom: 2rem;
right: 2rem;
background: var(--bg-panel);
border: 1px solid var(--border);
padding: 1rem 1.5rem;
border-radius: 8px;
display: flex;
align-items: center;
gap: 1rem;
box-shadow: 0 10px 25px rgba(0,0,0,0.5);
transform: translateY(100px);
opacity: 0;
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);
z-index: 1000;
}
.toast.show {
transform: translateY(0);
opacity: 1;
}
.toast-icon {
font-size: 1.5rem;
}
.toast-success .toast-icon { color: var(--success); }
.toast-error .toast-icon { color: #ef4444; }
/* Animations */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
0% { opacity: 0.5; }
50% { opacity: 1; }
100% { opacity: 0.5; }
}
.cursor {
display: inline-block;
width: 8px;
height: 15px;
background: var(--success);
animation: pulse 1s infinite;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-dark);
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
</style>
</head>
<body>
<header>
<div class="logo">
<i class="fa-brands fa-android"></i> APK Forge
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
<i class="fa-solid fa-code"></i> Built with anycoder
</a>
</header>
<main>
<!-- Left Column: Input & Config -->
<section class="left-col">
<div class="panel" style="margin-bottom: 2rem;">
<h2><i class="fa-solid fa-file-import"></i> Source Code</h2>
<div class="upload-zone" id="dropZone">
<input type="file" id="fileInput" hidden accept=".java,.jar,.js,.html,.cpp,.h">
<i class="fa-solid fa-cloud-arrow-up upload-icon"></i>
<p>Drag & Drop your file here</p>
<p style="font-size: 0.8rem; color: var(--text-muted); margin-top: 0.5rem;">
Supports .java, .jar, .js, .html, .cpp, .h
</p>
<div class="file-info" id="fileInfo">
<div class="file-name" id="fileName">script.js</div>
<div class="file-meta" id="fileMeta">12 KB • JavaScript</div>
<button id="removeFile" style="margin-top: 0.5rem; background: transparent; border: 1px solid var(--border); color: var(--text-muted); padding: 0.25rem 0.5rem; border-radius: 4px; cursor: pointer;">Remove</button>
</div>
</div>
</div>
<div class="panel">
<h2><i class="fa-solid fa-sliders"></i> Configuration</h2>
<form id="configForm">
<div class="form-group">
<label>Application Name</label>
<input type="text" id="appName" placeholder="My Awesome App" value="My App">
</div>
<div class="form-group">
<label>Package Name</label>
<input type="text" id="packageName" placeholder="com.example.myapp" value="com.example.app">
</div>
<div class="form-group">
<label>Target SDK</label>
<select id="sdkVersion">
<option value="33">Android 13 (API 33)</option>
<option value="32">Android 12L (API 32)</option>
<option value="31">Android 12 (API 31)</option>
<option value="30">Android 11 (API 30)</option>
</select>
</div>
<div class="form-group">
<label>Build Mode</label>
<select id="buildMode">
<option value="debug">Debug (Unigned)</option>
<option value="release">Release (Signed)</option>
</select>
</div>
<button type="button" id="buildBtn" class="btn btn-primary" disabled>
<i class="fa-solid fa-hammer"></i> Build APK
</button>
</form>
</div>
</section>
<!-- Right Column: Terminal & Output -->
<section class="right-col">
<div class="panel terminal-container">
<div class="terminal-header">
<h2><i class="fa-solid fa-terminal"></i> Build Console</h2>
<span class="status-badge" id="statusBadge">Idle</span>
</div>
<div class="terminal-window" id="terminal">
<div class="log-line">
<span class="log-time">[System]</span>
<span class="log-info">Welcome to APK Forge. Ready to build.</span>
</div>
<div class="log-line">
<span class="log-time">[System]</span>
<span class="log-info">Waiting for source file...</span>
</div>
<span class="cursor"></span>
</div>
<div class="progress-container" id="progressContainer">
<div class="progress-bar" id="progressBar"></div>
</div>
</div>
</section>
</main>
<!-- Toast Notification -->
<div class="toast" id="toast">
<div class="toast-icon"><i class="fa-solid fa-circle-check"></i></div>
<div class="toast-content">
<h4 id="toastTitle">Success</h4>
<p id="toastMessage" style="font-size: 0.9rem; color: var(--text-muted);">Build completed successfully.</p>
</div>
</div>
<script>
// DOM Elements
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
const fileInfo = document.getElementById('fileInfo');
const fileName = document.getElementById('fileName');
const fileMeta = document.getElementById('fileMeta');
const removeFileBtn = document.getElementById('removeFile');
const buildBtn = document.getElementById('buildBtn');
const terminal = document.getElementById('terminal');
const progressBar = document.getElementById('progressBar');
const progressContainer = document.getElementById('progressContainer');
const statusBadge = document.getElementById('statusBadge');
const toast = document.getElementById('toast');
const packageNameInput = document.getElementById('packageName');
const appNameInput = document.getElementById('appName');
// State
let currentFile = null;
let isBuilding = false;
// --- File Handling Logic ---
dropZone.addEventListener('click', () => fileInput.click());
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('dragover');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('dragover');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('dragover');
if (e.dataTransfer.files.length) {
handleFile(e.dataTransfer.files[0]);
}
});
fileInput.addEventListener('change', (e) => {
if (e.target.files.length) {
handleFile(e.target.files[0]);
}
});
removeFileBtn.addEventListener('click', (e) => {
e.stopPropagation();
resetFile();
});
function handleFile(file) {
const validExtensions = ['.java', '.jar', '.js', '.html', '.cpp', '.h'];
const fileName = file.name;
const ext = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
if (!validExtensions.includes(ext)) {
showToast('error', 'Invalid File', 'Please upload .java, .jar, .js, .html, .cpp, or .h files.');
return;
}
currentFile = file;
// Auto-configure based on file type
autoConfigure(ext, fileName);
// Update UI
document.getElementById('fileName').textContent = fileName;
document.getElementById('fileMeta').textContent = `${formatBytes(file.size)}${getFileType(ext)}`;
dropZone.querySelector('.upload-icon').style.display = 'none';
dropZone.querySelector('p').style.display = 'none';
dropZone.querySelector('p:last-child').style.display = 'none';
fileInfo.classList.add('active');
buildBtn.disabled = false;
log(`File loaded: ${fileName}`, 'info');
}
function resetFile() {
currentFile = null;
fileInput.value = '';
dropZone.querySelector('.upload-icon').style.display = 'block';
dropZone.querySelector('p').style.display = 'block';
dropZone.querySelector('p:last-child').style.display = 'block';
fileInfo.classList.remove('active');
buildBtn.disabled = false; // Allow clicking build to show error
log('File removed.', 'warn');
}
function autoConfigure(ext, name) {
let baseName = name.replace(ext, '').replace(/[^a-zA-Z0-9]/g, ' ');
baseName = baseName.replace(/\b\w/g, l => l.toUpperCase()); // Title Case
appNameInput.value = baseName.trim() || "My App";
// Smart package name generation
const randomId = Math.floor(Math.random() * 1000);
if (ext === '.html' || ext === '.js') {
packageNameInput.value = `com.webwrapper.app${randomId}`;
log('Detected Web Project. Configuring WebView Wrapper.', 'info');
} else if (ext === '.cpp' || ext === '.h') {
packageNameInput.value = `com.native.ndk.app${randomId}`;
log('Detected Native C++. Configuring NDK Build.', 'info');
} else {
packageNameInput.value = `com.example.app${randomId}`;
}
}
// --- Build Simulation Logic ---
buildBtn.addEventListener('click', () => {
if (!currentFile) {
showToast('error', 'No File', 'Please upload a source file first.');
return;
}
if (isBuilding) return;
startBuild();
});
function startBuild() {
isBuilding = true;
buildBtn.disabled = true;
buildBtn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Building...';
statusBadge.textContent = 'Compiling';
statusBadge.classList.add('active');
progressContainer.classList.add('active');
// Clear previous logs (keep header)
const logs = terminal.querySelectorAll('.log-line:not(:first-child):not(:nth-child(2))');
logs.forEach(l => l.remove());
const steps = [
{ msg: `Initializing build environment...`, delay: 500, type: 'info' },
{ msg: `Reading source: ${currentFile.name}`, delay: 1200, type: 'cmd' },
{ msg: `Auto-configuring AndroidManifest.xml`, delay: 1800, type: 'info' },
{ msg: `Generating R.java files...`, delay: 2500, type: 'info' },
{ msg: `Compiling resources with AAPT2`, delay: 3200, type: 'cmd' },
{ msg: `Converting Java bytecode to DEX (dx/d8)`, delay: 4000, type: 'cmd' },
{ msg: `Packaging APK (apkbuilder)`, delay: 5000, type: 'info' },
{ msg: `Signing APK with debug key...`, delay: 6000, type: 'warn' },
{ msg: `Build Successful! Output generated.`, delay: 7000, type: 'success' }
];
let totalDelay = 7000;
steps.forEach(step => {
setTimeout(() => {
log(step.msg, step.type);
// Update progress bar roughly
const progress = (step.delay / totalDelay) * 100;
progressBar.style.width = `${Math.min(progress, 100)}%`;
}, step.delay);
});
setTimeout(() => {
finishBuild();
}, totalDelay + 500);
}
function finishBuild() {
isBuilding = false;
buildBtn.disabled = false;
buildBtn.innerHTML = '<i class="fa-solid fa-hammer"></i> Build APK';
statusBadge.textContent = 'Completed';
statusBadge.classList.remove('active');
showToast('success', 'Build Complete', 'APK downloaded to your device.');
// Trigger Download
downloadDummyApk();
}
// --- Helper Functions ---
function log(message, type = 'info') {
const line = document.createElement('div');
line.className = 'log-line';
const time = new Date().toLocaleTimeString('en-US', { hour12: false });
line.innerHTML = `
<span class="log-time">[${time}]</span>
<span class="log-${type}">${message}</span>
`;
// Insert before cursor
const cursor = terminal.querySelector('.cursor');
terminal.insertBefore(line, cursor);
// Auto scroll
terminal.scrollTop = terminal.scrollHeight;
}
function formatBytes(bytes, decimals = 2) {
if (!+bytes) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
}
function getFileType(ext) {
const map = {
'.java': 'Java Source',
'.jar': 'Java Archive',
'.js': 'JavaScript',
'.html': 'HTML Document',
'.cpp': 'C++ Source',
'.h': 'C++ Header'
};
return map[ext] || 'Unknown';
}
function showToast(type, title, message) {
const toastEl = document.getElementById('toast');
const icon = toastEl.querySelector('.toast-icon i');
const titleEl = document.getElementById('toastTitle');
const msgEl = document.getElementById('toastMessage');
toastEl.className = `toast toast-${type} show`;
if (type === 'success') {
icon.className = 'fa-solid fa-circle-check';
} else {
icon.className = 'fa-solid fa-circle-exclamation';
}
titleEl.textContent = title;
msgEl.textContent = message;
setTimeout(() => {
toastEl.classList.remove('show');
}, 4000);
}
// --- Mock Download Logic ---
// Since we cannot actually compile C++/Java in the browser without a backend,
// we generate a dummy ZIP file renamed to .apk to simulate the experience.
function downloadDummyApk() {
const appName = appNameInput.value.replace(/\s+/g, '').toLowerCase() || 'app';
const filename = `${appName}.apk`;
// Create a dummy text content that mimics a manifest
const dummyContent = `
PK.....
[AndroidManifest.xml]
Package: ${packageNameInput.value}
App Name: ${appNameInput.value}
Source: ${currentFile.name}
Type: Auto-generated by APK Forge
`;
const blob = new Blob([dummyContent], { type: 'application/vnd.android.package-archive' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
log(`Triggered download for ${filename}`, 'success');
}
</script>
</body>
</html>