/**
* Theme Manager — Singleton
* Manages the active theme state and provides access to all available themes.
*/
import { ThemeStandard } from './types.js';
import { THEME_PRESETS } from './presets/index.js';
import { loadCustomThemes, getCustomThemeDir } from './custom/loader.js';
let activeTheme: ThemeStandard | null = null;
let customThemesCache: Record<string, ThemeStandard> | null = null;
/**
* Get all available themes (built-in presets + custom themes).
*/
export function getAvailableThemes(): Record<string, ThemeStandard> {
if (!customThemesCache) {
customThemesCache = loadCustomThemes();
}
return { ...THEME_PRESETS, ...customThemesCache };
}
/**
* Reload custom themes from disk (clears cache).
*/
export function reloadCustomThemes(): void {
customThemesCache = null;
}
/**
* Set the active theme by ID.
* Returns the theme if found, null otherwise.
*/
export function setActiveTheme(themeId: string): ThemeStandard | null {
const themes = getAvailableThemes();
const theme = themes[themeId] ?? null;
activeTheme = theme;
return theme;
}
/**
* Get the currently active theme (null if none).
*/
export function getActiveTheme(): ThemeStandard | null {
return activeTheme;
}
/**
* Clear the active theme (return to base Magento standards only).
*/
export function clearActiveTheme(): void {
activeTheme = null;
}
/**
* Get a formatted summary of all available themes.
*/
export function getThemesSummary(): string {
const themes = getAvailableThemes();
const active = getActiveTheme();
const lines: string[] = [];
lines.push('## Available Theme Standards\n');
const presetIds = Object.keys(THEME_PRESETS);
const customIds = Object.keys(themes).filter(id => !presetIds.includes(id));
lines.push('### Built-in Presets');
for (const id of presetIds) {
const theme = themes[id];
const marker = active?.id === id ? ' ✅ ACTIVE' : '';
lines.push(`- **${theme.name}** (\`${id}\`)${marker}`);
lines.push(` ${theme.description}`);
lines.push(` Rules: ${theme.validationRules.length} | Best Practices: ${theme.bestPractices.length}`);
}
if (customIds.length > 0) {
lines.push('\n### Custom Themes');
for (const id of customIds) {
const theme = themes[id];
const marker = active?.id === id ? ' ✅ ACTIVE' : '';
lines.push(`- **${theme.name}** (\`${id}\`)${marker}`);
lines.push(` ${theme.description}`);
lines.push(` Rules: ${theme.validationRules.length} | Best Practices: ${theme.bestPractices.length}`);
}
}
lines.push(`\nCustom themes directory: \`${getCustomThemeDir()}\``);
lines.push('Place .json files there to add custom theme standards.');
return lines.join('\n');
}
/**
* Get detailed info about a specific theme or the active theme.
*/
export function getThemeInfo(themeId?: string): string {
const theme = themeId ? getAvailableThemes()[themeId] : getActiveTheme();
if (!theme) {
if (themeId) {
return `Theme "${themeId}" not found. Use manage_theme({ action: "list" }) to see available themes.`;
}
return 'No active theme. Using base Magento standards only.\nUse manage_theme({ action: "set", themeId: "hyva" }) to activate a theme.';
}
const lines: string[] = [];
const isActive = getActiveTheme()?.id === theme.id;
lines.push(`## ${theme.name} (v${theme.version})${isActive ? ' — ACTIVE' : ''}`);
lines.push(`\n${theme.description}\n`);
lines.push('### Technologies to USE:');
for (const tech of theme.technologies.use) {
lines.push(` ✓ ${tech}`);
}
lines.push('\n### Technologies to AVOID:');
for (const tech of theme.technologies.avoid) {
lines.push(` ✗ ${tech}`);
}
lines.push(`\n### Validation Rules (${theme.validationRules.length}):`);
for (const rule of theme.validationRules) {
lines.push(` [${rule.type.toUpperCase()} sev:${rule.severity}] ${rule.rule}: ${rule.message}`);
if (rule.suggestion) {
lines.push(` → ${rule.suggestion}`);
}
}
lines.push(`\n### Best Practices (${theme.bestPractices.length}):`);
for (const practice of theme.bestPractices) {
lines.push(` • ${practice}`);
}
lines.push(`\n### Pattern Overrides (${theme.patternOverrides.length}):`);
for (const override of theme.patternOverrides) {
lines.push(` • "${override.taskKeyword}" → ${override.correctPattern}`);
}
return lines.join('\n');
}