"""SX docs page routes. Page GET routes are defined declaratively in sxc/pages/docs.sx via defpage. Example API endpoints are now defined in sx/handlers/examples.sx via defhandler. This file contains only SSE and marsh demo endpoints that need Python. """ from __future__ import annotations import asyncio import random from datetime import datetime from quart import Blueprint, Response, request def register(url_prefix: str = "/") -> Blueprint: bp = Blueprint("pages", __name__, url_prefix=url_prefix) # ------------------------------------------------------------------ # Reference API endpoints — remaining Python-only # # Most reference endpoints migrated to sx/sx/handlers/ref-api.sx. # SSE stays in Python — fundamentally different paradigm (async generator). # ------------------------------------------------------------------ @bp.get("/sx/(geography.(hypermedia.(reference.(api.sse-time))))") async def ref_sse_time(): async def generate(): for _ in range(30): # stream for 60 seconds max now = datetime.now().strftime("%H:%M:%S") sx_src = f'(span :class "text-emerald-700 font-mono text-sm" "Server time: {now}")' yield f"event: time\ndata: {sx_src}\n\n" await asyncio.sleep(2) return Response(generate(), content_type="text/event-stream", headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}) # --- Marsh demos --- _marsh_sale_idx = {"n": 0} @bp.get("/sx/(geography.(reactive.(api.flash-sale)))") async def api_marsh_flash_sale(): from shared.sx.helpers import sx_response prices = [14.99, 9.99, 24.99, 12.49, 7.99, 29.99, 4.99, 16.50] _marsh_sale_idx["n"] = (_marsh_sale_idx["n"] + 1) % len(prices) new_price = prices[_marsh_sale_idx["n"]] now = datetime.now().strftime("%H:%M:%S") sx_src = ( f'(<>' f' (div :class "space-y-2"' f' (p :class "text-sm text-emerald-600 font-medium"' f' "\u26A1 Flash sale! Price: ${new_price:.2f}")' f' (p :class "text-xs text-stone-400" "at {now}"))' f' (script :type "text/sx" :data-init ""' f' "(reset! (use-store \\"demo-price\\") {new_price})"))' ) return sx_response(sx_src) # --- Demo 3: sx-on-settle endpoint --- _settle_counter = {"n": 0} @bp.get("/sx/(geography.(reactive.(api.settle-data)))") async def api_settle_data(): from shared.sx.helpers import sx_response _settle_counter["n"] += 1 items = ["Widget", "Gadget", "Sprocket", "Gizmo", "Doohickey"] item = items[_settle_counter["n"] % len(items)] now = datetime.now().strftime("%H:%M:%S") sx_src = ( f'(div :class "space-y-1"' f' (p :class "text-sm font-medium text-stone-700" "Fetched: {item}")' f' (p :class "text-xs text-stone-400" "at {now}"))' ) return sx_response(sx_src) # --- Demo 4: signal-bound URL endpoints --- @bp.get("/sx/(geography.(reactive.(api.search-products)))") async def api_search_products(): from shared.sx.helpers import sx_response q = request.args.get("q", "") items = ["Artisan Widget", "Premium Gadget", "Handcrafted Sprocket", "Bespoke Gizmo", "Organic Doohickey"] matches = [i for i in items if q.lower() in i.lower()] if q else items rows = " ".join( f'(li :class "text-sm text-stone-600" "{m}")' for m in matches[:3] ) sx_src = ( f'(div :class "space-y-1"' f' (p :class "text-xs font-semibold text-violet-600 uppercase" "Products")' f' (ul :class "list-disc pl-4" {rows})' f' (p :class "text-xs text-stone-400" "{len(matches)} result(s)"))' ) return sx_response(sx_src) @bp.get("/sx/(geography.(reactive.(api.search-events)))") async def api_search_events(): from shared.sx.helpers import sx_response q = request.args.get("q", "") items = ["Summer Workshop", "Craft Fair", "Open Studio", "Artist Talk", "Gallery Opening"] matches = [i for i in items if q.lower() in i.lower()] if q else items rows = " ".join( f'(li :class "text-sm text-stone-600" "{m}")' for m in matches[:3] ) sx_src = ( f'(div :class "space-y-1"' f' (p :class "text-xs font-semibold text-emerald-600 uppercase" "Events")' f' (ul :class "list-disc pl-4" {rows})' f' (p :class "text-xs text-stone-400" "{len(matches)} result(s)"))' ) return sx_response(sx_src) @bp.get("/sx/(geography.(reactive.(api.search-posts)))") async def api_search_posts(): from shared.sx.helpers import sx_response q = request.args.get("q", "") items = ["On Craft and Code", "The SX Manifesto", "Islands and Lakes", "Reactive Marshes", "Self-Hosting Spec"] matches = [i for i in items if q.lower() in i.lower()] if q else items rows = " ".join( f'(li :class "text-sm text-stone-600" "{m}")' for m in matches[:3] ) sx_src = ( f'(div :class "space-y-1"' f' (p :class "text-xs font-semibold text-amber-600 uppercase" "Posts")' f' (ul :class "list-disc pl-4" {rows})' f' (p :class "text-xs text-stone-400" "{len(matches)} result(s)"))' ) return sx_response(sx_src) # --- Demo 5: marsh transform endpoint --- @bp.get("/sx/(geography.(reactive.(api.catalog)))") async def api_catalog(): from shared.sx.helpers import sx_response items = [ ("Artisan Widget", "19.99", "Hand-crafted with care"), ("Premium Gadget", "34.50", "Top-of-the-line quality"), ("Vintage Sprocket", "12.99", "Classic design"), ("Custom Gizmo", "27.00", "Made to order"), ] random.shuffle(items) now = datetime.now().strftime("%H:%M:%S") # Build an SX list literal for the data-init script. # Inner quotes must be escaped since the whole expression lives # inside an SX string literal (the script tag's text content). items_sx = "(list " + " ".join( f'(dict \\"name\\" \\"{n}\\" \\"price\\" \\"{p}\\" \\"desc\\" \\"{d}\\")' for n, p, d in items ) + ")" sx_src = ( f'(<>' f' (p :class "text-sm text-emerald-600 font-medium"' f' "Catalog loaded: {len(items)} items (shuffled at {now})")' f' (script :type "text/sx" :data-init ""' f' "(reset! (use-store \\"catalog-items\\") {items_sx})"))' ) return sx_response(sx_src) return bp