"""Client site template folders under project templates/template_*.""" from __future__ import annotations from pathlib import Path from django.conf import settings CLIENT_TEMPLATES_ROOT = Path(settings.BASE_DIR) / "templates" COPY_EXCLUDE_DIR_NAMES = frozenset( { "__MACOSX", "Documentation", "documentation", "node_modules", ".git", ".next", ".gitignore", } ) # Per-folder config: site path relative to templates/template_N/ TEMPLATE_FOLDER_CONFIG: dict[str, dict] = { "template_1": { "name": "NeuralSync", "description": "AI SaaS marketing template with multi-page layout.", "site_path": "Main File/NeuralSync", "kind": "static_html", "supports_basic": True, "supports_django": True, "brand_tokens": ["NeuralSync"], "hero_strings": ["Transform Your Business with AI Powered Solutions"], }, "template_2": { "name": "Eventio Conference", "description": "Next.js event and conference site (requires npm to run).", "site_path": "eventio/eventio-HTML", "kind": "nextjs", "supports_basic": True, "supports_django": True, "brand_tokens": ["Eventio", "EVENTIO"], "hero_strings": [ "Global Business Conference", "San Francisco, California", ], }, "template_3": { "name": "Waves Business", "description": "Multi-layout business one-page template pack.", "site_path": "site", "kind": "static_html", "supports_basic": True, "supports_django": True, "brand_tokens": ["Waves", "Welcome to Waves"], "hero_strings": ["Welcome to Waves", "A perfect template for both corporate and creative projects."], }, "template_4": { "name": "Multipurpose Business", "description": "Bootstrap business landing template with services and pricing.", "site_path": "site", "kind": "static_html", "supports_basic": True, "supports_django": True, "brand_tokens": ["Business template", "multipurpose business template"], "hero_strings": ["Business template"], }, } def _auto_detect_site_path(folder: Path) -> str | None: site_dir = folder / "site" if (site_dir / "index.html").exists(): return "site" candidates: list[tuple[int, str]] = [] for index_file in folder.rglob("index.html"): parts_lower = {part.lower() for part in index_file.parts} if "documentation" in parts_lower: continue rel_parent = index_file.parent.relative_to(folder) depth = len(rel_parent.parts) candidates.append((depth, str(rel_parent))) if not candidates: return None candidates.sort(key=lambda item: (item[0], len(item[1]))) return candidates[0][1] def discover_client_templates() -> list[dict]: definitions: list[dict] = [] for folder in sorted(CLIENT_TEMPLATES_ROOT.glob("template_*")): if not folder.is_dir(): continue folder_key = folder.name config = TEMPLATE_FOLDER_CONFIG.get(folder_key, {}) site_path = config.get("site_path") or _auto_detect_site_path(folder) if not site_path: continue source = CLIENT_TEMPLATES_ROOT / folder_key / site_path if not source.exists(): continue slug = folder_key.replace("_", "-") definitions.append( { "slug": slug, "name": config.get("name", folder_key.replace("_", " ").title()), "description": config.get( "description", f"Client template from templates/{folder_key}/", ), "source_folder": str(Path("templates") / folder_key / site_path), "kind": config.get("kind", "static_html"), "supports_basic": config.get("supports_basic", True), "supports_django": config.get("supports_django", True), "brand_tokens": config.get("brand_tokens", []), "hero_strings": config.get("hero_strings", []), "is_active": True, } ) return definitions def get_template_config(slug: str) -> dict | None: for item in discover_client_templates(): if item["slug"] == slug: return item return None def resolve_source_path(source_folder: str) -> Path: return Path(settings.BASE_DIR) / source_folder