SxExpr is now a str subclass so it works everywhere a plain string does (join, isinstance, f-strings) while serialize() still emits it unquoted. sx_call() and all internal render functions (_render_to_sx, async_eval_to_sx, etc.) return SxExpr, eliminating the "forgot to wrap" bug class that caused the sx_content leak and list serialization bugs. - Phase 0: SxExpr(str) with .source property, __add__/__radd__ - Phase 1: sx_call returns SxExpr (drop-in, all 200+ sites unchanged) - Phase 2: async_eval_to_sx, async_eval_slot_to_sx, _render_to_sx, mobile_menu_sx return SxExpr; remove isinstance(str) workaround - Phase 3: Remove ~150 redundant SxExpr() wrappings across 45 files - Phase 4: serialize() docstring, handler return docs, ;; returns: sx Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
197 lines
7.6 KiB
Python
197 lines
7.6 KiB
Python
"""Blog layout functions for defpage rendering."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Header helpers (moved from sx_components — thin sx_call wrappers)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def _settings_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
|
from shared.sx.helpers import sx_call
|
|
from quart import url_for as qurl
|
|
|
|
settings_href = qurl("settings.defpage_settings_home")
|
|
label_sx = sx_call("blog-admin-label")
|
|
nav_sx = _settings_nav_sx(ctx)
|
|
|
|
return sx_call("menu-row-sx",
|
|
id="root-settings-row", level=1,
|
|
link_href=settings_href,
|
|
link_label_content=label_sx,
|
|
nav=nav_sx or None,
|
|
child_id="root-settings-header-child", oob=oob)
|
|
|
|
|
|
def _settings_nav_sx(ctx: dict) -> str:
|
|
from shared.sx.helpers import sx_call
|
|
return sx_call("blog-settings-nav")
|
|
|
|
|
|
def _sub_settings_header_sx(row_id: str, child_id: str, href: str,
|
|
icon: str, label: str, ctx: dict,
|
|
*, oob: bool = False, nav_sx: str = "") -> str:
|
|
from shared.sx.helpers import sx_call
|
|
from shared.sx.parser import SxExpr
|
|
|
|
label_sx = sx_call("blog-sub-settings-label",
|
|
icon=f"fa fa-{icon}", label=label)
|
|
return sx_call("menu-row-sx",
|
|
id=row_id, level=2,
|
|
link_href=href,
|
|
link_label_content=label_sx,
|
|
nav=SxExpr(nav_sx) if nav_sx else None,
|
|
child_id=child_id, oob=oob)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Layouts
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def _register_blog_layouts() -> None:
|
|
from shared.sx.layouts import register_custom_layout
|
|
register_custom_layout("blog", _blog_full, _blog_oob)
|
|
register_custom_layout("blog-settings", _settings_full, _settings_oob,
|
|
mobile_fn=_settings_mobile)
|
|
register_custom_layout("blog-cache", _cache_full, _cache_oob)
|
|
register_custom_layout("blog-snippets", _snippets_full, _snippets_oob)
|
|
register_custom_layout("blog-menu-items", _menu_items_full, _menu_items_oob)
|
|
register_custom_layout("blog-tag-groups", _tag_groups_full, _tag_groups_oob)
|
|
register_custom_layout("blog-tag-group-edit",
|
|
_tag_group_edit_full, _tag_group_edit_oob)
|
|
|
|
|
|
# --- Blog layout (root + blog header) ---
|
|
|
|
async def _blog_full(ctx: dict, **kw: Any) -> str:
|
|
from shared.sx.helpers import render_to_sx_with_env
|
|
return await render_to_sx_with_env("blog-layout-full", {})
|
|
|
|
|
|
async def _blog_oob(ctx: dict, **kw: Any) -> str:
|
|
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
|
rows = await render_to_sx_with_env("blog-layout-full", {})
|
|
return await oob_header_sx("root-header-child", "blog-header-child", rows)
|
|
|
|
|
|
# --- Settings layout (root + settings header) ---
|
|
|
|
async def _settings_full(ctx: dict, **kw: Any) -> str:
|
|
from shared.sx.helpers import render_to_sx_with_env
|
|
return await render_to_sx_with_env("settings-layout-full", {},
|
|
settings_header=_settings_header_sx(ctx))
|
|
|
|
|
|
async def _settings_oob(ctx: dict, **kw: Any) -> str:
|
|
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
|
rows = await render_to_sx_with_env("settings-layout-full", {},
|
|
settings_header=_settings_header_sx(ctx))
|
|
return await oob_header_sx("root-header-child", "root-settings-header-child", rows)
|
|
|
|
|
|
def _settings_mobile(ctx: dict, **kw: Any) -> str:
|
|
return _settings_nav_sx(ctx)
|
|
|
|
|
|
# --- Sub-settings helpers ---
|
|
|
|
async def _sub_settings_full(ctx: dict, row_id: str, child_id: str,
|
|
endpoint: str, icon: str, label: str) -> str:
|
|
from shared.sx.helpers import render_to_sx_with_env
|
|
from quart import url_for as qurl
|
|
return await render_to_sx_with_env("sub-settings-layout-full", {},
|
|
settings_header=_settings_header_sx(ctx),
|
|
sub_header=_sub_settings_header_sx(
|
|
row_id, child_id, qurl(endpoint), icon, label, ctx))
|
|
|
|
|
|
async def _sub_settings_oob(ctx: dict, row_id: str, child_id: str,
|
|
endpoint: str, icon: str, label: str) -> str:
|
|
from shared.sx.helpers import oob_header_sx, sx_call
|
|
from quart import url_for as qurl
|
|
settings_hdr_oob = _settings_header_sx(ctx, oob=True)
|
|
sub_hdr = _sub_settings_header_sx(
|
|
row_id, child_id, qurl(endpoint), icon, label, ctx)
|
|
sub_oob = await oob_header_sx("root-settings-header-child", child_id, sub_hdr)
|
|
return sx_call("sub-settings-layout-oob",
|
|
settings_header_oob=settings_hdr_oob,
|
|
sub_header_oob=sub_oob)
|
|
|
|
|
|
# --- Cache ---
|
|
|
|
async def _cache_full(ctx: dict, **kw: Any) -> str:
|
|
return await _sub_settings_full(ctx, "cache-row", "cache-header-child",
|
|
"defpage_cache_page", "refresh", "Cache")
|
|
|
|
|
|
async def _cache_oob(ctx: dict, **kw: Any) -> str:
|
|
return await _sub_settings_oob(ctx, "cache-row", "cache-header-child",
|
|
"defpage_cache_page", "refresh", "Cache")
|
|
|
|
|
|
# --- Snippets ---
|
|
|
|
async def _snippets_full(ctx: dict, **kw: Any) -> str:
|
|
return await _sub_settings_full(ctx, "snippets-row", "snippets-header-child",
|
|
"defpage_snippets_page", "puzzle-piece", "Snippets")
|
|
|
|
|
|
async def _snippets_oob(ctx: dict, **kw: Any) -> str:
|
|
return await _sub_settings_oob(ctx, "snippets-row", "snippets-header-child",
|
|
"defpage_snippets_page", "puzzle-piece", "Snippets")
|
|
|
|
|
|
# --- Menu Items ---
|
|
|
|
async def _menu_items_full(ctx: dict, **kw: Any) -> str:
|
|
return await _sub_settings_full(ctx, "menu_items-row", "menu_items-header-child",
|
|
"defpage_menu_items_page", "bars", "Menu Items")
|
|
|
|
|
|
async def _menu_items_oob(ctx: dict, **kw: Any) -> str:
|
|
return await _sub_settings_oob(ctx, "menu_items-row", "menu_items-header-child",
|
|
"defpage_menu_items_page", "bars", "Menu Items")
|
|
|
|
|
|
# --- Tag Groups ---
|
|
|
|
async def _tag_groups_full(ctx: dict, **kw: Any) -> str:
|
|
return await _sub_settings_full(ctx, "tag-groups-row", "tag-groups-header-child",
|
|
"defpage_tag_groups_page", "tags", "Tag Groups")
|
|
|
|
|
|
async def _tag_groups_oob(ctx: dict, **kw: Any) -> str:
|
|
return await _sub_settings_oob(ctx, "tag-groups-row", "tag-groups-header-child",
|
|
"defpage_tag_groups_page", "tags", "Tag Groups")
|
|
|
|
|
|
# --- Tag Group Edit ---
|
|
|
|
async def _tag_group_edit_full(ctx: dict, **kw: Any) -> str:
|
|
from quart import request, url_for as qurl
|
|
from shared.sx.helpers import render_to_sx_with_env
|
|
g_id = (request.view_args or {}).get("id")
|
|
return await render_to_sx_with_env("sub-settings-layout-full", {},
|
|
settings_header=_settings_header_sx(ctx),
|
|
sub_header=_sub_settings_header_sx(
|
|
"tag-groups-row", "tag-groups-header-child",
|
|
qurl("defpage_tag_group_edit", id=g_id),
|
|
"tags", "Tag Groups", ctx))
|
|
|
|
|
|
async def _tag_group_edit_oob(ctx: dict, **kw: Any) -> str:
|
|
from quart import request, url_for as qurl
|
|
from shared.sx.helpers import oob_header_sx, sx_call
|
|
g_id = (request.view_args or {}).get("id")
|
|
settings_hdr_oob = _settings_header_sx(ctx, oob=True)
|
|
sub_hdr = _sub_settings_header_sx(
|
|
"tag-groups-row", "tag-groups-header-child",
|
|
qurl("defpage_tag_group_edit", id=g_id),
|
|
"tags", "Tag Groups", ctx)
|
|
sub_oob = await oob_header_sx("root-settings-header-child", "tag-groups-header-child", sub_hdr)
|
|
return sx_call("sub-settings-layout-oob",
|
|
settings_header_oob=settings_hdr_oob,
|
|
sub_header_oob=sub_oob)
|