Files
demo_sites/builder/template_registry.py
2026-05-17 18:29:30 -05:00

140 lines
4.5 KiB
Python

"""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