- Add React Router with theme browser, theme detail, and layout detail pages - Implement manifest-based theme discovery for better performance - Add Welcome component as home page with feature overview - Fix layout and styling issues with proper CSS centering - Implement introspective theme browsing (dynamically discover colors/variables) - Add layout preview system with iframe scaling - Create comprehensive theme detail page with color palette display - Fix TypeScript errors and build issues - Remove hardcoded theme assumptions in favor of dynamic discovery 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
74 lines
2.3 KiB
JavaScript
74 lines
2.3 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
const PUBLIC_THEMES_DIR = path.join(__dirname, '..', 'public', 'themes');
|
|
const MANIFEST_OUTPUT = path.join(__dirname, '..', 'public', 'themes-manifest.json');
|
|
|
|
/**
|
|
* Generates a manifest of all available themes and their layouts
|
|
*/
|
|
async function generateThemesManifest() {
|
|
const manifest = {
|
|
themes: {},
|
|
generated: new Date().toISOString()
|
|
};
|
|
|
|
try {
|
|
// Check if themes directory exists
|
|
if (!fs.existsSync(PUBLIC_THEMES_DIR)) {
|
|
console.warn('Themes directory not found:', PUBLIC_THEMES_DIR);
|
|
return;
|
|
}
|
|
|
|
// Read all theme directories
|
|
const themeDirectories = fs.readdirSync(PUBLIC_THEMES_DIR, { withFileTypes: true })
|
|
.filter(dirent => dirent.isDirectory())
|
|
.map(dirent => dirent.name);
|
|
|
|
for (const themeId of themeDirectories) {
|
|
const themePath = path.join(PUBLIC_THEMES_DIR, themeId);
|
|
const layoutsPath = path.join(themePath, 'layouts');
|
|
|
|
// Check for required files
|
|
const styleFile = path.join(themePath, 'style.css');
|
|
if (!fs.existsSync(styleFile)) {
|
|
console.warn(`Theme ${themeId} missing style.css, skipping`);
|
|
continue;
|
|
}
|
|
|
|
const themeInfo = {
|
|
id: themeId,
|
|
cssFile: 'style.css',
|
|
layouts: [],
|
|
hasMasterSlide: fs.existsSync(path.join(themePath, 'master-slide.html'))
|
|
};
|
|
|
|
// Discover layouts
|
|
if (fs.existsSync(layoutsPath)) {
|
|
const layoutFiles = fs.readdirSync(layoutsPath)
|
|
.filter(file => file.endsWith('.html'))
|
|
.map(file => path.basename(file, '.html'));
|
|
|
|
themeInfo.layouts = layoutFiles;
|
|
}
|
|
|
|
manifest.themes[themeId] = themeInfo;
|
|
console.log(`✓ Found theme: ${themeId} with ${themeInfo.layouts.length} layouts`);
|
|
}
|
|
|
|
// Write manifest file
|
|
fs.writeFileSync(MANIFEST_OUTPUT, JSON.stringify(manifest, null, 2));
|
|
console.log(`✓ Generated themes manifest: ${MANIFEST_OUTPUT}`);
|
|
console.log(`✓ Found ${Object.keys(manifest.themes).length} themes total`);
|
|
|
|
} catch (error) {
|
|
console.error('Error generating themes manifest:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
generateThemesManifest(); |