Files
mono/sx/bp/pages/routes.py
giles 13bcf755f6 Add OOB header swaps for sx docs navigation + enable OAuth + fragments
- OOB nav updates: AJAX navigation now swaps both menu bar levels
  (main nav highlighting + sub-nav with current page) using the same
  oob_header_sx/oob_page_sx pattern as blog/market/events
- Enable OAuth for sx and test apps (removed from _NO_OAUTH, added sx
  to ALLOWED_CLIENTS, added app_urls for sx/test/orders)
- Fetch real cross-service fragments (cart-mini, auth-menu, nav-tree)
  instead of hardcoding empty values
- Add :selected param to ~menu-row-sx for white text current-page label
- Fix duplicate element IDs: use menu-row-sx child_id/child mechanism
  instead of manual header_child_sx wrappers
- Fix home page copy: "Server-rendered DOM over the wire (no HTML)"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:22:01 +00:00

218 lines
8.0 KiB
Python

"""SX docs page routes."""
from __future__ import annotations
from datetime import datetime
from quart import Blueprint, Response, make_response, request
def register(url_prefix: str = "/") -> Blueprint:
bp = Blueprint("pages", __name__, url_prefix=url_prefix)
def _is_sx_request() -> bool:
return bool(request.headers.get("SX-Request") or request.headers.get("HX-Request"))
# ------------------------------------------------------------------
# Home
# ------------------------------------------------------------------
@bp.get("/")
async def index():
if _is_sx_request():
from shared.sx.helpers import sx_response
from sxc.sx_components import home_oob_sx
return sx_response(await home_oob_sx())
from shared.sx.page import get_template_context
from sxc.sx_components import render_home_page_sx
ctx = await get_template_context()
html = await render_home_page_sx(ctx)
return await make_response(html, 200)
# ------------------------------------------------------------------
# Docs
# ------------------------------------------------------------------
@bp.get("/docs/")
async def docs_index():
from quart import redirect
return redirect("/docs/introduction")
@bp.get("/docs/<slug>")
async def docs_page(slug: str):
if _is_sx_request():
from shared.sx.helpers import sx_response
from sxc.sx_components import docs_oob_sx
return sx_response(await docs_oob_sx(slug))
from shared.sx.page import get_template_context
from sxc.sx_components import render_docs_page_sx
ctx = await get_template_context()
html = await render_docs_page_sx(ctx, slug)
return await make_response(html, 200)
# ------------------------------------------------------------------
# Reference
# ------------------------------------------------------------------
@bp.get("/reference/")
async def reference_index():
if _is_sx_request():
from shared.sx.helpers import sx_response
from sxc.sx_components import reference_oob_sx
return sx_response(await reference_oob_sx(""))
from shared.sx.page import get_template_context
from sxc.sx_components import render_reference_page_sx
ctx = await get_template_context()
html = await render_reference_page_sx(ctx, "")
return await make_response(html, 200)
@bp.get("/reference/<slug>")
async def reference_page(slug: str):
if _is_sx_request():
from shared.sx.helpers import sx_response
from sxc.sx_components import reference_oob_sx
return sx_response(await reference_oob_sx(slug))
from shared.sx.page import get_template_context
from sxc.sx_components import render_reference_page_sx
ctx = await get_template_context()
html = await render_reference_page_sx(ctx, slug)
return await make_response(html, 200)
# ------------------------------------------------------------------
# Protocols
# ------------------------------------------------------------------
@bp.get("/protocols/")
async def protocols_index():
from quart import redirect
return redirect("/protocols/wire-format")
@bp.get("/protocols/<slug>")
async def protocol_page(slug: str):
if _is_sx_request():
from shared.sx.helpers import sx_response
from sxc.sx_components import protocol_oob_sx
return sx_response(await protocol_oob_sx(slug))
from shared.sx.page import get_template_context
from sxc.sx_components import render_protocol_page_sx
ctx = await get_template_context()
html = await render_protocol_page_sx(ctx, slug)
return await make_response(html, 200)
# ------------------------------------------------------------------
# Examples
# ------------------------------------------------------------------
@bp.get("/examples/")
async def examples_index():
from quart import redirect
return redirect("/examples/click-to-load")
@bp.get("/examples/<slug>")
async def examples_page(slug: str):
if _is_sx_request():
from shared.sx.helpers import sx_response
from sxc.sx_components import examples_oob_sx
return sx_response(await examples_oob_sx(slug))
from shared.sx.page import get_template_context
from sxc.sx_components import render_examples_page_sx
ctx = await get_template_context()
html = await render_examples_page_sx(ctx, slug)
return await make_response(html, 200)
# ------------------------------------------------------------------
# Example API endpoints (for live demos)
# ------------------------------------------------------------------
@bp.get("/examples/api/click")
async def api_click():
from shared.sx.helpers import sx_response
return sx_response('(~click-result)')
@bp.post("/examples/api/form")
async def api_form():
from shared.sx.helpers import sx_response
form = await request.form
name = form.get("name", "")
escaped = name.replace('"', '\\"')
return sx_response(f'(~form-result :name "{escaped}")')
_poll_count = {"n": 0}
@bp.get("/examples/api/poll")
async def api_poll():
from shared.sx.helpers import sx_response
_poll_count["n"] += 1
now = datetime.now().strftime("%H:%M:%S")
count = min(_poll_count["n"], 10)
return sx_response(f'(~poll-result :time "{now}" :count {count})')
@bp.delete("/examples/api/delete/<item_id>")
async def api_delete(item_id: str):
# Return empty response — the row's outerHTML swap removes it
return Response("", status=200, content_type="text/sx")
@bp.get("/examples/api/edit")
async def api_edit_form():
from shared.sx.helpers import sx_response
value = request.args.get("value", "")
escaped = value.replace('"', '\\"')
return sx_response(f'(~inline-edit-form :value "{escaped}")')
@bp.post("/examples/api/edit")
async def api_edit_save():
from shared.sx.helpers import sx_response
form = await request.form
value = form.get("value", "")
escaped = value.replace('"', '\\"')
return sx_response(f'(~inline-view :value "{escaped}")')
@bp.get("/examples/api/edit/cancel")
async def api_edit_cancel():
from shared.sx.helpers import sx_response
value = request.args.get("value", "")
escaped = value.replace('"', '\\"')
return sx_response(f'(~inline-view :value "{escaped}")')
@bp.get("/examples/api/oob")
async def api_oob():
from shared.sx.helpers import sx_response
now = datetime.now().strftime("%H:%M:%S")
return sx_response(
f'(<>'
f' (p :class "text-emerald-600 font-medium" "Box A updated!")'
f' (p :class "text-sm text-stone-500" "at {now}")'
f' (div :id "oob-box-b" :sx-swap-oob "innerHTML"'
f' (p :class "text-violet-600 font-medium" "Box B updated via OOB!")'
f' (p :class "text-sm text-stone-500" "at {now}")))'
)
# ------------------------------------------------------------------
# Essays
# ------------------------------------------------------------------
@bp.get("/essays/")
async def essays_index():
from quart import redirect
return redirect("/essays/sx-sucks")
@bp.get("/essays/<slug>")
async def essay_page(slug: str):
if _is_sx_request():
from shared.sx.helpers import sx_response
from sxc.sx_components import essay_oob_sx
return sx_response(await essay_oob_sx(slug))
from shared.sx.page import get_template_context
from sxc.sx_components import render_essay_page_sx
ctx = await get_template_context()
html = await render_essay_page_sx(ctx, slug)
return await make_response(html, 200)
return bp