diff --git a/sx/app.py b/sx/app.py index bba5790..797a9ce 100644 --- a/sx/app.py +++ b/sx/app.py @@ -114,10 +114,37 @@ def create_app() -> "Quart": from shared.sx.pages import auto_mount_pages auto_mount_pages(app, "sx") + # --- GraphSX catch-all route: SX expression URLs --- + from sxc.pages.sx_router import eval_sx_url, redirect_old_url + + @app.before_request + async def sx_url_redirect(): + """Redirect old-style paths to SX expression URLs (301).""" + from quart import request, redirect as q_redirect + path = request.path + # Skip non-page paths + if path.startswith(("/static/", "/internal/", "/auth/", "/sx/")): + return None + # Skip SX expression URLs (already in new format) + if path.startswith("/(") or path.startswith("/~"): + return None + # Skip API/handler paths + if "/api/" in path: + return None + new_url = redirect_old_url(path) + if new_url: + qs = request.query_string.decode() + if qs: + new_url += "?" + qs + return q_redirect(new_url, 301) + @app.before_request async def trailing_slash_redirect(): from quart import request, redirect path = request.path + # Skip SX expression URLs — they don't use trailing slashes + if "(" in path or path.startswith("/~"): + return None if (path != "/" and not path.endswith("/") and request.method == "GET" @@ -128,6 +155,15 @@ def create_app() -> "Quart": target = path + "/" + ("?" + qs if qs else "") return redirect(target, 301) + @app.get("/") + async def sx_eval_route(expr): + """Catch-all: evaluate SX expression URLs.""" + result = await eval_sx_url(f"/{expr}") + if result is None: + from quart import abort + abort(404) + return result + @app.errorhandler(404) async def sx_not_found(e): from quart import request, make_response diff --git a/sx/sx/analyzer.sx b/sx/sx/analyzer.sx index 34a2e9b..e3a4595 100644 --- a/sx/sx/analyzer.sx +++ b/sx/sx/analyzer.sx @@ -12,7 +12,7 @@ "Each bar shows how many of the " (strong (str total-components)) " total components a page actually needs, computed by the " - (a :href "/language/specs/deps" :class "text-violet-700 underline" "deps.sx") + (a :href "/(language.(spec.deps))" :class "text-violet-700 underline" "deps.sx") " transitive closure algorithm. " "Click a page to see its component tree; expand a component to see its SX source.") diff --git a/sx/sx/cssx.sx b/sx/sx/cssx.sx index e2e6277..e40b264 100644 --- a/sx/sx/cssx.sx +++ b/sx/sx/cssx.sx @@ -373,7 +373,7 @@ "directly. They arrive as part of the rendered HTML — keyframes, custom properties, " "scoped rules, anything.") (li (strong "External stylesheets: ") "Components can reference pre-loaded CSS files " - "or lazy-load them via " (code "~suspense") " (see " (a :href "/applications/cssx/async" "Async CSS") ").") + "or lazy-load them via " (code "~suspense") " (see " (a :href "/(applications.(cssx.async))" "Async CSS") ").") (li (strong "Custom properties: ") "A " (code "~theme") " component sets " (code "--color-primary") " etc. via a " (code "