#!/usr/bin/env python3
"""Static site generator for SciSiteForge."""
from __future__ import annotations
import argparse
from pathlib import Path
import sys
from typing import Any
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from scisiteforge.config import DEFAULT_THEME, load_config, save_config
from scisiteforge.content import (
SiteContent,
cards_from_config,
load_citegeist_cards,
load_didactopus_cards,
load_doclift_cards,
load_groundrecall_cards,
)
from scisiteforge.render import html_escape, read_text, render_template, write_text
from scisiteforge.notebook import load_notebooks, render_notebooks
from scisiteforge.themes import available_themes, get_theme, materialize_theme
def _prompt_for_config() -> dict[str, Any]:
print("=== SciSiteForge Site Config ===")
themes = available_themes()
print("Available themes:")
for theme in themes:
print(f" - {theme.name}: {theme.description}")
theme_name = input(f"Theme name (default: {DEFAULT_THEME}): ").strip() or DEFAULT_THEME
languages_input = input("Languages (code:name pairs, comma-separated; default: en:English): ").strip() or "en:English"
languages = []
for pair in languages_input.split(","):
code, name = pair.strip().split(":", 1)
languages.append({"code": code.strip(), "name": name.strip()})
return {
"lang": input("Language code (default: en): ").strip() or "en",
"title": input("Page title (default: SciSiteForge Preview): ").strip() or "SciSiteForge Preview",
"site_title": input("Site name (default: SciSiteForge): ").strip() or "SciSiteForge",
"license": input("License text (default: CC BY-SA 4.0): ").strip() or "CC BY-SA 4.0",
"github_url": input("GitHub URL (optional): ").strip() or "https://github.com/",
"contact_email": input("Contact email (optional): ").strip() or "admin@example.org",
"theme": theme_name,
"languages": languages,
"navigation": [
{"label": "Home", "href": "/"},
],
"hero": {
"kicker": "Preview",
"title": "A site shell that can adapt to more than one audience.",
"lede": "SciSiteForge now supports multiple theme presets and local content loaders for reusable science sites.",
"actions": [
{"label": "Read the overview", "href": "#overview", "primary": True},
{"label": "Theme catalog", "href": "#themes", "primary": False},
],
},
"content_sources": {},
"content": {},
"notebooks": [],
}
def _language_options_html(languages: list[dict[str, str]], current_lang: str) -> str:
visible_languages = [
item
for item in languages
if item.get("coverage", True) or item.get("code") == current_lang
]
if len(visible_languages) <= 1:
return ""
return "\n".join(
f''
for item in visible_languages
)
def _language_policy_html(language_policy: dict[str, Any]) -> str:
planned_languages = language_policy.get("planned_languages", [])
if not planned_languages:
return ""
planned_names = ", ".join(html_escape(item.get("name", item.get("code", ""))) for item in planned_languages if item.get("name") or item.get("code"))
if not planned_names:
return ""
return f'
Planned languages: {planned_names}
'
def _roadmap_html(roadmap: list[dict[str, Any]], language_policy: dict[str, Any]) -> str:
items = []
for item in roadmap:
if not isinstance(item, dict):
continue
title = html_escape(item.get("title", "Roadmap item"))
body = html_escape(item.get("body", ""))
status = html_escape(item.get("status", "Planned"))
items.append(f'
{title}{status}
{body}
')
planned_languages = language_policy.get("planned_languages", [])
planned_names = ", ".join(
html_escape(item.get("name", item.get("code", "")))
for item in planned_languages
if isinstance(item, dict) and (item.get("name") or item.get("code"))
)
if planned_names:
items.append(
'
Multilingual accessPlanned'
f'
Language options will appear only when reviewed coverage exists. Target languages under consideration: {planned_names}.