140 lines
4.5 KiB
Python
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
|