Rebrand sexp → sx across web platform (173 files)
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 11m37s

Rename all sexp directories, files, identifiers, and references to sx.
artdag/ excluded (separate media processing DSL).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 11:06:57 +00:00
parent 17cebe07e7
commit e8bc228c7f
174 changed files with 3126 additions and 2952 deletions

View File

@@ -1,6 +1,6 @@
from __future__ import annotations
import path_setup # noqa: F401 # adds shared/ to sys.path
import sexp.sexp_components as sexp_components # noqa: F401 # ensure Hypercorn --reload watches this file
import sx.sx_components as sx_components # noqa: F401 # ensure Hypercorn --reload watches this file
from pathlib import Path
from quart import g, request

View File

@@ -18,7 +18,7 @@ from shared.models import UserNewsletter
from shared.models.ghost_membership_entities import GhostNewsletter
from shared.infrastructure.urls import login_url
from shared.infrastructure.fragments import fetch_fragment, fetch_fragments
from shared.sexp.helpers import sexp_response
from shared.sx.helpers import sx_response
oob = {
"oob_extends": "oob_elements.html",
@@ -47,8 +47,8 @@ def register(url_prefix="/"):
@account_bp.get("/")
async def account():
from shared.browser.app.utils.htmx import is_htmx_request
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_account_page, render_account_oob
from shared.sx.page import get_template_context
from sx.sx_components import render_account_page, render_account_oob
if not g.get("user"):
return redirect(login_url("/"))
@@ -58,8 +58,8 @@ def register(url_prefix="/"):
html = await render_account_page(ctx)
return await make_response(html)
else:
sexp_src = await render_account_oob(ctx)
return sexp_response(sexp_src)
sx_src = await render_account_oob(ctx)
return sx_response(sx_src)
@account_bp.get("/newsletters/")
async def newsletters():
@@ -89,16 +89,16 @@ def register(url_prefix="/"):
"subscribed": un.subscribed if un else False,
})
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_newsletters_page, render_newsletters_oob
from shared.sx.page import get_template_context
from sx.sx_components import render_newsletters_page, render_newsletters_oob
ctx = await get_template_context()
if not is_htmx_request():
html = await render_newsletters_page(ctx, newsletter_list)
return await make_response(html)
else:
sexp_src = await render_newsletters_oob(ctx, newsletter_list)
return sexp_response(sexp_src)
sx_src = await render_newsletters_oob(ctx, newsletter_list)
return sx_response(sx_src)
@account_bp.post("/newsletter/<int:newsletter_id>/toggle/")
async def toggle_newsletter(newsletter_id: int):
@@ -125,8 +125,8 @@ def register(url_prefix="/"):
await g.s.flush()
from sexp.sexp_components import render_newsletter_toggle
return sexp_response(render_newsletter_toggle(un))
from sx.sx_components import render_newsletter_toggle
return sx_response(render_newsletter_toggle(un))
# Catch-all for fragment-provided pages — must be last
@account_bp.get("/<slug>/")
@@ -144,15 +144,15 @@ def register(url_prefix="/"):
if not fragment_html:
abort(404)
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_fragment_page, render_fragment_oob
from shared.sx.page import get_template_context
from sx.sx_components import render_fragment_page, render_fragment_oob
ctx = await get_template_context()
if not is_htmx_request():
html = await render_fragment_page(ctx, fragment_html)
return await make_response(html)
else:
sexp_src = await render_fragment_oob(ctx, fragment_html)
return sexp_response(sexp_src)
sx_src = await render_fragment_oob(ctx, fragment_html)
return sx_response(sx_src)
return account_bp

View File

