muspe-cli
Version:
MusPE Advanced Framework v2.1.3 - Mobile User-friendly Simple Progressive Engine with Enhanced CLI Tools, Specialized E-Commerce Templates, Material Design 3, Progressive Enhancement, Mobile Optimizations, Performance Analysis, and Enterprise-Grade Develo
1,807 lines (1,570 loc) ⢠85.1 kB
JavaScript
const fs = require('fs-extra');
const path = require('path');
const chalk = require('chalk');
const inquirer = require('inquirer');
const ora = require('ora');
const spawn = require('cross-spawn');
const { execSync } = require('child_process');
const validatePackageName = require('validate-npm-package-name');
async function createProject(projectName, options) {
const spinner = ora('Creating MusPE project...').start();
try {
// Validate project name
const validation = validatePackageName(projectName);
if (!validation.validForNewPackages) {
spinner.fail('Invalid project name');
console.log(chalk.red('Project name contains invalid characters'));
return;
}
const projectPath = path.join(process.cwd(), projectName);
// Check if directory exists
if (await fs.pathExists(projectPath)) {
spinner.fail('Directory already exists');
console.log(chalk.red(`Directory ${projectName} already exists`));
return;
}
// Interactive template selection if not specified
let template = options.template;
let framework = options.framework;
if (!options.template || !options.framework) {
spinner.stop();
const answers = await inquirer.prompt([
{
type: 'list',
name: 'template',
message: 'Choose your project template:',
choices: [
{
name: 'ļæ½ E-Commerce - Product catalog with shopping cart',
value: 'ecommerce'
},
{
name: 'š Dashboard - Analytics and data visualization',
value: 'dashboard'
},
{
name: 'š„ Social Media - Feed layout with user interactions',
value: 'social'
},
{
name: 'š¼ Portfolio - Professional showcase website',
value: 'portfolio'
},
{
name: 'š Blog - Content management with articles',
value: 'blog'
},
{
name: 'š± Mobile App - Basic mobile-optimized app',
value: 'mobile'
},
{
name: 'š Web App - Traditional web application',
value: 'web'
},
{
name: 'ā” PWA - Progressive Web App',
value: 'pwa'
},
{
name: 'š Hybrid - Mobile + Web hybrid app',
value: 'hybrid'
}
],
default: 'ecommerce'
},
{
type: 'list',
name: 'framework',
message: 'Choose your CSS framework:',
choices: [
{
name: 'šØ Material Design 3 - Google\'s latest design system',
value: 'material'
},
{
name: 'ā” Tailwind CSS - Utility-first CSS framework',
value: 'tailwind'
},
{
name: 'š
±ļø Bootstrap - Popular CSS framework',
value: 'bootstrap'
},
{
name: 'š Custom CSS - Pure CSS (no framework)',
value: 'custom'
}
],
default: 'material'
}
]);
template = answers.template;
framework = answers.framework;
spinner.start('Creating project structure...');
}
// Create project directory
await fs.ensureDir(projectPath);
// Generate project files based on template
await generateProjectStructure(projectPath, projectName, template, framework);
spinner.succeed('Project structure created');
// Install dependencies
if (!options.skipInstall) {
spinner.start('Installing dependencies...');
await installDependencies(projectPath);
spinner.succeed('Dependencies installed');
}
// Success message
console.log(chalk.green(`\n⨠Successfully created ${projectName}!`));
console.log(chalk.cyan('\nNext steps:'));
console.log(` ${chalk.gray('$')} cd ${projectName}`);
console.log(` ${chalk.gray('$')} muspe serve`);
console.log(chalk.gray('\nHappy coding! š\n'));
} catch (error) {
spinner.fail('Failed to create project');
console.error(chalk.red(error.message));
}
}
async function generateProjectStructure(projectPath, projectName, template, framework) {
// Create basic structure
const dirs = [
'src',
'src/components',
'src/pages',
'src/layouts',
'src/templates',
'src/styles',
'src/scripts',
'src/assets',
'src/assets/images',
'src/assets/icons',
'public'
];
// Add template-specific directories
if (template === 'ecommerce') {
dirs.push(
'src/services',
'src/utils',
'src/data',
'src/components/product',
'src/components/cart',
'src/components/search'
);
} else if (template === 'dashboard') {
dirs.push(
'src/services',
'src/utils',
'src/data',
'src/components/charts',
'src/components/widgets',
'src/components/analytics'
);
} else if (template === 'social') {
dirs.push(
'src/services',
'src/utils',
'src/data',
'src/components/feed',
'src/components/user',
'src/components/media'
);
} else if (template === 'portfolio') {
dirs.push(
'src/utils',
'src/data',
'src/components/gallery',
'src/components/projects',
'src/components/contact'
);
} else if (template === 'blog') {
dirs.push(
'src/services',
'src/utils',
'src/data',
'src/components/article',
'src/components/author',
'src/components/comments'
);
}
for (const dir of dirs) {
await fs.ensureDir(path.join(projectPath, dir));
}
// Generate package.json
const packageJson = {
name: projectName,
version: '1.0.0',
description: `A MusPE ${template} application`,
main: 'src/index.js',
scripts: {
start: 'muspe serve',
build: 'muspe build',
dev: 'muspe serve --open'
},
dependencies: {},
devDependencies: {},
muspe: {
template,
framework,
version: '1.0.0'
}
};
// Add framework-specific dependencies
if (framework === 'material') {
packageJson.dependencies['@material/web'] = '^1.5.0';
packageJson.dependencies['@material/material-color-utilities'] = '^0.2.7';
packageJson.dependencies['lit'] = '^3.1.0';
} else if (framework === 'tailwind') {
packageJson.devDependencies['tailwindcss'] = '^3.3.0';
packageJson.devDependencies['autoprefixer'] = '^10.4.14';
packageJson.devDependencies['postcss'] = '^8.4.24';
} else if (framework === 'bootstrap') {
packageJson.dependencies['bootstrap'] = '^5.3.0';
}
// Add Swiper.js for slide components (all frameworks)
packageJson.dependencies['swiper'] = '^11.0.0';
// Add base Material.io components for layout system (all frameworks)
if (framework !== 'material') {
packageJson.dependencies['@material/web'] = '^1.5.0';
packageJson.dependencies['@material/material-color-utilities'] = '^0.2.7';
}
// Add Vite for CSS imports (if not using a bundler)
packageJson.devDependencies['vite'] = '^5.0.0';
// Add template-specific dependencies
if (template === 'ecommerce') {
packageJson.dependencies['chart.js'] = '^4.4.0';
packageJson.dependencies['date-fns'] = '^2.30.0';
packageJson.dependencies['fuse.js'] = '^7.0.0'; // For search functionality
} else if (template === 'dashboard') {
packageJson.dependencies['chart.js'] = '^4.4.0';
packageJson.dependencies['chartjs-adapter-date-fns'] = '^3.0.0';
packageJson.dependencies['date-fns'] = '^2.30.0';
packageJson.dependencies['d3'] = '^7.8.5';
} else if (template === 'social') {
packageJson.dependencies['date-fns'] = '^2.30.0';
packageJson.dependencies['intersection-observer'] = '^0.12.2'; // For infinite scroll
} else if (template === 'portfolio') {
packageJson.dependencies['lightbox2'] = '^2.11.4';
packageJson.dependencies['aos'] = '^2.3.4'; // Animate on scroll
} else if (template === 'blog') {
packageJson.dependencies['markdown-it'] = '^13.0.2';
packageJson.dependencies['highlight.js'] = '^11.9.0';
packageJson.dependencies['date-fns'] = '^2.30.0';
}
await fs.writeJSON(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
// Generate configuration files
await generateConfigFiles(projectPath, template, framework);
// Generate template files
await generateTemplateFiles(projectPath, projectName, template, framework);
// Generate layout system
await generateLayoutSystem(projectPath, template, framework);
// Generate template system
await generateTemplateSystem(projectPath, template, framework);
// Copy core utilities
await copyUtilities(projectPath);
}
async function generateConfigFiles(projectPath, template, framework) {
// MusPE config
const config = `module.exports = {
template: '${template}',
framework: '${framework}',
server: {
port: 3000,
host: 'localhost',
open: true
},
build: {
outDir: 'dist',
minify: true,
sourcemap: true
},
pwa: {
enabled: ${template === 'pwa' || template === 'hybrid'},
manifest: './public/manifest.json',
serviceWorker: './src/sw.js'
},
mobile: {
viewport: 'width=device-width, initial-scale=1.0, user-scalable=no',
statusBar: 'default',
orientation: 'portrait',
touchOptimizations: true,
preventZoomOnInput: true,
fastClick: true,
viewportHeight: 'dynamic',
progressiveEnhancement: true
},
};`;
await fs.writeFile(path.join(projectPath, 'muspe.config.js'), config);
// Generate framework-specific configs
if (framework === 'material') {
const materialConfig = `// Material Design 3 Configuration
module.exports = {
materialDesign: {
theme: {
source: '#6750A4', // Primary brand color
scheme: 'light', // 'light' | 'dark' | 'auto'
},
typography: {
fontFamily: 'Roboto, sans-serif',
scale: 'standard' // 'standard' | 'large'
},
components: {
// Import all Material Web components
importAll: true,
// Or specify individual components
components: [
'button',
'card',
'textfield',
'dialog',
'fab',
'navigation-drawer',
'navigation-rail',
'tabs'
]
}
}
};`;
await fs.writeFile(path.join(projectPath, 'material.config.js'), materialConfig);
} else if (framework === 'tailwind') {
const tailwindConfig = `/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{html,js}",
"./public/**/*.html"
],
theme: {
extend: {
screens: {
'xs': '475px',
},
colors: {
primary: {
50: '#eff6ff',
500: '#3b82f6',
600: '#2563eb',
}
}
},
},
plugins: [],
}`;
await fs.writeFile(path.join(projectPath, 'tailwind.config.js'), tailwindConfig);
const postcssConfig = `module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}`;
await fs.writeFile(path.join(projectPath, 'postcss.config.js'), postcssConfig);
}
}
async function generateTemplateFiles(projectPath, projectName, template, framework) {
// Generate index.html
const htmlContent = generateHTMLTemplate(projectName, template, framework);
await fs.writeFile(path.join(projectPath, 'public/index.html'), htmlContent);
// Generate main CSS
const cssContent = generateCSSTemplate(framework);
await fs.writeFile(path.join(projectPath, 'src/styles/main.css'), cssContent);
// Generate main JS
const jsContent = generateJSTemplate(template, framework);
await fs.writeFile(path.join(projectPath, 'src/scripts/main.js'), jsContent);
// Generate example component
const componentContent = generateComponentTemplate(framework);
await fs.writeFile(path.join(projectPath, 'src/components/AppHeader.js'), componentContent);
// Generate template-specific files
if (template === 'ecommerce') {
await generateEcommerceFiles(projectPath, framework);
} else if (template === 'dashboard') {
await generateDashboardFiles(projectPath, framework);
} else if (template === 'social') {
await generateSocialFiles(projectPath, framework);
} else if (template === 'portfolio') {
await generatePortfolioFiles(projectPath, framework);
} else if (template === 'blog') {
await generateBlogFiles(projectPath, framework);
} else if (template === 'web') {
await generateWebFiles(projectPath, framework);
} else if (template === 'spa') {
await generateSPAFiles(projectPath, framework);
} else if (template === 'pwa') {
await generatePWASpecificFiles(projectPath, framework);
}
// Copy Cordova demo component and service for mobile/hybrid apps
if (template === 'mobile' || template === 'hybrid') {
await copyCordovaFiles(projectPath);
}
// Generate PWA files if needed
if (template === 'pwa' || template === 'hybrid') {
await generatePWAFiles(projectPath, projectName);
}
// Generate README
const readmeContent = `# ${projectName}
A ${template} application built with MusPE Framework v2.1.3
## Quick Start
\`\`\`bash
npm install
npm start
\`\`\`
## Features
- š± Mobile-optimized responsive design
- ā” Performance optimized
- šØ Material Design 3 components
- š Built-in analytics
- š§ Progressive enhancement
## Development
\`\`\`bash
# Development server
npm run dev
# Build for production
npm run build
# Run tests
npm test
\`\`\`
## Documentation
Visit [MusPE Documentation](https://github.com/your-org/muspe) for detailed guides.
`;
await fs.writeFile(path.join(projectPath, 'README.md'), readmeContent);
}
async function copyUtilities(projectPath) {
const utilsDir = path.join(projectPath, 'src/utils');
await fs.ensureDir(utilsDir);
// Copy DOM utilities
const domUtilsSource = path.join(__dirname, '../utils/dom.js');
const domUtilsTarget = path.join(utilsDir, 'dom.js');
if (await fs.pathExists(domUtilsSource)) {
await fs.copy(domUtilsSource, domUtilsTarget);
}
// Copy Fetch utilities
const fetchUtilsSource = path.join(__dirname, '../utils/fetch.js');
const fetchUtilsTarget = path.join(utilsDir, 'fetch.js');
if (await fs.pathExists(fetchUtilsSource)) {
await fs.copy(fetchUtilsSource, fetchUtilsTarget);
}
// Copy Cordova utilities
const cordovaUtilsSource = path.join(__dirname, '../utils/cordova.js');
const cordovaUtilsTarget = path.join(utilsDir, 'cordova.js');
if (await fs.pathExists(cordovaUtilsSource)) {
await fs.copy(cordovaUtilsSource, cordovaUtilsTarget);
}
// Copy core framework
const coreDir = path.join(projectPath, 'src/core');
await fs.ensureDir(coreDir);
const coreSource = path.join(__dirname, '../core/muspe.js');
const coreTarget = path.join(coreDir, 'muspe.js');
if (await fs.pathExists(coreSource)) {
await fs.copy(coreSource, coreTarget);
}
}
async function copyCordovaFiles(projectPath) {
// Copy Cordova demo component
const componentDir = path.join(projectPath, 'src/components');
const componentSource = path.join(__dirname, '../components/CordovaDemo.js');
const componentTarget = path.join(componentDir, 'CordovaDemo.js');
if (await fs.pathExists(componentSource)) {
await fs.copy(componentSource, componentTarget);
}
// Copy Cordova service
const serviceDir = path.join(projectPath, 'src/services');
await fs.ensureDir(serviceDir);
const serviceSource = path.join(__dirname, '../services/CordovaService.js');
const serviceTarget = path.join(serviceDir, 'CordovaService.js');
if (await fs.pathExists(serviceSource)) {
await fs.copy(serviceSource, serviceTarget);
}
}
function generateHTMLTemplate(projectName, template, framework) {
const isFramework7Like = template === 'mobile' || template === 'hybrid';
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0${isFramework7Like ? ', user-scalable=no' : ''}">
<meta name="description" content="${projectName} - Built with MusPE">
<title>${projectName}</title>
<!-- Material Symbols Icons -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<!-- Material.io Components -->
<script type="importmap">
{
"imports": {
"@material/web/": "https://esm.run/@material/web/"
}
}
</script>
<!-- CSS will be imported via JavaScript modules -->
${(template === 'pwa' || template === 'hybrid') ? '<link rel="manifest" href="./manifest.json">' : ''}
<!-- Mobile optimizations -->
<meta name="theme-color" content="#3b82f6">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<!-- Prevent zoom on input focus (mobile) -->
${isFramework7Like ? '<meta name="format-detection" content="telephone=no">' : ''}
</head>
<body>
<div id="app">
<!-- App Header -->
<header class="app-header">
<h1>Welcome to ${projectName}</h1>
<p>Built with MusPE Framework</p>
</header>
<!-- Main Content -->
<main class="app-main">
<div class="container">
<div class="welcome-card">
<h2>š Your ${template} app is ready!</h2>
<p>Start building amazing ${template === 'mobile' ? 'mobile experiences' : template === 'pwa' ? 'progressive web apps' : 'web applications'}.</p>
<div class="features">
<div class="feature">
<span class="icon">š±</span>
<span>Mobile-First</span>
</div>
<div class="feature">
<span class="icon">ā”</span>
<span>Fast & Lightweight</span>
</div>
<div class="feature">
<span class="icon">šØ</span>
<span>${framework === 'material' ? 'Material Design 3' : framework === 'tailwind' ? 'Tailwind CSS' : framework === 'bootstrap' ? 'Bootstrap' : 'Custom CSS'}</span>
</div>
<div class="feature">
<span class="icon">š§°</span>
<span>DOM & Fetch Utils</span>
</div>
</div>
<button class="cta-button" onclick="showInfo()">Get Started</button>
<button class="demo-button" onclick="demoFeatures()">Try Features</button>
</div>
</div>
</main>
</div>
<!-- Load MusPE Core and Utilities -->
<script src="./src/utils/dom.js"></script>
<script src="./src/utils/fetch.js"></script>
<script src="./src/core/muspe.js"></script>
<!-- Load Layout System -->
<script src="./src/layouts/index.js"></script>
<!-- Load Template System -->
<script src="./src/templates/index.js"></script>
<!-- Load Main App -->
<script src="./src/scripts/main.js"></script>
${framework === 'bootstrap' ? '<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>' : ''}
</body>
</html>`;
}
function generateCSSTemplate(framework) {
if (framework === 'material') {
return `/* Material Design 3 CSS Framework */
/* Import Material Symbols font */
@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200');
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap');
/* Material Design 3 Tokens */
:root {
/* Primary colors */
--md-sys-color-primary: #6750A4;
--md-sys-color-on-primary: #FFFFFF;
--md-sys-color-primary-container: #EADDFF;
--md-sys-color-on-primary-container: #21005D;
/* Surface colors */
--md-sys-color-surface: #FFFBFE;
--md-sys-color-on-surface: #1C1B1F;
--md-sys-color-surface-variant: #E7E0EC;
--md-sys-color-on-surface-variant: #49454F;
/* Background */
--md-sys-color-background: #FFFBFE;
--md-sys-color-on-background: #1C1B1F;
/* Typography */
--md-sys-typescale-display-large-font: 'Roboto';
--md-sys-typescale-display-large-size: 57px;
--md-sys-typescale-display-large-weight: 400;
--md-sys-typescale-headline-large-font: 'Roboto';
--md-sys-typescale-headline-large-size: 32px;
--md-sys-typescale-headline-large-weight: 400;
--md-sys-typescale-body-large-font: 'Roboto';
--md-sys-typescale-body-large-size: 16px;
--md-sys-typescale-body-large-weight: 400;
}
/* Base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--md-sys-typescale-body-large-font), sans-serif;
font-size: var(--md-sys-typescale-body-large-size);
line-height: 1.5;
color: var(--md-sys-color-on-background);
background-color: var(--md-sys-color-background);
}
/* App Structure with Material Design */
.app-header {
background-color: var(--md-sys-color-primary);
color: var(--md-sys-color-on-primary);
padding: 24px;
text-align: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.app-header h1 {
font-size: var(--md-sys-typescale-headline-large-size);
font-weight: var(--md-sys-typescale-headline-large-weight);
margin-bottom: 8px;
}
.app-main {
min-height: 100vh;
background-color: var(--md-sys-color-background);
padding: 24px 0;
}
.container {
max-width: 400px;
margin: 0 auto;
padding: 0 16px;
}
.welcome-card {
background-color: var(--md-sys-color-surface);
border-radius: 12px;
padding: 24px;
text-align: center;
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
margin-bottom: 16px;
}
.features {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin: 24px 0;
}
.feature {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.icon {
font-size: 24px;
}
/* Material Design 3 style buttons */
.cta-button,
.demo-button {
font-family: var(--md-sys-typescale-body-large-font);
font-size: 14px;
font-weight: 500;
padding: 10px 24px;
border: none;
border-radius: 20px;
cursor: pointer;
transition: all 0.2s ease;
margin: 4px;
}
.cta-button {
background-color: var(--md-sys-color-primary);
color: var(--md-sys-color-on-primary);
}
.cta-button:hover {
box-shadow: 0 2px 8px rgba(103, 80, 164, 0.3);
}
.demo-button {
background-color: var(--md-sys-color-surface-variant);
color: var(--md-sys-color-on-surface-variant);
}
.demo-button:hover {
background-color: var(--md-sys-color-on-surface-variant);
color: var(--md-sys-color-surface-variant);
}`;
} else if (framework === 'tailwind') {
return `@tailwind base;
@tailwind components;
@tailwind utilities;
/* Custom components */
@layer components {
.app-header {
@apply bg-primary-500 text-white p-6 text-center;
}
.app-main {
@apply min-h-screen bg-gray-50 py-8;
}
.container {
@apply max-w-md mx-auto px-4;
}
.welcome-card {
@apply bg-white rounded-xl shadow-lg p-6 text-center;
}
.features {
@apply grid grid-cols-3 gap-4 my-6;
}
.feature {
@apply flex flex-col items-center space-y-2;
}
.icon {
@apply text-2xl;
}
.cta-button {
@apply bg-primary-500 hover:bg-primary-600 text-white font-bold py-3 px-6 rounded-lg transition-colors mr-2;
}
.demo-button {
@apply bg-gray-500 hover:bg-gray-600 text-white font-bold py-3 px-6 rounded-lg transition-colors;
}
}`;
}
return `/* MusPE Framework - Mobile-First CSS */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f8f9fa;
}
/* App Structure */
.app-header {
background: linear-gradient(135deg, #3b82f6, #2563eb);
color: white;
padding: 2rem;
text-align: center;
}
.app-header h1 {
font-size: 1.8rem;
margin-bottom: 0.5rem;
}
.app-main {
min-height: 100vh;
padding: 2rem 1rem;
}
.container {
max-width: 400px;
margin: 0 auto;
}
/* Welcome Card */
.welcome-card {
background: white;
border-radius: 1rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 2rem;
text-align: center;
}
.welcome-card h2 {
color: #1f2937;
margin-bottom: 1rem;
}
.welcome-card p {
color: #6b7280;
margin-bottom: 2rem;
}
/* Features */
.features {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin: 2rem 0;
}
.feature {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.icon {
font-size: 1.5rem;
}
/* Button */
.cta-button {
background: #3b82f6;
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-weight: 600;
cursor: pointer;
transition: background-color 0.2s;
margin-right: 0.5rem;
}
.cta-button:hover {
background: #2563eb;
}
.demo-button {
background: #6b7280;
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-weight: 600;
cursor: pointer;
transition: background-color 0.2s;
}
.demo-button:hover {
background: #4b5563;
}
/* Mobile Optimizations */
@media (max-width: 480px) {
.app-header {
padding: 1.5rem;
}
.app-header h1 {
font-size: 1.5rem;
}
.container {
padding: 0 1rem;
}
.welcome-card {
padding: 1.5rem;
}
.features {
gap: 0.75rem;
}
}
/* Touch-friendly interactions */
@media (hover: none) {
.cta-button:hover {
background: #3b82f6;
}
.cta-button:active {
background: #1d4ed8;
transform: scale(0.98);
}
}
/* Material Symbols Icons Utility Classes */
.material-symbols-outlined {
font-variation-settings:
'FILL' 0,
'wght' 400,
'GRAD' 0,
'opsz' 24;
user-select: none;
vertical-align: middle;
}
/* Icon size variations */
.icon-xs { font-size: 16px; }
.icon-sm { font-size: 20px; }
.icon-md { font-size: 24px; }
.icon-lg { font-size: 32px; }
.icon-xl { font-size: 48px; }
/* Icon color variations */
.icon-primary { color: #3b82f6; }
.icon-secondary { color: #6b7280; }
.icon-success { color: #10b981; }
.icon-warning { color: #f59e0b; }
.icon-error { color: #ef4444; }
/* Basic Swiper Customizations */
.swiper {
width: 100%;
height: 100%;
}
.swiper-slide {
display: flex;
justify-content: center;
align-items: center;
background: #f8fafc;
border-radius: 0.5rem;
}
.swiper-button-next,
.swiper-button-prev {
color: #3b82f6;
}
.swiper-pagination-bullet {
background: #d1d5db;
}
.swiper-pagination-bullet-active {
background: #3b82f6;
}
/* Material.io Components Integration */
md-filled-button,
md-outlined-button,
md-text-button {
--md-sys-color-primary: #3b82f6;
--md-sys-color-on-primary: #ffffff;
--md-sys-color-surface-variant: #f1f5f9;
}
md-card {
--md-sys-color-surface-container-highest: #ffffff;
--md-sys-color-outline: #e2e8f0;
margin: 1rem 0;
}
md-top-app-bar {
--md-sys-color-surface-container: #3b82f6;
--md-sys-color-on-surface: #ffffff;
}
md-navigation-drawer {
--md-sys-color-surface-container-low: #f8fafc;
}
md-fab {
--md-sys-color-primary-container: #3b82f6;
--md-sys-color-on-primary-container: #ffffff;
}
md-dialog {
--md-sys-color-surface-container-highest: #ffffff;
}
md-list {
--md-sys-color-surface: #ffffff;
}
md-list-item {
--md-sys-color-on-surface: #1e293b;
--md-sys-color-surface-variant: #f1f5f9;
}
md-text-field,
md-outlined-text-field {
--md-sys-color-primary: #3b82f6;
--md-sys-color-outline: #cbd5e1;
margin: 0.5rem 0;
width: 100%;
}
md-checkbox,
md-radio {
--md-sys-color-primary: #3b82f6;
}
md-switch {
--md-sys-color-primary: #3b82f6;
--md-sys-color-outline: #cbd5e1;
}
md-chip-set {
margin: 0.5rem 0;
}
md-assist-chip,
md-filter-chip,
md-input-chip,
md-suggestion-chip {
--md-sys-color-primary: #3b82f6;
--md-sys-color-surface-variant: #f1f5f9;
}
md-linear-progress,
md-circular-progress {
--md-sys-color-primary: #3b82f6;
}
md-slider {
--md-sys-color-primary: #3b82f6;
width: 100%;
margin: 1rem 0;
}
md-tabs {
--md-sys-color-primary: #3b82f6;
--md-sys-color-surface: #ffffff;
}
md-primary-tab {
--md-sys-color-primary: #3b82f6;
}
/* Material.io Layout Helpers */
.material-layout {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.material-header {
position: sticky;
top: 0;
z-index: 100;
}
.material-main {
flex: 1;
display: flex;
}
.material-sidebar {
width: 280px;
border-right: 1px solid #e2e8f0;
}
.material-content {
flex: 1;
padding: 1rem;
max-width: 100%;
overflow-x: auto;
}
.material-footer {
background: #f8fafc;
padding: 1rem;
text-align: center;
border-top: 1px solid #e2e8f0;
}
/* Material.io responsive grid */
.material-grid {
display: grid;
gap: 1rem;
padding: 1rem;
}
.material-grid-2 { grid-template-columns: repeat(2, 1fr); }
.material-grid-3 { grid-template-columns: repeat(3, 1fr); }
.material-grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 768px) {
.material-grid-2,
.material-grid-3,
.material-grid-4 {
grid-template-columns: 1fr;
}
.material-sidebar {
width: 100%;
border-right: none;
border-bottom: 1px solid #e2e8f0;
}
.material-main {
flex-direction: column;
}
}
/* Hero slider specific styles */
.hero-slider {
height: 300px;
margin: 2rem 0;
border-radius: 1rem;
overflow: hidden;
}
.hero-slider .swiper-slide {
background: linear-gradient(45deg, #3b82f6, #10b981);
color: white;
text-align: center;
padding: 2rem;
}
/* Card slider styles */
.card-slider {
padding: 1rem 0;
}
.card-slider .swiper-slide {
background: white;
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
margin: 0.5rem;
height: auto;
}`;
}
function generateJSTemplate(template, framework) {
const materialImports = framework === 'material' ? `
// Import Material Design 3 Components
import '@material/web/button/filled-button.js';
import '@material/web/button/outlined-button.js';
import '@material/web/button/text-button.js';
import '@material/web/card/card.js';
import '@material/web/textfield/filled-text-field.js';
import '@material/web/textfield/outlined-text-field.js';
import '@material/web/fab/fab.js';
import '@material/web/checkbox/checkbox.js';
import '@material/web/radio/radio.js';
import '@material/web/switch/switch.js';
import '@material/web/chips/chip-set.js';
import '@material/web/chips/assist-chip.js';
import '@material/web/progress/linear-progress.js';
import '@material/web/progress/circular-progress.js';
import '@material/web/dialog/dialog.js';
import '@material/web/list/list.js';
import '@material/web/list/list-item.js';
// Material Design 3 Theme Configuration
import { argbFromHex, themeFromSourceColor, applyTheme } from '@material/material-color-utilities';
// Apply Material Design 3 Theme
function initMaterialTheme() {
// Get theme from source color
const theme = themeFromSourceColor(argbFromHex('#6750A4'));
// Apply the theme to the body by updating custom properties
applyTheme(theme, {target: document.body, dark: false});
}
// Initialize theme on load
document.addEventListener('DOMContentLoaded', initMaterialTheme);` : `
// Import basic Material.io Components for layout system
import '@material/web/button/filled-button.js';
import '@material/web/button/outlined-button.js';
import '@material/web/card/card.js';
import '@material/web/fab/fab.js';`;
return `// MusPE Framework - Main Application Script with CSS imports and Swiper.js
// Import CSS files through JavaScript (works with bundlers like Vite)
import './styles/main.css';
${framework === 'tailwind' ? "import './styles/tailwind.css';" : ''}
${framework === 'bootstrap' ? "import 'bootstrap/dist/css/bootstrap.min.css';" : ''}
// Import Swiper CSS and JS
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import 'swiper/css/scrollbar';
import { Swiper, Navigation, Pagination, Scrollbar, Autoplay } from 'swiper';
${materialImports}
// Configure Swiper with desired modules
Swiper.use([Navigation, Pagination, Scrollbar, Autoplay]);
// Make Swiper globally available
window.Swiper = Swiper;
// Global Swiper utility functions
window.createSwiper = function(container, options = {}) {
const defaultOptions = {
modules: [Navigation, Pagination],
slidesPerView: 1,
spaceBetween: 20,
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
pagination: {
el: '.swiper-pagination',
clickable: true,
},
...options
};
return new Swiper(container, defaultOptions);
};
// Example Swiper initialization
function initializeSliders() {
// Hero slider example
const heroSlider = document.querySelector('.hero-slider');
if (heroSlider) {
createSwiper(heroSlider, {
modules: [Navigation, Pagination, Autoplay],
autoplay: {
delay: 5000,
disableOnInteraction: false,
},
loop: true
});
}
// Card slider example
const cardSlider = document.querySelector('.card-slider');
if (cardSlider) {
createSwiper(cardSlider, {
slidesPerView: 1,
spaceBetween: 20,
breakpoints: {
640: { slidesPerView: 2 },
768: { slidesPerView: 3 },
1024: { slidesPerView: 4 }
}
});
}
}
// Global functions demonstrating MusPE utilities
function showInfo() {
// Create modal using Material Icons
const modal = MusPE.dom.create('div', {
class: 'modal-overlay',
style: {
position: 'fixed',
top: '0',
left: '0',
width: '100%',
height: '100%',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: '1000'
},
events: {
click: (e) => {
if (e.target === modal) {
MusPE.dom.fadeOut(modal, 200).then(() => MusPE.dom.remove(modal));
}
}
}
});
const content = MusPE.dom.create('div', {
class: 'modal-content',
style: {
background: 'white',
borderRadius: '1rem',
padding: '2rem',
maxWidth: '500px',
margin: '1rem'
},
html: \`
<div style="display: flex; align-items: center; margin-bottom: 1rem;">
<span class="material-symbols-outlined" style="margin-right: 0.5rem; color: #3b82f6; font-size: 2rem;">rocket_launch</span>
<h3 style="margin: 0;">Welcome to MusPE!</h3>
</div>
<p><strong>Your app includes:</strong></p>
<ul style="margin: 1rem 0;">
<li><span class="material-symbols-outlined" style="font-size: 1rem; vertical-align: middle;">palette</span> CSS imports via JavaScript</li>
<li><span class="material-symbols-outlined" style="font-size: 1rem; vertical-align: middle;">view_carousel</span> Swiper.js for slide components</li>
<li><span class="material-symbols-outlined" style="font-size: 1rem; vertical-align: middle;">widgets</span> Material.io web components</li>
<li><span class="material-symbols-outlined" style="font-size: 1rem; vertical-align: middle;">grid_view</span> Advanced layout system</li>
<li><span class="material-symbols-outlined" style="font-size: 1rem; vertical-align: middle;">description</span> Dynamic template engine</li>
<li><span class="material-symbols-outlined" style="font-size: 1rem; vertical-align: middle;">apps</span> Material Symbols icons</li>
<li><span class="material-symbols-outlined" style="font-size: 1rem; vertical-align: middle;">code</span> Modern development setup</li>
</ul>
<p><strong>Quick Examples:</strong></p>
<div style="margin: 1rem 0;">
<md-filled-button onclick="demoTemplates()">
<span slot="icon" class="material-symbols-outlined">auto_awesome</span>
Try Templates
</md-filled-button>
<md-outlined-button onclick="demoLayouts()" style="margin-left: 0.5rem;">
<span slot="icon" class="material-symbols-outlined">dashboard</span>
Try Layouts
</md-outlined-button>
</div>
<p><strong>Next steps:</strong></p>
<ul style="margin: 1rem 0;">
<li>Edit <code>src/layouts</code> for page layouts</li>
<li>Use <code>src/templates</code> for reusable UI patterns</li>
<li>Create Material.io components with <code><md-*></code> tags</li>
<li>Use <code>Templates.render()</code> for dynamic content</li>
<li>Run <code>muspe build</code> when ready to deploy</li>
</ul>
<md-filled-button onclick="MusPE.dom.fadeOut(this.closest('.modal-overlay'), 200).then(() => MusPE.dom.remove(this.closest('.modal-overlay')))"
style="display: flex; align-items: center; gap: 0.5rem; margin-top: 1rem;">
<span slot="icon" class="material-symbols-outlined">check</span>
Got it!
</md-filled-button>
\`
});
MusPE.dom.append(modal, content);
MusPE.dom.append('body', modal);
MusPE.dom.fadeIn(modal, 200);
}
async function demoFeatures() {
try {
const demoContainer = MusPE.$('.welcome-card');
// Demo Swiper creation
const swiperDemo = MusPE.dom.create('div', {
class: 'swiper-demo',
style: {
background: '#f0f9ff',
border: '2px solid #3b82f6',
borderRadius: '0.5rem',
padding: '1rem',
margin: '1rem 0'
},
html: \`
<div style="display: flex; align-items: center; margin-bottom: 1rem;">
<span class="material-symbols-outlined" style="margin-right: 0.5rem; color: #3b82f6;">view_carousel</span>
<strong>Swiper Demo</strong>
</div>
<div class="swiper demo-swiper" style="height: 200px;">
<div class="swiper-wrapper">
<div class="swiper-slide" style="background: linear-gradient(45deg, #3b82f6, #10b981); color: white; display: flex; align-items: center; justify-content: center; border-radius: 0.5rem;">
<div style="text-align: center;">
<span class="material-symbols-outlined" style="font-size: 3rem; margin-bottom: 0.5rem;">star</span>
<h3>Slide 1</h3>
<p>Beautiful slide components</p>
</div>
</div>
<div class="swiper-slide" style="background: linear-gradient(45deg, #f59e0b, #ef4444); color: white; display: flex; align-items: center; justify-content: center; border-radius: 0.5rem;">
<div style="text-align: center;">
<span class="material-symbols-outlined" style="font-size: 3rem; margin-bottom: 0.5rem;">speed</span>
<h3>Slide 2</h3>
<p>High performance</p>
</div>
</div>
<div class="swiper-slide" style="background: linear-gradient(45deg, #8b5cf6, #ec4899); color: white; display: flex; align-items: center; justify-content: center; border-radius: 0.5rem;">
<div style="text-align: center;">
<span class="material-symbols-outlined" style="font-size: 3rem; margin-bottom: 0.5rem;">devices</span>
<h3>Slide 3</h3>
<p>Responsive design</p>
</div>
</div>
</div>
<div class="swiper-pagination"></div>
<div class="swiper-button-next"></div>
<div class="swiper-button-prev"></div>
</div>
\`
});
MusPE.dom.append(demoContainer, swiperDemo);
await MusPE.dom.fadeIn(swiperDemo, 300);
// Initialize the demo swiper
setTimeout(() => {
createSwiper('.demo-swiper', {
modules: [Navigation, Pagination, Autoplay],
autoplay: {
delay: 3000,
disableOnInteraction: false,
},
loop: true
});
}, 100);
// Clean up after 10 seconds
setTimeout(() => {
MusPE.dom.fadeOut(swiperDemo, 300).then(() => MusPE.dom.remove(swiperDemo));
}, 10000);
} catch (error) {
console.error('Demo error:', error);
}
}
class MusPEApp {
constructor() {
this.init();
}
init() {
console.log('š MusPE App initialized with Material.io and advanced template system');
console.log('š¦ Available features:', {
dom: 'MusPE.dom or MusPE.$',
http: 'MusPE.http or MusPE.fetch',
swiper: 'createSwiper() or new Swiper()',
material: 'Material.io web components',
layouts: 'BaseLayout, MaterialLayout, MobileLayout, DashboardLayout',
templates: 'Templates.render() and common templates',
icons: 'Material Symbols (material-symbols-outlined)',
css: 'Imported via JavaScript modules'
});
this.setupEventListeners();
this.detectDevice();
this.initializeSliders();
this.setupLayoutSystem();
this.demoTemplateSystem();
${template === 'pwa' || template === 'hybrid' ? 'this.registerServiceWorker();' : ''}
}
setupEventListeners() {
MusPE.ready(() => {
console.log('š± DOM Content Loaded');
// Initialize any existing sliders
this.initializeSliders();
// Demo event delegation with Material Icons
MusPE.dom.on(document, 'click', '.feature', (e) => {
const feature = e.target.closest('.feature');
MusPE.dom.addClass(feature, 'clicked');
setTimeout(() => MusPE.dom.removeClass(feature, 'clicked'), 200);
});
});
// Handle touch events for mobile
if ('ontouchstart' in window) {
this.setupTouchEvents();
}
}
initializeSliders() {
// Initialize any Swiper sliders found in the page
initializeSliders();
}
setupTouchEvents() {
// Prevent double-tap zoom
let lastTouchEnd = 0;
document.addEventListener('touchend', (event) => {
const now = (new Date()).getTime();
if (now - lastTouchEnd <= 300) {
event.preventDefault();
}
lastTouchEnd = now;
}, false);
}
detectDevice() {
const device = MusPE.env;
console.log(\`š± Device Info:\`, {
mobile: device.isMobile,
tablet: device.isTablet,
touch: device.hasTouch,
online: device.isOnline,
pwa: device.isPWA
});
}
setupLayoutSystem() {
// Demo layout setup
console.log('šļø Setting up layout system...');
// You can uncomment and modify these to use different layouts:
// const layout = LayoutFactory.create('material', {
// title: 'My App',
// hasSidebar: false,
// fabAction: {
// icon: 'add',
// label: 'Add Item',
// handler: () => alert('FAB clicked!')
// }
// });
// layout.applyTo('#app', document.querySelector('#app').innerHTML);
}
demoTemplateSystem() {
// Demo template usage
console.log('š Template system ready');
// Example: Add a stats widget after 3 seconds
setTimeout(() => {
const statsHtml = Templates.statsWidget({
title: 'App Users',
value: '1,234',
icon: 'people',
color: 'primary',
change: '+5.2%',
changeType: 'positive',
subtitle: 'Active users this month'
});
// Uncomment to add to page:
// const container = document.querySelector('.welcome-card');
// if (container) {
// container.insertAdjacentHTML('afterend', statsHtml);
// }
}, 3000);
}
${template === 'pwa' || template === 'hybrid' ? `
async registerServiceWorker() {
if ('serviceWorker' in navigator) {
try {
const registration = await navigator.serviceWorker.register('./sw.js');
console.log('š§ ServiceWorker registered:', registration);
} catch (error) {
console.log('ā ServiceWorker registration failed:', error);
}
}
}` : ''}
}
// Initialize app when MusPE is ready
MusPE.ready(() => {
const app = new MusPEApp();
// Example of using MusPE features
MusPE.on('app:background', () => console.log('š± App went to background'));
MusPE.on('app:foreground', () => console.log('š± App came to foreground'));
MusPE.on('online', () => console.log('š App is online'));
MusPE.on('offline', () => console.log('š” App is offline'));
});
// Export for module usage
if (typeof module !== 'undefined' && module.exports) {
module.exports = MusPEApp;
}
// Demo Templates Function
window.demoTemplates = function() {
const container = document.querySelector('.welcome-card');
if (!container) return;
// Demo various templates
const templates = [
{
name: 'User Profile',
html: Templates.userProfile({
name: 'Sarah Wilson',
email: 'sarah@example.com',
role: 'Designer',
id: '123'
})
},
{
name: 'Stats Widget',
html: Templates.statsWidget({
title: 'Active Users',
value: '2,451',
icon: 'people',
color: 'success',
change: '+12%',
changeType: 'positive'
})
},
{
name: 'Alert Message',
html: Templates.alert({
type: 'success',
title: 'Success!',
message: 'Template system is working perfectly.',
dismissible: true
})
}
];
templates.forEach((template, index) => {
setTimeout(() => {
const demo = MusPE.dom.create('div', {
style: { margin: '1rem 0', opacity: '0' },
html: \`
<h4 style="color: #3b82f6; margin: 0.5rem 0;">š \${template.name} Template</h4>
\${template.html}
\`
});
container.insertAdjacentElement('afterend', demo);
MusPE.dom.fadeIn(demo, 300);
// Clean up after 8 seconds
setTimeout(() => {
MusPE.dom.fadeOut(demo, 300).then(() => MusPE.dom.remove(demo));
}, 8000);
}, index * 1000);
});
};
// Demo Layouts Function
window.demoLayouts = function() {
const originalContent = document.querySelector('#app').innerHTML;
// Demo Material Layout
const materialLayout = LayoutFactory.create('material', {
title: 'Material Layout Demo',
hasSidebar: true,
fabAction: {
icon: 'refresh',
label: 'Restore',
handler: () => {
document.querySelector('#app').innerHTML = originalContent;
// Re-initialize the app
setTimeout(() => {
const app = new MusPEApp();
}, 100);
}
}
});
const demoContent = \`
<div style="padding: 2rem;">
<h2>šļø Material Layout Demo</h2>
<p>This demonstrates the Material Design layout with sidebar and FAB.</p>
<div class="material-grid material-grid-2" style="margin: 2rem 0;">
\${Templates.statsWidget({
title: 'Dashboard Views',
value: '1,234',
icon: 'visibility',
color: 'primary'
})}
\${Templates.statsWidget({
title: 'Active Users',
value: '567',
icon: 'people',
color: 'success'
})}
</div>
\${Templates.alert({
type: 'info',
title: 'Layout Demo',
message: 'Click the refresh FAB (bottom-right) to return to the original layout.',
dismissible: false
})}
<div style="margin-top: 2rem;">
<md-outlined-button onclick="document.querySelector('#app').innerHTML = \`\${originalContent.replace(/'/g, '\\\\\'')}\`; setTimeout(() => new MusPEApp(), 100);">
<span slot="icon" class="material-symbols-outlined">arrow_back</span>
Back to Original
</md-outlined-button>
</div>
</div>
\`;
materialLayout.applyTo('#app', demoContent);
};`;
}
function generateComponentTemplate(framework) {
return `// MusPE Component - AppHeader
class AppHeader {
constructor(options = {}) {
this.title = options.title || 'MusPE App';
this.subtitle = options.subtitle || 'Mobile-First Framework';
this.element = null;
}
render() {
this.element = document.createElement('header');
this.element.className = 'app-header';
this.element.innerHTML = \`
<h1>\${this.title}</h1>
<p>\${this.subtitle}</p>
\`;
return this.element;
}
update(title, subtitle) {
if (this.element) {
this.title = title;
this.subtitle = subtitle;
this.element.querySelector('h1').textContent = title;
this.element.querySelector('p').textContent = subtitle;
}
}
destroy() {
if (this.element && this.element.parentNode) {
this.element.parentNode.removeChild(this.element);
}
}
}
// Export for module usage
if (typeof module !== 'undefined' && module.exports) {
module.exports = AppHeader;
}`;
}
async function generatePWAFiles(projectPath, projectName) {
// Generate manifest.json
const manifest = {
name: projectName,
short_name: projectName,
description: `${projectName} - Built with MusPE`,
start_url: '/',
display: 'standalone',
background_color: '#ffffff',
theme_color: '#3b82f6',
icons: [
{
src: './assets/icons/icon-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: './assets/icons/icon-512x512.png',
sizes: '512x512',
type: 'image/png'
}
]
};
await fs.writeJSON(path.join(projectPath, 'public/manifest.json'), manifest, { spaces: 2 });
// Generate service worker
const serviceWorker = `// MusPE Service Worker
const CACHE_NAME = '${projectName}-v1';
const urlsToCache = [
'