""" Shared helper functions for s-expression page rendering. These are used by per-service sexp_components.py files to build common page elements (headers, search, etc.) from template context. """ from __future__ import annotations from typing import Any from .jinja_bridge import sexp from .page import HAMBURGER_HTML, SEARCH_HEADERS_MOBILE, SEARCH_HEADERS_DESKTOP def call_url(ctx: dict, key: str, path: str = "/") -> str: """Call a URL helper from context (e.g., blog_url, account_url).""" fn = ctx.get(key) if callable(fn): return fn(path) return str(fn or "") + path def get_asset_url(ctx: dict) -> str: """Extract the asset URL base from context.""" au = ctx.get("asset_url") if callable(au): result = au("") return result.rsplit("/", 1)[0] if "/" in result else result return au or "" def root_header_html(ctx: dict, *, oob: bool = False) -> str: """Build the root header row HTML.""" return sexp( '(~header-row :cart-mini-html cmi :blog-url bu :site-title st' ' :nav-tree-html nth :auth-menu-html amh :nav-panel-html nph' ' :hamburger-html hh :oob oob)', cmi=ctx.get("cart_mini_html", ""), bu=call_url(ctx, "blog_url", ""), st=ctx.get("base_title", ""), nth=ctx.get("nav_tree_html", ""), amh=ctx.get("auth_menu_html", ""), nph=ctx.get("nav_panel_html", ""), hh=HAMBURGER_HTML, oob=oob, ) def search_mobile_html(ctx: dict) -> str: """Build mobile search input HTML.""" return sexp( '(~search-mobile :current-local-href clh :search s :search-count sc' ' :hx-select hs :search-headers-mobile shm)', clh=ctx.get("current_local_href", "/"), s=ctx.get("search", ""), sc=ctx.get("search_count", ""), hs=ctx.get("hx_select", "#main-panel"), shm=SEARCH_HEADERS_MOBILE, ) def search_desktop_html(ctx: dict) -> str: """Build desktop search input HTML.""" return sexp( '(~search-desktop :current-local-href clh :search s :search-count sc' ' :hx-select hs :search-headers-desktop shd)', clh=ctx.get("current_local_href", "/"), s=ctx.get("search", ""), sc=ctx.get("search_count", ""), hs=ctx.get("hx_select", "#main-panel"), shd=SEARCH_HEADERS_DESKTOP, ) def full_page(ctx: dict, *, header_rows_html: str, filter_html: str = "", aside_html: str = "", content_html: str = "", menu_html: str = "", body_end_html: str = "", meta_html: str = "") -> str: """Render a full app page with the standard layout.""" return sexp( '(~app-layout :title t :asset-url au :meta-html mh' ' :header-rows-html hrh :menu-html muh :filter-html fh' ' :aside-html ash :content-html ch :body-end-html beh)', t=ctx.get("base_title", "Rose Ash"), au=get_asset_url(ctx), mh=meta_html, hrh=header_rows_html, muh=menu_html, fh=filter_html, ash=aside_html, ch=content_html, beh=body_end_html, ) def oob_page(ctx: dict, *, oobs_html: str = "", filter_html: str = "", aside_html: str = "", content_html: str = "", menu_html: str = "") -> str: """Render an OOB response with standard swap targets.""" return sexp( '(~oob-response :oobs-html oh :filter-html fh :aside-html ash' ' :menu-html mh :content-html ch)', oh=oobs_html, fh=filter_html, ash=aside_html, mh=menu_html, ch=content_html, )