@@ -275,8 +275,8 @@ def register(url_prefix="/auth"):
redirect_url = pop_login_redirect_target()
return redirect(redirect_url)
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_login_page
from shared.sx.page import get_template_context
from sx.sx_components import render_login_page
ctx = await get_template_context()
return await render_login_page(ctx)
@@ -291,8 +291,8 @@ def register(url_prefix="/auth"):
is_valid, email = validate_email(email_input)
if not is_valid:
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_login_page
from shared.sx.page import get_template_context
from sx.sx_components import render_login_page
ctx = await get_template_context(error="Please enter a valid email address.", email=email_input)
return await render_login_page(ctx), 400
@@ -301,8 +301,8 @@ def register(url_prefix="/auth"):
try:
allowed, _ = await _check_rate_limit(f"magic_email:{email}", 5, 900)
if not allowed:
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_check_email_page
from shared.sx.page import get_template_context
from sx.sx_components import render_check_email_page
ctx = await get_template_context(email=email, email_error=None)
return await render_check_email_page(ctx), 200
except Exception:
@@ -324,8 +324,8 @@ def register(url_prefix="/auth"):
"Please try again in a moment."
)
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_check_email_page
from shared.sx.page import get_template_context
from sx.sx_components import render_check_email_page
ctx = await get_template_context(email=email, email_error=email_error)
return await render_check_email_page(ctx)
@@ -340,15 +340,15 @@ def register(url_prefix="/auth"):
user, error = await validate_magic_link(s, token)
if error:
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_login_page
from shared.sx.page import get_template_context
from sx.sx_components import render_login_page
ctx = await get_template_context(error=error)
return await render_login_page(ctx), 400
user_id = user.id
except Exception:
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_login_page
from shared.sx.page import get_template_context
from sx.sx_components import render_login_page
ctx = await get_template_context(error="Could not sign you in right now. Please try again.")
return await render_login_page(ctx), 502
@@ -679,8 +679,8 @@ def register(url_prefix="/auth"):
@auth_bp.get("/device/")
async def device_form():
"""Browser form where user enters the code displayed in terminal."""
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_device_page
from shared.sx.page import get_template_context
from sx.sx_components import render_device_page
code = request.args.get("code", "")
ctx = await get_template_context(code=code)
return await render_device_page(ctx)
@@ -693,8 +693,8 @@ def register(url_prefix="/auth"):
user_code = (form.get("code") or "").strip().replace("-", "").upper()
if not user_code or len(user_code) != 8:
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_device_page
from shared.sx.page import get_template_context
from sx.sx_components import render_device_page
ctx = await get_template_context(error="Please enter a valid 8-character code.", code=form.get("code", ""))
return await render_device_page(ctx), 400
@@ -703,8 +703,8 @@ def register(url_prefix="/auth"):
r = await get_auth_redis()
device_code = await r.get(f"devflow_uc:{user_code}")
if not device_code:
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_device_page
from shared.sx.page import get_template_context
from sx.sx_components import render_device_page
ctx = await get_template_context(error="Code not found or expired. Please try again.", code=form.get("code", ""))
return await render_device_page(ctx), 400
@@ -720,13 +720,13 @@ def register(url_prefix="/auth"):
# Logged in — approve immediately
ok = await _approve_device(device_code, g.user)
if not ok:
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_device_page
from shared.sx.page import get_template_context
from sx.sx_components import render_device_page
ctx = await get_template_context(error="Code expired or already used.")
return await render_device_page(ctx), 400
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_device_approved_page
from shared.sx.page import get_template_context
from sx.sx_components import render_device_approved_page
ctx = await get_template_context()
return await render_device_approved_page(ctx)
@@ -734,8 +734,8 @@ def register(url_prefix="/auth"):
@auth_bp.get("/device/complete/")
async def device_complete():
"""Post-login redirect — completes approval after magic link auth."""
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_device_page, render_device_approved_page
from shared.sx.page import get_template_context
from sx.sx_components import render_device_page, render_device_approved_page
device_code = request.args.get("code", "")

View File

