Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711#!/usr/bin/env node
// Single-file PACK bundler - creates one self-contained HTML file// Excludes image assets and generates directly from source// For KidLisp pieces, fetches and embeds source code at build time// Usage: node ac-pack-single-file.mjs <piece-name>
import { promises as fs } from "fs";import path from "path";import { fileURLToPath } from "url";import { execSync } from "child_process";import https from "https";
const __filename = fileURLToPath(import.meta.url);const __dirname = path.dirname(__filename);
const PIECE_NAME = process.argv[2] || "$eel";const OUTPUT_DIR = path.join(__dirname, "output");const SOURCE_DIR = path.resolve(__dirname, "..");
// Get git info for timestampconst gitHash = execSync("git rev-parse --short HEAD", { encoding: "utf8" }).trim();const gitDirty = execSync("git diff --quiet || echo dirty", { encoding: "utf8" }).trim();const gitVersion = gitHash + (gitDirty ? " (dirty)" : "");const packDate = new Date().toLocaleString("en-US", { month: "long", day: "numeric", year: "numeric", hour: "numeric", minute: "2-digit", second: "2-digit"});
async function fetchKidLispSource(piece) { console.log(`š” Fetching KidLisp source for: ${piece}...`); const url = `https://aesthetic.computer/.netlify/functions/store-kidlisp?code=${piece}`; return new Promise((resolve, reject) => { https.get(url, (res) => { let data = ''; res.on('data', (chunk) => data += chunk); res.on('end', () => { try { const json = JSON.parse(data); if (json.source) { console.log(`ā
KidLisp source: ${json.source.substring(0, 80)}...`); resolve(json.source); } else { console.log(`ā ļø No KidLisp source found for ${piece}`); resolve(null); } } catch (e) { console.log(`ā ļø Not a KidLisp piece: ${piece}`); resolve(null); } }); }).on('error', (e) => { console.log(`ā ļø Could not fetch KidLisp source: ${e.message}`); resolve(null); }); });}
// Recursively fetch all embedded KidLisp piecesasync function fetchAllEmbeddedPieces(mainPiece) { const pieces = new Map(); // Map of piece name -> source const toFetch = [mainPiece]; const fetched = new Set();
console.log(`\nš Scanning for embedded KidLisp pieces...`);
while (toFetch.length > 0) { const piece = toFetch.shift(); if (fetched.has(piece)) continue; fetched.add(piece); const source = await fetchKidLispSource(piece); if (source) { pieces.set(piece, source); // Scan for embedded $codes in the source const embeddedMatches = source.matchAll(/\$([a-z0-9]+)/gi); for (const match of embeddedMatches) { const embeddedPiece = match[1]; if (!fetched.has(embeddedPiece) && !toFetch.includes(embeddedPiece)) { console.log(` š Found embedded piece: $${embeddedPiece}`); toFetch.push(embeddedPiece); } } } }
console.log(`ā
Fetched ${pieces.size} total piece(s): ${Array.from(pieces.keys()).map(p => '$' + p).join(', ')}`); return pieces;}
async function inlineFile(filePath, type = "text") { try { if (type === "binary") { const buffer = await fs.readFile(filePath); return buffer.toString("base64"); } return await fs.readFile(filePath, "utf8"); } catch (error) { console.warn(`ā ļø Could not read ${filePath}: ${error.message}`); return ""; }}
async function inlineDirectory(dirPath, basePath = dirPath, excludeImages = false) { const files = {}; const entries = await fs.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) { const fullPath = path.join(dirPath, entry.name); const relativePath = path.relative(basePath, fullPath);
if (entry.isDirectory()) { const subFiles = await inlineDirectory(fullPath, basePath, excludeImages); Object.assign(files, subFiles); } else { // Determine if binary const ext = path.extname(entry.name).toLowerCase(); const isBinary = [".png", ".gif", ".jpg", ".jpeg", ".ico", ".woff", ".woff2", ".ttf", ".otf"].includes(ext); const isImage = [".png", ".gif", ".jpg", ".jpeg", ".ico"].includes(ext); // Skip images if excludeImages is true if (excludeImages && isImage) { continue; } files[relativePath] = { content: await inlineFile(fullPath, isBinary ? "binary" : "text"), binary: isBinary, type: ext.slice(1) || "txt" }; } }
return files;}
async function createSingleFile(kidlispSource, allPieces = new Map()) { console.log(`\nš¦ Creating single-file HTML for ${PIECE_NAME}...`); console.log(`š
Pack date: ${packDate}`); console.log(`š§ Git version: ${gitVersion}`);
// Inline essential files from source ac/ directory (excluding images and heavy deps) console.log("\nš Inlining ac/ directory..."); const acDir = path.join(SOURCE_DIR, "system/public/aesthetic.computer"); const essentialDirs = ["boot.mjs", "bios.mjs", "lib", "disks", "systems", "style.css", "cursors"]; const acFiles = {}; for (const item of essentialDirs) { const itemPath = path.join(acDir, item); const stats = await fs.stat(itemPath).catch(() => null); if (!stats) continue; if (stats.isDirectory()) { const dirFiles = await inlineDirectory(itemPath, acDir, false); // Include cursors (SVG files) Object.assign(acFiles, dirFiles); } else { // Single file const relativePath = path.relative(acDir, itemPath); const ext = path.extname(item).toLowerCase(); const isBinary = [".png", ".gif", ".jpg", ".jpeg", ".ico", ".woff", ".woff2", ".ttf", ".otf"].includes(ext); acFiles[relativePath] = { content: await inlineFile(itemPath, isBinary ? "binary" : "text"), binary: isBinary, type: ext.slice(1) || "txt" }; } } // Add only essential dependencies from dep/ console.log("š Inlining dep/gl-matrix..."); const glMatrixDir = path.join(acDir, "dep/gl-matrix"); const glMatrixFiles = await inlineDirectory(glMatrixDir, acDir, true); Object.assign(acFiles, glMatrixFiles);
console.log("š Inlining dep/@akamfoad/qr..."); const qrDir = path.join(acDir, "dep/@akamfoad/qr"); const qrFiles = await inlineDirectory(qrDir, acDir, true); Object.assign(acFiles, qrFiles);
console.log("š Inlining dep/idb.js..."); const idbPath = path.join(acDir, "dep/idb.js"); const idbContent = await fs.readFile(idbPath, "utf8"); acFiles["dep/idb.js"] = { content: idbContent, binary: false, type: "js" };
console.log("š Inlining dep/nanoid..."); const nanoidDir = path.join(acDir, "dep/nanoid"); const nanoidFiles = await inlineDirectory(nanoidDir, acDir, true); Object.assign(acFiles, nanoidFiles);
console.log("š Inlining dep/geckos.io-client..."); const geckosPath = path.join(acDir, "dep/geckos.io-client.2.3.2.min.js"); const geckosContent = await fs.readFile(geckosPath, "utf8"); acFiles["dep/geckos.io-client.2.3.2.min.js"] = { content: geckosContent, binary: false, type: "js" }; console.log(` ā Found ${Object.keys(acFiles).length} files`);
// Inline type/ directory (fonts) console.log("š Inlining type/ directory..."); const typeDir = path.join(SOURCE_DIR, "system/public/type"); const typeFiles = await inlineDirectory(typeDir, typeDir, false); console.log(` ā Found ${Object.keys(typeFiles).length} files`);
// Create virtual filesystem (no images!) const vfs = { ...Object.fromEntries(Object.entries(acFiles).map(([k, v]) => [`ac/${k}`, v])), ...Object.fromEntries(Object.entries(typeFiles).map(([k, v]) => [`type/${k}`, v])) };
console.log(`\nš Total files in VFS: ${Object.keys(vfs).length}`);
// Prepare KidLisp embedding const kidlispSourceEscaped = kidlispSource ? kidlispSource.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n') : ''; if (kidlispSource) { console.log(`š Embedded KidLisp source: ${kidlispSource.length} chars`); }
// Create the single file HTML // CRITICAL: Escape </script> in JSON to prevent premature script tag closure const vfsJson = JSON.stringify(vfs).replace(/<\/script>/gi, '<\\/script>'); // Build list of all module paths for runtime import map generation const modulePaths = Object.keys(vfs).filter(path => path.endsWith('.js') || path.endsWith('.mjs') ); console.log(`š¦ Found ${modulePaths.length} JavaScript modules for import map`); const modulePathsJson = JSON.stringify(modulePaths); const singleFileHtml = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${PIECE_NAME} - Aesthetic Computer</title>
<script>console.log('Aesthetic.Computer');console.log('');console.log('${PIECE_NAME} is a piece by @jeffrey');console.log('');${kidlispSource ? `console.log('KidLisp source: ${kidlispSourceEscaped}');` : `console.log('(source embedded in hash)');`}console.log('');console.log('This copy was packed on ${packDate}');console.log('');console.log('Using aesthetic-computer git version ${gitVersion}');
// Virtual FileSystem - all files embedded as base64/textwindow.VFS = ${vfsJson};
${kidlispSource ? `// Embedded KidLisp source for OBJKT offline modewindow.objktKidlispCodes = ${JSON.stringify(Object.fromEntries( Array.from(allPieces.entries()).map(([name, source]) => [ name, source ])))};window.EMBEDDED_KIDLISP_SOURCE = '${kidlispSourceEscaped}';window.EMBEDDED_KIDLISP_PIECE = '${PIECE_NAME.replace(/^\$/, '')}';console.log('š Embedded ${allPieces.size} KidLisp piece(s) for offline use');` : ''}window.acPACK_MODE = true;window.acSTARTING_PIECE = '${PIECE_NAME}';window.acPACK_PIECE = '${PIECE_NAME}';window.acPACK_DATE = '${packDate}';window.acPACK_VERSION = '${gitVersion}';
// Intercept appendChild to prevent CSS link elements from being addedconst originalAppendChild = Element.prototype.appendChild;Element.prototype.appendChild = function(child) { // Block CSS link elements to prevent 404s (fonts already work via VFS XHR) if (child.tagName === 'LINK' && child.rel === 'stylesheet' && child.href && child.href.includes('.css')) { console.log('š« Blocked CSS link to prevent 404:', child.href); return child; // Return without appending } return originalAppendChild.call(this, child);};console.log('ā
CSS link blocking installed');
// Also intercept document.body.append (used in bios.mjs)const originalBodyAppend = HTMLBodyElement.prototype.append;HTMLBodyElement.prototype.append = function(...nodes) { const filteredNodes = nodes.filter(node => { if (node.tagName === 'LINK' && node.rel === 'stylesheet' && node.href && node.href.includes('.css')) { console.log('š« Blocked CSS link via body.append:', node.href); return false; } return true; }); return originalBodyAppend.call(this, ...filteredNodes);};console.log('ā
CSS link blocking (body.append) installed');
// Intercept Image src loading to serve SVG cursors from VFSconst OriginalImage = window.Image;window.Image = function(...args) { const img = new OriginalImage(...args); const originalSrcDescriptor = Object.getOwnPropertyDescriptor(HTMLImageElement.prototype, 'src'); Object.defineProperty(img, 'src', { set: function(value) { // Intercept cursor SVG loading if (value && value.includes('.svg') && (value.includes('cursors/') || value.includes('aesthetic.computer'))) { console.log('š¼ļø Intercepting Image src:', value); // Normalize path let vfsPath = value .replace(/^\\.?\\//, '') .replace(/^https?:\\/\\/[^\\/]+\\//, '') .replace(/^aesthetic\\.computer\\//, ''); // Add ac/ prefix if not present if (!vfsPath.startsWith('ac/') && !vfsPath.startsWith('type/')) { vfsPath = 'ac/' + vfsPath; } console.log('š¼ļø Looking for VFS path:', vfsPath); if (window.VFS[vfsPath]) { const file = window.VFS[vfsPath]; // Create data URI for SVG const svgContent = file.binary ? atob(file.content) : file.content; const dataUri = 'data:image/svg+xml;base64,' + (file.binary ? file.content : btoa(svgContent)); console.log('ā Image served from VFS:', vfsPath); originalSrcDescriptor.set.call(this, dataUri); return; } } // Fallback to original behavior originalSrcDescriptor.set.call(this, value); }, get: function() { return originalSrcDescriptor.get.call(this); } }); return img;};console.log('ā
Image src interception installed');
// Create blob URLs for all JS modules with rewritten importswindow.VFS_BLOB_URLS = {};
console.log('š§ Creating blob URLs for all modules...');
// Paths of all modules that need blob URLswindow.modulePaths = ${modulePathsJson};
for (const filepath of window.modulePaths) { const filedata = window.VFS[filepath]; if (filedata && !filedata.binary) { const rewrittenCode = rewriteImports(filedata.content, filepath); const blob = new Blob([rewrittenCode], { type: 'application/javascript' }); const blobUrl = URL.createObjectURL(blob); window.VFS_BLOB_URLS[filepath] = blobUrl; }}
console.log(\`ā
Created \${Object.keys(window.VFS_BLOB_URLS).length} blob URLs\`);
// Helper to rewrite imports in module codefunction rewriteImports(code, modulePath) { const dir = modulePath.split('/').slice(0, -1).join('/'); // Rewrite aesthetic.computer/ imports to ac/ format code = code.replace(/from\\s+['"]aesthetic\\.computer\\/disks\\/([^'"]+)['"]/g, (match, path) => { return 'from \\'ac/disks/' + path + '\\''; }); code = code.replace(/import\\s*\\((['"]aesthetic\\.computer\\/disks\\/([^'"]+)['")])\\)/g, (match, fullPath, path) => { return 'import(\\'ac/disks/' + path + '\\')'; }); // Rewrite relative imports to bare specifiers (no leading /) code = code.replace(/from\\s+['"](\\.\\.\\/[^'"]+|\\.\\/[^'"]+)['"]/g, (match, path) => { const resolved = resolvePath(dir, path); return 'from \\'' + resolved + '\\''; // No leading / }); code = code.replace(/import\\s*\\((['"](\\.\\.\\/[^'"]+|\\.\\/[^'"]+)['")])\\)/g, (match, fullPath, path) => { const resolved = resolvePath(dir, path); return 'import(\\'' + resolved + '\\')'; // No leading / }); // Also handle backtick template literals: import(\\\`./path\\\`) code = code.replace(/import\\s*\\(\\\`(\\.\\.\\/[^\\\`]+|\\.\\/[^\\\`]+)\\\`\\)/g, (match, path) => { const resolved = resolvePath(dir, path); return 'import(\\'' + resolved + '\\')'; // Convert to regular quotes with resolved path }); return code;}
function resolvePath(base, relative) { if (!relative.startsWith('.')) return relative; const parts = base.split('/').filter(p => p); const relParts = relative.split('/'); for (const part of relParts) { if (part === '..') parts.pop(); else if (part !== '.' && part !== '') parts.push(part); } return parts.join('/');}</script>
<script>const importMapEntries = {};
for (const filepath of window.modulePaths) { if (window.VFS_BLOB_URLS[filepath]) { // Add normalized path (ac/disks/...) importMapEntries[filepath] = window.VFS_BLOB_URLS[filepath]; // Also add legacy path (aesthetic.computer/disks/...) if (filepath.startsWith('ac/disks/')) { const legacyPath = filepath.replace('ac/disks/', 'aesthetic.computer/disks/'); importMapEntries[legacyPath] = window.VFS_BLOB_URLS[filepath]; // Also add with leading slash importMapEntries['/' + legacyPath] = window.VFS_BLOB_URLS[filepath]; } // Also add with leading slash for ac/ paths importMapEntries['/' + filepath] = window.VFS_BLOB_URLS[filepath]; }}
const importMapScript = document.createElement('script');importMapScript.type = 'importmap';importMapScript.textContent = JSON.stringify({ imports: importMapEntries });document.head.appendChild(importMapScript);
console.log(\`ā
Import map activated with \${Object.keys(importMapEntries).length} entries\`);console.log(\`š Sample entries:\`, Object.keys(importMapEntries).filter(k => k.includes('sfx')).slice(0, 3));</script>
<script>// Intercept fetch() and XMLHttpRequest to serve from VFSconst originalFetch = window.fetch;const originalXHROpen = XMLHttpRequest.prototype.open;const originalXHRSend = XMLHttpRequest.prototype.send;
window.fetch = function(url, options) { const urlStr = typeof url === 'string' ? url : url.url; // SPECIAL: Intercept KidLisp/painting API calls and return embedded data if (window.EMBEDDED_KIDLISP_SOURCE) { // Intercept painting-code lookups for the embedded piece if (urlStr.includes('/api/painting-code') && urlStr.includes(window.EMBEDDED_KIDLISP_PIECE)) { console.log('šÆ Intercepted painting-code API, returning embedded KidLisp metadata'); return Promise.resolve(new Response(JSON.stringify({ slug: window.EMBEDDED_KIDLISP_PIECE, handle: 'jeffrey', code: window.EMBEDDED_KIDLISP_PIECE }), { status: 200, headers: { 'Content-Type': 'application/json' } })); } // Intercept KidLisp source fetches if (urlStr.includes('/store-kidlisp') || urlStr.includes(\`/\${window.EMBEDDED_KIDLISP_PIECE}.lisp\`)) { console.log('šÆ Intercepted KidLisp source API, returning embedded source'); return Promise.resolve(new Response(window.EMBEDDED_KIDLISP_SOURCE, { status: 200, headers: { 'Content-Type': 'text/plain' } })); } } // Normalize URL (remove leading ./ and absolute paths, and strip hash/query) let vfsPath = urlStr .replace(/^\\.?\\//, '') .replace(/^https?:\\/\\/[^\\/]+\\//, '') .replace(/#.*$/, '') // Strip hash (timestamp) .replace(/\\?.*$/, ''); // Strip query params // Decode URL encoding (%20 -> space, etc.) try { vfsPath = decodeURIComponent(vfsPath); } catch (e) { console.warn('Failed to decode URL:', vfsPath); } // Remove 'aesthetic.computer/' prefix if present (legacy paths) vfsPath = vfsPath.replace(/^aesthetic\\.computer\\//, ''); // Add ac/ prefix if not present (for relative paths like ../disks/prompt.mjs or cursors/precise.svg) if (!vfsPath.startsWith('ac/') && !vfsPath.startsWith('type/')) { // Remove leading ../ if present vfsPath = vfsPath.replace(/^\\.\\.\\//g, ''); if (!vfsPath.startsWith('ac/') && !vfsPath.startsWith('type/')) { vfsPath = 'ac/' + vfsPath; } } console.log('š VFS fetch:', urlStr, 'ā', vfsPath); if (window.VFS[vfsPath]) { const file = window.VFS[vfsPath]; let content = file.content; // Convert base64 to blob for binary files if (file.binary) { const binaryString = atob(content); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } content = new Blob([bytes], { type: getMimeType(file.type) }); } console.log('ā VFS served:', vfsPath, file.binary ? '(binary)' : '(text)'); return Promise.resolve(new Response(content, { status: 200, headers: { 'Content-Type': file.binary ? getMimeType(file.type) : 'text/plain' } })); } console.warn('ā ļø VFS miss:', vfsPath, '- falling back to network'); return originalFetch(url, options);};
// Intercept XMLHttpRequest to serve from VFSXMLHttpRequest.prototype.open = function(method, url, ...args) { this._url = url; // Store for later return originalXHROpen.call(this, method, url, ...args);};
XMLHttpRequest.prototype.send = function(...args) { if (this._url) { const urlStr = this._url; // Same normalization as fetch let vfsPath = urlStr .replace(/^\\.?\\//, '') .replace(/^https?:\\/\\/[^\\/]+\\//, '') .replace(/#.*$/, '') .replace(/\\?.*$/, ''); // Decode URL encoding try { vfsPath = decodeURIComponent(vfsPath); } catch (e) { console.warn('Failed to decode URL:', vfsPath); } vfsPath = vfsPath.replace(/^aesthetic\\.computer\\//, ''); if (!vfsPath.startsWith('ac/') && !vfsPath.startsWith('type/')) { vfsPath = vfsPath.replace(/^\\.\\.\\//g, ''); if (!vfsPath.startsWith('ac/') && !vfsPath.startsWith('type/')) { vfsPath = 'ac/' + vfsPath; } } console.log('š XHR fetch:', urlStr, 'ā', vfsPath); if (window.VFS[vfsPath]) { const file = window.VFS[vfsPath]; // Override XHR to return VFS content Object.defineProperty(this, 'responseText', { writable: true, value: file.content }); Object.defineProperty(this, 'response', { writable: true, value: file.content }); Object.defineProperty(this, 'status', { writable: true, value: 200 }); Object.defineProperty(this, 'readyState', { writable: true, value: 4 }); console.log('ā XHR served from VFS:', vfsPath); // Trigger events setTimeout(() => { if (this.onreadystatechange) this.onreadystatechange(); if (this.onload) this.onload(); }, 0); return; } console.warn('ā ļø XHR VFS miss:', vfsPath, '- falling back to network'); } return originalXHRSend.call(this, ...args);};
// Get MIME type from extensionfunction getMimeType(ext) { const types = { 'js': 'application/javascript', 'mjs': 'application/javascript', 'json': 'application/json', 'css': 'text/css', 'html': 'text/html', 'png': 'image/png', 'gif': 'image/gif', 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'svg': 'image/svg+xml', 'ico': 'image/x-icon', 'woff': 'font/woff', 'woff2': 'font/woff2', 'ttf': 'font/ttf', 'otf': 'font/otf' }; return types[ext] || 'application/octet-stream';}</script>
<script type="module">// Load style.css from VFSconst styleEl = document.createElement('style');styleEl.textContent = window.VFS['ac/style.css'].content;document.head.appendChild(styleEl);
// Auto-load the piece if no hash is setif (!window.location.hash) { window.location.hash = '#${PIECE_NAME}';}
// Load boot.mjs using blob URLconst bootUrl = window.VFS_BLOB_URLS['ac/boot.mjs'] || window.VFS_BLOB_URLS['boot.mjs'];if (!bootUrl) { console.error('Failed to find blob URL for boot.mjs'); console.log('Available VFS_BLOB_URLS keys:', Object.keys(window.VFS_BLOB_URLS).filter(k => k.includes('boot'))); document.body.innerHTML = '<pre>Error: Could not load boot.mjs from VFS</pre>';} else { console.log('š Loading boot from:', bootUrl); import(bootUrl).catch(err => { console.error('Failed to load boot:', err); document.body.innerHTML = '<pre>Error loading boot.mjs: ' + err.message + '\\n\\n' + err.stack + '</pre>'; });}</script>
</head><body class="nogap native-cursor"><canvas id="canvas"></canvas><div id="console"></div></body></html>`;
// Write single file const outputPath = path.join(OUTPUT_DIR, `${PIECE_NAME.replace(/\$/g, '')}-single-file.html`); await fs.writeFile(outputPath, singleFileHtml);
const stats = await fs.stat(outputPath); const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
console.log(`\nā
Single file created!`); console.log(` š ${outputPath}`); console.log(` š¾ Size: ${sizeMB} MB`); console.log(` š¼ļø No images included (code only)`); console.log(`\nš This file is completely self-contained!`); console.log(` ⢠No external dependencies`); console.log(` ⢠No IPFS/CDN required`); console.log(` ⢠Works on objkt.com`); console.log(` ⢠Can be minted directly`); if (kidlispSource) { console.log(` ⢠KidLisp source embedded (${kidlispSource.length} chars)`); } console.log(` ⢠Current timestamp: ${packDate}`);}
// Main - fetch all KidLisp sources (including embedded pieces), then create bundle(async () => { try { const allPieces = await fetchAllEmbeddedPieces(PIECE_NAME.replace(/^\$/, '')); const mainSource = allPieces.get(PIECE_NAME.replace(/^\$/, '')); await createSingleFile(mainSource, allPieces); } catch (error) { console.error("ā Error:", error); process.exit(1); }})();