-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuilder.html
More file actions
276 lines (238 loc) · 14.1 KB
/
builder.html
File metadata and controls
276 lines (238 loc) · 14.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Builder | Baracuda Mini Apps</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Lexend:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<link rel="icon" type="image/png" href="assets/images/logo.png">
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
'spixi': '#17334F',
'spixi-dark': '#F0F2F4',
'accent': '#24BBFF'
},
fontFamily: {
'lexend': ['Lexend', 'sans-serif'],
}
}
}
}
</script>
<style>
body {
font-family: 'Lexend', sans-serif;
}
.bg-pattern-overlay {
background-image: url('assets/images/bg-pattern.png');
background-repeat: repeat;
opacity: 0.05;
}
</style>
</head>
<body class="bg-gray-50 dark:bg-[#090B0D] text-spixi dark:text-spixi-dark font-lexend flex flex-col min-h-screen">
<div class="fixed inset-0 bg-pattern-overlay pointer-events-none z-0"></div>
<!-- Navbar -->
<header class="bg-[#090B0D]/80 backdrop-blur-md border-b border-gray-800 sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex justify-between items-center py-4">
<div class="flex items-center">
<a href="index.html" class="flex items-center space-x-3">
<img src="assets/images/logo.png" alt="Baracuda Logo" class="h-10 w-auto">
<span class="text-2xl font-bold text-white tracking-tight">Baracuda</span>
</a>
<nav class="hidden md:flex ml-10 space-x-8">
<a href="index.html" class="text-gray-300 hover:text-accent transition-colors">Home</a>
<a href="builder.html" class="text-accent hover:text-accent transition-colors">Builder</a>
<a href="howitworks.html" class="text-gray-300 hover:text-accent transition-colors">How it works</a>
</nav>
</div>
<!-- Hamburger Button -->
<button id="navbar-hamburger-btn" type="button"
class="md:hidden inline-flex items-center justify-center p-2 w-10 h-10 text-sm text-gray-500 rounded-lg hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600">
<span class="sr-only">Open main menu</span>
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M1 1h15M1 7h15M1 13h15" />
</svg>
</button>
</div>
</header>
<!-- Full Screen Mobile Menu Overlay -->
<div id="navbar-hamburger"
class="hidden fixed inset-0 z-[60] bg-gray-900/95 backdrop-blur-xl flex-col justify-center items-center">
<button id="navbar-hamburger-close" class="absolute top-6 right-6 text-gray-400 hover:text-white">
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
<div class="flex flex-col md:flex-row gap-10 md:gap-40 text-center">
<ul class="flex flex-col gap-6">
<li><a href="index.html" class="text-4xl font-bold text-white hover:text-accent">Home</a></li>
<li><a href="builder.html" class="text-4xl font-bold text-white hover:text-accent">Builder</a></li>
<li><a href="howitworks.html" class="text-4xl font-bold text-white hover:text-accent">How it works</a>
</li>
<li><a href="howitworks.html#faq" class="text-4xl font-bold text-white hover:text-accent">FAQ</a></li>
</ul>
<ul class="flex flex-col gap-6">
<li><a href="privacy.html" class="text-4xl font-bold text-white hover:text-accent">Privacy Policy</a>
</li>
<li><a href="terms.html" class="text-4xl font-bold text-white hover:text-accent">Terms of Use</a></li>
<li><a href="https://www.ixian.io/" target="_blank"
class="text-4xl font-bold text-white hover:text-accent">Ixian Website</a></li>
<li><a href="https://www.ixilabs.co.uk/" target="_blank"
class="text-4xl font-bold text-white hover:text-accent">Ixi Labs Website</a></li>
</ul>
</div>
</div>
<main class="flex-grow relative z-10 pt-10 pb-20">
<div class="pt-6 pb-4 px-4 md:px-8 max-w-4xl mx-auto">
<h1 class="font-lexend text-3xl md:text-5xl font-semibold text-white leading-normal text-center mb-8">
Mini App Packer
</h1>
<p class="text-center text-gray-400 max-w-2xl mx-auto mb-16">
Pack specific Spixi Mini App folder directly in the browser.
</p>
<div class="bg-[#12171F] border border-gray-800 rounded-2xl p-8">
<div class="space-y-6">
<div
class="bg-[#090B0D] border-2 border-dashed border-gray-700 rounded-xl p-8 text-center transition-colors hover:border-accent group relative min-h-[200px] flex flex-col justify-center items-center">
<input type="file" id="folderInput" webkitdirectory directory multiple
class="absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10" />
<div class="flex flex-col items-center justify-center pointer-events-none">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor"
class="w-16 h-16 text-gray-500 group-hover:text-accent mb-4 transition-colors">
<path stroke-linecap="round" stroke-linejoin="round"
d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" />
</svg>
<p class="text-gray-300 font-medium text-lg">Drop your app folder here</p>
<p class="text-gray-500 text-sm mt-2">Must contain: appinfo.spixi, icon.png, app/</p>
</div>
</div>
<div id="packerStatus"
class="hidden p-4 rounded-lg bg-blue-900/20 text-blue-200 text-sm text-center"></div>
<div id="downloadArea" class="hidden flex flex-col items-center gap-6 mt-8">
<h3 class="text-xl font-semibold text-white flex items-center gap-2">
<span class="text-green-500">✓</span> Packing Complete
</h3>
<div class="w-full max-w-sm">
<a id="downloadZip"
class="block w-full bg-[#1F2937] hover:bg-[#374151] border border-gray-700 text-white font-medium py-4 px-6 rounded-xl text-center transition-all cursor-pointer flex flex-col items-center justify-center gap-2 group">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor"
class="w-8 h-8 text-accent group-hover:scale-110 transition-transform">
<path stroke-linecap="round" stroke-linejoin="round"
d="M20.25 7.5l-.625 10.632a2.25 2.25 0 01-2.247 2.118H6.622a2.25 2.25 0 01-2.247-2.118L3.75 7.5m8.25 3.75h3.75M12 15.75h3.75M12 7.5V3.75m0 3.75H8.25m4.125 0a2.25 2.25 0 11-4.5 0m4.5 0a2.25 2.25 0 11-4.5 0m-7.5 7.5h15" />
</svg>
<span>Download ZIP Archive</span>
<span class="text-xs text-gray-500">Ready to distribute</span>
</a>
</div>
</div>
</div>
</div>
<div class="mt-8 text-sm text-gray-500 text-center">
<p>Upload these files to your web host to distribute your Mini App.</p>
</div>
</div>
</main>
<footer class="bg-[#090B0D] border-t border-gray-800 py-12 relative z-10">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center text-gray-500 text-sm">
<p>© 2025 Spixi. All rights reserved.</p>
<div class="mt-4 flex justify-center gap-6">
<a href="privacy.html" class="hover:text-accent">Privacy Policy</a>
<a href="terms.html" class="hover:text-accent">Terms of Use</a>
</div>
</div>
</footer>
<script>
// Hamburger Menu Logic
const hamburgerBtn = document.getElementById('navbar-hamburger-btn');
const hamburgerMenu = document.getElementById('navbar-hamburger');
const hamburgerClose = document.getElementById('navbar-hamburger-close');
function toggleMenu() {
hamburgerMenu.classList.toggle('hidden');
hamburgerMenu.classList.toggle('flex');
document.body.classList.toggle('overflow-hidden');
}
if (hamburgerBtn) hamburgerBtn.addEventListener('click', toggleMenu);
if (hamburgerClose) hamburgerClose.addEventListener('click', toggleMenu);
// Close menu when clicking a link
hamburgerMenu.querySelectorAll('a').forEach(link => {
link.addEventListener('click', () => {
if (!hamburgerMenu.classList.contains('hidden')) toggleMenu();
});
});
// --- Packer Logic ---
document.getElementById('folderInput').addEventListener('change', async function (e) {
const files = Array.from(e.target.files);
const statusEl = document.getElementById('packerStatus');
const downloadArea = document.getElementById('downloadArea');
const downloadLink = document.getElementById('downloadZip');
if (files.length === 0) return;
statusEl.classList.remove('hidden');
statusEl.textContent = 'Analyzing files...';
downloadArea.classList.add('hidden');
try {
// Find root based on appinfo.spixi
const appInfoFile = files.find(f => f.webkitRelativePath.endsWith('appinfo.spixi') || f.name === 'appinfo.spixi');
if (!appInfoFile) {
throw new Error('Missing "appinfo.spixi". Please select the root folder of your app.');
}
// Determine root path prefix (e.g., "Pong/")
const rootPrefix = appInfoFile.webkitRelativePath.replace('appinfo.spixi', '');
// Validate other required files
const hasIcon = files.some(f => f.webkitRelativePath === rootPrefix + 'icon.png');
const hasAppFolder = files.some(f => f.webkitRelativePath.startsWith(rootPrefix + 'app/'));
const missing = [];
if (!hasIcon) missing.push('icon.png');
if (!hasAppFolder) missing.push('app/ folder');
if (missing.length > 0) {
throw new Error(`Missing required files: ${missing.join(', ')}`);
}
// Read appinfo to get name
const appInfoText = await appInfoFile.text();
const appNameMatch = appInfoText.match(/^\s*name\s*=\s*(.*?)\s*$/m);
const rawAppName = appNameMatch ? appNameMatch[1] : 'app';
const safeName = rawAppName.trim().replace(/\s+/g, '-').toLowerCase();
statusEl.textContent = `Packing ${rawAppName}...`;
const zip = new JSZip();
let filesAdded = 0;
for (const file of files) {
// pathParts will be ["RootFolder", "file.ext"] or ["RootFolder", "app", "index.html"]
// We need to strip the "RootFolder/" part to check standard paths
const fullPath = file.webkitRelativePath;
if (!fullPath.startsWith(rootPrefix)) continue; // Should not happen given we found rootPrefix from a file in list
const relPath = fullPath.substring(rootPrefix.length);
if (relPath.startsWith('app/') ||
relPath === 'appinfo.spixi' ||
relPath === 'icon.png') {
zip.file(relPath, file);
filesAdded++;
}
}
if (filesAdded === 0) {
throw new Error('No valid app files found to pack.');
}
const content = await zip.generateAsync({ type: "blob" });
// Create download link
const url = URL.createObjectURL(content);
downloadLink.href = url;
downloadLink.download = `${safeName}.zip`;
statusEl.textContent = `✅ Successfully packed ${rawAppName}!`;
downloadArea.classList.remove('hidden');
} catch (err) {
statusEl.textContent = `❌ Error: ${err.message}`;
console.error(err);
}
});
</script>
</body>
</html>