@@ -1,6 +1,6 @@
"""Account app fragment endpoints.
Exposes sexp fragments at ``/internal/fragments/<type>`` for consumption
Exposes sx fragments at ``/internal/fragments/<type>`` for consumption
by other coop apps via the fragment client.
Fragments:
@@ -18,15 +18,15 @@ def register():
bp = Blueprint("fragments", __name__, url_prefix="/internal/fragments")
# ---------------------------------------------------------------
# Fragment handlers — return sexp source text
# Fragment handlers — return sx source text
# ---------------------------------------------------------------
async def _auth_menu():
from shared.infrastructure.urls import account_url
from shared.sexp.helpers import sexp_call
from shared.sx.helpers import sx_call
user_email = request.args.get("email", "")
return sexp_call("auth-menu",
return sx_call("auth-menu",
user_email=user_email or None,
account_url=account_url(""))
@@ -47,8 +47,8 @@ def register():
async def get_fragment(fragment_type: str):
handler = _handlers.get(fragment_type)
if handler is None:
return Response("", status=200, content_type="text/sexp")
return Response("", status=200, content_type="text/sx")
src = await handler()
return Response(src, status=200, content_type="text/sexp")
return Response(src, status=200, content_type="text/sx")
return bp

View File

@@ -9,13 +9,13 @@ from __future__ import annotations
import os
from typing import Any
from shared.sexp.jinja_bridge import load_service_components
from shared.sexp.helpers import (
call_url, sexp_call, SexpExpr,
root_header_sexp, full_page_sexp, header_child_sexp, oob_page_sexp,
from shared.sx.jinja_bridge import load_service_components
from shared.sx.helpers import (
call_url, sx_call, SxExpr,
root_header_sx, full_page_sx, header_child_sx, oob_page_sx,
)
# Load account-specific .sexpr components at import time
# Load account-specific .sx components at import time
load_service_components(os.path.dirname(os.path.dirname(__file__)))
@@ -23,10 +23,10 @@ load_service_components(os.path.dirname(os.path.dirname(__file__)))
# Header helpers
# ---------------------------------------------------------------------------
def _auth_nav_sexp(ctx: dict) -> str:
def _auth_nav_sx(ctx: dict) -> str:
"""Auth section desktop nav items."""
parts = [
sexp_call("nav-link",
sx_call("nav-link",
href=call_url(ctx, "account_url", "/newsletters/"),
label="newsletters",
select_colours=ctx.get("select_colours", ""),
@@ -38,22 +38,22 @@ def _auth_nav_sexp(ctx: dict) -> str:
return "(<> " + " ".join(parts) + ")"
def _auth_header_sexp(ctx: dict, *, oob: bool = False) -> str:
def _auth_header_sx(ctx: dict, *, oob: bool = False) -> str:
"""Build the account section header row."""
return sexp_call(
return sx_call(
"menu-row-sx",
id="auth-row", level=1, colour="sky",
link_href=call_url(ctx, "account_url", "/"),
link_label="account", icon="fa-solid fa-user",
nav=SexpExpr(_auth_nav_sexp(ctx)),
nav=SxExpr(_auth_nav_sx(ctx)),
child_id="auth-header-child", oob=oob,
)
def _auth_nav_mobile_sexp(ctx: dict) -> str:
def _auth_nav_mobile_sx(ctx: dict) -> str:
"""Mobile nav menu for auth section."""
parts = [
sexp_call("nav-link",
sx_call("nav-link",
href=call_url(ctx, "account_url", "/newsletters/"),
label="newsletters",
select_colours=ctx.get("select_colours", ""),
@@ -69,7 +69,7 @@ def _auth_nav_mobile_sexp(ctx: dict) -> str:
# Account dashboard (GET /)
# ---------------------------------------------------------------------------
def _account_main_panel_sexp(ctx: dict) -> str:
def _account_main_panel_sx(ctx: dict) -> str:
"""Account info panel with user details and logout."""
from quart import g
from shared.browser.app.csrf import generate_csrf_token
@@ -77,33 +77,33 @@ def _account_main_panel_sexp(ctx: dict) -> str:
user = getattr(g, "user", None)
error = ctx.get("error", "")
error_sexp = sexp_call("account-error-banner", error=error) if error else ""
error_sx = sx_call("account-error-banner", error=error) if error else ""
user_email_sexp = ""
user_name_sexp = ""
user_email_sx = ""
user_name_sx = ""
if user:
user_email_sexp = sexp_call("account-user-email", email=user.email)
user_email_sx = sx_call("account-user-email", email=user.email)
if user.name:
user_name_sexp = sexp_call("account-user-name", name=user.name)
user_name_sx = sx_call("account-user-name", name=user.name)
logout_sexp = sexp_call("account-logout-form", csrf_token=generate_csrf_token())
logout_sx = sx_call("account-logout-form", csrf_token=generate_csrf_token())
labels_sexp = ""
labels_sx = ""
if user and hasattr(user, "labels") and user.labels:
label_items = " ".join(
sexp_call("account-label-item", name=label.name)
sx_call("account-label-item", name=label.name)
for label in user.labels
)
labels_sexp = sexp_call("account-labels-section",
items=SexpExpr("(<> " + label_items + ")"))
labels_sx = sx_call("account-labels-section",
items=SxExpr("(<> " + label_items + ")"))
return sexp_call(
return sx_call(
"account-main-panel",
error=SexpExpr(error_sexp) if error_sexp else None,
email=SexpExpr(user_email_sexp) if user_email_sexp else None,
name=SexpExpr(user_name_sexp) if user_name_sexp else None,
logout=SexpExpr(logout_sexp),
labels=SexpExpr(labels_sexp) if labels_sexp else None,
error=SxExpr(error_sx) if error_sx else None,
email=SxExpr(user_email_sx) if user_email_sx else None,
name=SxExpr(user_name_sx) if user_name_sx else None,
logout=SxExpr(logout_sx),
labels=SxExpr(labels_sx) if labels_sx else None,
)
@@ -111,7 +111,7 @@ def _account_main_panel_sexp(ctx: dict) -> str:
# Newsletters (GET /newsletters/)
# ---------------------------------------------------------------------------
def _newsletter_toggle_sexp(un: Any, account_url_fn: Any, csrf_token: str) -> str:
def _newsletter_toggle_sx(un: Any, account_url_fn: Any, csrf_token: str) -> str:
"""Render a single newsletter toggle switch."""
nid = un.newsletter_id
toggle_url = account_url_fn(f"/newsletter/{nid}/toggle/")
@@ -123,7 +123,7 @@ def _newsletter_toggle_sexp(un: Any, account_url_fn: Any, csrf_token: str) -> st
bg = "bg-stone-300"
translate = "translate-x-1"
checked = "false"
return sexp_call(
return sx_call(
"account-newsletter-toggle",
id=f"nl-{nid}", url=toggle_url,
hdrs=f'{{"X-CSRFToken": "{csrf_token}"}}',
@@ -134,9 +134,9 @@ def _newsletter_toggle_sexp(un: Any, account_url_fn: Any, csrf_token: str) -> st
)
def _newsletter_toggle_off_sexp(nid: int, toggle_url: str, csrf_token: str) -> str:
def _newsletter_toggle_off_sx(nid: int, toggle_url: str, csrf_token: str) -> str:
"""Render an unsubscribed newsletter toggle (no subscription record yet)."""
return sexp_call(
return sx_call(
"account-newsletter-toggle-off",
id=f"nl-{nid}", url=toggle_url,
hdrs=f'{{"X-CSRFToken": "{csrf_token}"}}',
@@ -144,7 +144,7 @@ def _newsletter_toggle_off_sexp(nid: int, toggle_url: str, csrf_token: str) -> s
)
def _newsletters_panel_sexp(ctx: dict, newsletter_list: list) -> str:
def _newsletters_panel_sx(ctx: dict, newsletter_list: list) -> str:
"""Newsletters management panel."""
from shared.browser.app.csrf import generate_csrf_token
@@ -157,30 +157,30 @@ def _newsletters_panel_sexp(ctx: dict, newsletter_list: list) -> str:
nl = item["newsletter"]
un = item.get("un")
desc_sexp = sexp_call(
desc_sx = sx_call(
"account-newsletter-desc", description=nl.description
) if nl.description else ""
if un:
toggle = _newsletter_toggle_sexp(un, account_url_fn, csrf)
toggle = _newsletter_toggle_sx(un, account_url_fn, csrf)
else:
toggle_url = account_url_fn(f"/newsletter/{nl.id}/toggle/")
toggle = _newsletter_toggle_off_sexp(nl.id, toggle_url, csrf)
toggle = _newsletter_toggle_off_sx(nl.id, toggle_url, csrf)
items.append(sexp_call(
items.append(sx_call(
"account-newsletter-item",
name=nl.name,
desc=SexpExpr(desc_sexp) if desc_sexp else None,
toggle=SexpExpr(toggle),
desc=SxExpr(desc_sx) if desc_sx else None,
toggle=SxExpr(toggle),
))
list_sexp = sexp_call(
list_sx = sx_call(
"account-newsletter-list",
items=SexpExpr("(<> " + " ".join(items) + ")"),
items=SxExpr("(<> " + " ".join(items) + ")"),
)
else:
list_sexp = sexp_call("account-newsletter-empty")
list_sx = sx_call("account-newsletter-empty")
return sexp_call("account-newsletters-panel", list=SexpExpr(list_sexp))
return sx_call("account-newsletters-panel", list=SxExpr(list_sx))
# ---------------------------------------------------------------------------
@@ -196,11 +196,11 @@ def _login_page_content(ctx: dict) -> str:
email = ctx.get("email", "")
action = url_for("auth.start_login")
error_sexp = sexp_call("account-login-error", error=error) if error else ""
error_sx = sx_call("account-login-error", error=error) if error else ""
return sexp_call(
return sx_call(
"account-login-form",
error=SexpExpr(error_sexp) if error_sexp else None,
error=SxExpr(error_sx) if error_sx else None,
action=action,
csrf_token=generate_csrf_token(), email=email,
)
@@ -215,11 +215,11 @@ def _device_page_content(ctx: dict) -> str:
code = ctx.get("code", "")
action = url_for("auth.device_submit")
error_sexp = sexp_call("account-device-error", error=error) if error else ""
error_sx = sx_call("account-device-error", error=error) if error else ""
return sexp_call(
return sx_call(
"account-device-form",
error=SexpExpr(error_sexp) if error_sexp else None,
error=SxExpr(error_sx) if error_sx else None,
action=action,
csrf_token=generate_csrf_token(), code=code,
)
@@ -227,7 +227,7 @@ def _device_page_content(ctx: dict) -> str:
def _device_approved_content() -> str:
"""Device approved success content."""
return sexp_call("account-device-approved")
return sx_call("account-device-approved")
# ---------------------------------------------------------------------------
@@ -236,26 +236,26 @@ def _device_approved_content() -> str:
async def render_account_page(ctx: dict) -> str:
"""Full page: account dashboard."""
main = _account_main_panel_sexp(ctx)
main = _account_main_panel_sx(ctx)
hdr = root_header_sexp(ctx)
hdr_child = header_child_sexp(_auth_header_sexp(ctx))
hdr = root_header_sx(ctx)
hdr_child = header_child_sx(_auth_header_sx(ctx))
header_rows = "(<> " + hdr + " " + hdr_child + ")"
return full_page_sexp(ctx, header_rows=header_rows,
return full_page_sx(ctx, header_rows=header_rows,
content=main,
menu=_auth_nav_mobile_sexp(ctx))
menu=_auth_nav_mobile_sx(ctx))
async def render_account_oob(ctx: dict) -> str:
"""OOB response for account dashboard."""
main = _account_main_panel_sexp(ctx)
main = _account_main_panel_sx(ctx)
oobs = "(<> " + _auth_header_sexp(ctx, oob=True) + " " + root_header_sexp(ctx, oob=True) + ")"
oobs = "(<> " + _auth_header_sx(ctx, oob=True) + " " + root_header_sx(ctx, oob=True) + ")"
return oob_page_sexp(oobs=oobs,
return oob_page_sx(oobs=oobs,
content=main,
menu=_auth_nav_mobile_sexp(ctx))
menu=_auth_nav_mobile_sx(ctx))
# ---------------------------------------------------------------------------
@@ -264,26 +264,26 @@ async def render_account_oob(ctx: dict) -> str:
async def render_newsletters_page(ctx: dict, newsletter_list: list) -> str:
"""Full page: newsletters."""
main = _newsletters_panel_sexp(ctx, newsletter_list)
main = _newsletters_panel_sx(ctx, newsletter_list)
hdr = root_header_sexp(ctx)
hdr_child = header_child_sexp(_auth_header_sexp(ctx))
hdr = root_header_sx(ctx)
hdr_child = header_child_sx(_auth_header_sx(ctx))
header_rows = "(<> " + hdr + " " + hdr_child + ")"
return full_page_sexp(ctx, header_rows=header_rows,
return full_page_sx(ctx, header_rows=header_rows,
content=main,
menu=_auth_nav_mobile_sexp(ctx))
menu=_auth_nav_mobile_sx(ctx))
async def render_newsletters_oob(ctx: dict, newsletter_list: list) -> str:
"""OOB response for newsletters."""
main = _newsletters_panel_sexp(ctx, newsletter_list)
main = _newsletters_panel_sx(ctx, newsletter_list)
oobs = "(<> " + _auth_header_sexp(ctx, oob=True) + " " + root_header_sexp(ctx, oob=True) + ")"
oobs = "(<> " + _auth_header_sx(ctx, oob=True) + " " + root_header_sx(ctx, oob=True) + ")"
return oob_page_sexp(oobs=oobs,
return oob_page_sx(oobs=oobs,
content=main,
menu=_auth_nav_mobile_sexp(ctx))
menu=_auth_nav_mobile_sx(ctx))
# ---------------------------------------------------------------------------
@@ -292,22 +292,22 @@ async def render_newsletters_oob(ctx: dict, newsletter_list: list) -> str:
async def render_fragment_page(ctx: dict, page_fragment_html: str) -> str:
"""Full page: fragment-provided content."""
hdr = root_header_sexp(ctx)
hdr_child = header_child_sexp(_auth_header_sexp(ctx))
hdr = root_header_sx(ctx)
hdr_child = header_child_sx(_auth_header_sx(ctx))
header_rows = "(<> " + hdr + " " + hdr_child + ")"
return full_page_sexp(ctx, header_rows=header_rows,
content=f'(~rich-text :html "{_sexp_escape(page_fragment_html)}")',
menu=_auth_nav_mobile_sexp(ctx))
return full_page_sx(ctx, header_rows=header_rows,
content=f'(~rich-text :html "{_sx_escape(page_fragment_html)}")',
menu=_auth_nav_mobile_sx(ctx))
async def render_fragment_oob(ctx: dict, page_fragment_html: str) -> str:
"""OOB response for fragment pages."""
oobs = "(<> " + _auth_header_sexp(ctx, oob=True) + " " + root_header_sexp(ctx, oob=True) + ")"
oobs = "(<> " + _auth_header_sx(ctx, oob=True) + " " + root_header_sx(ctx, oob=True) + ")"
return oob_page_sexp(oobs=oobs,
content=f'(~rich-text :html "{_sexp_escape(page_fragment_html)}")',
menu=_auth_nav_mobile_sexp(ctx))
return oob_page_sx(oobs=oobs,
content=f'(~rich-text :html "{_sx_escape(page_fragment_html)}")',
menu=_auth_nav_mobile_sx(ctx))
# ---------------------------------------------------------------------------
@@ -316,24 +316,24 @@ async def render_fragment_oob(ctx: dict, page_fragment_html: str) -> str:
async def render_login_page(ctx: dict) -> str:
"""Full page: login form."""
hdr = root_header_sexp(ctx)
return full_page_sexp(ctx, header_rows=hdr,
hdr = root_header_sx(ctx)
return full_page_sx(ctx, header_rows=hdr,
content=_login_page_content(ctx),
meta_html='<title>Login \u2014 Rose Ash</title>')
async def render_device_page(ctx: dict) -> str:
"""Full page: device authorization form."""
hdr = root_header_sexp(ctx)
return full_page_sexp(ctx, header_rows=hdr,
hdr = root_header_sx(ctx)
return full_page_sx(ctx, header_rows=hdr,
content=_device_page_content(ctx),
meta_html='<title>Authorize Device \u2014 Rose Ash</title>')
async def render_device_approved_page(ctx: dict) -> str:
"""Full page: device approved."""
hdr = root_header_sexp(ctx)
return full_page_sexp(ctx, header_rows=hdr,
hdr = root_header_sx(ctx)
return full_page_sx(ctx, header_rows=hdr,
content=_device_approved_content(),
meta_html='<title>Device Authorized \u2014 Rose Ash</title>')
@@ -346,14 +346,14 @@ def _check_email_content(email: str, email_error: str | None = None) -> str:
"""Check email confirmation content."""
from markupsafe import escape
error_sexp = sexp_call(
error_sx = sx_call(
"account-check-email-error", error=str(escape(email_error))
) if email_error else ""
return sexp_call(
return sx_call(
"account-check-email",
email=str(escape(email)),
error=SexpExpr(error_sexp) if error_sexp else None,
error=SxExpr(error_sx) if error_sx else None,
)
@@ -361,8 +361,8 @@ async def render_check_email_page(ctx: dict) -> str:
"""Full page: check email after magic link sent."""
email = ctx.get("email", "")
email_error = ctx.get("email_error")
hdr = root_header_sexp(ctx)
return full_page_sexp(ctx, header_rows=hdr,
hdr = root_header_sx(ctx)
return full_page_sx(ctx, header_rows=hdr,
content=_check_email_content(email, email_error),
meta_html='<title>Check your email \u2014 Rose Ash</title>')
@@ -380,13 +380,13 @@ def render_newsletter_toggle(un) -> str:
if account_url_fn is None:
from shared.infrastructure.urls import account_url
account_url_fn = account_url
return _newsletter_toggle_sexp(un, account_url_fn, generate_csrf_token())
return _newsletter_toggle_sx(un, account_url_fn, generate_csrf_token())
# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------
def _sexp_escape(s: str) -> str:
"""Escape a string for embedding in sexp string literals."""
def _sx_escape(s: str) -> str:
"""Escape a string for embedding in sx string literals."""
return s.replace("\\", "\\\\").replace('"', '\\"')