``)是写死的静态 HTML,不随 DATA 变化——给出新标题时替换这两处,并把 MANIFEST 的 系统.名称 一并 同步;不给则保持模板原文。单文件/结构化两条填充路径共用。 """ if title: overrides = dict(manifest_overrides or {}) system = overrides.get("系统") system = dict(system) if isinstance(system, dict) else {} system["名称"] = title overrides["系统"] = system manifest_overrides = overrides escaped = _escape_html(title) text = re.sub(r"
).*?(
)", lambda m: f"{m.group(1)}{escaped_sub}{m.group(2)}", text, count=1, flags=re.DOTALL, ) return text, manifest_overrides def _fill_structured( template_id: str, data_obj: Any, *, manifest_overrides: dict[str, Any] | None, update_date: date | None, title: str | None, subtitle: str | None, ) -> str: """结构化模板填充:标记组合出完整单文件 HTML(数据内嵌 JSON 元素 + runtime 内联)。""" skeleton, manifest_overrides = _apply_page_titles( _structured_text(template_id, "template.html"), title, subtitle, manifest_overrides ) manifest = _read_default_manifest(template_id) if update_date is not None: system = manifest.get("系统") if isinstance(system, dict): system["更新时间"] = update_date.isoformat() if manifest_overrides: _deep_merge(manifest, manifest_overrides) return compose_structured( skeleton=skeleton, runtime=_structured_text(template_id, "runtime.js"), data=data_obj, manifest=manifest, ) def fill_template( template_id: str, data_obj: Any, *, manifest_overrides: dict[str, Any] | None = None, update_date: date | None = None, title: str | None = None, subtitle: str | None = None, ) -> str: """把 ``data_obj`` 填进模板,产出完整 HTML 文档。 单文件模板:行级替换 ``const DATA=…``;结构化模板::func:`_fill_structured` 标记组合。可选地更新 MANIFEST 的「系统.更新时间」(``update_date``,默认不动) 以及任意 ``manifest_overrides``(深合并到解析后的 MANIFEST,如改系统名称)。 ``title``/``subtitle`` 来自转换结果里的「页面标题/页面副标题」:模板页头 ()是写死的静态 HTML,不随 DATA
变化——给出新标题时替换这三处,并把 MANIFEST 的 系统.名称 一并同步;
不给则保持模板原文。
"""
spec = _require(template_id)
if spec.structured:
return _fill_structured(
template_id,
data_obj,
manifest_overrides=manifest_overrides,
update_date=update_date,
title=title,
subtitle=subtitle,
)
text = read_template_text(template_id)
text, manifest_overrides = _apply_page_titles(text, title, subtitle, manifest_overrides)
data_re = _const_line_re(spec.data_var)
if data_re.search(text) is None:
raise TemplateError(f"模板 {template_id} 中未找到 const {spec.data_var}= 行")
new_data_line = _dump_const(spec.data_var, data_obj)
text = data_re.sub(lambda _m: new_data_line, text, count=1)
if spec.manifest_var and (manifest_overrides or update_date is not None):
manifest_re = _const_line_re(spec.manifest_var)
match = manifest_re.search(text)
if match is not None:
try:
manifest = _extract_const_object(text, spec.manifest_var)
except TemplateError:
manifest = None
if isinstance(manifest, dict):
if update_date is not None:
system = manifest.get("系统")
if isinstance(system, dict):
system["更新时间"] = update_date.isoformat()
if manifest_overrides:
_deep_merge(manifest, manifest_overrides)
new_manifest_line = _dump_const(spec.manifest_var, manifest)
text = manifest_re.sub(lambda _m: new_manifest_line, text, count=1)
return text
def _deep_merge(base: dict[str, Any], overrides: dict[str, Any]) -> None:
for k, v in overrides.items():
if isinstance(v, dict) and isinstance(base.get(k), dict):
_deep_merge(base[k], v)
else:
base[k] = v
# ── require_html 模式选「内置模板」作参考 ─────────────────────────────────────────
# 「参考收藏页面」下拉里模板选项的值存成 ``tpl: